diff --git a/BUILD.gn b/BUILD.gn
index a4625d7..5dbfd23a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -86,6 +86,8 @@
   if (!is_fuchsia) {
     deps += [
       "//components:components_unittests",
+      "//services:services_unittests",
+      "//services/service_manager/public/cpp",
       "//skia:skia_unittests",
       "//tools/metrics:metrics_metadata",
       "//ui/base:ui_base_unittests",
@@ -177,8 +179,6 @@
       "//mojo/edk/test:mojo_public_bindings_unittests",
       "//mojo/edk/test:mojo_public_system_unittests",
       "//net:net_perftests",
-      "//services:services_unittests",
-      "//services/service_manager/public/cpp",
       "//storage:storage_unittests",
       "//third_party/WebKit/Source/platform:blink_platform_unittests",
       "//third_party/WebKit/Source/platform/heap:blink_heap_unittests",
diff --git a/DEPS b/DEPS
index 6fd83f49..2c90aff 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '2bd381bffd36cdcffacf606d952547ce66fed7e9',
+  'skia_revision': '60fb0b2548c7f03b58fc52aa298b07ed137c84e3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'a361a94a9378ce4b8b38304ae9dbf43de3a8dbcd',
+  'v8_revision': '2d55c365fd5a5f837e3775126ac6e6836d3e2ca6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/android_webview/browser/aw_web_contents_view_delegate.cc b/android_webview/browser/aw_web_contents_view_delegate.cc
index ac878b8..7319b41 100644
--- a/android_webview/browser/aw_web_contents_view_delegate.cc
+++ b/android_webview/browser/aw_web_contents_view_delegate.cc
@@ -4,7 +4,6 @@
 
 #include "android_webview/browser/aw_web_contents_view_delegate.h"
 
-#include "content/public/browser/android/content_view_core.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/context_menu_params.h"
 
@@ -17,8 +16,7 @@
 }
 
 AwWebContentsViewDelegate::AwWebContentsViewDelegate(
-    content::WebContents* web_contents)
-    : web_contents_(web_contents) {
+    content::WebContents* web_contents) {
   // Cannot instantiate web_contents_view_delegate_ here because
   // AwContents::SetWebDelegate is not called yet.
 }
@@ -32,13 +30,4 @@
   return NULL;
 }
 
-void AwWebContentsViewDelegate::ShowContextMenu(
-    content::RenderFrameHost* render_frame_host,
-    const content::ContextMenuParams& params) {
-  content::ContentViewCore* content_view_core =
-      content::ContentViewCore::FromWebContents(web_contents_);
-  if (content_view_core)
-    content_view_core->ShowSelectionMenu(params);
-}
-
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_web_contents_view_delegate.h b/android_webview/browser/aw_web_contents_view_delegate.h
index 4ae7af4..8ef322c 100644
--- a/android_webview/browser/aw_web_contents_view_delegate.h
+++ b/android_webview/browser/aw_web_contents_view_delegate.h
@@ -24,16 +24,10 @@
 
   // content::WebContentsViewDelegate implementation.
   content::WebDragDestDelegate* GetDragDestDelegate() override;
-  void ShowContextMenu(content::RenderFrameHost* render_frame_host,
-                       const content::ContextMenuParams& params) override;
 
  private:
   AwWebContentsViewDelegate(content::WebContents* web_contents);
 
-  // Weak pointer due to ownership graph:
-  // WebContents->WebContentsView->this.
-  content::WebContents* web_contents_;
-
   DISALLOW_COPY_AND_ASSIGN(AwWebContentsViewDelegate);
 };
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index e508c8a..a2e5d00 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -3296,7 +3296,9 @@
 
         @Override
         public boolean onHoverEvent(MotionEvent event) {
-            return isDestroyedOrNoOperation(NO_WARN) ? false : mContentViewCore.onHoverEvent(event);
+            return isDestroyedOrNoOperation(NO_WARN)
+                    ? false
+                    : mWebContents.getEventForwarder().onHoverEvent(event);
         }
 
         @Override
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index 2e3d1b0..4d84acf4 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -134,7 +134,8 @@
   autorun_file.write('/system/' + os.path.basename(test_name))
   autorun_file.write(' --test-launcher-retry-limit=0')
   if test_launcher_filter_file:
-    test_launcher_filter_file = os.path.abspath(test_launcher_filter_file)
+    test_launcher_filter_file = os.path.normpath(
+            os.path.join(output_directory, test_launcher_filter_file))
     filter_file_on_device = MakeTargetImageName(
           common_prefix, output_directory, test_launcher_filter_file)
     autorun_file.write(' --test-launcher-filter-file=/system/' +
diff --git a/cc/layers/layer_position_constraint_unittest.cc b/cc/layers/layer_position_constraint_unittest.cc
index 952d9bf..89fc3d0 100644
--- a/cc/layers/layer_position_constraint_unittest.cc
+++ b/cc/layers/layer_position_constraint_unittest.cc
@@ -89,6 +89,7 @@
     // viewport scroll layer.
     root_ = Layer::Create();
     inner_viewport_container_layer_ = Layer::Create();
+    page_scale_layer_ = Layer::Create();
     scroll_layer_ = Layer::Create();
     outer_viewport_container_layer_ = Layer::Create();
     child_transform_layer_ = Layer::Create();
@@ -104,6 +105,8 @@
     SetLayerPropertiesForTesting(inner_viewport_container_layer_.get(),
                                  IdentityMatrix, transform_origin, position,
                                  clip_bounds, true);
+    SetLayerPropertiesForTesting(page_scale_layer_.get(), IdentityMatrix,
+                                 transform_origin, position, clip_bounds, true);
     SetLayerPropertiesForTesting(scroll_layer_.get(), IdentityMatrix,
                                  transform_origin, position, bounds, true);
     SetLayerPropertiesForTesting(outer_viewport_container_layer_.get(),
@@ -136,12 +139,13 @@
     child_transform_layer_->AddChild(child_);
     outer_viewport_container_layer_->AddChild(child_transform_layer_);
     scroll_layer_->AddChild(outer_viewport_container_layer_);
-    inner_viewport_container_layer_->AddChild(scroll_layer_);
+    page_scale_layer_->AddChild(scroll_layer_);
+    inner_viewport_container_layer_->AddChild(page_scale_layer_);
     root_->AddChild(inner_viewport_container_layer_);
 
     layer_tree_host_->SetRootLayer(root_);
     LayerTreeHost::ViewportLayers viewport_layers;
-    viewport_layers.page_scale = root_;
+    viewport_layers.page_scale = page_scale_layer_;
     viewport_layers.inner_viewport_container = inner_viewport_container_layer_;
     viewport_layers.outer_viewport_container = outer_viewport_container_layer_;
     viewport_layers.inner_viewport_scroll = scroll_layer_;
@@ -186,6 +190,7 @@
   std::unique_ptr<AnimationHost> animation_host_;
   std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
   scoped_refptr<Layer> root_;
+  scoped_refptr<Layer> page_scale_layer_;
   scoped_refptr<Layer> inner_viewport_container_layer_;
   scoped_refptr<Layer> scroll_layer_;
   scoped_refptr<Layer> outer_viewport_container_layer_;
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index b8e5fb8..07e579a 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -150,6 +150,9 @@
   ALWAYS_INLINE SkColorFilter* getColorFilter() const {
     return paint_.getColorFilter();
   }
+  ALWAYS_INLINE sk_sp<SkColorFilter> refColorFilter() const {
+    return paint_.refColorFilter();
+  }
   ALWAYS_INLINE void setColorFilter(sk_sp<SkColorFilter> filter) {
     paint_.setColorFilter(std::move(filter));
   }
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 86a6ecec..57dec2eb 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -910,6 +910,18 @@
 void LayerTreeHost::RegisterViewportLayers(const ViewportLayers& layers) {
   DCHECK(!layers.inner_viewport_scroll ||
          layers.inner_viewport_scroll != layers.outer_viewport_scroll);
+  // The page scale layer only affects the innter viewport scroll layer. The
+  // page scale layer should be sandwiched between the inner viewport scroll and
+  // inner viewport container layers:
+  //   inner viewport container
+  //     overscroll elasticity (optional)
+  //       page scale
+  //         inner viewport scroll
+  DCHECK(!layers.page_scale ||
+         layers.inner_viewport_scroll->parent() == layers.page_scale);
+  DCHECK(!layers.page_scale ||
+         layers.page_scale->parent() == layers.overscroll_elasticity ||
+         layers.page_scale->parent() == layers.inner_viewport_container);
   viewport_layers_.overscroll_elasticity = layers.overscroll_elasticity;
   viewport_layers_.page_scale = layers.page_scale;
   viewport_layers_.inner_viewport_container = layers.inner_viewport_container;
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 7f5565b..5b604c8 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -8218,13 +8218,15 @@
 
 TEST_F(LayerTreeHostCommonTest, NodesAffectedByViewportBoundsDeltaGetUpdated) {
   scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> page_scale_layer = Layer::Create();
   scoped_refptr<Layer> inner_viewport_container_layer = Layer::Create();
   scoped_refptr<Layer> inner_viewport_scroll_layer = Layer::Create();
   scoped_refptr<Layer> outer_viewport_container_layer = Layer::Create();
   scoped_refptr<Layer> outer_viewport_scroll_layer = Layer::Create();
 
   root->AddChild(inner_viewport_container_layer);
-  inner_viewport_container_layer->AddChild(inner_viewport_scroll_layer);
+  inner_viewport_container_layer->AddChild(page_scale_layer);
+  page_scale_layer->AddChild(inner_viewport_scroll_layer);
   inner_viewport_scroll_layer->AddChild(outer_viewport_container_layer);
   outer_viewport_container_layer->AddChild(outer_viewport_scroll_layer);
 
@@ -8238,7 +8240,7 @@
 
   host()->SetRootLayer(root);
   LayerTreeHost::ViewportLayers viewport_layers;
-  viewport_layers.page_scale = root;
+  viewport_layers.page_scale = page_scale_layer;
   viewport_layers.inner_viewport_container = inner_viewport_container_layer;
   viewport_layers.outer_viewport_container = outer_viewport_container_layer;
   viewport_layers.inner_viewport_scroll = inner_viewport_scroll_layer;
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 4076f147..acffb0a 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -7328,37 +7328,36 @@
     // -root
     //   -pre page scale
     //   -page scale
-    //     -page scale child1
+    //     -inner viewport scroll
     //       -page scale grandchild
-    //     -page scale child2
     //   -post page scale
 
     scoped_refptr<Layer> root = Layer::Create();
     scoped_refptr<Layer> pre_page_scale = Layer::Create();
     scoped_refptr<Layer> page_scale = Layer::Create();
-    scoped_refptr<Layer> page_scale_child1 = Layer::Create();
+    scoped_refptr<Layer> inner_viewport_scroll = Layer::Create();
     scoped_refptr<Layer> page_scale_grandchild = Layer::Create();
-    scoped_refptr<Layer> page_scale_child2 = Layer::Create();
     scoped_refptr<Layer> post_page_scale = Layer::Create();
 
     root->AddChild(pre_page_scale);
     root->AddChild(page_scale);
     root->AddChild(post_page_scale);
 
-    page_scale->AddChild(page_scale_child1);
-    page_scale->AddChild(page_scale_child2);
-    page_scale_child1->AddChild(page_scale_grandchild);
+    page_scale->AddChild(inner_viewport_scroll);
+    inner_viewport_scroll->AddChild(page_scale_grandchild);
+    inner_viewport_scroll->SetIsContainerForFixedPositionLayers(true);
 
     layer_tree_host()->SetRootLayer(root);
     LayerTreeTest::SetupTree();
 
     LayerTreeHost::ViewportLayers viewport_layers;
+    viewport_layers.inner_viewport_container = root;
     viewport_layers.page_scale = page_scale;
+    viewport_layers.inner_viewport_scroll = inner_viewport_scroll;
     layer_tree_host()->RegisterViewportLayers(viewport_layers);
 
     affected_by_page_scale_.push_back(page_scale->id());
-    affected_by_page_scale_.push_back(page_scale_child1->id());
-    affected_by_page_scale_.push_back(page_scale_child2->id());
+    affected_by_page_scale_.push_back(inner_viewport_scroll->id());
     affected_by_page_scale_.push_back(page_scale_grandchild->id());
 
     not_affected_by_page_scale_.push_back(root->id());
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 5a45e96..490a055 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -1046,25 +1046,22 @@
     node.scrollable = scrollable;
     node.main_thread_scrolling_reasons = main_thread_scrolling_reasons;
     node.non_fast_scrollable_region = layer->non_fast_scrollable_region();
-    gfx::Size clip_bounds;
-    if (LayerType* scroll_clip_layer = layer->scroll_clip_layer()) {
-      SetIsScrollClipLayer(scroll_clip_layer);
-      clip_bounds = scroll_clip_layer->bounds();
-      DCHECK(scroll_clip_layer->transform_tree_index() !=
-             TransformTree::kInvalidNodeId);
-      node.max_scroll_offset_affected_by_page_scale =
-          !data_from_ancestor.property_trees->transform_tree
-               .Node(scroll_clip_layer->transform_tree_index())
-               ->in_subtree_of_page_scale_layer &&
-          data_from_ancestor.in_subtree_of_page_scale_layer;
-    }
 
-    node.scroll_clip_layer_bounds = clip_bounds;
     node.scrolls_inner_viewport =
         layer == data_from_ancestor.inner_viewport_scroll_layer;
     node.scrolls_outer_viewport =
         layer == data_from_ancestor.outer_viewport_scroll_layer;
 
+    if (node.scrolls_inner_viewport &&
+        data_from_ancestor.in_subtree_of_page_scale_layer) {
+      node.max_scroll_offset_affected_by_page_scale = true;
+    }
+
+    if (LayerType* scroll_clip_layer = layer->scroll_clip_layer()) {
+      SetIsScrollClipLayer(scroll_clip_layer);
+      node.scroll_clip_layer_bounds = scroll_clip_layer->bounds();
+    }
+
     node.bounds = layer->bounds();
     node.offset_to_transform_parent = layer->offset_to_transform_parent();
     node.should_flatten = layer->should_flatten_transform_from_property_tree();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
index a181dc00..9f95186 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ChromeDownloadDelegate.java
@@ -76,7 +76,7 @@
                 // Check to see if we have an SDCard.
                 String status = Environment.getExternalStorageState();
                 File fullDirPath = getDownloadDirectoryFullPath();
-                return new Pair(status, fullDirPath);
+                return new Pair<String, File>(status, fullDirPath);
             }
 
             @Override
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d4442ba7..a69b5e8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1425,6 +1425,8 @@
     "web_data_service_factory.h",
     "webshare/share_target_pref_helper.cc",
     "webshare/share_target_pref_helper.h",
+    "webshare/webshare_target.cc",
+    "webshare/webshare_target.h",
     "win/app_icon.cc",
     "win/app_icon.h",
     "win/browser_util.cc",
@@ -1578,6 +1580,7 @@
     "//components/rappor:rappor_recorder",
     "//components/renderer_context_menu",
     "//components/resources",
+    "//components/safe_browsing/common:interfaces",
     "//components/safe_json",
     "//components/search",
     "//components/search_engines",
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index f762d24..357898da 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -9,11 +9,11 @@
           "autofill::mojom::PasswordManagerDriver",
           "chrome::mojom::CacheStatsRecorder",
           "chrome::mojom::NetBenchmarking",
-          "chrome::mojom::SafeBrowsing",
           "extensions::StashService",
           "metrics::mojom::LeakDetector",
           "mojom::ModuleEventSink",
           "rappor::mojom::RapporRecorder",
+          "safe_browsing::mojom::SafeBrowsing",
           "spellcheck::mojom::SpellCheckHost",
           "spellcheck::mojom::SpellCheckPanelHost",
           "startup_metric_utils::mojom::StartupMetricHost",
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index b01f0aa..520be4f 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -451,6 +451,7 @@
     int renderer_id,
     int render_frame_id,
     const content::PushSubscriptionOptions& options,
+    bool user_gesture,
     const RegisterCallback& callback) {
   PushMessagingAppIdentifier app_identifier =
       PushMessagingAppIdentifier::Generate(requesting_origin,
@@ -482,7 +483,7 @@
   // Push does not allow permission requests from iframes.
   PermissionManager::Get(profile_)->RequestPermission(
       CONTENT_SETTINGS_TYPE_PUSH_MESSAGING, web_contents->GetMainFrame(),
-      requesting_origin, true /* user_gesture */,
+      requesting_origin, user_gesture,
       base::Bind(&PushMessagingServiceImpl::DoSubscribe,
                  weak_factory_.GetWeakPtr(), app_identifier, options,
                  callback));
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index 2db809e..b195d0db 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -81,6 +81,7 @@
                              int renderer_id,
                              int render_frame_id,
                              const content::PushSubscriptionOptions& options,
+                             bool user_gesture,
                              const RegisterCallback& callback) override;
   void SubscribeFromWorker(const GURL& requesting_origin,
                            int64_t service_worker_registration_id,
diff --git a/chrome/browser/resources/md_bookmarks/command_manager.js b/chrome/browser/resources/md_bookmarks/command_manager.js
index e50b808..f4cb394 100644
--- a/chrome/browser/resources/md_bookmarks/command_manager.js
+++ b/chrome/browser/resources/md_bookmarks/command_manager.js
@@ -86,12 +86,14 @@
 
     /**
      * Display the command context menu at (|x|, |y|) in window co-ordinates.
-     * Commands will execute on the currently selected items.
+     * Commands will execute on |items| if given, or on the currently selected
+     * items.
      * @param {number} x
      * @param {number} y
+     * @param {Set<string>=} items
      */
-    openCommandMenuAtPosition: function(x, y) {
-      this.menuIds_ = this.getState().selection.items;
+    openCommandMenuAtPosition: function(x, y, items) {
+      this.menuIds_ = items || this.getState().selection.items;
       /** @type {!CrActionMenuElement} */ (this.$.dropdown)
           .showAtPosition({top: y, left: x});
     },
diff --git a/chrome/browser/resources/md_bookmarks/folder_node.html b/chrome/browser/resources/md_bookmarks/folder_node.html
index e0039ace..deb4b1c 100644
--- a/chrome/browser/resources/md_bookmarks/folder_node.html
+++ b/chrome/browser/resources/md_bookmarks/folder_node.html
@@ -84,6 +84,7 @@
         class="v-centered"
         draggable="true"
         on-tap="selectFolder_"
+        on-contextmenu="onContextMenu_"
         tabindex$="[[getTabIndex_(isSelectedFolder_)]]"
         hidden="[[isRootFolder_(depth)]]">
       <template is="dom-if" if="[[hasChildFolder_(item_.children)]]">
diff --git a/chrome/browser/resources/md_bookmarks/folder_node.js b/chrome/browser/resources/md_bookmarks/folder_node.js
index 77aa4375..c6885ce 100644
--- a/chrome/browser/resources/md_bookmarks/folder_node.js
+++ b/chrome/browser/resources/md_bookmarks/folder_node.js
@@ -259,8 +259,21 @@
 
   /** @private */
   selectFolder_: function() {
-    this.dispatch(
-        bookmarks.actions.selectFolder(this.item_.id, this.getState().nodes));
+    if (!this.isSelectedFolder_) {
+      this.dispatch(
+          bookmarks.actions.selectFolder(this.itemId, this.getState().nodes));
+    }
+  },
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onContextMenu_: function(e) {
+    e.preventDefault();
+    this.selectFolder_();
+    bookmarks.CommandManager.getInstance().openCommandMenuAtPosition(
+        e.clientX, e.clientY, new Set([this.itemId]));
   },
 
   /**
@@ -278,7 +291,7 @@
    */
   toggleFolder_: function(e) {
     this.dispatch(
-        bookmarks.actions.changeFolderOpen(this.item_.id, this.isClosed_));
+        bookmarks.actions.changeFolderOpen(this.itemId, this.isClosed_));
     e.stopPropagation();
   },
 
diff --git a/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html b/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html
index 3532bf06..3f97ff6 100644
--- a/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html
+++ b/chrome/browser/resources/settings/certificate_manager_page/certificate_manager_page.html
@@ -22,11 +22,18 @@
       }
 
       paper-tabs {
+        font-size: inherit;
         height: 40px;
         margin-bottom: 24px;
       }
 
       paper-tab {
+        --paper-tab-content: {
+          color: var(--paper-grey-800);
+        };
+        --paper-tab-content-unselected: {
+          color: var(--paper-grey-600);
+        };
         text-transform: uppercase;
       }
     </style>
diff --git a/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc
index 08d61ca..8aed827 100644
--- a/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc
+++ b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc
@@ -44,7 +44,7 @@
 // !database_manager()->IsSupported().
 // TODO(yzshen): Make sure it also works on Andorid.
 // TODO(yzshen): Do all the logging like what BaseResourceThrottle does.
-class SafeBrowsingUrlCheckerImpl : public chrome::mojom::SafeBrowsingUrlChecker,
+class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
                                    public SafeBrowsingDatabaseManager::Client {
  public:
   SafeBrowsingUrlCheckerImpl(
@@ -75,7 +75,7 @@
       std::move(callbacks_[i]).Run(false);
   }
 
-  // chrome::mojom::SafeBrowsingUrlChecker implementation.
+  // mojom::SafeBrowsingUrlChecker implementation.
   void CheckUrl(const GURL& url, CheckUrlCallback callback) override {
     DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
@@ -278,7 +278,7 @@
     scoped_refptr<SafeBrowsingUIManager> ui_manager,
     int render_process_id,
     const service_manager::BindSourceInfo& source_info,
-    chrome::mojom::SafeBrowsingRequest request) {
+    mojom::SafeBrowsingRequest request) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   mojo::MakeStrongBinding(base::MakeUnique<MojoSafeBrowsingImpl>(
                               std::move(database_manager),
@@ -288,7 +288,7 @@
 
 void MojoSafeBrowsingImpl::CreateCheckerAndCheck(
     int32_t render_frame_id,
-    chrome::mojom::SafeBrowsingUrlCheckerRequest request,
+    mojom::SafeBrowsingUrlCheckerRequest request,
     const GURL& url,
     int32_t load_flags,
     content::ResourceType resource_type,
diff --git a/chrome/browser/safe_browsing/mojo_safe_browsing_impl.h b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.h
index d7b8e11d8..606c950 100644
--- a/chrome/browser/safe_browsing/mojo_safe_browsing_impl.h
+++ b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
-#include "chrome/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "ipc/ipc_message.h"
 
@@ -20,7 +20,7 @@
 
 // This class implements the Mojo interface for renderers to perform
 // SafeBrowsing URL checks.
-class MojoSafeBrowsingImpl : public chrome::mojom::SafeBrowsing {
+class MojoSafeBrowsingImpl : public mojom::SafeBrowsing {
  public:
   MojoSafeBrowsingImpl(
       scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
@@ -33,17 +33,16 @@
       scoped_refptr<SafeBrowsingUIManager> ui_manager,
       int render_process_id,
       const service_manager::BindSourceInfo& source_info,
-      chrome::mojom::SafeBrowsingRequest request);
+      mojom::SafeBrowsingRequest request);
 
  private:
-  // chrome::mojom::SafeBrowsing implementation.
-  void CreateCheckerAndCheck(
-      int32_t render_frame_id,
-      chrome::mojom::SafeBrowsingUrlCheckerRequest request,
-      const GURL& url,
-      int32_t load_flags,
-      content::ResourceType resource_type,
-      CreateCheckerAndCheckCallback callback) override;
+  // mojom::SafeBrowsing implementation.
+  void CreateCheckerAndCheck(int32_t render_frame_id,
+                             mojom::SafeBrowsingUrlCheckerRequest request,
+                             const GURL& url,
+                             int32_t load_flags,
+                             content::ResourceType resource_type,
+                             CreateCheckerAndCheckCallback callback) override;
 
   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
   scoped_refptr<SafeBrowsingUIManager> ui_manager_;
diff --git a/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc b/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc
index 2f525e05..2fa7fe9 100644
--- a/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc
+++ b/chrome/browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android.cc
@@ -6,7 +6,6 @@
 
 #include "base/logging.h"
 #include "chrome/browser/ui/android/context_menu_helper.h"
-#include "content/public/browser/android/content_view_core.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view_delegate.h"
 #include "content/public/common/context_menu_params.h"
@@ -30,11 +29,6 @@
 void ChromeWebContentsViewDelegateAndroid::ShowContextMenu(
     content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
-  content::ContentViewCore* content_view_core =
-      content::ContentViewCore::FromWebContents(web_contents_);
-  if (content_view_core && content_view_core->ShowSelectionMenu(params))
-    return;
-
   // TODO(dtrainor, kouhei): Give WebView a Populator/delegate so it can use
   // the same context menu code.
   ContextMenuHelper* helper =
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index 2ab0399..98ead91 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -22,9 +22,9 @@
 #endif  // OS_CHROMEOS
 
 class Browser;
-class GURL;
 class LoginHandler;
 class Profile;
+class WebShareTarget;
 struct WebApplicationInfo;
 
 namespace content {
@@ -144,19 +144,18 @@
 payments::PaymentRequestDialog* CreatePaymentRequestDialog(
     payments::PaymentRequest* request);
 
-// Used to return the target the user picked or nullopt if the user cancelled
+// Used to return the target the user picked or nullptr if the user cancelled
 // the share.
 using WebShareTargetPickerCallback =
-    base::OnceCallback<void(const base::Optional<std::string>&)>;
+    base::OnceCallback<void(const WebShareTarget*)>;
 
 // Shows the dialog to choose a share target app. |targets| is a list of app
 // title and manifest URL pairs that will be shown in a list. If the user picks
 // a target, this calls |callback| with the manifest URL of the chosen target,
 // or supplies null if the user cancelled the share.
-void ShowWebShareTargetPickerDialog(
-    gfx::NativeWindow parent_window,
-    const std::vector<std::pair<base::string16, GURL>>& targets,
-    WebShareTargetPickerCallback callback);
+void ShowWebShareTargetPickerDialog(gfx::NativeWindow parent_window,
+                                    std::vector<WebShareTarget> targets,
+                                    WebShareTargetPickerCallback callback);
 
 #endif  // TOOLKIT_VIEWS
 
diff --git a/chrome/browser/ui/views/webshare/webshare_target_picker_view.cc b/chrome/browser/ui/views/webshare/webshare_target_picker_view.cc
index b793422..c1c8d66 100644
--- a/chrome/browser/ui/views/webshare/webshare_target_picker_view.cc
+++ b/chrome/browser/ui/views/webshare/webshare_target_picker_view.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/webshare/webshare_target_picker_view.h"
 
+#include <utility>
+
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
@@ -17,7 +19,6 @@
 #include "ui/views/controls/table/table_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/window/dialog_client_view.h"
-#include "url/gurl.h"
 
 namespace {
 
@@ -29,8 +30,7 @@
 // Supplies data to the table view.
 class TargetPickerTableModel : public ui::TableModel {
  public:
-  explicit TargetPickerTableModel(
-      const std::vector<std::pair<base::string16, GURL>>* targets);
+  explicit TargetPickerTableModel(const std::vector<WebShareTarget>& targets);
 
  private:
   // ui::TableModel overrides:
@@ -39,24 +39,24 @@
   void SetObserver(ui::TableModelObserver* observer) override;
 
   // Owned by WebShareTargetPickerView.
-  const std::vector<std::pair<base::string16, GURL>>* targets_;
+  const std::vector<WebShareTarget>& targets_;
 
   DISALLOW_COPY_AND_ASSIGN(TargetPickerTableModel);
 };
 
 TargetPickerTableModel::TargetPickerTableModel(
-    const std::vector<std::pair<base::string16, GURL>>* targets)
+    const std::vector<WebShareTarget>& targets)
     : targets_(targets) {}
 
 int TargetPickerTableModel::RowCount() {
-  return targets_->size();
+  return targets_.size();
 }
 
 base::string16 TargetPickerTableModel::GetText(int row, int /*column_id*/) {
   // Show "title (origin)", to disambiguate titles that are the same, and as a
   // security measure.
-  return (*targets_)[row].first +
-         base::UTF8ToUTF16(" (" + (*targets_)[row].second.GetOrigin().spec() +
+  return base::UTF8ToUTF16(targets_[row].name() + " (" +
+                           targets_[row].manifest_url().GetOrigin().spec() +
                            ")");
 }
 
@@ -66,20 +66,21 @@
 
 void ShowWebShareTargetPickerDialog(
     gfx::NativeWindow parent_window,
-    const std::vector<std::pair<base::string16, GURL>>& targets,
+    std::vector<WebShareTarget> targets,
     chrome::WebShareTargetPickerCallback callback) {
   constrained_window::CreateBrowserModalDialogViews(
-      new WebShareTargetPickerView(targets, std::move(callback)), parent_window)
+      new WebShareTargetPickerView(std::move(targets), std::move(callback)),
+      parent_window)
       ->Show();
 }
 
 }  // namespace chrome
 
 WebShareTargetPickerView::WebShareTargetPickerView(
-    const std::vector<std::pair<base::string16, GURL>>& targets,
+    std::vector<WebShareTarget> targets,
     chrome::WebShareTargetPickerCallback close_callback)
-    : targets_(targets),
-      table_model_(base::MakeUnique<TargetPickerTableModel>(&targets_)),
+    : targets_(std::move(targets)),
+      table_model_(base::MakeUnique<TargetPickerTableModel>(targets_)),
       close_callback_(std::move(close_callback)) {
   const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
   views::BoxLayout* layout = new views::BoxLayout(
@@ -132,7 +133,7 @@
 
 bool WebShareTargetPickerView::Cancel() {
   if (!close_callback_.is_null())
-    std::move(close_callback_).Run(base::nullopt);
+    std::move(close_callback_).Run(nullptr);
 
   return true;
 }
@@ -140,8 +141,7 @@
 bool WebShareTargetPickerView::Accept() {
   if (!close_callback_.is_null()) {
     DCHECK(!table_->selection_model().empty());
-    std::move(close_callback_)
-        .Run(targets_[table_->FirstSelectedRow()].second.spec());
+    std::move(close_callback_).Run(&targets_[table_->FirstSelectedRow()]);
   }
 
   return true;
diff --git a/chrome/browser/ui/views/webshare/webshare_target_picker_view.h b/chrome/browser/ui/views/webshare/webshare_target_picker_view.h
index cc90df6..9906c8d 100644
--- a/chrome/browser/ui/views/webshare/webshare_target_picker_view.h
+++ b/chrome/browser/ui/views/webshare/webshare_target_picker_view.h
@@ -5,17 +5,18 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_WEBSHARE_WEBSHARE_TARGET_PICKER_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_WEBSHARE_WEBSHARE_TARGET_PICKER_VIEW_H_
 
+#include <memory>
 #include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/webshare/webshare_target.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/views/controls/table/table_view_observer.h"
 #include "ui/views/window/dialog_delegate.h"
 
-class GURL;
 class TargetPickerTableModel;
 class WebShareTargetPickerViewTest;
 
@@ -36,9 +37,8 @@
   // in a list. If the user picks a target, this calls |callback| with the
   // manifest URL of the chosen target, or returns null if the user cancelled
   // the share.
-  WebShareTargetPickerView(
-      const std::vector<std::pair<base::string16, GURL>>& targets,
-      chrome::WebShareTargetPickerCallback close_callback);
+  WebShareTargetPickerView(std::vector<WebShareTarget> targets,
+                           chrome::WebShareTargetPickerCallback close_callback);
   ~WebShareTargetPickerView() override;
 
   // views::View overrides:
@@ -64,7 +64,7 @@
 
   views::TableView* table_ = nullptr;
 
-  const std::vector<std::pair<base::string16, GURL>> targets_;
+  const std::vector<WebShareTarget> targets_;
   std::unique_ptr<TargetPickerTableModel> table_model_;
 
   chrome::WebShareTargetPickerCallback close_callback_;
diff --git a/chrome/browser/ui/views/webshare/webshare_target_picker_view_unittest.cc b/chrome/browser/ui/views/webshare/webshare_target_picker_view_unittest.cc
index cb598db1..b6adb488 100644
--- a/chrome/browser/ui/views/webshare/webshare_target_picker_view_unittest.cc
+++ b/chrome/browser/ui/views/webshare/webshare_target_picker_view_unittest.cc
@@ -4,12 +4,13 @@
 
 #include "chrome/browser/ui/views/webshare/webshare_target_picker_view.h"
 
+#include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
@@ -25,6 +26,12 @@
 #include "ui/views/window/dialog_delegate.h"
 #include "url/gurl.h"
 
+constexpr char kAppName1[] = "App One";
+constexpr char kAppName2[] = "App Two";
+constexpr char kTemplate[] = "share?title={title}";
+constexpr char kUrl1[] = "https://appone.com/path/bits";
+constexpr char kUrl2[] = "https://apptwo.xyz";
+
 class WebShareTargetPickerViewTest : public views::ViewsTestBase {
  public:
   WebShareTargetPickerViewTest() {}
@@ -59,10 +66,11 @@
 
  protected:
   // Creates the WebShareTargetPickerView (available as view()).
-  void CreateView(const std::vector<std::pair<base::string16, GURL>>& targets) {
+  void CreateView(std::vector<WebShareTarget> targets) {
     view_ = new WebShareTargetPickerView(
-        targets, base::BindOnce(&WebShareTargetPickerViewTest::OnCallback,
-                                base::Unretained(this)));
+        std::move(targets),
+        base::BindOnce(&WebShareTargetPickerViewTest::OnCallback,
+                       base::Unretained(this)));
     constrained_window::CreateBrowserModalDialogViews(
         view_, parent_widget_->GetNativeWindow())
         ->Show();
@@ -80,10 +88,10 @@
   views::TableView* table() { return view_->table_; }
 
   // The result that was returned to the dialog's callback.
-  const base::Optional<std::string>& result() { return result_; }
+  const WebShareTarget* result() { return result_; }
 
  private:
-  void OnCallback(const base::Optional<std::string>& result) {
+  void OnCallback(const WebShareTarget* result) {
     result_ = result;
     if (quit_closure_)
       quit_closure_.Run();
@@ -92,7 +100,7 @@
   std::unique_ptr<views::Widget> parent_widget_;
   WebShareTargetPickerView* view_ = nullptr;
 
-  base::Optional<std::string> result_;
+  const WebShareTarget* result_;
 
   base::Closure quit_closure_;
 
@@ -101,7 +109,7 @@
 
 // Table with 0 targets. Choose to cancel.
 TEST_F(WebShareTargetPickerViewTest, EmptyListCancel) {
-  CreateView(std::vector<std::pair<base::string16, GURL>>());
+  CreateView(std::vector<WebShareTarget>());
   EXPECT_EQ(0, table()->RowCount());
   EXPECT_EQ(-1, table()->FirstSelectedRow());  // Nothing selected.
   EXPECT_FALSE(view()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
@@ -113,17 +121,16 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(base::nullopt, result());
+  EXPECT_EQ(nullptr, result());
 }
 
 // Table with 2 targets. Choose second target and share.
 TEST_F(WebShareTargetPickerViewTest, ChooseItem) {
-  std::vector<std::pair<base::string16, GURL>> targets{
-      std::make_pair(base::ASCIIToUTF16("App One"),
-                     GURL("https://appone.com/path/bits")),
-      std::make_pair(base::ASCIIToUTF16("App Two"),
-                     GURL("https://apptwo.xyz"))};
-  CreateView(targets);
+  std::vector<WebShareTarget> targets;
+  targets.emplace_back(GURL(kUrl1), kAppName1, kTemplate);
+  targets.emplace_back(GURL(kUrl2), kAppName2, kTemplate);
+
+  CreateView(std::move(targets));
   EXPECT_EQ(2, table()->RowCount());
   EXPECT_EQ(base::ASCIIToUTF16("App One (https://appone.com/)"),
             table()->model()->GetText(0, 0));
@@ -147,14 +154,15 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(base::Optional<std::string>("https://apptwo.xyz/"), result());
+  EXPECT_EQ(WebShareTarget(GURL(kUrl2), kAppName2, kTemplate), *result());
 }
 
 // Table with 1 target. Select using double-click.
 TEST_F(WebShareTargetPickerViewTest, ChooseItemWithDoubleClick) {
-  std::vector<std::pair<base::string16, GURL>> targets{std::make_pair(
-      base::ASCIIToUTF16("App One"), GURL("https://appone.com/path/bits"))};
-  CreateView(targets);
+  std::vector<WebShareTarget> targets;
+  targets.emplace_back(GURL(kUrl1), kAppName1, kTemplate);
+
+  CreateView(std::move(targets));
   EXPECT_EQ(1, table()->RowCount());
   EXPECT_EQ(base::ASCIIToUTF16("App One (https://appone.com/)"),
             table()->model()->GetText(0, 0));
@@ -168,6 +176,30 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(base::Optional<std::string>("https://appone.com/path/bits"),
-            result());
+  EXPECT_EQ(WebShareTarget(GURL(kUrl1), kAppName1, kTemplate), *result());
+}
+
+// Table with 1 target. Select, share and GetText.
+TEST_F(WebShareTargetPickerViewTest, GetTextAfterAccept) {
+  std::vector<WebShareTarget> targets;
+  targets.emplace_back(GURL(kUrl1), kAppName1, kTemplate);
+
+  CreateView(std::move(targets));
+  EXPECT_EQ(1, table()->RowCount());
+  EXPECT_EQ(base::ASCIIToUTF16("App One (https://appone.com/)"),
+            table()->model()->GetText(0, 0));
+  EXPECT_EQ(0, table()->FirstSelectedRow());
+  EXPECT_TRUE(view()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
+
+  base::RunLoop run_loop;
+  SetQuitClosure(run_loop.QuitClosure());
+
+  view()->Accept();
+
+  run_loop.Run();
+
+  EXPECT_EQ(base::ASCIIToUTF16("App One (https://appone.com/)"),
+            table()->model()->GetText(0, 0));
+
+  EXPECT_EQ(WebShareTarget(GURL(kUrl1), kAppName1, kTemplate), *result());
 }
diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc
index 5af18330..0f8973f7 100644
--- a/chrome/browser/webshare/share_service_impl.cc
+++ b/chrome/browser/webshare/share_service_impl.cc
@@ -6,10 +6,10 @@
 
 #include <algorithm>
 #include <functional>
+#include <map>
 #include <utility>
 
 #include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/webshare/webshare_target.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -105,11 +106,11 @@
 }
 
 void ShareServiceImpl::ShowPickerDialog(
-    const std::vector<std::pair<base::string16, GURL>>& targets,
+    std::vector<WebShareTarget> targets,
     chrome::WebShareTargetPickerCallback callback) {
   // TODO(mgiuca): Get the browser window as |parent_window|.
-  chrome::ShowWebShareTargetPickerDialog(nullptr /* parent_window */, targets,
-                                         std::move(callback));
+  chrome::ShowWebShareTargetPickerDialog(
+      nullptr /* parent_window */, std::move(targets), std::move(callback));
 }
 
 Browser* ShareServiceImpl::GetBrowser() {
@@ -122,18 +123,6 @@
                    browser->tab_strip_model()->active_index() + 1, true);
 }
 
-std::string ShareServiceImpl::GetTargetTemplate(
-    const std::string& target_url,
-    const base::DictionaryValue& share_targets) {
-  const base::DictionaryValue* share_target_info_dict = nullptr;
-  share_targets.GetDictionaryWithoutPathExpansion(target_url,
-                                                  &share_target_info_dict);
-
-  std::string url_template;
-  share_target_info_dict->GetString("url_template", &url_template);
-  return url_template;
-}
-
 PrefService* ShareServiceImpl::GetPrefService() {
   return GetBrowser()->profile()->GetPrefs();
 }
@@ -145,29 +134,36 @@
   return site_engagement_service->GetEngagementLevel(url);
 }
 
-// static
-std::vector<std::pair<base::string16, GURL>>
-ShareServiceImpl::GetTargetsWithSufficientEngagement(
-    const base::DictionaryValue& share_targets) {
+std::vector<WebShareTarget>
+ShareServiceImpl::GetTargetsWithSufficientEngagement() {
   constexpr blink::mojom::EngagementLevel kMinimumEngagementLevel =
       blink::mojom::EngagementLevel::LOW;
 
-  std::vector<std::pair<base::string16, GURL>> sufficiently_engaged_targets;
+  PrefService* pref_service = GetPrefService();
 
-  for (base::DictionaryValue::Iterator it(share_targets); !it.IsAtEnd();
-       it.Advance()) {
-    GURL manifest_url(it.key());
-    if (GetEngagementLevel(manifest_url) >= kMinimumEngagementLevel) {
-      const base::DictionaryValue* share_target_dict;
-      bool result = it.value().GetAsDictionary(&share_target_dict);
-      DCHECK(result);
+  std::unique_ptr<base::DictionaryValue> share_targets_dict =
+      pref_service->GetDictionary(prefs::kWebShareVisitedTargets)
+          ->CreateDeepCopy();
 
-      std::string name;
-      share_target_dict->GetString("name", &name);
+  std::vector<WebShareTarget> sufficiently_engaged_targets;
+  for (const auto& it : *share_targets_dict) {
+    GURL manifest_url(it.first);
+    DCHECK(manifest_url.is_valid());
 
-      sufficiently_engaged_targets.push_back(
-          make_pair(base::UTF8ToUTF16(name), manifest_url));
-    }
+    if (GetEngagementLevel(manifest_url) < kMinimumEngagementLevel)
+      continue;
+
+    const base::DictionaryValue* share_target_dict;
+    bool result = it.second->GetAsDictionary(&share_target_dict);
+    DCHECK(result);
+
+    std::string name;
+    share_target_dict->GetString("name", &name);
+    std::string url_template;
+    share_target_dict->GetString("url_template", &url_template);
+
+    sufficiently_engaged_targets.emplace_back(
+        std::move(manifest_url), std::move(name), std::move(url_template));
   }
 
   return sufficiently_engaged_targets;
@@ -177,39 +173,29 @@
                              const std::string& text,
                              const GURL& share_url,
                              ShareCallback callback) {
-  std::unique_ptr<base::DictionaryValue> share_targets;
+  std::vector<WebShareTarget> sufficiently_engaged_targets =
+      GetTargetsWithSufficientEngagement();
 
-  share_targets = GetPrefService()
-                      ->GetDictionary(prefs::kWebShareVisitedTargets)
-                      ->CreateDeepCopy();
-
-  std::vector<std::pair<base::string16, GURL>> sufficiently_engaged_targets =
-      GetTargetsWithSufficientEngagement(*share_targets);
-
-  ShowPickerDialog(
-      sufficiently_engaged_targets,
-      base::BindOnce(&ShareServiceImpl::OnPickerClosed,
-                     weak_factory_.GetWeakPtr(), base::Passed(&share_targets),
-                     title, text, share_url, std::move(callback)));
+  ShowPickerDialog(std::move(sufficiently_engaged_targets),
+                   base::BindOnce(&ShareServiceImpl::OnPickerClosed,
+                                  weak_factory_.GetWeakPtr(), title, text,
+                                  share_url, std::move(callback)));
 }
 
-void ShareServiceImpl::OnPickerClosed(
-    std::unique_ptr<base::DictionaryValue> share_targets,
-    const std::string& title,
-    const std::string& text,
-    const GURL& share_url,
-    ShareCallback callback,
-    const base::Optional<std::string>& result) {
-  if (!result.has_value()) {
+void ShareServiceImpl::OnPickerClosed(const std::string& title,
+                                      const std::string& text,
+                                      const GURL& share_url,
+                                      ShareCallback callback,
+                                      const WebShareTarget* result) {
+  if (result == nullptr) {
     std::move(callback).Run(blink::mojom::ShareError::CANCELED);
     return;
   }
 
-  std::string chosen_target = result.value();
+  const GURL& chosen_target = result->manifest_url();
 
-  std::string url_template = GetTargetTemplate(chosen_target, *share_targets);
   std::string url_template_filled;
-  if (!ReplacePlaceholders(url_template, title, text, share_url,
+  if (!ReplacePlaceholders(result->url_template(), title, text, share_url,
                            &url_template_filled)) {
     // TODO(mgiuca): This error should not be possible at share time, because
     // targets with invalid templates should not be chooseable. Fix
@@ -220,9 +206,10 @@
 
   // The template is relative to the manifest URL (minus the filename).
   // Concatenate to make an absolute URL.
+  const std::string& chosen_target_spec = chosen_target.spec();
   base::StringPiece url_base(
-      chosen_target.data(),
-      chosen_target.size() - GURL(chosen_target).ExtractFileName().size());
+      chosen_target_spec.data(),
+      chosen_target_spec.size() - chosen_target.ExtractFileName().size());
   const GURL target(url_base.as_string() + url_template_filled);
   // User should not be able to cause an invalid target URL. Possibilities are:
   // - The base URL: can't be invalid since it's derived from the manifest URL.
diff --git a/chrome/browser/webshare/share_service_impl.h b/chrome/browser/webshare/share_service_impl.h
index 538c661..e9ec359 100644
--- a/chrome/browser/webshare/share_service_impl.h
+++ b/chrome/browser/webshare/share_service_impl.h
@@ -11,7 +11,6 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
@@ -21,6 +20,7 @@
 
 class DictionaryValue;
 class GURL;
+class WebShareTarget;
 
 // Desktop implementation of the ShareService Mojo service.
 class ShareServiceImpl : public blink::mojom::ShareService {
@@ -57,9 +57,8 @@
   // presented to the user. Passes the result to |callback|. If the user picks a
   // target, the result passed to |callback| is the manifest URL of the chosen
   // target, or is null if the user cancelled the share. Virtual for testing.
-  virtual void ShowPickerDialog(
-      const std::vector<std::pair<base::string16, GURL>>& targets,
-      chrome::WebShareTargetPickerCallback callback);
+  virtual void ShowPickerDialog(std::vector<WebShareTarget> targets,
+                                chrome::WebShareTargetPickerCallback callback);
 
   // Opens a new tab and navigates to |target_url|.
   // Virtual for testing purposes.
@@ -67,9 +66,7 @@
 
   // Returns all stored Share Targets that have a high enough engagement score
   // with the user.
-  std::vector<std::pair<base::string16, GURL>>
-  GetTargetsWithSufficientEngagement(
-      const base::DictionaryValue& share_targets);
+  std::vector<WebShareTarget> GetTargetsWithSufficientEngagement();
 
   // Writes to |url_template_filled|, a copy of |url_template| with all
   // instances of "{title}", "{text}", and "{url}" replaced with
@@ -85,12 +82,11 @@
                                   const GURL& share_url,
                                   std::string* url_template_filled);
 
-  void OnPickerClosed(std::unique_ptr<base::DictionaryValue> share_targets,
-                      const std::string& title,
+  void OnPickerClosed(const std::string& title,
                       const std::string& text,
                       const GURL& share_url,
                       ShareCallback callback,
-                      const base::Optional<std::string>& result);
+                      const WebShareTarget* result);
 
   base::WeakPtrFactory<ShareServiceImpl> weak_factory_;
 
diff --git a/chrome/browser/webshare/share_service_impl_unittest.cc b/chrome/browser/webshare/share_service_impl_unittest.cc
index b123ba8..b6ee469e 100644
--- a/chrome/browser/webshare/share_service_impl_unittest.cc
+++ b/chrome/browser/webshare/share_service_impl_unittest.cc
@@ -2,13 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <map>
 #include <memory>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/webshare/share_service_impl.h"
+#include "chrome/browser/webshare/webshare_target.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -81,20 +84,30 @@
 
   const std::string& GetLastUsedTargetURL() { return last_used_target_url_; }
 
-  const std::vector<std::pair<base::string16, GURL>>& GetTargetsInPicker() {
+  const std::vector<WebShareTarget>& GetTargetsInPicker() {
     return targets_in_picker_;
   }
 
+  void PickTarget(const std::string& target_url) {
+    const auto& it =
+        std::find_if(targets_in_picker_.begin(), targets_in_picker_.end(),
+                     [&target_url](const WebShareTarget& target) {
+                       return target.manifest_url().spec() == target_url;
+                     });
+    DCHECK(it != targets_in_picker_.end());
+    std::move(picker_callback_).Run(&*it);
+  }
+
   chrome::WebShareTargetPickerCallback picker_callback() {
     return std::move(picker_callback_);
   }
 
  private:
   void ShowPickerDialog(
-      const std::vector<std::pair<base::string16, GURL>>& targets,
+      std::vector<WebShareTarget> targets,
       chrome::WebShareTargetPickerCallback callback) override {
     // Store the arguments passed to the picker dialog.
-    targets_in_picker_ = targets;
+    targets_in_picker_ = std::move(targets);
     picker_callback_ = std::move(callback);
 
     // Quit the test's run loop. It is the test's responsibility to call the
@@ -122,9 +135,9 @@
   // The last URL passed to OpenTargetURL.
   std::string last_used_target_url_;
   // The targets passed to ShowPickerDialog.
-  std::vector<std::pair<base::string16, GURL>> targets_in_picker_;
+  std::vector<WebShareTarget> targets_in_picker_;
   // The callback passed to ShowPickerDialog (which is supposed to be called
-  // with the user's chosen result, or nullopt if cancelled).
+  // with the user's chosen result, or nullptr if cancelled).
   chrome::WebShareTargetPickerCallback picker_callback_;
 };
 
@@ -185,14 +198,15 @@
 
   run_loop.Run();
 
-  const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
-      make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlHigh)),
-      make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
-  EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
+  std::vector<WebShareTarget> expected_targets;
+  expected_targets.emplace_back(GURL(kManifestUrlHigh), kTargetName,
+                                kUrlTemplate);
+  expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
+                                kUrlTemplate);
+  EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
 
   // Pick example-low.com.
-  share_service_helper()->picker_callback().Run(
-      base::Optional<std::string>(kManifestUrlLow));
+  share_service_helper()->PickTarget(kManifestUrlLow);
 
   const char kExpectedURL[] =
       "https://www.example-low.com/target/"
@@ -219,7 +233,7 @@
   EXPECT_TRUE(share_service_helper()->GetTargetsInPicker().empty());
 
   // Cancel the dialog.
-  share_service_helper()->picker_callback().Run(base::nullopt);
+  share_service_helper()->picker_callback().Run(nullptr);
 
   EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
 }
@@ -243,13 +257,15 @@
 
   run_loop.Run();
 
-  const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
-      make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlHigh)),
-      make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
-  EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
+  std::vector<WebShareTarget> expected_targets;
+  expected_targets.emplace_back(GURL(kManifestUrlHigh), kTargetName,
+                                kUrlTemplate);
+  expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
+                                kUrlTemplate);
+  EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
 
   // Cancel the dialog.
-  share_service_helper()->picker_callback().Run(base::nullopt);
+  share_service_helper()->picker_callback().Run(nullptr);
 
   EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
 }
@@ -274,13 +290,13 @@
 
   run_loop.Run();
 
-  const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
-      make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlHigh))};
-  EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
+  std::vector<WebShareTarget> expected_targets;
+  expected_targets.emplace_back(GURL(kManifestUrlHigh), kTargetName,
+                                kBrokenUrlTemplate);
+  EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
 
   // Pick example-high.com.
-  share_service_helper()->picker_callback().Run(
-      base::Optional<std::string>(kManifestUrlHigh));
+  share_service_helper()->PickTarget(kManifestUrlHigh);
 
   EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
 }
@@ -303,13 +319,13 @@
 
   run_loop.Run();
 
-  const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
-      make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
-  EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
+  std::vector<WebShareTarget> expected_targets;
+  expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
+                                kUrlTemplate);
+  EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
 
   // Pick example-low.com.
-  share_service_helper()->picker_callback().Run(
-      base::Optional<std::string>(kManifestUrlLow));
+  share_service_helper()->PickTarget(kManifestUrlLow);
 
   const char kExpectedURL[] =
       "https://www.example-low.com/target/"
@@ -338,9 +354,10 @@
 
   run_loop.Run();
 
-  const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
-      make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
-  EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
+  std::vector<WebShareTarget> expected_targets;
+  expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
+                                kUrlTemplate);
+  EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
 
   chrome::WebShareTargetPickerCallback picker_callback =
       share_service_helper()->picker_callback();
@@ -348,7 +365,7 @@
   DeleteShareService();
 
   // Pick example-low.com.
-  std::move(picker_callback).Run(base::Optional<std::string>(kManifestUrlLow));
+  std::move(picker_callback).Run(&expected_targets[0]);
 }
 
 // Replace various numbers of placeholders in various orders. Placeholders are
diff --git a/chrome/browser/webshare/webshare_target.cc b/chrome/browser/webshare/webshare_target.cc
new file mode 100644
index 0000000..634e5ea
--- /dev/null
+++ b/chrome/browser/webshare/webshare_target.cc
@@ -0,0 +1,29 @@
+// 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/webshare/webshare_target.h"
+
+#include <string>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+
+WebShareTarget::WebShareTarget(GURL manifest_url,
+                               std::string name,
+                               std::string url_template)
+    : manifest_url_(std::move(manifest_url)),
+      name_(std::move(name)),
+      url_template_(std::move(url_template)) {}
+
+WebShareTarget::~WebShareTarget() {}
+
+bool WebShareTarget::operator==(const WebShareTarget& other) const {
+  return std::tie(manifest_url_, name_, url_template_) ==
+         std::tie(other.manifest_url_, other.name_, other.url_template_);
+}
+
+std::ostream& operator<<(std::ostream& out, const WebShareTarget& target) {
+  return out << "WebShareTarget(GURL(" << target.manifest_url().spec() << "), "
+             << target.name() << ", " << target.url_template() << ")";
+}
diff --git a/chrome/browser/webshare/webshare_target.h b/chrome/browser/webshare/webshare_target.h
new file mode 100644
index 0000000..3ca8e3c4
--- /dev/null
+++ b/chrome/browser/webshare/webshare_target.h
@@ -0,0 +1,44 @@
+// 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_WEBSHARE_WEBSHARE_TARGET_H_
+#define CHROME_BROWSER_WEBSHARE_WEBSHARE_TARGET_H_
+
+#include <string>
+
+#include "url/gurl.h"
+
+// Represents a Web Share Target and its attributes. The attributes are usually
+// retrieved from the share_target field in the site's manifest.
+class WebShareTarget {
+ public:
+  WebShareTarget(GURL manifest_url, std::string name, std::string url_template);
+  ~WebShareTarget();
+
+  // Move constructor
+  WebShareTarget(WebShareTarget&& other) = default;
+
+  // Move assigment
+  WebShareTarget& operator=(WebShareTarget&& other) = default;
+
+  const std::string& name() const { return name_; }
+  const GURL& manifest_url() const { return manifest_url_; }
+  // The URL template that contains placeholders to be replaced with shared
+  // data.
+  const std::string& url_template() const { return url_template_; }
+
+  bool operator==(const WebShareTarget& other) const;
+
+ private:
+  GURL manifest_url_;
+  std::string name_;
+  std::string url_template_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebShareTarget);
+};
+
+// Used by gtest to print a readable output on test failures.
+std::ostream& operator<<(std::ostream& out, const WebShareTarget& target);
+
+#endif  // CHROME_BROWSER_WEBSHARE_WEBSHARE_TARGET_H_
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 26d77c4..ee281cd 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -705,17 +705,6 @@
   ]
 }
 
-mojom("once_callback_mojo_bindings") {
-  sources = [
-    "safe_browsing.mojom",
-  ]
-
-  public_deps = [
-    "//content/public/common:resource_type_bindings",
-    "//url/mojo:url_mojom_gurl",
-  ]
-}
-
 mojom("mojo_bindings") {
   sources = [
     "cache_stats_recorder.mojom",
@@ -742,7 +731,6 @@
   }
 
   public_deps = [
-    ":once_callback_mojo_bindings",
     "//components/content_settings/core/common:mojo_bindings",
     "//mojo/common:common_custom_types",
     "//ui/gfx/geometry/mojo",
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 899a4bb..87494b8d 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -125,6 +125,7 @@
     "//components/plugins/renderer",
     "//components/rappor/public/interfaces",
     "//components/resources:components_resources",
+    "//components/safe_browsing/common:interfaces",
     "//components/spellcheck:build_features",
     "//components/startup_metric_utils/common:interfaces",
     "//components/subresource_filter/content/renderer",
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 0fa2bc9..a6746db 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -25,6 +25,7 @@
   "+components/printing/common",
   "+components/printing/renderer",
   "+components/rappor/public/interfaces",
+  "+components/safe_browsing/common",
   "+components/safe_browsing/renderer",
   "+components/signin/core/common",
   "+components/spellcheck",
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 8a88a7b9..6656c6d9 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -16,9 +16,9 @@
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/strings/string16.h"
-#include "chrome/common/safe_browsing.mojom.h"
 #include "chrome/renderer/media/chrome_key_systems_provider.h"
 #include "components/rappor/public/interfaces/rappor_recorder.mojom.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
 #include "components/spellcheck/spellcheck_build_features.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "extensions/features/features.h"
@@ -245,7 +245,7 @@
 
   chrome::ChromeKeySystemsProvider key_systems_provider_;
 
-  chrome::mojom::SafeBrowsingPtr safe_browsing_;
+  safe_browsing::mojom::SafeBrowsingPtr safe_browsing_;
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
   std::unique_ptr<SpellCheck> spellcheck_;
diff --git a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.cc b/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.cc
index 514acc9..16837d5 100644
--- a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.cc
+++ b/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.cc
@@ -11,7 +11,7 @@
 namespace safe_browsing {
 
 SafeBrowsingURLLoaderThrottle::SafeBrowsingURLLoaderThrottle(
-    chrome::mojom::SafeBrowsing* safe_browsing,
+    mojom::SafeBrowsing* safe_browsing,
     int render_frame_id)
     : safe_browsing_(safe_browsing),
       render_frame_id_(render_frame_id),
diff --git a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h b/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h
index aea48f7..3cd284a 100644
--- a/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h
+++ b/chrome/renderer/safe_browsing/safe_browsing_url_loader_throttle.h
@@ -6,7 +6,7 @@
 #define CHROME_RENDERER_SAFE_BROWSING_SAFE_BROWSING_URL_LOADER_THROTTLE_H_
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/common/safe_browsing.mojom.h"
 #include "content/public/common/url_loader_throttle.h"
 
 namespace chrome {
@@ -26,7 +26,7 @@
   // |safe_browsing| must stay alive until WillStartRequest() (if it is called)
   // or the end of this object.
   // |render_frame_id| is used for displaying SafeBrowsing UI when necessary.
-  SafeBrowsingURLLoaderThrottle(chrome::mojom::SafeBrowsing* safe_browsing,
+  SafeBrowsingURLLoaderThrottle(mojom::SafeBrowsing* safe_browsing,
                                 int render_frame_id);
   ~SafeBrowsingURLLoaderThrottle() override;
 
@@ -44,10 +44,10 @@
 
   void OnConnectionError();
 
-  chrome::mojom::SafeBrowsing* safe_browsing_;
+  mojom::SafeBrowsing* safe_browsing_;
   const int render_frame_id_;
 
-  chrome::mojom::SafeBrowsingUrlCheckerPtr url_checker_;
+  mojom::SafeBrowsingUrlCheckerPtr url_checker_;
 
   size_t pending_checks_ = 0;
   bool blocked_ = false;
diff --git a/chrome/test/base/mojo_test_connector.cc b/chrome/test/base/mojo_test_connector.cc
index 50fe20ca..e76c270 100644
--- a/chrome/test/base/mojo_test_connector.cc
+++ b/chrome/test/base/mojo_test_connector.cc
@@ -157,10 +157,10 @@
 
 }  // namespace
 
-// ServiceProcessLauncher::Delegate that makes exe:mash_browser_tests to
+// ServiceProcessLauncherDelegate that makes exe:mash_browser_tests to
 // exe:browser_tests and removes '--run-in-mash'.
 class MojoTestConnector::ServiceProcessLauncherDelegateImpl
-    : public service_manager::ServiceProcessLauncher::Delegate {
+    : public service_manager::ServiceProcessLauncherDelegate {
  public:
   explicit ServiceProcessLauncherDelegateImpl(
       const std::string& test_runner_name)
diff --git a/chrome/test/data/webui/md_bookmarks/folder_node_test.js b/chrome/test/data/webui/md_bookmarks/folder_node_test.js
index 7a0a5f98..bd3790c 100644
--- a/chrome/test/data/webui/md_bookmarks/folder_node_test.js
+++ b/chrome/test/data/webui/md_bookmarks/folder_node_test.js
@@ -44,16 +44,22 @@
         firstGen[0].$['descendants'].querySelectorAll('bookmarks-folder-node');
 
     // Select nested folder.
-    firedId = '';
     MockInteractions.tap(secondGen[0].$['folder-label']);
     assertEquals('select-folder', store.lastAction.name);
     assertEquals(secondGen[0].itemId, store.lastAction.id);
 
     // Select folder in a separate subtree.
-    firedId = '';
     MockInteractions.tap(rootFolders[1].$['folder-label']);
     assertEquals('select-folder', store.lastAction.name);
     assertEquals(rootFolders[1].itemId, store.lastAction.id);
+
+    // Doesn't re-select if the folder is already selected.
+    store.data.selectedFolder = '7';
+    store.notifyObservers();
+    store.resetLastAction();
+
+    MockInteractions.tap(rootFolders[1].$['folder-label']);
+    assertEquals(null, store.lastAction);
   });
 
   test('depth calculation', function() {
@@ -131,4 +137,15 @@
     assertEquals(null, getNextChild('1', '2', false));
     assertEquals('2', getNextChild('0', '7', true).itemId);
   });
+
+  test('right click opens context menu', function() {
+    var commandManager = new TestCommandManager();
+    document.body.appendChild(commandManager);
+
+    var node = getFolderNode('2');
+    node.$.container.dispatchEvent(new MouseEvent('contextmenu'));
+
+    assertDeepEquals(bookmarks.actions.selectFolder('2'), store.lastAction);
+    commandManager.assertMenuOpenForIds(['2']);
+  });
 });
diff --git a/chrome/test/data/webui/md_bookmarks/test_command_manager.js b/chrome/test/data/webui/md_bookmarks/test_command_manager.js
index 7dbc9707..3891435 100644
--- a/chrome/test/data/webui/md_bookmarks/test_command_manager.js
+++ b/chrome/test/data/webui/md_bookmarks/test_command_manager.js
@@ -19,7 +19,11 @@
     realHandle(command, itemIds);
   };
 
-  commandManager.assertLastCommand = function (command, ids) {
+  /**
+   * @param {Command} command
+   * @param {!Array<string>} ids
+   */
+  commandManager.assertLastCommand = function(command, ids) {
     assertEquals(command, lastCommand);
     if (ids)
       assertDeepEquals(ids, normalizeSet(lastCommandIds));
@@ -27,5 +31,10 @@
     lastCommandIds = null;
   };
 
+  /** @param {!Array<string>} ids */
+  commandManager.assertMenuOpenForIds = function(ids) {
+    assertDeepEquals(ids, normalizeSet(commandManager.menuIds_));
+  };
+
   return commandManager;
 }
diff --git a/components/download/internal/controller.h b/components/download/internal/controller.h
index 7ed4804..af75fca 100644
--- a/components/download/internal/controller.h
+++ b/components/download/internal/controller.h
@@ -31,7 +31,7 @@
   TIMEOUT = 3,
   // The download is failed for unknown reasons.
   UNKNOWN = 4,
-  // The download is cancelled.
+  // The download is cancelled by the client.
   CANCEL = 5,
 };
 
diff --git a/components/download/internal/controller_impl.cc b/components/download/internal/controller_impl.cc
index 5a215f2..136a145 100644
--- a/components/download/internal/controller_impl.cc
+++ b/components/download/internal/controller_impl.cc
@@ -33,6 +33,29 @@
   model->Update(*entry);
 }
 
+// Helper function to move from a CompletionType to a Client::FailureReason.
+Client::FailureReason FailureReasonFromCompletionType(CompletionType type) {
+  // SUCCEED does not map to a FailureReason.
+  DCHECK_NE(CompletionType::SUCCEED, type);
+
+  switch (type) {
+    case CompletionType::FAIL:
+      return Client::FailureReason::NETWORK;
+    case CompletionType::ABORT:
+      return Client::FailureReason::ABORTED;
+    case CompletionType::TIMEOUT:
+      return Client::FailureReason::TIMEDOUT;
+    case CompletionType::UNKNOWN:
+      return Client::FailureReason::UNKNOWN;
+    case CompletionType::CANCEL:
+      return Client::FailureReason::CANCELLED;
+    default:
+      NOTREACHED();
+  }
+
+  return Client::FailureReason::UNKNOWN;
+}
+
 }  // namespace
 
 ControllerImpl::ControllerImpl(
@@ -103,7 +126,7 @@
 
   if (!entry || entry->state == Entry::State::PAUSED ||
       entry->state == Entry::State::COMPLETE ||
-      entry->state == Entry::State::WATCHDOG) {
+      entry->state == Entry::State::NEW) {
     return;
   }
 
@@ -140,7 +163,7 @@
   if (entry->state == Entry::State::NEW) {
     // Check if we're currently trying to add the download.
     DCHECK(start_callbacks_.find(entry->guid) != start_callbacks_.end());
-    HandleStartDownloadResponse(entry->client, entry->guid,
+    HandleStartDownloadResponse(entry->client, guid,
                                 DownloadParams::StartResult::CLIENT_CANCELLED);
     return;
   }
@@ -219,6 +242,8 @@
     base::ResetAndReturn(&task_finished_callbacks_[task_type])
         .Run(needs_reschedule);
   }
+  // TODO(dtrainor): It might be useful to log how many downloads we have
+  // running when we're asked to stop processing.
   stats::LogScheduledTaskStatus(task_type, status);
   task_finished_callbacks_.erase(task_type);
 }
@@ -246,11 +271,15 @@
     HandleCompleteDownload(CompletionType::ABORT, entry->guid);
   }
 }
+
 void ControllerImpl::OnDownloadFailed(const DriverEntry& download, int reason) {
   Entry* entry = model_->Get(download.guid);
   if (!entry)
     return;
 
+  // TODO(dtrainor): Add retry logic here.  Connect to restart code for tracking
+  // number of retries.
+
   HandleCompleteDownload(CompletionType::FAIL, download.guid);
 }
 
@@ -304,8 +333,6 @@
   TransitTo(entry, Entry::State::AVAILABLE, model_.get());
 
   ActivateMoreDownloads();
-
-  // TODO(dtrainor): Make sure we're running all of the downloads we care about.
 }
 
 void ControllerImpl::OnItemUpdated(bool success,
@@ -313,16 +340,20 @@
                                    const std::string& guid) {
   Entry* entry = model_->Get(guid);
   DCHECK(entry);
-  if (entry->state == Entry::State::COMPLETE ||
-      entry->state == Entry::State::WATCHDOG) {
+
+  // Now that we're sure that our state is set correctly, it is OK to remove the
+  // DriverEntry.  If we restart we'll see a COMPLETE state and handle it
+  // accordingly.
+  if (entry->state == Entry::State::COMPLETE)
     driver_->Remove(guid);
-  }
+
+  // TODO(dtrainor): If failed, clean up any download state accordingly.
 }
 
 void ControllerImpl::OnItemRemoved(bool success,
                                    DownloadClient client,
                                    const std::string& guid) {
-  // TODO(dtrainor): Fail and clean up the download if necessary.
+  // TODO(dtrainor): If failed, clean up any download state accordingly.
 }
 
 void ControllerImpl::OnDeviceStatusChanged(const DeviceStatus& device_status) {
@@ -347,9 +378,6 @@
   ResolveInitialRequestStates();
   UpdateDriverStates();
   PullCurrentRequestStatus();
-
-  // TODO(dtrainor): Post this so that the initialization step is finalized
-  // before Clients can take action.
   NotifyClientsOfStartup();
   ProcessScheduledTasks();
 
@@ -417,7 +445,6 @@
     case Entry::State::AVAILABLE:
     case Entry::State::NEW:
     case Entry::State::COMPLETE:
-    case Entry::State::WATCHDOG:
       break;
     default:
       NOTREACHED();
@@ -429,8 +456,9 @@
 }
 
 void ControllerImpl::NotifyClientsOfStartup() {
-  auto categorized = util::MapEntriesToClients(clients_->GetRegisteredClients(),
-                                               model_->PeekEntries());
+  std::set<Entry::State> ignored_states = {Entry::State::COMPLETE};
+  auto categorized = util::MapEntriesToClients(
+      clients_->GetRegisteredClients(), model_->PeekEntries(), ignored_states);
 
   for (auto client_id : clients_->GetRegisteredClients()) {
     clients_->GetClient(client_id)->OnServiceInitialized(
@@ -454,8 +482,12 @@
     const DownloadParams::StartCallback& callback) {
   stats::LogStartDownloadResult(client, result);
 
-  if (result != DownloadParams::StartResult::ACCEPTED) {
-    // TODO(dtrainor): Clean up any download state.
+  // UNEXPECTED_GUID means the guid was already in use.  Don't remove this entry
+  // from the model because it's there due to another request.
+  if (result != DownloadParams::StartResult::ACCEPTED &&
+      result != DownloadParams::StartResult::UNEXPECTED_GUID &&
+      model_->Get(guid) != nullptr) {
+    model_->Remove(guid);
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -468,14 +500,28 @@
   DCHECK(entry);
   stats::LogDownloadCompletion(type);
 
-  if (entry->state == Entry::State::COMPLETE ||
-      entry->state == Entry::State::WATCHDOG) {
+  if (entry->state == Entry::State::COMPLETE) {
     DVLOG(1) << "Download is already completed.";
     return;
   }
 
-  // TODO(xingliu): Notify the client based on completion type.
-  TransitTo(entry, Entry::State::COMPLETE, model_.get());
+  auto* client = clients_->GetClient(entry->client);
+  DCHECK(client);
+
+  if (type == CompletionType::SUCCEED) {
+    auto driver_entry = driver_->Find(guid);
+    DCHECK(driver_entry.has_value());
+    // TODO(dtrainor): Move the FilePath generation to the controller and store
+    // it in Entry.  Then pass it into the DownloadDriver.
+    // TODO(dtrainor): PostTask this instead of putting it inline.
+    client->OnDownloadSucceeded(guid, base::FilePath(),
+                                driver_entry->bytes_downloaded);
+    TransitTo(entry, Entry::State::COMPLETE, model_.get());
+  } else {
+    client->OnDownloadFailed(guid, FailureReasonFromCompletionType(type));
+    model_->Remove(guid);
+  }
+
   ActivateMoreDownloads();
 }
 
diff --git a/components/download/internal/controller_impl_unittest.cc b/components/download/internal/controller_impl_unittest.cc
index 3d2dd355..b88ceae 100644
--- a/components/download/internal/controller_impl_unittest.cc
+++ b/components/download/internal/controller_impl_unittest.cc
@@ -27,11 +27,19 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
+using testing::Return;
 
 namespace download {
 
 namespace {
 
+DriverEntry BuildDriverEntry(const Entry& entry, DriverEntry::State state) {
+  DriverEntry dentry;
+  dentry.guid = entry.guid;
+  dentry.state = state;
+  return dentry;
+}
+
 class MockTaskScheduler : public TaskScheduler {
  public:
   MockTaskScheduler() = default;
@@ -511,6 +519,9 @@
   std::vector<Entry> entries = {entry};
 
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*client_,
+              OnDownloadFailed(entry.guid, Client::FailureReason::CANCELLED))
+      .Times(1);
   EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
   EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
 
@@ -523,7 +534,7 @@
   driver_->AddTestData(std::vector<DriverEntry>{driver_entry});
 
   controller_->CancelDownload(entry.guid);
-  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
+  EXPECT_EQ(nullptr, model_->Get(entry.guid));
 
   task_runner_->RunUntilIdle();
 }
@@ -534,6 +545,9 @@
   std::vector<Entry> entries = {entry};
 
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*client_,
+              OnDownloadFailed(entry.guid, Client::FailureReason::NETWORK))
+      .Times(1);
   EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
   EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
 
@@ -545,7 +559,9 @@
   driver_entry.guid = entry.guid;
 
   driver_->NotifyDownloadFailed(driver_entry, 1);
-  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
+  EXPECT_EQ(nullptr, model_->Get(entry.guid));
+
+  task_runner_->RunUntilIdle();
 }
 
 TEST_F(DownloadServiceControllerImplTest, OnDownloadSucceeded) {
@@ -554,6 +570,7 @@
   std::vector<Entry> entries = {entry};
 
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*client_, OnDownloadSucceeded(entry.guid, _, _)).Times(1);
   EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
   EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
 
@@ -568,6 +585,8 @@
 
   driver_->NotifyDownloadSucceeded(driver_entry, path);
   EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
+
+  task_runner_->RunUntilIdle();
 }
 
 TEST_F(DownloadServiceControllerImplTest, OnDownloadUpdated) {
@@ -594,4 +613,55 @@
   EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry.guid)->state);
 }
 
+TEST_F(DownloadServiceControllerImplTest, DownloadCompletionTest) {
+  // TODO(dtrainor): Simulate a TIMEOUT once that is supported.
+  // TODO(dtrainor): Simulate a UNKNOWN once that is supported.
+
+  Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
+  Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
+  Entry entry3 = test::BuildBasicEntry(Entry::State::ACTIVE);
+
+  DriverEntry dentry1 =
+      BuildDriverEntry(entry1, DriverEntry::State::IN_PROGRESS);
+  // dentry2 will effectively be created by the test to simulate a start
+  // download.
+  DriverEntry dentry3 =
+      BuildDriverEntry(entry3, DriverEntry::State::IN_PROGRESS);
+
+  std::vector<Entry> entries = {entry1, entry2, entry3};
+  std::vector<DriverEntry> dentries = {dentry1, dentry3};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+
+  // Set up the Controller.
+  driver_->AddTestData(dentries);
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+  task_runner_->RunUntilIdle();
+
+  // Test FailureReason::CANCELLED.
+  EXPECT_CALL(*client_,
+              OnDownloadFailed(entry1.guid, Client::FailureReason::CANCELLED))
+      .Times(1);
+  controller_->CancelDownload(entry1.guid);
+
+  // Test FailureReason::ABORTED.
+  EXPECT_CALL(*client_, OnDownloadStarted(entry2.guid, _, _))
+      .Times(1)
+      .WillOnce(Return(Client::ShouldDownload::ABORT));
+  EXPECT_CALL(*client_,
+              OnDownloadFailed(entry2.guid, Client::FailureReason::ABORTED))
+      .Times(1);
+  driver_->Start(RequestParams(), entry2.guid, NO_TRAFFIC_ANNOTATION_YET);
+
+  // Test FailureReason::NETWORK.
+  EXPECT_CALL(*client_,
+              OnDownloadFailed(entry3.guid, Client::FailureReason::NETWORK))
+      .Times(1);
+  driver_->NotifyDownloadFailed(dentry3, 1);
+
+  task_runner_->RunUntilIdle();
+}
+
 }  // namespace download
diff --git a/components/download/internal/entry.h b/components/download/internal/entry.h
index e4e787e..9f4a04ad 100644
--- a/components/download/internal/entry.h
+++ b/components/download/internal/entry.h
@@ -33,13 +33,9 @@
     // be run until it is resumed by the Client.
     PAUSED = 3,
 
-    // The download is 'complete' by some definition of that term (could have
-    // failed, could have succeeded, etc.).  It is ready to have UMA logs saved.
+    // The download is 'complete' and successful.  At this point we are leaving
+    // this entry around to make sure the files on disk are cleaned up.
     COMPLETE = 4,
-
-    // The download is finished.  We are leaving this entry around to make sure
-    // the files on disk are cleaned up.
-    WATCHDOG = 5,
   };
 
   Entry();
diff --git a/components/download/internal/entry_utils.cc b/components/download/internal/entry_utils.cc
index 211f8ae2..35aa3ed 100644
--- a/components/download/internal/entry_utils.cc
+++ b/components/download/internal/entry_utils.cc
@@ -21,10 +21,14 @@
 
 std::map<DownloadClient, std::vector<std::string>> MapEntriesToClients(
     const std::set<DownloadClient>& clients,
-    const std::vector<Entry*>& entries) {
+    const std::vector<Entry*>& entries,
+    const std::set<Entry::State>& ignored_states) {
   std::map<DownloadClient, std::vector<std::string>> categorized;
 
   for (auto* entry : entries) {
+    if (ignored_states.find(entry->state) != ignored_states.end())
+      continue;
+
     DownloadClient client = entry->client;
     if (clients.find(client) == clients.end())
       client = DownloadClient::INVALID;
diff --git a/components/download/internal/entry_utils.h b/components/download/internal/entry_utils.h
index a50244b..780b494 100644
--- a/components/download/internal/entry_utils.h
+++ b/components/download/internal/entry_utils.h
@@ -28,10 +28,12 @@
 // Effectively runs a map reduce to turn a list of Entry objects into a map of
 // [Client Id] -> List of entries.  Any Entry in |entries| that does not have a
 // matching DownloadClient in |clients| will be put in the
-// DownloadClient::INVALID bucket.
+// DownloadClient::INVALID bucket.  Any Entry in |entries| with an Entry::State
+// that is in |ignored_states| will not be included.
 std::map<DownloadClient, std::vector<std::string>> MapEntriesToClients(
     const std::set<DownloadClient>& clients,
-    const std::vector<Entry*>& entries);
+    const std::vector<Entry*>& entries,
+    const std::set<Entry::State>& ignored_states);
 
 // Gets the least strict scheduling criteria from |entries|, the criteria is
 // used to schedule platform background tasks.
diff --git a/components/download/internal/entry_utils_unittest.cc b/components/download/internal/entry_utils_unittest.cc
index 446009c..472151dc 100644
--- a/components/download/internal/entry_utils_unittest.cc
+++ b/components/download/internal/entry_utils_unittest.cc
@@ -30,35 +30,90 @@
   Entry entry1 = test::BuildBasicEntry();
   Entry entry2 = test::BuildBasicEntry();
   Entry entry3 = test::BuildBasicEntry();
+  Entry entry4 = test::BuildBasicEntry(Entry::State::COMPLETE);
+  Entry entry5 = test::BuildBasicEntry(Entry::State::AVAILABLE);
 
-  std::vector<Entry*> entries = {&entry1, &entry2, &entry3};
-  std::vector<std::string> expected_list = {entry1.guid, entry2.guid,
-                                            entry3.guid};
-
+  std::vector<Entry*> entries = {&entry1, &entry2, &entry3, &entry4, &entry5};
+  std::vector<std::string> expected_list = {
+      entry1.guid, entry2.guid, entry3.guid, entry4.guid, entry5.guid};
+  std::vector<std::string> expected_pruned_list = {entry1.guid, entry2.guid,
+                                                   entry3.guid, entry5.guid};
   // If DownloadClient::TEST isn't a valid Client, all of the associated entries
   // should move to the DownloadClient::INVALID bucket.
-  auto mapped1 = util::MapEntriesToClients(std::set<DownloadClient>(), entries);
+  auto mapped1 = util::MapEntriesToClients(std::set<DownloadClient>(), entries,
+                                           std::set<Entry::State>());
   EXPECT_EQ(1U, mapped1.size());
   EXPECT_NE(mapped1.end(), mapped1.find(DownloadClient::INVALID));
   EXPECT_EQ(mapped1.end(), mapped1.find(DownloadClient::TEST));
 
   auto list1 = mapped1.find(DownloadClient::INVALID)->second;
-  EXPECT_EQ(3U, list1.size());
+  EXPECT_EQ(5U, list1.size());
   EXPECT_TRUE(
       std::equal(expected_list.begin(), expected_list.end(), list1.begin()));
 
   // If DownloadClient::TEST is a valid Client, it should have the associated
   // entries.
   std::set<DownloadClient> clients = {DownloadClient::TEST};
-  auto mapped2 = util::MapEntriesToClients(clients, entries);
+  auto mapped2 =
+      util::MapEntriesToClients(clients, entries, std::set<Entry::State>());
   EXPECT_EQ(1U, mapped2.size());
   EXPECT_NE(mapped2.end(), mapped2.find(DownloadClient::TEST));
   EXPECT_EQ(mapped2.end(), mapped2.find(DownloadClient::INVALID));
 
   auto list2 = mapped2.find(DownloadClient::TEST)->second;
-  EXPECT_EQ(3U, list2.size());
+  EXPECT_EQ(5U, list2.size());
   EXPECT_TRUE(
       std::equal(expected_list.begin(), expected_list.end(), list2.begin()));
+
+  // If we are pruning entries with certain states, make sure those entries
+  // don't show up in the results.
+  std::set<Entry::State> ignored_states = {Entry::State::ACTIVE,
+                                           Entry::State::COMPLETE};
+  auto mapped3 = util::MapEntriesToClients(clients, entries, ignored_states);
+  EXPECT_EQ(1U, mapped3.size());
+  auto list3 = mapped3.find(DownloadClient::TEST)->second;
+  EXPECT_EQ(4U, list3.size());
+  EXPECT_TRUE(std::equal(expected_pruned_list.begin(),
+                         expected_pruned_list.end(), list3.begin()));
+}
+
+TEST(DownloadServiceEntryUtilsTest, GetSchedulingCriteria) {
+  Entry entry1 = test::BuildBasicEntry();
+  Entry entry2 = test::BuildBasicEntry();
+  Entry entry3 = test::BuildBasicEntry();
+  Entry entry4 = test::BuildBasicEntry();
+
+  entry1.scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::UNMETERED;
+  entry1.scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+
+  entry2.scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::NONE;
+  entry2.scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+
+  entry3.scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::UNMETERED;
+  entry3.scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+
+  entry4.scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::NONE;
+  entry4.scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+
+  Model::EntryList list1 = {&entry1};
+  Model::EntryList list2 = {&entry1, &entry2};
+  Model::EntryList list3 = {&entry1, &entry3};
+  Model::EntryList list4 = {&entry1, &entry2, &entry3};
+  Model::EntryList list5 = {&entry1, &entry4};
+
+  EXPECT_EQ(Criteria(true, true), util::GetSchedulingCriteria(list1));
+  EXPECT_EQ(Criteria(true, false), util::GetSchedulingCriteria(list2));
+  EXPECT_EQ(Criteria(false, true), util::GetSchedulingCriteria(list3));
+  EXPECT_EQ(Criteria(false, false), util::GetSchedulingCriteria(list4));
+  EXPECT_EQ(Criteria(false, false), util::GetSchedulingCriteria(list5));
 }
 
 }  // namespace download
diff --git a/components/download/internal/model_impl.cc b/components/download/internal/model_impl.cc
index 035fa49..3fb79e9 100644
--- a/components/download/internal/model_impl.cc
+++ b/components/download/internal/model_impl.cc
@@ -56,11 +56,14 @@
   const auto& it = entries_.find(guid);
   DCHECK(it != entries_.end());
 
+  // Pull out a separate guid and a DownloadClient so that when we destroy the
+  // entry we don't destroy the std::string that is backing the guid.
+  std::string standalone_guid = guid;
   DownloadClient client = it->second->client;
   entries_.erase(it);
-  store_->Remove(guid,
-                 base::BindOnce(&ModelImpl::OnRemoveFinished,
-                                weak_ptr_factory_.GetWeakPtr(), client, guid));
+  store_->Remove(standalone_guid, base::BindOnce(&ModelImpl::OnRemoveFinished,
+                                                 weak_ptr_factory_.GetWeakPtr(),
+                                                 client, standalone_guid));
 }
 
 Entry* ModelImpl::Get(const std::string& guid) {
diff --git a/components/download/internal/proto/entry.proto b/components/download/internal/proto/entry.proto
index 8241f9fa..56e1f3a1 100644
--- a/components/download/internal/proto/entry.proto
+++ b/components/download/internal/proto/entry.proto
@@ -33,7 +33,6 @@
     ACTIVE = 2;
     PAUSED = 3;
     COMPLETE = 4;
-    WATCHDOG = 5;
   }
 
   // Identification Parameters.
diff --git a/components/download/internal/proto_conversions.cc b/components/download/internal/proto_conversions.cc
index abe26287..737920c 100644
--- a/components/download/internal/proto_conversions.cc
+++ b/components/download/internal/proto_conversions.cc
@@ -23,8 +23,6 @@
       return protodb::Entry_State_PAUSED;
     case Entry::State::COMPLETE:
       return protodb::Entry_State_COMPLETE;
-    case Entry::State::WATCHDOG:
-      return protodb::Entry_State_WATCHDOG;
   }
 
   NOTREACHED();
@@ -44,8 +42,6 @@
       return Entry::State::PAUSED;
     case protodb::Entry_State_COMPLETE:
       return Entry::State::COMPLETE;
-    case protodb::Entry_State_WATCHDOG:
-      return Entry::State::WATCHDOG;
   }
 
   NOTREACHED();
diff --git a/components/download/internal/proto_conversions_unittest.cc b/components/download/internal/proto_conversions_unittest.cc
index 04198c5..1f39dbb 100644
--- a/components/download/internal/proto_conversions_unittest.cc
+++ b/components/download/internal/proto_conversions_unittest.cc
@@ -24,9 +24,9 @@
 };
 
 TEST_F(ProtoConversionsTest, StateConversion) {
-  Entry::State states[] = {Entry::State::NEW,      Entry::State::AVAILABLE,
-                           Entry::State::ACTIVE,   Entry::State::PAUSED,
-                           Entry::State::COMPLETE, Entry::State::WATCHDOG};
+  Entry::State states[] = {Entry::State::NEW, Entry::State::AVAILABLE,
+                           Entry::State::ACTIVE, Entry::State::PAUSED,
+                           Entry::State::COMPLETE};
   for (auto state : states) {
     ASSERT_EQ(state, RequestStateFromProto(RequestStateToProto(state)));
   }
diff --git a/components/download/internal/scheduler/device_status.cc b/components/download/internal/scheduler/device_status.cc
index b206ddf..80c88c00 100644
--- a/components/download/internal/scheduler/device_status.cc
+++ b/components/download/internal/scheduler/device_status.cc
@@ -59,6 +59,11 @@
 Criteria::Criteria()
     : requires_battery_charging(true), requires_unmetered_network(true) {}
 
+Criteria::Criteria(bool requires_battery_charging,
+                   bool requires_unmetered_network)
+    : requires_battery_charging(requires_battery_charging),
+      requires_unmetered_network(requires_unmetered_network) {}
+
 bool Criteria::operator==(const Criteria& other) const {
   return requires_battery_charging == other.requires_battery_charging &&
          requires_unmetered_network == other.requires_unmetered_network;
diff --git a/components/download/internal/scheduler/device_status.h b/components/download/internal/scheduler/device_status.h
index 43c3b22..720a126 100644
--- a/components/download/internal/scheduler/device_status.h
+++ b/components/download/internal/scheduler/device_status.h
@@ -49,6 +49,7 @@
 // The criteria when the background download task should start.
 struct Criteria {
   Criteria();
+  Criteria(bool requires_battery_charging, bool requires_unmetered_network);
   bool operator==(const Criteria& other) const;
   bool requires_battery_charging;
   bool requires_unmetered_network;
diff --git a/components/download/internal/test/empty_client.cc b/components/download/internal/test/empty_client.cc
index e90db302..5318bc5 100644
--- a/components/download/internal/test/empty_client.cc
+++ b/components/download/internal/test/empty_client.cc
@@ -20,11 +20,8 @@
 void EmptyClient::OnDownloadUpdated(const std::string& guid,
                                     uint64_t bytes_downloaded) {}
 
-void EmptyClient::OnDownloadFailed(const std::string& guid) {}
-
-void EmptyClient::OnDownloadTimedOut(const std::string& guid) {}
-
-void EmptyClient::OnDownloadAborted(const std::string& guid) {}
+void EmptyClient::OnDownloadFailed(const std::string& guid,
+                                   FailureReason reason) {}
 
 void EmptyClient::OnDownloadSucceeded(const std::string& guid,
                                       const base::FilePath& path,
diff --git a/components/download/internal/test/empty_client.h b/components/download/internal/test/empty_client.h
index a855b20..cbfdf2a 100644
--- a/components/download/internal/test/empty_client.h
+++ b/components/download/internal/test/empty_client.h
@@ -25,9 +25,7 @@
       const scoped_refptr<const net::HttpResponseHeaders>& headers) override;
   void OnDownloadUpdated(const std::string& guid,
                          uint64_t bytes_downloaded) override;
-  void OnDownloadFailed(const std::string& guid) override;
-  void OnDownloadTimedOut(const std::string& guid) override;
-  void OnDownloadAborted(const std::string& guid) override;
+  void OnDownloadFailed(const std::string& guid, FailureReason reason) override;
   void OnDownloadSucceeded(const std::string& guid,
                            const base::FilePath& path,
                            uint64_t size) override;
diff --git a/components/download/internal/test/entry_utils.cc b/components/download/internal/test/entry_utils.cc
index 141d502..9ed50ca 100644
--- a/components/download/internal/test/entry_utils.cc
+++ b/components/download/internal/test/entry_utils.cc
@@ -51,6 +51,12 @@
   return BuildEntry(DownloadClient::TEST, base::GenerateGUID());
 }
 
+Entry BuildBasicEntry(Entry::State state) {
+  Entry entry = BuildBasicEntry();
+  entry.state = state;
+  return entry;
+}
+
 Entry BuildEntry(DownloadClient client, const std::string& guid) {
   Entry entry;
   entry.client = client;
diff --git a/components/download/internal/test/entry_utils.h b/components/download/internal/test/entry_utils.h
index 2a95eac..7ea94b8 100644
--- a/components/download/internal/test/entry_utils.h
+++ b/components/download/internal/test/entry_utils.h
@@ -23,6 +23,8 @@
 
 Entry BuildBasicEntry();
 
+Entry BuildBasicEntry(Entry::State state);
+
 Entry BuildEntry(DownloadClient client, const std::string& guid);
 
 Entry BuildEntry(DownloadClient client,
diff --git a/components/download/internal/test/mock_client.h b/components/download/internal/test/mock_client.h
index 442e29e..f4b255b 100644
--- a/components/download/internal/test/mock_client.h
+++ b/components/download/internal/test/mock_client.h
@@ -25,9 +25,7 @@
                      const std::vector<GURL>&,
                      const scoped_refptr<const net::HttpResponseHeaders>&));
   MOCK_METHOD2(OnDownloadUpdated, void(const std::string&, uint64_t));
-  MOCK_METHOD1(OnDownloadFailed, void(const std::string&));
-  MOCK_METHOD1(OnDownloadTimedOut, void(const std::string&));
-  MOCK_METHOD1(OnDownloadAborted, void(const std::string&));
+  MOCK_METHOD2(OnDownloadFailed, void(const std::string&, FailureReason));
   MOCK_METHOD3(OnDownloadSucceeded,
                void(const std::string&, const base::FilePath&, uint64_t));
 
diff --git a/components/download/public/client.h b/components/download/public/client.h
index 43349b6..6f65610 100644
--- a/components/download/public/client.h
+++ b/components/download/public/client.h
@@ -26,6 +26,31 @@
     ABORT,
   };
 
+  // Used by OnDownloadFailed to determine the reason of the abort.
+  enum class FailureReason {
+    // Used when the download has been aborted after reaching a threshold where
+    // we decide it is not worth attempting to start again.  This could be
+    // either due to a specific number of failed retry attempts or a specific
+    // number of wasted bytes due to the download restarting.
+    NETWORK,
+
+    // Used when the download was not completed before the
+    // DownloadParams::cancel_after timeout.
+    TIMEDOUT,
+
+    // Used when the download was cancelled by the Client.
+    CANCELLED,
+
+    // Used when the download was aborted by the Client in response to the
+    // download starting (see OnDownloadStarted()).
+    ABORTED,
+
+    // Used when the failure reason is unknown.  This generally means that we
+    // detect that the download failed during a restart, but aren't sure exactly
+    // what triggered the failure before shutdown.
+    UNKNOWN,
+  };
+
   virtual ~Client() = default;
 
   // Called when the DownloadService is initialized and ready to be interacted
@@ -51,18 +76,11 @@
   virtual void OnDownloadUpdated(const std::string& guid,
                                  uint64_t bytes_downloaded) = 0;
 
-  // TODO(dtrainor): Expose a useful error message with the failed download.
-  virtual void OnDownloadFailed(const std::string& guid) = 0;
-
-  // Called when the download was not completed before the
-  // DownloadParams::cancel_after timeout.
-  virtual void OnDownloadTimedOut(const std::string& guid) = 0;
-
-  // Called when the download has been aborted after reaching a threshold where
-  // we decide it is not worth attempting to start again.  This could be either
-  // due to a specific number of failed retry attempts or a specific number of
-  // wasted bytes due to the download restarting.
-  virtual void OnDownloadAborted(const std::string& guid) = 0;
+  // Called when a download failed.  Check FailureReason for a list of possible
+  // reasons why this failure occurred.  Note that this will also be called for
+  // cancelled downloads.
+  virtual void OnDownloadFailed(const std::string& guid,
+                                FailureReason reason) = 0;
 
   // Called when a download has been successfully completed.  After this call
   // the download entry will be purged from the database.  The file will be
diff --git a/components/filesystem/BUILD.gn b/components/filesystem/BUILD.gn
index dc3bc41..ff3ec9b 100644
--- a/components/filesystem/BUILD.gn
+++ b/components/filesystem/BUILD.gn
@@ -36,60 +36,63 @@
   ]
 }
 
-service("filesystem") {
-  sources = [
-    "file_system_app.cc",
-    "file_system_app.h",
-    "main.cc",
-  ]
+if (!is_ios) {
+  # service binaries are not supported on iOS.
+  service("filesystem") {
+    sources = [
+      "file_system_app.cc",
+      "file_system_app.h",
+      "main.cc",
+    ]
 
-  deps = [
-    ":lib",
-    "//base",
-    "//components/filesystem/public/interfaces",
-    "//mojo/common",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp",
-  ]
-}
+    deps = [
+      ":lib",
+      "//base",
+      "//components/filesystem/public/interfaces",
+      "//mojo/common",
+      "//mojo/public/cpp/bindings",
+      "//mojo/public/cpp/system",
+      "//services/service_manager/public/cpp",
+    ]
+  }
 
-service_manifest("manifest") {
-  name = "filesystem"
-  source = "manifest.json"
-}
+  service_manifest("manifest") {
+    name = "filesystem"
+    source = "manifest.json"
+  }
 
-service_test("filesystem_service_unittests") {
-  sources = [
-    "directory_impl_unittest.cc",
-    "file_impl_unittest.cc",
-    "files_test_base.cc",
-    "files_test_base.h",
-  ]
+  service_test("filesystem_service_unittests") {
+    sources = [
+      "directory_impl_unittest.cc",
+      "file_impl_unittest.cc",
+      "files_test_base.cc",
+      "files_test_base.h",
+    ]
 
-  catalog = ":filesystem_service_unittests_catalog"
+    catalog = ":filesystem_service_unittests_catalog"
 
-  deps = [
-    "//base",
-    "//components/filesystem/public/interfaces",
-    "//mojo/common",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp:service_test_support",
-  ]
+    deps = [
+      "//base",
+      "//components/filesystem/public/interfaces",
+      "//mojo/common",
+      "//mojo/public/cpp/bindings",
+      "//mojo/public/cpp/system",
+      "//services/service_manager/public/cpp",
+      "//services/service_manager/public/cpp:service_test_support",
+    ]
 
-  data_deps = [
-    ":filesystem",
-  ]
-}
+    data_deps = [
+      ":filesystem",
+    ]
+  }
 
-service_manifest("test_manifest") {
-  name = "filesystem_service_unittests"
-  source = "test_manifest.json"
-}
+  service_manifest("test_manifest") {
+    name = "filesystem_service_unittests"
+    source = "test_manifest.json"
+  }
 
-catalog("filesystem_service_unittests_catalog") {
-  embedded_services = [ ":test_manifest" ]
-  standalone_services = [ ":manifest" ]
+  catalog("filesystem_service_unittests_catalog") {
+    embedded_services = [ ":test_manifest" ]
+    standalone_services = [ ":manifest" ]
+  }
 }
diff --git a/components/safe_browsing/common/BUILD.gn b/components/safe_browsing/common/BUILD.gn
index 212b21df..51b0570 100644
--- a/components/safe_browsing/common/BUILD.gn
+++ b/components/safe_browsing/common/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//mojo/public/tools/bindings/mojom.gni")
+
 source_set("common") {
   sources = [
     "safebrowsing_constants.cc",
@@ -45,3 +47,14 @@
     "//testing/gtest",
   ]
 }
+
+mojom("interfaces") {
+  sources = [
+    "safe_browsing.mojom",
+  ]
+
+  public_deps = [
+    "//content/public/common:resource_type_bindings",
+    "//url/mojo:url_mojom_gurl",
+  ]
+}
diff --git a/components/safe_browsing/common/OWNERS b/components/safe_browsing/common/OWNERS
index 6734395..599596c 100644
--- a/components/safe_browsing/common/OWNERS
+++ b/components/safe_browsing/common/OWNERS
@@ -2,3 +2,5 @@
 
 per-file *_messages*.h=set noparent
 per-file *_messages*.h=file://ipc/SECURITY_OWNERS
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chrome/common/safe_browsing.mojom b/components/safe_browsing/common/safe_browsing.mojom
similarity index 97%
rename from chrome/common/safe_browsing.mojom
rename to components/safe_browsing/common/safe_browsing.mojom
index 07253a33..5b7ae95 100644
--- a/chrome/common/safe_browsing.mojom
+++ b/components/safe_browsing/common/safe_browsing.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-module chrome.mojom;
+module safe_browsing.mojom;
 
 import "content/public/common/resource_type.mojom";
 import "url/mojo/url.mojom";
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index cb6cb98..a90d107 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -1086,6 +1086,14 @@
     return empty_data;
 }
 
+const ui::AXTreeData& BrowserAccessibility::GetTreeData() const {
+  CR_DEFINE_STATIC_LOCAL(ui::AXTreeData, empty_data, ());
+  if (manager())
+    return manager()->GetTreeData();
+  else
+    return empty_data;
+}
+
 gfx::NativeWindow BrowserAccessibility::GetTopLevelWidget() {
   NOTREACHED();
   return nullptr;
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 404107a..8d9152b 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -382,6 +382,7 @@
 
   // AXPlatformNodeDelegate.
   const ui::AXNodeData& GetData() const override;
+  const ui::AXTreeData& GetTreeData() const override;
   gfx::NativeWindow GetTopLevelWidget() override;
   gfx::NativeViewAccessible GetParent() override;
   int GetChildCount() override;
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 6b09cae3..4695d7bf 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -670,46 +670,14 @@
   if (!owner())
     return E_FAIL;
 
-  if (!value)
-    return E_INVALIDARG;
-
-  BrowserAccessibilityComWin* target = GetTargetFromChildID(var_id);
-  if (!target)
-    return E_INVALIDARG;
-
-  if (target->IsRangeValueSupported()) {
-    base::string16 value_text = target->GetRangeValueText();
-    *value = SysAllocString(value_text.c_str());
-    DCHECK(*value);
-    return S_OK;
-  }
-
-  // Expose color well value.
-  if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
-    unsigned int color = static_cast<unsigned int>(
-        target->owner()->GetIntAttribute(ui::AX_ATTR_COLOR_VALUE));
-    unsigned int red = SkColorGetR(color);
-    unsigned int green = SkColorGetG(color);
-    unsigned int blue = SkColorGetB(color);
-    base::string16 value_text;
-    value_text = base::UintToString16(red * 100 / 255) + L"% red " +
-                 base::UintToString16(green * 100 / 255) + L"% green " +
-                 base::UintToString16(blue * 100 / 255) + L"% blue";
-    *value = SysAllocString(value_text.c_str());
-    DCHECK(*value);
-    return S_OK;
-  }
-
-  *value = SysAllocString(target->value().c_str());
-  DCHECK(*value);
-  return S_OK;
+  return AXPlatformNodeWin::get_accValue(var_id, value);
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_accHelpTopic(BSTR* help_file,
                                                           VARIANT var_id,
                                                           LONG* topic_id) {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_HELP_TOPIC);
-  return E_NOTIMPL;
+  return AXPlatformNodeWin::get_accHelpTopic(help_file, var_id, topic_id);
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_accSelection(VARIANT* selected) {
@@ -774,11 +742,17 @@
 
 STDMETHODIMP
 BrowserAccessibilityComWin::put_accName(VARIANT var_id, BSTR put_name) {
-  return E_NOTIMPL;
+  if (!owner())
+    return E_FAIL;
+
+  return AXPlatformNodeWin::put_accName(var_id, put_name);
 }
 STDMETHODIMP
 BrowserAccessibilityComWin::put_accValue(VARIANT var_id, BSTR put_val) {
-  return E_NOTIMPL;
+  if (!owner())
+    return E_FAIL;
+
+  return AXPlatformNodeWin::put_accValue(var_id, put_val);
 }
 
 //
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 9b7b091..e635cc2 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -273,6 +273,12 @@
   return true;
 }
 
+bool BrowserAccessibilityManagerAndroid::OnHoverEvent(
+    const ui::MotionEventAndroid& event) {
+  WebContentsAccessibilityAndroid* wcax = GetWebContentsAXFromRootManager();
+  return wcax ? wcax->OnHoverEvent(event) : false;
+}
+
 void BrowserAccessibilityManagerAndroid::HandleHoverEvent(
     BrowserAccessibility* node) {
   WebContentsAccessibilityAndroid* wcax = GetWebContentsAXFromRootManager();
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.h b/content/browser/accessibility/browser_accessibility_manager_android.h
index 1ad4396d..13df13f7 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.h
+++ b/content/browser/accessibility/browser_accessibility_manager_android.h
@@ -7,6 +7,10 @@
 
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 
+namespace ui {
+class MotionEventAndroid;
+}
+
 namespace content {
 
 // A Java counterpart will be generated for this enum.
@@ -69,6 +73,9 @@
   bool ShouldRespectDisplayedPasswordText();
   bool ShouldExposePasswordText();
 
+  // Consume hover event if necessary, and return true if it did.
+  bool OnHoverEvent(const ui::MotionEventAndroid& event);
+
   // BrowserAccessibilityManager overrides.
   BrowserAccessibility* GetFocus() override;
   void NotifyAccessibilityEvent(
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 05660f5..29f4da4 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -18,6 +18,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/common/content_features.h"
 #include "jni/WebContentsAccessibility_jni.h"
+#include "ui/events/android/motion_event_android.h"
 
 using base::android::AttachCurrentThread;
 using base::android::JavaParamRef;
@@ -524,6 +525,25 @@
   Java_WebContentsAccessibility_handleHover(env, obj, unique_id);
 }
 
+bool WebContentsAccessibilityAndroid::OnHoverEvent(
+    const ui::MotionEventAndroid& event) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return false;
+
+  if (!Java_WebContentsAccessibility_onHoverEvent(env, obj, event.GetAction()))
+    return false;
+
+  // |HitTest| sends an IPC to the render process to do the hit testing.
+  // The response is handled by HandleHover when it returns.
+  // Hover event was consumed by accessibility by now. Return true to
+  // stop the event from proceeding.
+  if (event.GetAction() != ui::MotionEvent::ACTION_HOVER_EXIT && root_manager_)
+    root_manager_->HitTest(gfx::ToFlooredPoint(event.GetPoint()));
+  return true;
+}
+
 void WebContentsAccessibilityAndroid::HandleNavigate() {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
diff --git a/content/browser/accessibility/web_contents_accessibility_android.h b/content/browser/accessibility/web_contents_accessibility_android.h
index 8ec7177..77fc9db9 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.h
+++ b/content/browser/accessibility/web_contents_accessibility_android.h
@@ -10,6 +10,10 @@
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
 
+namespace ui {
+class MotionEventAndroid;
+}
+
 namespace content {
 
 class BrowserAccessibilityAndroid;
@@ -209,6 +213,7 @@
   void HandleEditableTextChanged(int32_t unique_id);
   void HandleSliderChanged(int32_t unique_id);
   void SendDelayedWindowContentChangedEvent();
+  bool OnHoverEvent(const ui::MotionEventAndroid& event);
   void HandleHover(int32_t unique_id);
   void HandleNavigate();
 
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 24ed19c..4411f85b 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -51,7 +51,6 @@
 #include "device/geolocation/geolocation_service_context.h"
 #include "jni/ContentViewCore_jni.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
-#include "third_party/WebKit/public/web/WebContextMenuData.h"
 #include "ui/android/view_android.h"
 #include "ui/android/window_android.h"
 #include "ui/events/blink/blink_event_util.h"
@@ -71,9 +70,7 @@
 using base::android::JavaParamRef;
 using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
-using blink::WebContextMenuData;
 using blink::WebGestureEvent;
-using blink::WebContextMenuData;
 using blink::WebInputEvent;
 
 namespace content {
@@ -570,46 +567,6 @@
     Java_ContentViewCore_requestDisallowInterceptTouchEvent(env, obj);
 }
 
-bool ContentViewCoreImpl::ShowSelectionMenu(const ContextMenuParams& params) {
-  // Display paste pop-up only when selection is empty and editable.
-  const bool from_touch = params.source_type == ui::MENU_SOURCE_TOUCH ||
-                          params.source_type == ui::MENU_SOURCE_LONG_PRESS ||
-                          params.source_type == ui::MENU_SOURCE_TOUCH_HANDLE ||
-                          params.source_type == ui::MENU_SOURCE_STYLUS;
-  if (!from_touch || (!params.is_editable && params.selection_text.empty()))
-    return false;
-
-  RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
-  if (!view)
-    return false;
-
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
-  if (obj.is_null())
-    return false;
-
-  const bool can_select_all =
-      !!(params.edit_flags & WebContextMenuData::kCanSelectAll);
-  const bool can_edit_richly =
-      !!(params.edit_flags & blink::WebContextMenuData::kCanEditRichly);
-  int handle_height = GetRenderWidgetHostViewAndroid()->GetTouchHandleHeight();
-  const bool is_password_type =
-      params.input_field_type ==
-      blink::WebContextMenuData::kInputFieldTypePassword;
-  const ScopedJavaLocalRef<jstring> jselected_text =
-      ConvertUTF16ToJavaString(env, params.selection_text);
-  const bool should_suggest = params.source_type == ui::MENU_SOURCE_TOUCH ||
-                              params.source_type == ui::MENU_SOURCE_LONG_PRESS;
-
-  Java_ContentViewCore_showSelectionMenu(
-      env, obj, params.selection_rect.x(), params.selection_rect.y(),
-      params.selection_rect.right(),
-      params.selection_rect.bottom() + handle_height, params.is_editable,
-      is_password_type, jselected_text, can_select_all, can_edit_richly,
-      should_suggest);
-  return true;
-}
-
 void ContentViewCoreImpl::ShowDisambiguationPopup(
     const gfx::Rect& rect_pixels,
     const SkBitmap& zoomed_bitmap) {
@@ -1109,27 +1066,6 @@
   }
 }
 
-void ContentViewCoreImpl::OnShowUnhandledTapUIIfNeeded(int x_dip, int y_dip) {
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
-  if (obj.is_null())
-    return;
-  Java_ContentViewCore_onShowUnhandledTapUIIfNeeded(
-      env, obj, static_cast<jint>(x_dip * dpi_scale()),
-      static_cast<jint>(y_dip * dpi_scale()));
-}
-
-void ContentViewCoreImpl::OnSelectWordAroundCaretAck(bool did_select,
-                                                     int start_adjust,
-                                                     int end_adjust) {
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
-  if (obj.is_null())
-    return;
-  Java_ContentViewCore_onSelectWordAroundCaretAck(env, obj, did_select,
-                                                  start_adjust, end_adjust);
-}
-
 void ContentViewCoreImpl::HidePopupsAndPreserveSelection() {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index 59e4e7f..cb7e7a4 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -38,7 +38,6 @@
 class GinJavaBridgeDispatcherHost;
 class RenderFrameHost;
 class RenderWidgetHostViewAndroid;
-struct ContextMenuParams;
 struct MenuItem;
 
 class ContentViewCoreImpl : public ContentViewCore,
@@ -56,7 +55,6 @@
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject() override;
   WebContents* GetWebContents() const override;
   ui::WindowAndroid* GetWindowAndroid() const override;
-  bool ShowSelectionMenu(const ContextMenuParams& params) override;
 
   void AddObserver(ContentViewCoreImplObserver* observer);
   void RemoveObserver(ContentViewCoreImplObserver* observer);
@@ -291,10 +289,6 @@
   void SelectBetweenCoordinates(const gfx::PointF& base,
                                 const gfx::PointF& extent);
 
-  void OnShowUnhandledTapUIIfNeeded(int x_dip, int y_dip);
-  void OnSelectWordAroundCaretAck(bool did_select,
-                                  int start_adjust,
-                                  int end_adjust);
   void OnTouchDown(const base::android::ScopedJavaLocalRef<jobject>& event);
 
   ui::ViewAndroid* GetViewAndroid() const;
diff --git a/content/browser/android/selection_popup_controller.cc b/content/browser/android/selection_popup_controller.cc
index 5f56370c..dddea6e 100644
--- a/content/browser/android/selection_popup_controller.cc
+++ b/content/browser/android/selection_popup_controller.cc
@@ -9,12 +9,16 @@
 #include "base/android/scoped_java_ref.h"
 #include "content/browser/renderer_host/render_widget_host_view_android.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/context_menu_params.h"
 #include "jni/SelectionPopupController_jni.h"
+#include "third_party/WebKit/public/web/WebContextMenuData.h"
 
 using base::android::AttachCurrentThread;
+using base::android::ConvertUTF16ToJavaString;
 using base::android::ConvertUTF8ToJavaString;
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
+using blink::WebContextMenuData;
 
 namespace content {
 
@@ -71,4 +75,64 @@
   Java_SelectionPopupController_onSelectionChanged(env, obj, jtext);
 }
 
+bool SelectionPopupController::ShowSelectionMenu(
+    const ContextMenuParams& params,
+    int handle_height) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_obj_.get(env);
+  if (obj.is_null())
+    return false;
+
+  // Display paste pop-up only when selection is empty and editable.
+  const bool from_touch = params.source_type == ui::MENU_SOURCE_TOUCH ||
+                          params.source_type == ui::MENU_SOURCE_LONG_PRESS ||
+                          params.source_type == ui::MENU_SOURCE_TOUCH_HANDLE ||
+                          params.source_type == ui::MENU_SOURCE_STYLUS;
+  if (!from_touch || (!params.is_editable && params.selection_text.empty()))
+    return false;
+
+  const bool can_select_all =
+      !!(params.edit_flags & WebContextMenuData::kCanSelectAll);
+  const bool can_edit_richly =
+      !!(params.edit_flags & blink::WebContextMenuData::kCanEditRichly);
+  const bool is_password_type =
+      params.input_field_type ==
+      blink::WebContextMenuData::kInputFieldTypePassword;
+  const ScopedJavaLocalRef<jstring> jselected_text =
+      ConvertUTF16ToJavaString(env, params.selection_text);
+  const bool should_suggest = params.source_type == ui::MENU_SOURCE_TOUCH ||
+                              params.source_type == ui::MENU_SOURCE_LONG_PRESS;
+
+  Java_SelectionPopupController_showSelectionMenu(
+      env, obj, params.selection_rect.x(), params.selection_rect.y(),
+      params.selection_rect.right(),
+      params.selection_rect.bottom() + handle_height, params.is_editable,
+      is_password_type, jselected_text, can_select_all, can_edit_richly,
+      should_suggest);
+  return true;
+}
+
+void SelectionPopupController::OnShowUnhandledTapUIIfNeeded(int x_dip,
+                                                            int y_dip,
+                                                            float dip_scale) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_obj_.get(env);
+  if (obj.is_null())
+    return;
+  Java_SelectionPopupController_onShowUnhandledTapUIIfNeeded(
+      env, obj, static_cast<jint>(x_dip * dip_scale),
+      static_cast<jint>(y_dip * dip_scale));
+}
+
+void SelectionPopupController::OnSelectWordAroundCaretAck(bool did_select,
+                                                          int start_adjust,
+                                                          int end_adjust) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_obj_.get(env);
+  if (obj.is_null())
+    return;
+  Java_SelectionPopupController_onSelectWordAroundCaretAck(
+      env, obj, did_select, start_adjust, end_adjust);
+}
+
 }  // namespace content
diff --git a/content/browser/android/selection_popup_controller.h b/content/browser/android/selection_popup_controller.h
index b5d1b3e..b768471 100644
--- a/content/browser/android/selection_popup_controller.h
+++ b/content/browser/android/selection_popup_controller.h
@@ -15,6 +15,7 @@
 namespace content {
 
 class RenderWidgetHostViewAndroid;
+struct ContextMenuParams;
 
 class SelectionPopupController : public RenderWidgetHostConnector {
  public:
@@ -31,6 +32,11 @@
   void OnSelectionEvent(ui::SelectionEventType event,
                         const gfx::RectF& selection_rect);
   void OnSelectionChanged(const std::string& text);
+  bool ShowSelectionMenu(const ContextMenuParams& params, int handle_height);
+  void OnShowUnhandledTapUIIfNeeded(int x_dip, int y_dip, float dip_scale);
+  void OnSelectWordAroundCaretAck(bool did_select,
+                                  int start_adjust,
+                                  int end_adjust);
 
  private:
   ~SelectionPopupController() override {}
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 710b3be..6f4bc94 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1325,7 +1325,11 @@
   }
   {
     TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:AudioMan");
-    audio_manager_->Shutdown();
+    if (!audio_manager_->Shutdown()) {
+      // Intentionally leak AudioManager if shutdown failed.
+      // We might run into various CHECK(s) in AudioManager destructor.
+      ignore_result(audio_manager_.release());
+    }
   }
 
   if (parts_) {
diff --git a/content/browser/push_messaging/push_messaging_manager.cc b/content/browser/push_messaging/push_messaging_manager.cc
index 33ee69c..3b0ee0f 100644
--- a/content/browser/push_messaging/push_messaging_manager.cc
+++ b/content/browser/push_messaging/push_messaging_manager.cc
@@ -111,6 +111,9 @@
   SubscribeCallback callback;
   // The following member should only be read if FromDocument() is true.
   int render_frame_id;
+
+  // True if the call to register was made with a user gesture.
+  bool user_gesture;
 };
 
 // Inner core of the PushMessagingManager which lives on the UI thread.
@@ -281,6 +284,7 @@
 void PushMessagingManager::Subscribe(int32_t render_frame_id,
                                      int64_t service_worker_registration_id,
                                      const PushSubscriptionOptions& options,
+                                     bool user_gesture,
                                      SubscribeCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // TODO(mvanouwerkerk): Validate arguments?
@@ -292,6 +296,7 @@
   data.service_worker_registration_id = service_worker_registration_id;
   data.callback = std::move(callback);
   data.options = options;
+  data.user_gesture = user_gesture;
 
   ServiceWorkerRegistration* service_worker_registration =
       service_worker_context_->GetLiveRegistration(
@@ -504,7 +509,7 @@
           GURL requesting_origin = data.requesting_origin;
           browser_context->GetPermissionManager()->RequestPermission(
               PermissionType::PUSH_MESSAGING, render_frame_host,
-              requesting_origin, false /* user_gesture */,
+              requesting_origin, data.user_gesture,
               base::Bind(
                   &PushMessagingManager::Core::DidRequestPermissionInIncognito,
                   weak_factory_ui_to_ui_.GetWeakPtr(), base::Passed(&data)));
@@ -521,7 +526,7 @@
   if (data.FromDocument()) {
     push_service->SubscribeFromDocument(
         requesting_origin, registration_id, render_process_id_, render_frame_id,
-        options,
+        options, data.user_gesture,
         base::Bind(&Core::DidRegister, weak_factory_ui_to_ui_.GetWeakPtr(),
                    base::Passed(&data)));
   } else {
diff --git a/content/browser/push_messaging/push_messaging_manager.h b/content/browser/push_messaging/push_messaging_manager.h
index d89c75e2..495f9bce 100644
--- a/content/browser/push_messaging/push_messaging_manager.h
+++ b/content/browser/push_messaging/push_messaging_manager.h
@@ -46,6 +46,7 @@
   void Subscribe(int32_t render_frame_id,
                  int64_t service_worker_registration_id,
                  const PushSubscriptionOptions& options,
+                 bool user_gesture,
                  SubscribeCallback callback) override;
   void Unsubscribe(int64_t service_worker_registration_id,
                    UnsubscribeCallback callback) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index d3c0879..e1437b4 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -646,23 +646,25 @@
 
 void RenderWidgetHostViewAndroid::OnShowUnhandledTapUIIfNeeded(int x_dip,
                                                                int y_dip) {
-  if (!content_view_core_)
+  if (!selection_popup_controller_ || !content_view_core_)
     return;
   // Validate the coordinates are within the viewport.
+  // TODO(jinsukkim): Get viewport size from ViewAndroid.
   gfx::Size viewport_size = content_view_core_->GetViewportSizeDip();
   if (x_dip < 0 || x_dip > viewport_size.width() ||
       y_dip < 0 || y_dip > viewport_size.height())
     return;
-  content_view_core_->OnShowUnhandledTapUIIfNeeded(x_dip, y_dip);
+  selection_popup_controller_->OnShowUnhandledTapUIIfNeeded(
+      x_dip, y_dip, view_.GetDipScale());
 }
 
 void RenderWidgetHostViewAndroid::OnSelectWordAroundCaretAck(bool did_select,
                                                              int start_adjust,
                                                              int end_adjust) {
-  if (!content_view_core_)
+  if (!selection_popup_controller_)
     return;
-  content_view_core_->OnSelectWordAroundCaretAck(did_select, start_adjust,
-                                                 end_adjust);
+  selection_popup_controller_->OnSelectWordAroundCaretAck(
+      did_select, start_adjust, end_adjust);
 }
 
 gfx::Rect RenderWidgetHostViewAndroid::GetViewBounds() const {
@@ -1889,6 +1891,15 @@
   }
 }
 
+bool RenderWidgetHostViewAndroid::ShowSelectionMenu(
+    const ContextMenuParams& params) {
+  if (!selection_popup_controller_)
+    return false;
+
+  return selection_popup_controller_->ShowSelectionMenu(params,
+                                                        GetTouchHandleHeight());
+}
+
 void RenderWidgetHostViewAndroid::ResolveTapDisambiguation(
     double timestamp_seconds,
     gfx::Point tap_viewport_offset,
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index 7dd93bb1..295c9da 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -58,6 +58,7 @@
 class SynchronousCompositorClient;
 class WebContentsAccessibilityAndroid;
 struct NativeWebKeyboardEvent;
+struct ContextMenuParams;
 
 // -----------------------------------------------------------------------------
 // See comments in render_widget_host_view.h about this class and its members.
@@ -241,6 +242,7 @@
   void SendMouseEvent(const ui::MotionEventAndroid&, int action_button);
   void SendMouseWheelEvent(const blink::WebMouseWheelEvent& event);
   void SendGestureEvent(const blink::WebGestureEvent& event);
+  bool ShowSelectionMenu(const ContextMenuParams& params);
   void ResolveTapDisambiguation(double timestamp_seconds,
                                 gfx::Point tap_viewport_offset,
                                 bool is_long_press);
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc
index 2fc4681..623b8fb 100644
--- a/content/browser/web_contents/web_contents_view_android.cc
+++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -8,6 +8,7 @@
 #include "base/android/jni_string.h"
 #include "base/logging.h"
 #include "cc/layers/layer.h"
+#include "content/browser/accessibility/browser_accessibility_manager_android.h"
 #include "content/browser/android/content_view_core_impl.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
@@ -98,8 +99,7 @@
 void WebContentsViewAndroid::SetContentViewCore(
     ContentViewCoreImpl* content_view_core) {
   content_view_core_ = content_view_core;
-  RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
-      web_contents_->GetRenderWidgetHostView());
+  RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
   if (rwhv)
     rwhv->SetContentViewCore(content_view_core_);
 
@@ -118,8 +118,7 @@
 void WebContentsViewAndroid::SetOverscrollRefreshHandler(
     std::unique_ptr<ui::OverscrollRefreshHandler> overscroll_refresh_handler) {
   overscroll_refresh_handler_ = std::move(overscroll_refresh_handler);
-  RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
-      web_contents_->GetRenderWidgetHostView());
+  RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
   if (rwhv)
     rwhv->OnOverscrollRefreshHandlerAvailable();
 
@@ -153,6 +152,12 @@
   return GetNativeView();
 }
 
+RenderWidgetHostViewAndroid*
+WebContentsViewAndroid::GetRenderWidgetHostViewAndroid() {
+  return static_cast<RenderWidgetHostViewAndroid*>(
+      web_contents_->GetRenderWidgetHostView());
+}
+
 gfx::NativeWindow WebContentsViewAndroid::GetTopLevelNativeWindow() const {
   return content_view_core_ ? content_view_core_->GetWindowAndroid() : nullptr;
 }
@@ -185,8 +190,7 @@
 }
 
 void WebContentsViewAndroid::Focus() {
-  RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
-      web_contents_->GetRenderWidgetHostView());
+  RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
   if (web_contents_->ShowingInterstitialPage()) {
     web_contents_->GetInterstitialPage()->Focus();
   } else {
@@ -266,6 +270,12 @@
 
 void WebContentsViewAndroid::ShowContextMenu(
     RenderFrameHost* render_frame_host, const ContextMenuParams& params) {
+  RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
+  // See if context menu is handled by SelectionController as a selection menu.
+  // If not, use the delegate to show it.
+  if (view && view->ShowSelectionMenu(params))
+    return;
+
   if (delegate_)
     delegate_->ShowContextMenu(render_frame_host, params);
 }
@@ -451,6 +461,19 @@
   return false;  // let the children handle the actual event.
 }
 
+bool WebContentsViewAndroid::OnMouseEvent(const ui::MotionEventAndroid& event) {
+  // Hover events can be intercepted when in accessibility mode.
+  auto action = event.GetAction();
+  if (action != ui::MotionEventAndroid::ACTION_HOVER_ENTER &&
+      action != ui::MotionEventAndroid::ACTION_HOVER_EXIT &&
+      action != ui::MotionEventAndroid::ACTION_HOVER_MOVE)
+    return false;
+
+  auto* manager = static_cast<BrowserAccessibilityManagerAndroid*>(
+      web_contents_->GetRootBrowserAccessibilityManager());
+  return manager && manager->OnHoverEvent(event);
+}
+
 void WebContentsViewAndroid::OnPhysicalBackingSizeChanged() {
   if (web_contents_->GetRenderWidgetHostView())
     web_contents_->SendScreenRects();
diff --git a/content/browser/web_contents/web_contents_view_android.h b/content/browser/web_contents/web_contents_view_android.h
index bc6961a1..205d2a54 100644
--- a/content/browser/web_contents/web_contents_view_android.h
+++ b/content/browser/web_contents/web_contents_view_android.h
@@ -20,6 +20,7 @@
 
 namespace content {
 class ContentViewCoreImpl;
+class RenderWidgetHostViewAndroid;
 class SynchronousCompositorClient;
 class WebContentsImpl;
 
@@ -48,6 +49,8 @@
   void SetOverscrollRefreshHandler(
       std::unique_ptr<ui::OverscrollRefreshHandler> overscroll_refresh_handler);
 
+  RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid();
+
   // WebContentsView implementation --------------------------------------------
   gfx::NativeView GetNativeView() const override;
   gfx::NativeView GetContentNativeView() const override;
@@ -99,6 +102,7 @@
   // ui::ViewClient implementation.
   bool OnTouchEvent(const ui::MotionEventAndroid& event,
                     bool for_touch_handle) override;
+  bool OnMouseEvent(const ui::MotionEventAndroid& event) override;
   bool OnDragEvent(const ui::DragEventAndroid& event) override;
   void OnPhysicalBackingSizeChanged() override;
 
diff --git a/content/child/push_messaging/push_provider.cc b/content/child/push_messaging/push_provider.cc
index adcc0eb..54761ccb 100644
--- a/content/child/push_messaging/push_provider.cc
+++ b/content/child/push_messaging/push_provider.cc
@@ -121,6 +121,7 @@
 void PushProvider::Subscribe(
     blink::WebServiceWorkerRegistration* service_worker_registration,
     const blink::WebPushSubscriptionOptions& options,
+    bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
   DCHECK(service_worker_registration);
   DCHECK(callbacks);
@@ -136,7 +137,7 @@
 
   push_messaging_manager_->Subscribe(
       ChildProcessHost::kInvalidUniqueID, service_worker_registration_id,
-      content_options,
+      content_options, user_gesture,
       // Safe to use base::Unretained because |push_messaging_manager_ |is owned
       // by |this|.
       base::Bind(&PushProvider::DidSubscribe, base::Unretained(this),
diff --git a/content/child/push_messaging/push_provider.h b/content/child/push_messaging/push_provider.h
index f9e823e..6264fc18 100644
--- a/content/child/push_messaging/push_provider.h
+++ b/content/child/push_messaging/push_provider.h
@@ -48,6 +48,7 @@
   void Subscribe(
       blink::WebServiceWorkerRegistration* service_worker_registration,
       const blink::WebPushSubscriptionOptions& options,
+      bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) override;
   void Unsubscribe(
       blink::WebServiceWorkerRegistration* service_worker_registration,
diff --git a/content/common/push_messaging.mojom b/content/common/push_messaging.mojom
index 6ec28f5..94b0ea1 100644
--- a/content/common/push_messaging.mojom
+++ b/content/common/push_messaging.mojom
@@ -143,7 +143,8 @@
 interface PushMessaging {
   Subscribe(int32 render_frame_id,
             int64 service_worker_registration_id,
-            PushSubscriptionOptions options)
+            PushSubscriptionOptions options,
+            bool user_gesture)
            => (PushRegistrationStatus status,
                url.mojom.Url? endpoint,
                PushSubscriptionOptions? options,
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentView.java b/content/public/android/java/src/org/chromium/content/browser/ContentView.java
index 527e60a4..73e9b7d7 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentView.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentView.java
@@ -196,7 +196,7 @@
      */
     @Override
     public boolean onHoverEvent(MotionEvent event) {
-        boolean consumed = mContentViewCore.onHoverEvent(event);
+        boolean consumed = getEventForwarder().onHoverEvent(event);
         if (!mContentViewCore.isTouchExplorationEnabled()) super.onHoverEvent(event);
         return consumed;
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index 4a8080b6..123e6932 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -845,18 +845,6 @@
         destroyPastePopup();
     }
 
-    @SuppressWarnings("unused")
-    @CalledByNative
-    private void onShowUnhandledTapUIIfNeeded(int x, int y) {
-        mSelectionPopupController.onShowUnhandledTapUIIfNeeded(x, y);
-    }
-
-    @SuppressWarnings("unused")
-    @CalledByNative
-    private void onSelectWordAroundCaretAck(boolean didSelect, int startAdjust, int endAdjust) {
-        mSelectionPopupController.onSelectWordAroundCaretAck(didSelect, startAdjust, endAdjust);
-    }
-
     /**
      * Called just prior to a tap or press gesture being forwarded to the renderer.
      */
@@ -1251,27 +1239,6 @@
     }
 
     /**
-     * @see View#onHoverEvent(MotionEvent)
-     * Mouse move events are sent on hover move.
-     */
-    public boolean onHoverEvent(MotionEvent event) {
-        TraceEvent.begin("onHoverEvent");
-
-        MotionEvent offset = createOffsetMotionEvent(event);
-        try {
-            if (mWebContentsAccessibility != null && !mIsObscuredByAnotherView
-                    && mWebContentsAccessibility.onHoverEvent(offset)) {
-                return true;
-            }
-
-            return getEventForwarder().onMouseEvent(event);
-        } finally {
-            offset.recycle();
-            TraceEvent.end("onHoverEvent");
-        }
-    }
-
-    /**
      * Removes noise from joystick motion events.
      */
     private static float getFilteredAxisValue(MotionEvent event, int axis) {
@@ -1326,12 +1293,6 @@
         getEventForwarder().setCurrentTouchEventOffsets(dx, dy);
     }
 
-    private MotionEvent createOffsetMotionEvent(MotionEvent src) {
-        MotionEvent dst = MotionEvent.obtain(src);
-        dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
-        return dst;
-    }
-
     /**
      * @see View#scrollBy(int, int)
      * Currently the ContentView scrolling happens in the native side. In
@@ -1678,14 +1639,6 @@
         mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
     }
 
-    @CalledByNative
-    private void showSelectionMenu(int left, int top, int right, int bottom, boolean isEditable,
-            boolean isPasswordType, String selectionText, boolean canSelectAll,
-            boolean canEditRichly, boolean shouldSuggest) {
-        mSelectionPopupController.showSelectionMenu(left, top, right, bottom, isEditable,
-                isPasswordType, selectionText, canSelectAll, canEditRichly, shouldSuggest);
-    }
-
     private void destroyPastePopup() {
         mSelectionPopupController.destroyPastePopup();
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
index 9d1e517..54006b0 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
@@ -220,7 +220,8 @@
         mAllowedMenuItems = allowedMenuItems;
     }
 
-    public void showSelectionMenu(int left, int top, int right, int bottom, boolean isEditable,
+    @CalledByNative
+    private void showSelectionMenu(int left, int top, int right, int bottom, boolean isEditable,
             boolean isPasswordType, String selectionText, boolean canSelectAll,
             boolean canRichlyEdit, boolean shouldSuggest) {
         mSelectionRect.set(left, top, right, bottom);
@@ -1070,13 +1071,15 @@
         assert !mHidden;
     }
 
-    void onShowUnhandledTapUIIfNeeded(int x, int y) {
+    @CalledByNative
+    private void onShowUnhandledTapUIIfNeeded(int x, int y) {
         if (mSelectionClient != null) {
             mSelectionClient.showUnhandledTapUIIfNeeded(x, y);
         }
     }
 
-    void onSelectWordAroundCaretAck(boolean didSelect, int startAdjust, int endAdjust) {
+    @CalledByNative
+    private void onSelectWordAroundCaretAck(boolean didSelect, int startAdjust, int endAdjust) {
         if (mSelectionClient != null) {
             mSelectionClient.selectWordAroundCaretAck(didSelect, startAdjust, endAdjust);
         }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java
index 0645b46..fd39ba9 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java
@@ -140,7 +140,7 @@
     }
 
     public boolean isEnabled() {
-        return nativeIsEnabled(mNativeObj);
+        return mNativeObj != 0 ? nativeIsEnabled(mNativeObj) : false;
     }
 
     public void enable() {
@@ -375,15 +375,14 @@
         }
     }
 
-    /**
-     * @see View#onHoverEvent(MotionEvent)
-     */
-    public boolean onHoverEvent(MotionEvent event) {
+    // Returns true if the hover event is to be consumed by accessibility feature.
+    @CalledByNative
+    private boolean onHoverEvent(int action) {
         if (!mAccessibilityManager.isEnabled()) {
             return false;
         }
 
-        if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+        if (action == MotionEvent.ACTION_HOVER_EXIT) {
             mIsHovering = false;
             if (mLastHoverId != View.NO_ID) {
                 sendAccessibilityEvent(mLastHoverId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
@@ -398,16 +397,6 @@
 
         mIsHovering = true;
         mUserHasTouchExplored = true;
-        float x = event.getX();
-        float y = event.getY();
-
-        // Convert to CSS coordinates.
-        int cssX = (int) (mRenderCoordinates.fromPixToLocalCss(x));
-        int cssY = (int) (mRenderCoordinates.fromPixToLocalCss(y));
-
-        // This sends an IPC to the render process to do the hit testing.
-        // The response is handled by handleHover.
-        nativeHitTest(mNativeObj, cssX, cssY);
         return true;
     }
 
@@ -1242,7 +1231,6 @@
             long nativeWebContentsAccessibilityAndroid, int id);
     private native int nativeGetEditableTextSelectionEnd(
             long nativeWebContentsAccessibilityAndroid, int id);
-    private native void nativeHitTest(long nativeWebContentsAccessibilityAndroid, int x, int y);
     private native boolean nativePopulateAccessibilityNodeInfo(
             long nativeWebContentsAccessibilityAndroid, AccessibilityNodeInfo info, int id);
     private native boolean nativePopulateAccessibilityEvent(
diff --git a/content/public/browser/android/content_view_core.h b/content/public/browser/android/content_view_core.h
index 781f20ba..a032485 100644
--- a/content/public/browser/android/content_view_core.h
+++ b/content/public/browser/android/content_view_core.h
@@ -22,7 +22,6 @@
 namespace content {
 
 class WebContents;
-struct ContextMenuParams;
 
 // DEPRECATED. Do not add methods.
 // Refer to the public WebContents interface or elsewhere instead.
@@ -35,7 +34,6 @@
 
   // May return null reference.
   virtual base::android::ScopedJavaLocalRef<jobject> GetJavaObject() = 0;
-  virtual bool ShowSelectionMenu(const ContextMenuParams& params) = 0;
 
   virtual ui::WindowAndroid* GetWindowAndroid() const = 0;
 
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h
index 41cbd4d..42cb5d2 100644
--- a/content/public/browser/push_messaging_service.h
+++ b/content/public/browser/push_messaging_service.h
@@ -53,6 +53,7 @@
                                      int renderer_id,
                                      int render_frame_id,
                                      const PushSubscriptionOptions& options,
+                                     bool user_gesture,
                                      const RegisterCallback& callback) = 0;
 
   // Subscribe the given |options.sender_info| with the push messaging service.
diff --git a/content/renderer/push_messaging/push_messaging_client.cc b/content/renderer/push_messaging/push_messaging_client.cc
index ab44099..51785ca 100644
--- a/content/renderer/push_messaging/push_messaging_client.cc
+++ b/content/renderer/push_messaging/push_messaging_client.cc
@@ -46,6 +46,7 @@
 void PushMessagingClient::Subscribe(
     blink::WebServiceWorkerRegistration* service_worker_registration,
     const blink::WebPushSubscriptionOptions& options,
+    bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
   DCHECK(service_worker_registration);
   DCHECK(callbacks);
@@ -55,16 +56,17 @@
   if (options.application_server_key.IsEmpty()) {
     RenderFrameImpl::FromRoutingID(routing_id())
         ->manifest_manager()
-        ->GetManifest(base::Bind(
-            &PushMessagingClient::DidGetManifest, base::Unretained(this),
-            service_worker_registration, options, base::Passed(&callbacks)));
+        ->GetManifest(base::Bind(&PushMessagingClient::DidGetManifest,
+                                 base::Unretained(this),
+                                 service_worker_registration, options,
+                                 user_gesture, base::Passed(&callbacks)));
   } else {
     PushSubscriptionOptions content_options;
     content_options.user_visible_only = options.user_visible_only;
     // Just treat the server key as a string of bytes and pass it to the push
     // service.
     content_options.sender_info = options.application_server_key.Latin1();
-    DoSubscribe(service_worker_registration, content_options,
+    DoSubscribe(service_worker_registration, content_options, user_gesture,
                 std::move(callbacks));
   }
 }
@@ -72,6 +74,7 @@
 void PushMessagingClient::DidGetManifest(
     blink::WebServiceWorkerRegistration* service_worker_registration,
     const blink::WebPushSubscriptionOptions& options,
+    bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
     const GURL& manifest_url,
     const Manifest& manifest,
@@ -92,13 +95,14 @@
         base::UTF16ToUTF8(manifest.gcm_sender_id.string());
   }
 
-  DoSubscribe(service_worker_registration, content_options,
+  DoSubscribe(service_worker_registration, content_options, user_gesture,
               std::move(callbacks));
 }
 
 void PushMessagingClient::DoSubscribe(
     blink::WebServiceWorkerRegistration* service_worker_registration,
     const PushSubscriptionOptions& options,
+    bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
   int64_t service_worker_registration_id =
       static_cast<WebServiceWorkerRegistrationImpl*>(
@@ -113,7 +117,7 @@
 
   DCHECK(push_messaging_manager_);
   push_messaging_manager_->Subscribe(
-      routing_id(), service_worker_registration_id, options,
+      routing_id(), service_worker_registration_id, options, user_gesture,
       // Safe to use base::Unretained because |push_messaging_manager_ |is
       // owned by |this|.
       base::Bind(&PushMessagingClient::DidSubscribe, base::Unretained(this),
diff --git a/content/renderer/push_messaging/push_messaging_client.h b/content/renderer/push_messaging/push_messaging_client.h
index e0a99fa1..695479e0 100644
--- a/content/renderer/push_messaging/push_messaging_client.h
+++ b/content/renderer/push_messaging/push_messaging_client.h
@@ -44,11 +44,13 @@
   void Subscribe(
       blink::WebServiceWorkerRegistration* service_worker_registration,
       const blink::WebPushSubscriptionOptions& options,
+      bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) override;
 
   void DidGetManifest(
       blink::WebServiceWorkerRegistration* service_worker_registration,
       const blink::WebPushSubscriptionOptions& options,
+      bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
       const GURL& manifest_url,
       const Manifest& manifest,
@@ -57,6 +59,7 @@
   void DoSubscribe(
       blink::WebServiceWorkerRegistration* service_worker_registration,
       const PushSubscriptionOptions& options,
+      bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks);
 
   void DidSubscribe(
diff --git a/content/shell/browser/layout_test/layout_test_push_messaging_service.cc b/content/shell/browser/layout_test/layout_test_push_messaging_service.cc
index 49319359..6679236 100644
--- a/content/shell/browser/layout_test/layout_test_push_messaging_service.cc
+++ b/content/shell/browser/layout_test/layout_test_push_messaging_service.cc
@@ -75,6 +75,7 @@
     int renderer_id,
     int render_frame_id,
     const PushSubscriptionOptions& options,
+    bool user_gesture,
     const RegisterCallback& callback) {
   SubscribeFromWorker(requesting_origin, service_worker_registration_id,
                       options, callback);
diff --git a/content/shell/browser/layout_test/layout_test_push_messaging_service.h b/content/shell/browser/layout_test/layout_test_push_messaging_service.h
index 536b375..ef6cfb3d 100644
--- a/content/shell/browser/layout_test/layout_test_push_messaging_service.h
+++ b/content/shell/browser/layout_test/layout_test_push_messaging_service.h
@@ -31,6 +31,7 @@
                              int renderer_id,
                              int render_frame_id,
                              const PushSubscriptionOptions& options,
+                             bool user_gesture,
                              const RegisterCallback& callback) override;
   void SubscribeFromWorker(const GURL& requesting_origin,
                            int64_t service_worker_registration_id,
diff --git a/content/shell/browser/shell_web_contents_view_delegate_android.cc b/content/shell/browser/shell_web_contents_view_delegate_android.cc
index 7917d51..63dd182 100644
--- a/content/shell/browser/shell_web_contents_view_delegate_android.cc
+++ b/content/shell/browser/shell_web_contents_view_delegate_android.cc
@@ -21,6 +21,7 @@
 ShellWebContentsViewDelegate::ShellWebContentsViewDelegate(
     WebContents* web_contents)
     : web_contents_(web_contents) {
+  DCHECK(web_contents_);  // Avoids 'unused private field' build error.
 }
 
 ShellWebContentsViewDelegate::~ShellWebContentsViewDelegate() {
@@ -28,11 +29,6 @@
 
 void ShellWebContentsViewDelegate::ShowContextMenu(
     RenderFrameHost* render_frame_host,
-    const ContextMenuParams& params) {
-  content::ContentViewCore* content_view_core =
-      ContentViewCore::FromWebContents(web_contents_);
-  if (content_view_core)
-    content_view_core->ShowSelectionMenu(params);
-}
+    const ContextMenuParams& params) {}
 
 }  // namespace content
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index f4e2b6c..25dd04f 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -49,6 +49,8 @@
     "test/fake_central.h",
     "test/fake_peripheral.cc",
     "test/fake_peripheral.h",
+    "test/fake_remote_gatt_service.cc",
+    "test/fake_remote_gatt_service.h",
   ]
 
   deps = [
diff --git a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
index 1e7b4bf..bba5e49 100644
--- a/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
+++ b/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom
@@ -88,4 +88,11 @@
   // This method aims to simulate the response once all of these procedures
   // have completed or if there was an error during any of them.
   SetNextGATTDiscoveryResponse(string address, uint16 code) => (bool success);
+
+  // Adds a fake GATT Service with |service_uuid| to be found when
+  // discovering the peripheral's GATT Attributes. Runs its callback with
+  // the fake service's Id. Runs its callback with nullopt if it failed to
+  // add the fake service.
+  AddFakeService(string peripheral_address, UUID service_uuid)
+      => (string? service_id);
 };
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index 74adb7b95..a3e8cf6 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_discovery_filter.h"
@@ -75,6 +76,19 @@
   std::move(callback).Run(true);
 }
 
+void FakeCentral::AddFakeService(const std::string& peripheral_address,
+                                 const device::BluetoothUUID& service_uuid,
+                                 AddFakeServiceCallback callback) {
+  auto device_iter = devices_.find(peripheral_address);
+  if (device_iter == devices_.end()) {
+    std::move(callback).Run(base::nullopt);
+  }
+
+  FakePeripheral* fake_peripheral =
+      static_cast<FakePeripheral*>(device_iter->second.get());
+  std::move(callback).Run(fake_peripheral->AddFakeService(service_uuid));
+}
+
 std::string FakeCentral::GetAddress() const {
   NOTREACHED();
   return std::string();
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 828cdb3..4786038 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "device/bluetooth/bluetooth_adapter.h"
@@ -37,6 +38,9 @@
       const std::string& address,
       uint16_t code,
       SetNextGATTDiscoveryResponseCallback callback) override;
+  void AddFakeService(const std::string& peripheral_address,
+                      const device::BluetoothUUID& service_uuid,
+                      AddFakeServiceCallback callback) override;
 
   // BluetoothAdapter overrides:
   std::string GetAddress() const override;
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index 3a0e0e7..3eba213 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -4,7 +4,12 @@
 
 #include "device/bluetooth/test/fake_peripheral.h"
 
+#include <utility>
+
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "device/bluetooth/test/fake_remote_gatt_service.h"
 
 namespace bluetooth {
 
@@ -42,6 +47,22 @@
   next_discovery_response_ = code;
 }
 
+std::string FakePeripheral::AddFakeService(
+    const device::BluetoothUUID& service_uuid) {
+  std::string new_service_id = base::SizeTToString(++last_service_id_);
+
+  GattServiceMap::iterator it;
+  bool inserted;
+
+  std::tie(it, inserted) = gatt_services_.emplace(
+      new_service_id,
+      base::MakeUnique<FakeRemoteGattService>(new_service_id, service_uuid,
+                                              true /* is_primary */, this));
+
+  DCHECK(inserted);
+  return it->second->GetIdentifier();
+}
+
 uint32_t FakePeripheral::GetBluetoothClass() const {
   NOTREACHED();
   return 0;
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index b42a4b2..a23e86d 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/test/fake_central.h"
+#include "device/bluetooth/test/fake_remote_gatt_service.h"
 
 namespace bluetooth {
 
@@ -45,6 +46,10 @@
   // after IsGattDiscoveryComplete is called.
   void SetNextGATTDiscoveryResponse(uint16_t code);
 
+  // Adds a fake primary service with |service_uuid| to this peripheral.
+  // Returns the service's Id.
+  std::string AddFakeService(const device::BluetoothUUID& service_uuid);
+
   // BluetoothDevice overrides:
   uint32_t GetBluetoothClass() const override;
 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
@@ -114,6 +119,10 @@
   // True when this Bluetooth interface is connected to the device.
   bool gatt_connected_;
 
+  // Keeps track of the last Id used to create a fake service. Incremented
+  // every time a new fake service is added.
+  size_t last_service_id_;
+
   // Used to simulate a GATT Discovery procedure.
   // Mutable because IsGattServicesDiscoveryComplete needs to set this but
   // is const.
diff --git a/device/bluetooth/test/fake_remote_gatt_service.cc b/device/bluetooth/test/fake_remote_gatt_service.cc
new file mode 100644
index 0000000..151c544
--- /dev/null
+++ b/device/bluetooth/test/fake_remote_gatt_service.cc
@@ -0,0 +1,60 @@
+// 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 "device/bluetooth/test/fake_remote_gatt_service.h"
+
+#include <vector>
+
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace bluetooth {
+
+FakeRemoteGattService::FakeRemoteGattService(
+    const std::string& service_id,
+    const device::BluetoothUUID& service_uuid,
+    bool is_primary,
+    device::BluetoothDevice* device)
+    : service_id_(service_id),
+      service_uuid_(service_uuid),
+      is_primary_(is_primary),
+      device_(device) {}
+
+FakeRemoteGattService::~FakeRemoteGattService() {}
+
+std::string FakeRemoteGattService::GetIdentifier() const {
+  return service_id_;
+}
+
+device::BluetoothUUID FakeRemoteGattService::GetUUID() const {
+  return service_uuid_;
+}
+
+bool FakeRemoteGattService::IsPrimary() const {
+  return is_primary_;
+}
+
+device::BluetoothDevice* FakeRemoteGattService::GetDevice() const {
+  return device_;
+}
+
+std::vector<device::BluetoothRemoteGattCharacteristic*>
+FakeRemoteGattService::GetCharacteristics() const {
+  NOTREACHED();
+  return std::vector<device::BluetoothRemoteGattCharacteristic*>();
+}
+
+std::vector<device::BluetoothRemoteGattService*>
+FakeRemoteGattService::GetIncludedServices() const {
+  NOTREACHED();
+  return std::vector<device::BluetoothRemoteGattService*>();
+}
+
+device::BluetoothRemoteGattCharacteristic*
+FakeRemoteGattService::GetCharacteristic(const std::string& identifier) const {
+  NOTREACHED();
+  return nullptr;
+}
+
+}  // namespace bluetooth
diff --git a/device/bluetooth/test/fake_remote_gatt_service.h b/device/bluetooth/test/fake_remote_gatt_service.h
new file mode 100644
index 0000000..fda3bfe6
--- /dev/null
+++ b/device/bluetooth/test/fake_remote_gatt_service.h
@@ -0,0 +1,54 @@
+// 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 DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_SERVICE_H_
+#define DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_SERVICE_H_
+
+#include <string>
+#include <vector>
+
+#include "device/bluetooth/bluetooth_remote_gatt_service.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+class BluetoothDevice;
+class BluetoothRemoteGattService;
+class BluetoothRemoteGattCharacteristic;
+}
+
+namespace bluetooth {
+
+// Implements device::BluetoothRemoteGattService. Meant to be used by
+// FakePeripheral to keep track of the service's state and attributes.
+class FakeRemoteGattService : public device::BluetoothRemoteGattService {
+ public:
+  FakeRemoteGattService(const std::string& service_id,
+                        const device::BluetoothUUID& service_uuid,
+                        bool is_primary,
+                        device::BluetoothDevice* device);
+  ~FakeRemoteGattService() override;
+
+  // device::BluetoothGattService overrides:
+  std::string GetIdentifier() const override;
+  device::BluetoothUUID GetUUID() const override;
+  bool IsPrimary() const override;
+
+  // device::BluetoothRemoteGattService overrides:
+  device::BluetoothDevice* GetDevice() const override;
+  std::vector<device::BluetoothRemoteGattCharacteristic*> GetCharacteristics()
+      const override;
+  std::vector<device::BluetoothRemoteGattService*> GetIncludedServices()
+      const override;
+  device::BluetoothRemoteGattCharacteristic* GetCharacteristic(
+      const std::string& identifier) const override;
+
+ private:
+  const std::string service_id_;
+  const device::BluetoothUUID service_uuid_;
+  const bool is_primary_;
+  device::BluetoothDevice* device_;
+};
+
+}  // namespace bluetooth
+
+#endif  // DEVICE_BLUETOOTH_TEST_FAKE_REMOTE_GATT_SERVICE_H_
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn
index 08dc472..b7250e9 100644
--- a/gpu/ipc/common/BUILD.gn
+++ b/gpu/ipc/common/BUILD.gn
@@ -152,9 +152,6 @@
     "//ui/gfx/geometry/mojo",
   ]
 
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-
   # TODO(crbug.com/699569): Convert to use the new JS bindings.
   use_new_js_bindings = false
 }
@@ -168,9 +165,6 @@
   public_deps = [
     ":interfaces",
   ]
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
 }
 
 source_set("struct_traits") {
diff --git a/gpu/ipc/common/struct_traits_unittest.cc b/gpu/ipc/common/struct_traits_unittest.cc
index d220704..3846468 100644
--- a/gpu/ipc/common/struct_traits_unittest.cc
+++ b/gpu/ipc/common/struct_traits_unittest.cc
@@ -31,58 +31,54 @@
  private:
   // TraitsTestService:
   void EchoDxDiagNode(const DxDiagNode& d,
-                      const EchoDxDiagNodeCallback& callback) override {
-    callback.Run(d);
+                      EchoDxDiagNodeCallback callback) override {
+    std::move(callback).Run(d);
   }
 
   void EchoGpuDevice(const GPUInfo::GPUDevice& g,
-                     const EchoGpuDeviceCallback& callback) override {
-    callback.Run(g);
+                     EchoGpuDeviceCallback callback) override {
+    std::move(callback).Run(g);
   }
 
-  void EchoGpuInfo(const GPUInfo& g,
-                   const EchoGpuInfoCallback& callback) override {
-    callback.Run(g);
+  void EchoGpuInfo(const GPUInfo& g, EchoGpuInfoCallback callback) override {
+    std::move(callback).Run(g);
   }
 
-  void EchoMailbox(const Mailbox& m,
-                   const EchoMailboxCallback& callback) override {
-    callback.Run(m);
+  void EchoMailbox(const Mailbox& m, EchoMailboxCallback callback) override {
+    std::move(callback).Run(m);
   }
 
   void EchoMailboxHolder(const MailboxHolder& r,
-                         const EchoMailboxHolderCallback& callback) override {
-    callback.Run(r);
+                         EchoMailboxHolderCallback callback) override {
+    std::move(callback).Run(r);
   }
 
   void EchoSyncToken(const SyncToken& s,
-                     const EchoSyncTokenCallback& callback) override {
-    callback.Run(s);
+                     EchoSyncTokenCallback callback) override {
+    std::move(callback).Run(s);
   }
 
   void EchoVideoDecodeAcceleratorSupportedProfile(
       const VideoDecodeAcceleratorSupportedProfile& v,
-      const EchoVideoDecodeAcceleratorSupportedProfileCallback& callback)
-      override {
-    callback.Run(v);
+      EchoVideoDecodeAcceleratorSupportedProfileCallback callback) override {
+    std::move(callback).Run(v);
   }
 
   void EchoVideoDecodeAcceleratorCapabilities(
       const VideoDecodeAcceleratorCapabilities& v,
-      const EchoVideoDecodeAcceleratorCapabilitiesCallback& callback) override {
-    callback.Run(v);
+      EchoVideoDecodeAcceleratorCapabilitiesCallback callback) override {
+    std::move(callback).Run(v);
   }
 
   void EchoVideoEncodeAcceleratorSupportedProfile(
       const VideoEncodeAcceleratorSupportedProfile& v,
-      const EchoVideoEncodeAcceleratorSupportedProfileCallback& callback)
-      override {
-    callback.Run(v);
+      EchoVideoEncodeAcceleratorSupportedProfileCallback callback) override {
+    std::move(callback).Run(v);
   }
 
   void EchoGpuPreferences(const GpuPreferences& prefs,
-                          const EchoGpuPreferencesCallback& callback) override {
-    callback.Run(prefs);
+                          EchoGpuPreferencesCallback callback) override {
+    std::move(callback).Run(prefs);
   }
 
   base::MessageLoop loop_;
diff --git a/ios/web_view/test/BUILD.gn b/ios/web_view/test/BUILD.gn
index 4518b7cc..8b22400c 100644
--- a/ios/web_view/test/BUILD.gn
+++ b/ios/web_view/test/BUILD.gn
@@ -45,11 +45,12 @@
   sources = [
     "observer.h",
     "observer.mm",
-    "web_view_interaction_test_util.h",
-    "web_view_interaction_test_util.mm",
+    "web_view_test_util.h",
+    "web_view_test_util.mm",
   ]
 
   deps = [
+    "//base:base",
     "//ios/testing:ios_test_support",
     "//ios/web_view:web_view+link",
   ]
diff --git a/ios/web_view/test/chrome_web_view_kvo_inttest.mm b/ios/web_view/test/chrome_web_view_kvo_inttest.mm
index c7badae..4cbd0c2 100644
--- a/ios/web_view/test/chrome_web_view_kvo_inttest.mm
+++ b/ios/web_view/test/chrome_web_view_kvo_inttest.mm
@@ -5,13 +5,12 @@
 #import <ChromeWebView/ChromeWebView.h>
 #import <Foundation/Foundation.h>
 
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/stringprintf.h"
 #import "base/strings/sys_string_conversions.h"
 #import "ios/testing/wait_util.h"
 #import "ios/web_view/test/chrome_web_view_test.h"
 #import "ios/web_view/test/observer.h"
-#import "ios/web_view/test/web_view_interaction_test_util.h"
+#import "ios/web_view/test/web_view_test_util.h"
 #import "net/base/mac/url_conversions.h"
 #include "testing/gtest_mac.h"
 #include "url/gurl.h"
@@ -20,23 +19,11 @@
 #error "This file requires ARC support."
 #endif
 
+namespace ios_web_view {
+
 // Tests that the KVO compliant properties of CWVWebView correctly report
 // changes.
-class ChromeWebViewKvoTest : public ios_web_view::ChromeWebViewTest {
- protected:
-  ChromeWebViewKvoTest() {
-    CWVWebViewConfiguration* configuration =
-        [CWVWebViewConfiguration defaultConfiguration];
-    web_view_.reset([[CWVWebView alloc]
-        initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)
-        configuration:configuration]);
-  }
-
-  // Web View used to listen for expected KVO property changes.
-  base::scoped_nsobject<CWVWebView> web_view_;
-};
-
-namespace ios_web_view {
+typedef ios_web_view::ChromeWebViewTest ChromeWebViewKvoTest;
 
 // Tests that CWVWebView correctly reports |canGoBack| and |canGoForward| state.
 TEST_F(ChromeWebViewKvoTest, CanGoBackForward) {
@@ -60,38 +47,38 @@
       "<a id='link_1' href='" + page_2_url.spec() + "'>Link 1</a>";
   GURL page_1_url = GetUrlForPageWithHtmlBody(page_1_html);
 
-  LoadUrl(web_view_, net::NSURLWithGURL(page_1_url));
+  ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page_1_url)));
   // Loading initial URL should not affect back/forward navigation state.
   EXPECT_FALSE([back_observer.lastValue boolValue]);
   EXPECT_FALSE([forward_observer.lastValue boolValue]);
 
   // Navigate to page 2.
-  EXPECT_TRUE(test::TapChromeWebViewElementWithId(web_view_, @"link_1"));
-  WaitForPageLoadCompletion(web_view_);
+  EXPECT_TRUE(test::TapWebViewElementWithId(web_view_, @"link_1"));
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_TRUE([back_observer.lastValue boolValue]);
   EXPECT_FALSE([forward_observer.lastValue boolValue]);
 
   // Navigate back to page 1.
   [web_view_ goBack];
-  WaitForPageLoadCompletion(web_view_);
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_FALSE([back_observer.lastValue boolValue]);
   EXPECT_TRUE([forward_observer.lastValue boolValue]);
 
   // Navigate forward to page 2.
   [web_view_ goForward];
-  WaitForPageLoadCompletion(web_view_);
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_TRUE([back_observer.lastValue boolValue]);
   EXPECT_FALSE([forward_observer.lastValue boolValue]);
 
   // Navigate to page 3.
-  EXPECT_TRUE(test::TapChromeWebViewElementWithId(web_view_, @"link_2"));
-  WaitForPageLoadCompletion(web_view_);
+  EXPECT_TRUE(test::TapWebViewElementWithId(web_view_, @"link_2"));
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_TRUE([back_observer.lastValue boolValue]);
   EXPECT_FALSE([forward_observer.lastValue boolValue]);
 
   // Navigate back to page 2.
   [web_view_ goBack];
-  WaitForPageLoadCompletion(web_view_);
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_TRUE([back_observer.lastValue boolValue]);
   EXPECT_TRUE([forward_observer.lastValue boolValue]);
 }
@@ -111,17 +98,17 @@
   GURL page_1_url = GetUrlForPageWithTitleAndBody(
       base::SysNSStringToUTF8(page_1_title), page_1_html);
 
-  LoadUrl(web_view_, net::NSURLWithGURL(page_1_url));
+  ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page_1_url)));
   EXPECT_NSEQ(page_1_title, observer.lastValue);
 
   // Navigate to page 2.
-  EXPECT_TRUE(test::TapChromeWebViewElementWithId(web_view_, @"link_1"));
-  WaitForPageLoadCompletion(web_view_);
+  EXPECT_TRUE(test::TapWebViewElementWithId(web_view_, @"link_1"));
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_NSEQ(page_2_title, observer.lastValue);
 
   // Navigate back to page 1.
   [web_view_ goBack];
-  WaitForPageLoadCompletion(web_view_);
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_NSEQ(page_1_title, observer.lastValue);
 }
 
@@ -136,19 +123,19 @@
       "<a id='link_1' href='%s'>Link 1</a>", page_2_url.spec().c_str());
   GURL page_1_url = GetUrlForPageWithTitleAndBody("Page 1", page_1_html);
 
-  LoadUrl(web_view_, net::NSURLWithGURL(page_1_url));
+  ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page_1_url)));
   EXPECT_TRUE([observer.previousValue boolValue]);
   EXPECT_FALSE([observer.lastValue boolValue]);
 
   // Navigate to page 2.
-  EXPECT_TRUE(test::TapChromeWebViewElementWithId(web_view_, @"link_1"));
-  WaitForPageLoadCompletion(web_view_);
+  EXPECT_TRUE(test::TapWebViewElementWithId(web_view_, @"link_1"));
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_TRUE([observer.previousValue boolValue]);
   EXPECT_FALSE([observer.lastValue boolValue]);
 
   // Navigate back to page 1.
   [web_view_ goBack];
-  WaitForPageLoadCompletion(web_view_);
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_TRUE([observer.previousValue boolValue]);
   EXPECT_FALSE([observer.lastValue boolValue]);
 }
@@ -175,19 +162,19 @@
   // |visibleURL| will update immediately
   EXPECT_NSEQ(page_1_url, visible_url_observer.lastValue);
 
-  WaitForPageLoadCompletion(web_view_);
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_NSEQ(page_1_url, last_committed_url_observer.lastValue);
   EXPECT_NSEQ(page_1_url, visible_url_observer.lastValue);
 
   // Navigate to page 2.
-  EXPECT_TRUE(test::TapChromeWebViewElementWithId(web_view_, @"link_1"));
-  WaitForPageLoadCompletion(web_view_);
+  EXPECT_TRUE(test::TapWebViewElementWithId(web_view_, @"link_1"));
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_NSEQ(page_2_url, last_committed_url_observer.lastValue);
   EXPECT_NSEQ(page_2_url, visible_url_observer.lastValue);
 
   // Navigate back to page 1.
   [web_view_ goBack];
-  WaitForPageLoadCompletion(web_view_);
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
   EXPECT_NSEQ(page_1_url, last_committed_url_observer.lastValue);
   EXPECT_NSEQ(page_1_url, visible_url_observer.lastValue);
 }
diff --git a/ios/web_view/test/chrome_web_view_restorable_state_inttest.mm b/ios/web_view/test/chrome_web_view_restorable_state_inttest.mm
index 7c17e25b..0c682e0 100644
--- a/ios/web_view/test/chrome_web_view_restorable_state_inttest.mm
+++ b/ios/web_view/test/chrome_web_view_restorable_state_inttest.mm
@@ -5,20 +5,16 @@
 #import <ChromeWebView/ChromeWebView.h>
 
 #import "ios/web_view/test/chrome_web_view_test.h"
+#import "ios/web_view/test/web_view_test_util.h"
 #include "testing/gtest_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-namespace {
+namespace ios_web_view {
 
-// Creates web view for testing.
-CWVWebView* CreateWebView() {
-  return [[CWVWebView alloc]
-      initWithFrame:CGRectMake(0, 0, 50, 50)
-      configuration:[CWVWebViewConfiguration defaultConfiguration]];
-}
+namespace {
 
 // Creates a new web view and restores its state from |source_web_view|.
 CWVWebView* CreateWebViewWithState(CWVWebView* source_web_view) {
@@ -29,45 +25,40 @@
   [archiver finishEncoding];
   NSKeyedUnarchiver* unarchiver =
       [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
-  CWVWebView* result = CreateWebView();
+  CWVWebView* result = test::CreateWebView();
   [result decodeRestorableStateWithCoder:unarchiver];
   return result;
 }
 
 }  // namespace
 
-namespace ios_web_view {
-
 // Tests encodeRestorableStateWithCoder: and decodeRestorableStateWithCoder:
 // methods.
 typedef ios_web_view::ChromeWebViewTest ChromeWebViewRestorableStateTest;
 TEST_F(ChromeWebViewRestorableStateTest, EncodeDecode) {
-  // Create web view that will be used as a source for state restoration.
-  CWVWebView* web_view = CreateWebView();
-
   // Load 2 URLs to create non-default state.
-  ASSERT_FALSE(web_view.lastCommittedURL);
-  ASSERT_FALSE(web_view.visibleURL);
-  ASSERT_FALSE(web_view.canGoBack);
-  ASSERT_FALSE(web_view.canGoForward);
-  LoadUrl(web_view, [NSURL URLWithString:@"about:newtab"]);
-  ASSERT_NSEQ(@"about:newtab", web_view.lastCommittedURL.absoluteString);
-  ASSERT_NSEQ(@"about:newtab", web_view.visibleURL.absoluteString);
-  LoadUrl(web_view, [NSURL URLWithString:@"about:blank"]);
-  ASSERT_NSEQ(@"about:blank", web_view.lastCommittedURL.absoluteString);
-  ASSERT_NSEQ(@"about:blank", web_view.visibleURL.absoluteString);
-  ASSERT_TRUE(web_view.canGoBack);
-  ASSERT_FALSE(web_view.canGoForward);
+  ASSERT_FALSE([web_view_ lastCommittedURL]);
+  ASSERT_FALSE([web_view_ visibleURL]);
+  ASSERT_FALSE([web_view_ canGoBack]);
+  ASSERT_FALSE([web_view_ canGoForward]);
+  ASSERT_TRUE(test::LoadUrl(web_view_, [NSURL URLWithString:@"about:newtab"]));
+  ASSERT_NSEQ(@"about:newtab", [web_view_ lastCommittedURL].absoluteString);
+  ASSERT_NSEQ(@"about:newtab", [web_view_ visibleURL].absoluteString);
+  ASSERT_TRUE(test::LoadUrl(web_view_, [NSURL URLWithString:@"about:blank"]));
+  ASSERT_NSEQ(@"about:blank", [web_view_ lastCommittedURL].absoluteString);
+  ASSERT_NSEQ(@"about:blank", [web_view_ visibleURL].absoluteString);
+  ASSERT_TRUE([web_view_ canGoBack]);
+  ASSERT_FALSE([web_view_ canGoForward]);
 
   // Create second web view and restore its state from the first web view.
-  CWVWebView* restored_web_view = CreateWebViewWithState(web_view);
+  CWVWebView* restored_web_view = CreateWebViewWithState(web_view_);
 
   // Verify that the state has been restored correctly.
   EXPECT_NSEQ(@"about:blank",
-              restored_web_view.lastCommittedURL.absoluteString);
-  EXPECT_NSEQ(@"about:blank", restored_web_view.visibleURL.absoluteString);
-  EXPECT_TRUE(web_view.canGoBack);
-  EXPECT_FALSE(web_view.canGoForward);
+              [restored_web_view lastCommittedURL].absoluteString);
+  EXPECT_NSEQ(@"about:blank", [restored_web_view visibleURL].absoluteString);
+  EXPECT_TRUE([web_view_ canGoBack]);
+  EXPECT_FALSE([web_view_ canGoForward]);
 }
 
 }  // namespace ios_web_view
diff --git a/ios/web_view/test/chrome_web_view_test.h b/ios/web_view/test/chrome_web_view_test.h
index 389338a..d6e73534 100644
--- a/ios/web_view/test/chrome_web_view_test.h
+++ b/ios/web_view/test/chrome_web_view_test.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#import "base/mac/scoped_nsobject.h"
 #include "testing/platform_test.h"
 
 namespace net {
@@ -41,16 +42,13 @@
   GURL GetUrlForPageWithTitleAndBody(const std::string& title,
                                      const std::string& body);
 
-  // Loads |URL| in |web_view| and waits until the load completes. Asserts if
-  // loading does not complete.
-  void LoadUrl(CWVWebView* web_view, NSURL* url);
-
-  // Waits until |web_view| stops loading. Asserts if loading does not complete.
-  void WaitForPageLoadCompletion(CWVWebView* web_view);
-
   // PlatformTest methods.
   void SetUp() override;
 
+  // CWVWebView created with default configuration and frame equal to screen
+  // bounds.
+  base::scoped_nsobject<CWVWebView> web_view_;
+
   // Embedded server for handling requests sent to the URLs returned by the
   // GetURL* methods.
   std::unique_ptr<net::test_server::EmbeddedTestServer> test_server_;
diff --git a/ios/web_view/test/chrome_web_view_test.mm b/ios/web_view/test/chrome_web_view_test.mm
index d124099..039eee4 100644
--- a/ios/web_view/test/chrome_web_view_test.mm
+++ b/ios/web_view/test/chrome_web_view_test.mm
@@ -10,7 +10,7 @@
 #include "base/base64.h"
 #import "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
-#import "ios/testing/wait_util.h"
+#import "ios/web_view/test/web_view_test_util.h"
 #include "net/base/url_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
@@ -97,7 +97,8 @@
 namespace ios_web_view {
 
 ChromeWebViewTest::ChromeWebViewTest()
-    : test_server_(base::MakeUnique<net::EmbeddedTestServer>(
+    : web_view_(test::CreateWebView()),
+      test_server_(base::MakeUnique<net::EmbeddedTestServer>(
           net::test_server::EmbeddedTestServer::TYPE_HTTP)) {
   test_server_->RegisterRequestHandler(base::Bind(&TestRequestHandler));
 }
@@ -131,18 +132,4 @@
   return url;
 }
 
-void ChromeWebViewTest::LoadUrl(CWVWebView* web_view, NSURL* url) {
-  [web_view loadRequest:[NSURLRequest requestWithURL:url]];
-
-  WaitForPageLoadCompletion(web_view);
-}
-
-void ChromeWebViewTest::WaitForPageLoadCompletion(CWVWebView* web_view) {
-  BOOL success =
-      testing::WaitUntilConditionOrTimeout(testing::kWaitForPageLoadTimeout, ^{
-        return !web_view.isLoading;
-      });
-  ASSERT_TRUE(success);
-}
-
 }  // namespace ios_web_view
diff --git a/ios/web_view/test/web_view_interaction_test_util.h b/ios/web_view/test/web_view_interaction_test_util.h
deleted file mode 100644
index eba3c51b..0000000
--- a/ios/web_view/test/web_view_interaction_test_util.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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 IOS_WEB_VIEW_TEST_WEB_VIEW_INTERACTION_TEST_UTIL_H_
-#define IOS_WEB_VIEW_TEST_WEB_VIEW_INTERACTION_TEST_UTIL_H_
-
-#import <Foundation/Foundation.h>
-
-@class CWVWebView;
-
-namespace ios_web_view {
-namespace test {
-
-// Returns whether the element with |element_id| in the passed |web_view| has
-// been tapped using a JavaScript click() event.
-bool TapChromeWebViewElementWithId(CWVWebView* web_view, NSString* element_id);
-
-// Waits until |script| is executed and synchronously returns the evaluation
-// result.
-id EvaluateJavaScript(CWVWebView* web_view, NSString* script, NSError** error);
-
-// Waits for |web_view| to contain |text|. Returns false if the condition is not
-// met within a timeout.
-bool WaitForWebViewContainingTextOrTimeout(CWVWebView* web_view,
-                                           NSString* text);
-
-}  // namespace test
-}  // namespace ios_web_view
-
-#endif  // IOS_WEB_VIEW_TEST_WEB_VIEW_INTERACTION_TEST_UTIL_H_
diff --git a/ios/web_view/test/web_view_test_util.h b/ios/web_view/test/web_view_test_util.h
new file mode 100644
index 0000000..fbf604b
--- /dev/null
+++ b/ios/web_view/test/web_view_test_util.h
@@ -0,0 +1,46 @@
+// 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 IOS_WEB_VIEW_TEST_WEB_VIEW_TEST_UTIL_H_
+#define IOS_WEB_VIEW_TEST_WEB_VIEW_TEST_UTIL_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/compiler_specific.h"
+
+@class CWVWebView;
+
+namespace ios_web_view {
+namespace test {
+
+// Creates web view with default configuration and frame equal to screen bounds.
+CWVWebView* CreateWebView() WARN_UNUSED_RESULT;
+
+// Loads |URL| in |web_view| and waits until the load completes. Asserts if
+// loading does not complete.
+bool LoadUrl(CWVWebView* web_view, NSURL* url) WARN_UNUSED_RESULT;
+
+// Returns whether the element with |element_id| in the passed |web_view| has
+// been tapped using a JavaScript click() event.
+bool TapWebViewElementWithId(CWVWebView* web_view,
+                             NSString* element_id) WARN_UNUSED_RESULT;
+
+// Waits until |script| is executed and synchronously returns the evaluation
+// result.
+id EvaluateJavaScript(CWVWebView* web_view, NSString* script, NSError** error);
+
+// Waits for |web_view| to contain |text|. Returns false if the condition is not
+// met within a timeout.
+bool WaitForWebViewContainingTextOrTimeout(CWVWebView* web_view,
+                                           NSString* text) WARN_UNUSED_RESULT;
+
+// Waits until |web_view| stops loading. Returns false if the condition is not
+// met within a timeout.
+bool WaitForWebViewLoadCompletionOrTimeout(CWVWebView* web_view)
+    WARN_UNUSED_RESULT;
+
+}  // namespace test
+}  // namespace ios_web_view
+
+#endif  // IOS_WEB_VIEW_TEST_WEB_VIEW_TEST_UTIL_H_
diff --git a/ios/web_view/test/web_view_interaction_test_util.mm b/ios/web_view/test/web_view_test_util.mm
similarity index 76%
rename from ios/web_view/test/web_view_interaction_test_util.mm
rename to ios/web_view/test/web_view_test_util.mm
index 56572335..f253870 100644
--- a/ios/web_view/test/web_view_interaction_test_util.mm
+++ b/ios/web_view/test/web_view_test_util.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web_view/test/web_view_interaction_test_util.h"
+#import "ios/web_view/test/web_view_test_util.h"
 
 #import <ChromeWebView/ChromeWebView.h>
 
@@ -17,7 +17,19 @@
 namespace ios_web_view {
 namespace test {
 
-bool TapChromeWebViewElementWithId(CWVWebView* web_view, NSString* element_id) {
+CWVWebView* CreateWebView() {
+  return [[CWVWebView alloc]
+      initWithFrame:UIScreen.mainScreen.bounds
+      configuration:[CWVWebViewConfiguration defaultConfiguration]];
+}
+
+bool LoadUrl(CWVWebView* web_view, NSURL* url) {
+  [web_view loadRequest:[NSURLRequest requestWithURL:url]];
+
+  return WaitForWebViewLoadCompletionOrTimeout(web_view);
+}
+
+bool TapWebViewElementWithId(CWVWebView* web_view, NSString* element_id) {
   // TODO(crbug.com/725524): Share this script with Chrome.
   NSString* script = [NSString
       stringWithFormat:@"(function() {"
@@ -62,5 +74,11 @@
   });
 }
 
+bool WaitForWebViewLoadCompletionOrTimeout(CWVWebView* web_view) {
+  return WaitUntilConditionOrTimeout(testing::kWaitForPageLoadTimeout, ^{
+    return !web_view.loading;
+  });
+}
+
 }  // namespace test
 }  // namespace ios_web_view
diff --git a/media/audio/audio_manager.cc b/media/audio/audio_manager.cc
index 87a2b121..54eb1bc 100644
--- a/media/audio/audio_manager.cc
+++ b/media/audio/audio_manager.cc
@@ -81,6 +81,11 @@
                               base::Unretained(this)));
   }
 
+  bool IsAudioThreadHung() {
+    base::AutoLock lock(hang_lock_);
+    return audio_thread_status_ == THREAD_HUNG;
+  }
+
   base::SingleThreadTaskRunner* monitor_task_runner() const {
     return monitor_task_runner_.get();
   }
@@ -204,6 +209,7 @@
 
   void HistogramThreadStatus(ThreadStatus status) {
     DCHECK(monitor_task_runner_->BelongsToCurrentThread());
+    hang_lock_.AssertAcquired();
     audio_thread_status_ = status;
     UMA_HISTOGRAM_ENUMERATION("Media.AudioThreadStatus", audio_thread_status_,
                               THREAD_MAX + 1);
@@ -329,11 +335,16 @@
   return g_last_created;
 }
 
-void AudioManager::Shutdown() {
+bool AudioManager::Shutdown() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  // TODO(alokp): Suspend hang monitor.
+  // Do not attempt to stop the audio thread if it is hung.
+  // Otherwise the current thread will hang too: crbug.com/729494
+  // TODO(olka, grunell): Will be fixed when audio is its own process.
+  if (GetHelper()->IsAudioThreadHung())
+    return false;
 
+  // TODO(alokp): Suspend hang monitor.
   if (audio_thread_->GetTaskRunner()->BelongsToCurrentThread()) {
     ShutdownOnAudioThread();
   } else {
@@ -343,6 +354,7 @@
   }
   audio_thread_->Stop();
   shutdown_ = true;
+  return true;
 }
 
 }  // namespace media
diff --git a/media/audio/audio_manager.h b/media/audio/audio_manager.h
index f92651e..f91f6bd 100644
--- a/media/audio/audio_manager.h
+++ b/media/audio/audio_manager.h
@@ -83,10 +83,12 @@
   // like src/chrome.
   static AudioManager* Get();
 
-  // Releases all audio resources.
+  // Synchronously releases all audio resources.
   // Must be called before deletion and on the same thread as AudioManager
   // was created.
-  void Shutdown();
+  // Returns true on success but false if AudioManager could not be shutdown.
+  // AudioManager instance must not be deleted if shutdown failed.
+  bool Shutdown();
 
   // Log callback used for sending log messages from a stream to the object
   // that manages the stream.
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index 3c54ea5..f5eb54b 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -93,14 +93,13 @@
 void HeartbeatSender::OnSignalStrategyStateChange(SignalStrategy::State state) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (state == SignalStrategy::CONNECTED) {
+    DCHECK(!iq_sender_);
     iq_sender_ = base::MakeUnique<IqSender>(signal_strategy_);
-    SendStanza();
-    timer_.Start(FROM_HERE, interval_, this, &HeartbeatSender::SendStanza);
+    SendHeartbeat();
   } else if (state == SignalStrategy::DISCONNECTED) {
     request_.reset();
     iq_sender_.reset();
     timer_.Stop();
-    timer_resend_.Stop();
   }
 }
 
@@ -124,13 +123,7 @@
   DCHECK(host_offline_reason_timeout_timer_.IsRunning());
   host_offline_reason_timeout_timer_.Stop();
 
-  // Run the ACK callback under a clean stack via PostTask() (because the
-  // callback can end up deleting |this| HeartbeatSender [i.e. when used from
-  // HostSignalingManager]).
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(base::ResetAndReturn(&host_offline_reason_ack_callback_),
-                 true));
+  base::ResetAndReturn(&host_offline_reason_ack_callback_).Run(true);
 }
 
 void HeartbeatSender::SetHostOfflineReason(
@@ -139,89 +132,138 @@
     const base::Callback<void(bool success)>& ack_callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(host_offline_reason_ack_callback_.is_null());
+
   host_offline_reason_ = host_offline_reason;
   host_offline_reason_ack_callback_ = ack_callback;
   host_offline_reason_timeout_timer_.Start(
       FROM_HERE, timeout, this, &HeartbeatSender::OnHostOfflineReasonTimeout);
   if (signal_strategy_->GetState() == SignalStrategy::CONNECTED) {
-    DoSendStanza();
+    // Drop timer or pending heartbeat and send a new heartbeat immediately.
+    request_.reset();
+    timer_.Stop();
+
+    SendHeartbeat();
   }
 }
 
-void HeartbeatSender::SendStanza() {
-  // Make sure we don't send another heartbeat before the heartbeat interval
-  // has expired.
-  timer_resend_.Stop();
-  DoSendStanza();
-}
-
-void HeartbeatSender::ResendStanza() {
-  // Make sure we don't send another heartbeat before the heartbeat interval
-  // has expired.
-  timer_.Reset();
-  DoSendStanza();
-}
-
-void HeartbeatSender::DoSendStanza() {
+void HeartbeatSender::SendHeartbeat() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(signal_strategy_->GetState() == SignalStrategy::CONNECTED);
+  DCHECK_EQ(signal_strategy_->GetState(), SignalStrategy::CONNECTED);
   VLOG(1) << "Sending heartbeat stanza to " << directory_bot_jid_;
 
   request_ = iq_sender_->SendIq(
       buzz::STR_SET, directory_bot_jid_, CreateHeartbeatMessage(),
-      base::Bind(&HeartbeatSender::ProcessResponse, base::Unretained(this),
-                 !host_offline_reason_.empty()));
+      base::Bind(&HeartbeatSender::OnResponse, base::Unretained(this)));
   request_->SetTimeout(kHeartbeatResponseTimeout);
   ++sequence_id_;
 }
 
-void HeartbeatSender::ProcessResponse(bool is_offline_heartbeat_response,
-                                      IqRequest* request,
-                                      const XmlElement* response) {
+void HeartbeatSender::OnResponse(IqRequest* request,
+                                 const buzz::XmlElement* response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (!response) {
+  HeartbeatResult result = ProcessResponse(response);
+
+  if (result == HeartbeatResult::SUCCESS) {
+    heartbeat_succeeded_ = true;
+    failed_heartbeat_count_ = 0;
+
+    // Notify listener of the first successful heartbeat.
+    if (!on_heartbeat_successful_callback_.is_null()) {
+      base::ResetAndReturn(&on_heartbeat_successful_callback_).Run();
+    }
+
+    // Notify caller of SetHostOfflineReason that we got an ack and don't
+    // schedule another heartbeat.
+    if (!host_offline_reason_.empty()) {
+      OnHostOfflineReasonAck();
+      return;
+    }
+  } else {
+    failed_heartbeat_count_++;
+  }
+
+  if (result == HeartbeatResult::TIMEOUT) {
     timed_out_heartbeats_count_++;
     if (timed_out_heartbeats_count_ >= kMaxHeartbeatTimeouts) {
       LOG(ERROR) << "Heartbeat timed out. Reconnecting XMPP.";
       timed_out_heartbeats_count_ = 0;
       // SignalingConnector will reconnect SignalStrategy.
       signal_strategy_->Disconnect();
-    } else {
-      LOG(ERROR) << "Heartbeat timed out.";
+      return;
     }
-    return;
+
+    LOG(ERROR) << "Heartbeat timed out.";
   } else {
     timed_out_heartbeats_count_ = 0;
   }
 
+  // If the host was registered immediately before it sends a heartbeat,
+  // then server-side latency may prevent the server recognizing the
+  // host ID in the heartbeat. So even if all of the first few heartbeats
+  // get a "host ID not found" error, that's not a good enough reason to
+  // exit.
+  if (result == HeartbeatResult::INVALID_HOST_ID &&
+      (heartbeat_succeeded_ ||
+       (failed_heartbeat_count_ > kMaxResendOnHostNotFoundCount))) {
+    on_unknown_host_id_error_.Run();
+    return;
+  }
+
+  // Response can be received only while signaling connection is active, so we
+  // should be able to schedule next message.
+  DCHECK_EQ(signal_strategy_->GetState(), SignalStrategy::CONNECTED);
+
+  // Calculate delay before sending the next message.
+  base::TimeDelta delay;
+  switch (result) {
+    case HeartbeatResult::SUCCESS:
+      delay = interval_;
+      break;
+
+    case HeartbeatResult::INVALID_HOST_ID:
+      delay = kResendDelayOnHostNotFound;
+      break;
+
+    case HeartbeatResult::SET_SEQUENCE_ID:
+      if (failed_heartbeat_count_ == 1 && !heartbeat_succeeded_) {
+        // Send next heartbeat immediately after receiving the first
+        // set-sequence-id response. Repeated set-sequence-id responses are
+        // unexpected, and therefore are handled as errors to avoid overloading
+        // the server when it malfunctions.
+        delay = base::TimeDelta();
+        break;
+      }
+    // Fall through to handle other cases as errors.
+
+    case HeartbeatResult::TIMEOUT:
+    case HeartbeatResult::ERROR:
+      delay = pow(2.0, failed_heartbeat_count_) * (1 + base::RandDouble()) *
+              kResendDelay;
+      break;
+  }
+
+  timer_.Start(FROM_HERE, delay, this, &HeartbeatSender::SendHeartbeat);
+}
+
+HeartbeatSender::HeartbeatResult HeartbeatSender::ProcessResponse(
+    const buzz::XmlElement* response) {
+  if (!response)
+    return HeartbeatResult::TIMEOUT;
+
   std::string type = response->Attr(buzz::QN_TYPE);
   if (type == buzz::STR_ERROR) {
     const XmlElement* error_element =
         response->FirstNamed(QName(buzz::NS_CLIENT, kErrorTag));
-    if (error_element) {
-      if (error_element->FirstNamed(QName(buzz::NS_STANZA, kNotFoundTag))) {
-        LOG(ERROR) << "Received error: Host ID not found";
-        // If the host was registered immediately before it sends a heartbeat,
-        // then server-side latency may prevent the server recognizing the
-        // host ID in the heartbeat. So even if all of the first few heartbeats
-        // get a "host ID not found" error, that's not a good enough reason to
-        // exit.
-        failed_startup_heartbeat_count_++;
-        if (!heartbeat_succeeded_ && (failed_startup_heartbeat_count_ <=
-                                      kMaxResendOnHostNotFoundCount)) {
-          timer_resend_.Start(FROM_HERE, kResendDelayOnHostNotFound, this,
-                              &HeartbeatSender::ResendStanza);
-          return;
-        }
-        on_unknown_host_id_error_.Run();
-        return;
-      }
+    if (error_element &&
+        error_element->FirstNamed(QName(buzz::NS_STANZA, kNotFoundTag))) {
+      LOG(ERROR) << "Received error: Host ID not found";
+      return HeartbeatResult::INVALID_HOST_ID;
     }
-
     LOG(ERROR) << "Received error in response to heartbeat: "
                << response->Str();
-    return;
+
+    return HeartbeatResult::ERROR;
   }
 
   // This method must only be called for error or result stanzas.
@@ -241,11 +283,10 @@
         LOG(ERROR) << "Received invalid set-interval: "
                    << set_interval_element->Str();
       } else {
-        SetInterval(base::TimeDelta::FromSeconds(interval_seconds));
+        interval_ = base::TimeDelta::FromSeconds(interval_seconds);
       }
     }
 
-    bool did_set_sequence_id = false;
     const XmlElement* expected_sequence_id_element =
         result_element->FirstNamed(QName(kChromotingXmlNamespace,
                                          kExpectedSequenceIdTag));
@@ -258,63 +299,16 @@
       if (!base::StringToInt(expected_sequence_id_str, &expected_sequence_id)) {
         LOG(ERROR) << "Received invalid " << kExpectedSequenceIdTag << ": " <<
             expected_sequence_id_element->Str();
-      } else {
-        SetSequenceId(expected_sequence_id);
-        sequence_id_recent_set_num_++;
-        did_set_sequence_id = true;
-      }
-    }
-    if (!did_set_sequence_id) {
-      // It seems the bot accepted our signature and our message.
-      sequence_id_recent_set_num_ = 0;
 
-      // Notify listener of the first successful heartbeat.
-      if (!heartbeat_succeeded_) {
-        on_heartbeat_successful_callback_.Run();
+        return HeartbeatResult::ERROR;
       }
-      heartbeat_succeeded_ = true;
 
-      // Notify caller of SetHostOfflineReason that we got an ack.
-      if (is_offline_heartbeat_response) {
-        OnHostOfflineReasonAck();
-      }
+      sequence_id_ = expected_sequence_id;
+      return HeartbeatResult::SET_SEQUENCE_ID;
     }
   }
-}
 
-void HeartbeatSender::SetInterval(base::TimeDelta interval) {
-  if (interval != interval_) {
-    interval_ = interval;
-
-    // Restart the timer with the new interval.
-    if (timer_.IsRunning()) {
-      timer_.Stop();
-      timer_.Start(FROM_HERE, interval_, this, &HeartbeatSender::SendStanza);
-    }
-  }
-}
-
-void HeartbeatSender::SetSequenceId(int sequence_id) {
-  sequence_id_ = sequence_id;
-  // Setting the sequence ID may be a symptom of a temporary server-side
-  // problem, which would affect many hosts, so don't send a new heartbeat
-  // immediately, as many hosts doing so may overload the server.
-  // But the server will usually set the sequence ID when it receives the first
-  // heartbeat from a host. In that case, we can send a new heartbeat
-  // immediately, as that only happens once per host instance.
-  if (!sequence_id_was_set_) {
-    ResendStanza();
-  } else {
-    HOST_LOG << "The heartbeat sequence ID has been set more than once: "
-              << "the new value is " << sequence_id;
-    base::TimeDelta delay = pow(2.0, sequence_id_recent_set_num_) *
-                            (1 + base::RandDouble()) * kResendDelay;
-    if (delay <= interval_) {
-      timer_resend_.Start(FROM_HERE, delay, this,
-                          &HeartbeatSender::ResendStanza);
-    }
-  }
-  sequence_id_was_set_ = true;
+  return HeartbeatResult::SUCCESS;
 }
 
 std::unique_ptr<XmlElement> HeartbeatSender::CreateHeartbeatMessage() {
diff --git a/remoting/host/heartbeat_sender.h b/remoting/host/heartbeat_sender.h
index 3553a86f..882df61 100644
--- a/remoting/host/heartbeat_sender.h
+++ b/remoting/host/heartbeat_sender.h
@@ -18,6 +18,10 @@
 #include "remoting/base/rsa_key_pair.h"
 #include "remoting/signaling/signal_strategy.h"
 
+#ifdef ERROR
+#undef ERROR
+#endif
+
 namespace base {
 class TimeDelta;
 }  // namespace base
@@ -121,27 +125,24 @@
       const base::TimeDelta& timeout,
       const base::Callback<void(bool success)>& ack_callback);
 
+ private:
+  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, SetInterval);
+
+  enum class HeartbeatResult {
+    SUCCESS,
+    SET_SEQUENCE_ID,
+    INVALID_HOST_ID,
+    TIMEOUT,
+    ERROR,
+  };
+
   // SignalStrategy::Listener interface.
   void OnSignalStrategyStateChange(SignalStrategy::State state) override;
   bool OnSignalStrategyIncomingStanza(const buzz::XmlElement* stanza) override;
 
- private:
-  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest,
-                           DoSendStanzaWithExpectedSequenceId);
-  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ProcessResponseSetInterval);
-  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest,
-                           ProcessResponseExpectedSequenceId);
-  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ResponseTimeout);
-  friend class HeartbeatSenderTest;
-
-  void SendStanza();
-  void ResendStanza();
-  void DoSendStanza();
-  void ProcessResponse(bool is_offline_heartbeat_response,
-                       IqRequest* request,
-                       const buzz::XmlElement* response);
-  void SetInterval(base::TimeDelta interval);
-  void SetSequenceId(int sequence_id);
+  void SendHeartbeat();
+  void OnResponse(IqRequest* request, const buzz::XmlElement* response);
+  HeartbeatResult ProcessResponse(const buzz::XmlElement* response);
 
   // Handlers for host-offline-reason completion and timeout.
   void OnHostOfflineReasonTimeout();
@@ -154,19 +155,19 @@
   base::Closure on_heartbeat_successful_callback_;
   base::Closure on_unknown_host_id_error_;
   std::string host_id_;
-  SignalStrategy* signal_strategy_;
+  SignalStrategy* const signal_strategy_;
   scoped_refptr<const RsaKeyPair> host_key_pair_;
   std::string directory_bot_jid_;
   std::unique_ptr<IqSender> iq_sender_;
   std::unique_ptr<IqRequest> request_;
+
   base::TimeDelta interval_;
-  base::RepeatingTimer timer_;
-  base::OneShotTimer timer_resend_;
+  base::OneShotTimer timer_;
+
+  int failed_heartbeat_count_ = 0;
+
   int sequence_id_ = 0;
-  bool sequence_id_was_set_ = false;
-  int sequence_id_recent_set_num_ = 0;
   bool heartbeat_succeeded_ = false;
-  int failed_startup_heartbeat_count_ = 0;
   int timed_out_heartbeats_count_ = 0;
 
   // Fields to send and indicate completion of sending host-offline-reason.
diff --git a/remoting/host/heartbeat_sender_unittest.cc b/remoting/host/heartbeat_sender_unittest.cc
index 55de05f..37da688 100644
--- a/remoting/host/heartbeat_sender_unittest.cc
+++ b/remoting/host/heartbeat_sender_unittest.cc
@@ -6,16 +6,18 @@
 
 #include <set>
 
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/mock_callback.h"
+#include "base/test/test_mock_time_task_runner.h"
 #include "remoting/base/constants.h"
 #include "remoting/base/rsa_key_pair.h"
 #include "remoting/base/test_rsa_key_pair.h"
+#include "remoting/signaling/fake_signal_strategy.h"
 #include "remoting/signaling/iq_sender.h"
-#include "remoting/signaling/mock_signal_strategy.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
@@ -40,37 +42,29 @@
 const char kHostId[] = "0";
 const char kTestJid[] = "User@gmail.com/chromotingABC123";
 const char kTestJidNormalized[] = "user@gmail.com/chromotingABC123";
-const char kStanzaId[] = "123";
 constexpr base::TimeDelta kTestInterval = base::TimeDelta::FromSeconds(123);
 constexpr base::TimeDelta kOfflineReasonTimeout =
     base::TimeDelta::FromSeconds(123);
 
 }  // namespace
 
-ACTION_P(AddListener, list) {
-  list->insert(arg0);
-}
-ACTION_P(RemoveListener, list) {
-  EXPECT_TRUE(list->find(arg0) != list->end());
-  list->erase(arg0);
-}
-
 class HeartbeatSenderTest
     : public testing::Test {
  protected:
-  HeartbeatSenderTest() : signal_strategy_(SignalingAddress(kTestJid)) {}
+  HeartbeatSenderTest()
+      : signal_strategy_(SignalingAddress(kTestJid)),
+        bot_signal_strategy_(SignalingAddress(kTestBotJid)) {}
 
   void SetUp() override {
+    FakeSignalStrategy::Connect(&signal_strategy_, &bot_signal_strategy_);
+
+    // Start in disconnected state.
+    signal_strategy_.Disconnect();
+
     key_pair_ = RsaKeyPair::FromString(kTestRsaKeyPair);
     ASSERT_TRUE(key_pair_.get());
-    EXPECT_CALL(signal_strategy_, GetState())
-        .WillOnce(Return(SignalStrategy::DISCONNECTED));
-    EXPECT_CALL(signal_strategy_, AddListener(NotNull()))
-        .WillRepeatedly(AddListener(&signal_strategy_listeners_));
-    EXPECT_CALL(signal_strategy_, RemoveListener(NotNull()))
-        .WillRepeatedly(RemoveListener(&signal_strategy_listeners_));
-    EXPECT_CALL(mock_unknown_host_id_error_callback_, Run())
-        .Times(0);
+
+    EXPECT_CALL(mock_unknown_host_id_error_callback_, Run()).Times(0);
 
     heartbeat_sender_.reset(
         new HeartbeatSender(mock_heartbeat_successful_callback_.Get(),
@@ -80,282 +74,49 @@
 
   void TearDown() override {
     heartbeat_sender_.reset();
-    EXPECT_TRUE(signal_strategy_listeners_.empty());
+    base::RunLoop().RunUntilIdle();
   }
 
   void ValidateHeartbeatStanza(XmlElement* stanza,
-                               const char* expected_sequence_id,
-                               const char* expected_host_offline_reason);
+                               const std::string& expected_sequence_id,
+                               const std::string& expected_host_offline_reason);
 
-  void ProcessResponseWithInterval(bool is_offline_heartbeat_response,
-                                   base::TimeDelta interval);
+  void SendResponse(int message_index,
+                    base::TimeDelta interval = base::TimeDelta(),
+                    int expected_sequence_id = 0);
 
   base::MessageLoop message_loop_;
-  MockSignalStrategy signal_strategy_;
+  scoped_refptr<base::TestMockTimeTaskRunner> fake_time_task_runner_ =
+      new base::TestMockTimeTaskRunner();
+
+  FakeSignalStrategy signal_strategy_;
+  FakeSignalStrategy bot_signal_strategy_;
+
   base::MockCallback<base::Closure> mock_heartbeat_successful_callback_;
   base::MockCallback<base::Closure> mock_unknown_host_id_error_callback_;
-  std::set<SignalStrategy::Listener*> signal_strategy_listeners_;
   scoped_refptr<RsaKeyPair> key_pair_;
   std::unique_ptr<HeartbeatSender> heartbeat_sender_;
 };
 
-// Call Start() followed by Stop(), and make sure a valid heartbeat is sent.
-TEST_F(HeartbeatSenderTest, DoSendStanza) {
-  XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
-  EXPECT_CALL(signal_strategy_, GetState())
-      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  std::unique_ptr<XmlElement> stanza(sent_iq);
-  ASSERT_TRUE(stanza != nullptr);
-  ValidateHeartbeatStanza(stanza.get(), "0", nullptr);
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
-  base::RunLoop().RunUntilIdle();
-}
-
-// Call Start() followed by Stop(), twice, and make sure two valid heartbeats
-// are sent, with the correct sequence IDs.
-TEST_F(HeartbeatSenderTest, DoSendStanzaTwice) {
-  XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
-  EXPECT_CALL(signal_strategy_, GetState())
-      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  std::unique_ptr<XmlElement> stanza(sent_iq);
-  ASSERT_TRUE(stanza != nullptr);
-  ValidateHeartbeatStanza(stanza.get(), "0", nullptr);
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId + 1));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  std::unique_ptr<XmlElement> stanza2(sent_iq);
-  ValidateHeartbeatStanza(stanza2.get(), "1", nullptr);
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
-  base::RunLoop().RunUntilIdle();
-}
-
-// Call Start() followed by Stop(), make sure a valid Iq stanza is sent,
-// reply with an expected sequence ID, and make sure two valid heartbeats
-// are sent, with the correct sequence IDs.
-TEST_F(HeartbeatSenderTest, DoSendStanzaWithExpectedSequenceId) {
-  XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
-  EXPECT_CALL(signal_strategy_, GetState())
-      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  std::unique_ptr<XmlElement> stanza(sent_iq);
-  ASSERT_TRUE(stanza != nullptr);
-  ValidateHeartbeatStanza(stanza.get(), "0", nullptr);
-
-  XmlElement* sent_iq2 = nullptr;
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId + 1));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq2), Return(true)));
-
-  std::unique_ptr<XmlElement> response(new XmlElement(buzz::QN_IQ));
-  response->AddAttr(QName(std::string(), "type"), "result");
-  XmlElement* result =
-      new XmlElement(QName(kChromotingXmlNamespace, "heartbeat-result"));
-  response->AddElement(result);
-  XmlElement* expected_sequence_id = new XmlElement(
-      QName(kChromotingXmlNamespace, "expected-sequence-id"));
-  result->AddElement(expected_sequence_id);
-  const int kExpectedSequenceId = 456;
-  expected_sequence_id->AddText(base::IntToString(kExpectedSequenceId));
-  heartbeat_sender_->ProcessResponse(false, nullptr, response.get());
-  base::RunLoop().RunUntilIdle();
-
-  std::unique_ptr<XmlElement> stanza2(sent_iq2);
-  ASSERT_TRUE(stanza2 != nullptr);
-  ValidateHeartbeatStanza(stanza2.get(),
-                          base::IntToString(kExpectedSequenceId).c_str(),
-                          nullptr);
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
-  base::RunLoop().RunUntilIdle();
-}
-
-void HeartbeatSenderTest::ProcessResponseWithInterval(
-    bool is_offline_heartbeat_response,
-    base::TimeDelta interval) {
-  std::unique_ptr<XmlElement> response(new XmlElement(buzz::QN_IQ));
-  response->AddAttr(QName(std::string(), "type"), "result");
-
-  XmlElement* result = new XmlElement(
-      QName(kChromotingXmlNamespace, "heartbeat-result"));
-  response->AddElement(result);
-
-  XmlElement* set_interval = new XmlElement(
-      QName(kChromotingXmlNamespace, "set-interval"));
-  result->AddElement(set_interval);
-
-  set_interval->AddText(base::IntToString(interval.InSeconds()));
-
-  heartbeat_sender_->ProcessResponse(
-      is_offline_heartbeat_response, nullptr, response.get());
-}
-
-// Verify that ProcessResponse parses set-interval result.
-TEST_F(HeartbeatSenderTest, ProcessResponseSetInterval) {
-  EXPECT_CALL(mock_heartbeat_successful_callback_, Run());
-
-  ProcessResponseWithInterval(false, kTestInterval);
-
-  EXPECT_EQ(kTestInterval, heartbeat_sender_->interval_);
-}
-
-// Make sure SetHostOfflineReason sends a correct stanza.
-TEST_F(HeartbeatSenderTest, DoSetHostOfflineReason) {
-  XmlElement* sent_iq = nullptr;
-  base::MockCallback<base::Callback<void(bool success)>> mock_ack_callback;
-
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
-  EXPECT_CALL(signal_strategy_, GetState())
-      .WillOnce(Return(SignalStrategy::DISCONNECTED))
-      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
-  EXPECT_CALL(mock_ack_callback, Run(_)).Times(0);
-
-  heartbeat_sender_->SetHostOfflineReason("test_error", kOfflineReasonTimeout,
-                                          mock_ack_callback.Get());
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  std::unique_ptr<XmlElement> stanza(sent_iq);
-  ASSERT_TRUE(stanza != nullptr);
-  ValidateHeartbeatStanza(stanza.get(), "0", "test_error");
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
-  base::RunLoop().RunUntilIdle();
-}
-
-// Make sure SetHostOfflineReason triggers a callback when bot responds.
-TEST_F(HeartbeatSenderTest, ProcessHostOfflineResponses) {
-  base::MockCallback<base::Callback<void(bool success)>> mock_ack_callback;
-
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(DeleteArg<0>(), Return(true)));
-  EXPECT_CALL(signal_strategy_, GetState())
-      .WillOnce(Return(SignalStrategy::DISCONNECTED))
-      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
-  EXPECT_CALL(mock_heartbeat_successful_callback_, Run())
-      .WillRepeatedly(Return());
-
-  // Callback should not run, until response to offline-reason.
-  EXPECT_CALL(mock_ack_callback, Run(_)).Times(0);
-
-  heartbeat_sender_->SetHostOfflineReason("test_error", kOfflineReasonTimeout,
-                                          mock_ack_callback.Get());
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  ProcessResponseWithInterval(
-      false,  // <- This is not a response to offline-reason.
-      kTestInterval);
-  base::RunLoop().RunUntilIdle();
-
-  // Callback should run once, when we get response to offline-reason.
-  EXPECT_CALL(mock_ack_callback, Run(true /* success */)).Times(1);
-  ProcessResponseWithInterval(
-      true,  // <- This is a response to offline-reason.
-      kTestInterval);
-  base::RunLoop().RunUntilIdle();
-
-  // When subsequent responses to offline-reason come,
-  // the callback should not be called again.
-  EXPECT_CALL(mock_ack_callback, Run(_)).Times(0);
-  ProcessResponseWithInterval(true, kTestInterval);
-  base::RunLoop().RunUntilIdle();
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
-  base::RunLoop().RunUntilIdle();
-}
-
-// The first heartbeat should include host OS information.
-TEST_F(HeartbeatSenderTest, HostOsInfo) {
-  XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetNextId())
-      .WillOnce(Return(kStanzaId));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
-  EXPECT_CALL(signal_strategy_, GetState())
-      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
-  base::RunLoop().RunUntilIdle();
-
-  std::unique_ptr<XmlElement> stanza(sent_iq);
-  ASSERT_TRUE(stanza != nullptr);
-
-  XmlElement* heartbeat_stanza =
-      stanza->FirstNamed(QName(kChromotingXmlNamespace, "heartbeat"));
-
-  std::string host_os_name =
-      heartbeat_stanza->TextNamed(
-          QName(kChromotingXmlNamespace, "host-os-name"));
-  EXPECT_TRUE(!host_os_name.empty());
-
-  std::string host_os_version =
-      heartbeat_stanza->TextNamed(
-          QName(kChromotingXmlNamespace, "host-os-version"));
-  EXPECT_TRUE(!host_os_version.empty());
-
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::DISCONNECTED);
-  base::RunLoop().RunUntilIdle();
-}
-
-// Validate a heartbeat stanza.
 void HeartbeatSenderTest::ValidateHeartbeatStanza(
     XmlElement* stanza,
-    const char* expected_sequence_id,
-    const char* expected_host_offline_reason) {
+    const std::string& expected_sequence_id,
+    const std::string& expected_host_offline_reason) {
   EXPECT_EQ(stanza->Attr(buzz::QName(std::string(), "to")),
             std::string(kTestBotJid));
   EXPECT_EQ(stanza->Attr(buzz::QName(std::string(), "type")), "set");
   XmlElement* heartbeat_stanza =
       stanza->FirstNamed(QName(kChromotingXmlNamespace, "heartbeat"));
   ASSERT_TRUE(heartbeat_stanza != nullptr);
-  EXPECT_EQ(expected_sequence_id, heartbeat_stanza->Attr(
-      buzz::QName(kChromotingXmlNamespace, "sequence-id")));
-  if (expected_host_offline_reason == nullptr) {
+  EXPECT_EQ(expected_sequence_id, heartbeat_stanza->Attr(buzz::QName(
+                                      kChromotingXmlNamespace, "sequence-id")));
+  if (expected_host_offline_reason.empty()) {
     EXPECT_FALSE(heartbeat_stanza->HasAttr(
         buzz::QName(kChromotingXmlNamespace, "host-offline-reason")));
   } else {
-    EXPECT_EQ(expected_host_offline_reason, heartbeat_stanza->Attr(
-        buzz::QName(kChromotingXmlNamespace, "host-offline-reason")));
+    EXPECT_EQ(expected_host_offline_reason,
+              heartbeat_stanza->Attr(
+                  buzz::QName(kChromotingXmlNamespace, "host-offline-reason")));
   }
   EXPECT_EQ(std::string(kHostId),
             heartbeat_stanza->Attr(QName(kChromotingXmlNamespace, "hostid")));
@@ -372,34 +133,171 @@
   EXPECT_EQ(expected_signature, signature->BodyText());
 }
 
+void HeartbeatSenderTest::SendResponse(int message_index,
+                                       base::TimeDelta interval,
+                                       int expected_sequence_id) {
+  auto response = base::MakeUnique<XmlElement>(buzz::QN_IQ);
+  response->AddAttr(QName(std::string(), "type"), "result");
+  response->AddAttr(QName(std::string(), "to"), kTestJid);
+
+  ASSERT_LT(message_index,
+            static_cast<int>(bot_signal_strategy_.received_messages().size()));
+  std::string iq_id =
+      bot_signal_strategy_.received_messages()[message_index]->Attr(
+          QName(std::string(), "id"));
+
+  response->SetAttr(QName(std::string(), "id"), iq_id);
+
+  XmlElement* result =
+      new XmlElement(QName(kChromotingXmlNamespace, "heartbeat-result"));
+  response->AddElement(result);
+
+  if (interval != base::TimeDelta()) {
+    XmlElement* set_interval =
+        new XmlElement(QName(kChromotingXmlNamespace, "set-interval"));
+    result->AddElement(set_interval);
+    set_interval->AddText(base::IntToString(interval.InSeconds()));
+  }
+
+  if (expected_sequence_id > 0) {
+    XmlElement* expected_sequence_id_tag =
+        new XmlElement(QName(kChromotingXmlNamespace, "expected-sequence-id"));
+    result->AddElement(expected_sequence_id_tag);
+    expected_sequence_id_tag->AddText(base::IntToString(expected_sequence_id));
+  }
+
+  bot_signal_strategy_.SendStanza(std::move(response));
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(HeartbeatSenderTest, SendHeartbeat) {
+  signal_strategy_.Connect();
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(bot_signal_strategy_.received_messages().size(), 1U);
+  ValidateHeartbeatStanza(bot_signal_strategy_.received_messages()[0].get(),
+                          "0", std::string());
+
+  EXPECT_CALL(mock_heartbeat_successful_callback_, Run());
+  SendResponse(0);
+
+  signal_strategy_.Disconnect();
+}
+
+// Ensure a new heartbeat is sent after signaling is connected.
+TEST_F(HeartbeatSenderTest, AfterSignalingReconnect) {
+  signal_strategy_.Connect();
+  base::RunLoop().RunUntilIdle();
+
+  signal_strategy_.Disconnect();
+
+  signal_strategy_.Connect();
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(bot_signal_strategy_.received_messages().size(), 2U);
+  ValidateHeartbeatStanza(bot_signal_strategy_.received_messages()[0].get(),
+                          "0", std::string());
+  ValidateHeartbeatStanza(bot_signal_strategy_.received_messages()[1].get(),
+                          "1", std::string());
+
+  signal_strategy_.Disconnect();
+}
+
+// Verify that a second heartbeat is sent immediately after a response from
+// server with expected-sequence-id field set.
+TEST_F(HeartbeatSenderTest, ExpectedSequenceId) {
+  signal_strategy_.Connect();
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(bot_signal_strategy_.received_messages().size(), 1U);
+  ValidateHeartbeatStanza(bot_signal_strategy_.received_messages()[0].get(),
+                          "0", std::string());
+
+  const int kExpectedSequenceId = 456;
+  SendResponse(0, base::TimeDelta(), kExpectedSequenceId);
+
+  ASSERT_EQ(bot_signal_strategy_.received_messages().size(), 2U);
+  ValidateHeartbeatStanza(bot_signal_strategy_.received_messages()[1].get(),
+                          base::IntToString(kExpectedSequenceId).c_str(),
+                          std::string());
+
+  signal_strategy_.Disconnect();
+}
+
+// Verify that ProcessResponse parses set-interval result.
+TEST_F(HeartbeatSenderTest, SetInterval) {
+  signal_strategy_.Connect();
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(bot_signal_strategy_.received_messages().size(), 1U);
+
+  EXPECT_CALL(mock_heartbeat_successful_callback_, Run());
+  SendResponse(0, kTestInterval);
+  EXPECT_EQ(kTestInterval, heartbeat_sender_->interval_);
+}
+
+// Make sure SetHostOfflineReason sends a correct stanza and triggers callback
+// when the bot responds.
+TEST_F(HeartbeatSenderTest, SetHostOfflineReason) {
+  base::MockCallback<base::Callback<void(bool success)>> mock_ack_callback;
+  EXPECT_CALL(mock_ack_callback, Run(_)).Times(0);
+
+  heartbeat_sender_->SetHostOfflineReason("test_error", kOfflineReasonTimeout,
+                                          mock_ack_callback.Get());
+
+  testing::Mock::VerifyAndClearExpectations(&mock_ack_callback);
+
+  signal_strategy_.Connect();
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(bot_signal_strategy_.received_messages().size(), 1U);
+  ValidateHeartbeatStanza(bot_signal_strategy_.received_messages()[0].get(),
+                          "0", "test_error");
+
+  // Callback should run once, when we get response to offline-reason.
+  EXPECT_CALL(mock_ack_callback, Run(_)).Times(1);
+  EXPECT_CALL(mock_heartbeat_successful_callback_, Run());
+  SendResponse(0);
+}
+
+// The first heartbeat should include host OS information.
+TEST_F(HeartbeatSenderTest, HostOsInfo) {
+  signal_strategy_.Connect();
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(bot_signal_strategy_.received_messages().size(), 1U);
+  XmlElement* stanza = bot_signal_strategy_.received_messages()[0].get();
+
+  XmlElement* heartbeat_stanza =
+      stanza->FirstNamed(QName(kChromotingXmlNamespace, "heartbeat"));
+
+  std::string host_os_name = heartbeat_stanza->TextNamed(
+      QName(kChromotingXmlNamespace, "host-os-name"));
+  EXPECT_TRUE(!host_os_name.empty());
+
+  std::string host_os_version = heartbeat_stanza->TextNamed(
+      QName(kChromotingXmlNamespace, "host-os-version"));
+  EXPECT_TRUE(!host_os_version.empty());
+
+  signal_strategy_.Disconnect();
+}
+
 TEST_F(HeartbeatSenderTest, ResponseTimeout) {
-  XmlElement* sent_iq = nullptr;
-  EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
-  EXPECT_CALL(signal_strategy_, GetState())
-      .WillRepeatedly(Return(SignalStrategy::CONNECTED));
+  base::TestMockTimeTaskRunner::ScopedContext scoped_context(
+      fake_time_task_runner_.get());
 
-  heartbeat_sender_->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
+  signal_strategy_.Connect();
   base::RunLoop().RunUntilIdle();
 
-  std::unique_ptr<XmlElement> stanza(sent_iq);
-  ASSERT_TRUE(stanza);
-
-  XmlElement* sent_iq2 = nullptr;
-  EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId + 1));
-  EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
-      .WillOnce(DoAll(SaveArg<0>(&sent_iq2), Return(true)));
-
-  heartbeat_sender_->ProcessResponse(false, nullptr, nullptr /* timeout */);
-  heartbeat_sender_->DoSendStanza();
+  // Simulate heartbeat timeout.
+  fake_time_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
   base::RunLoop().RunUntilIdle();
 
-  std::unique_ptr<XmlElement> stanza2(sent_iq2);
-  ASSERT_TRUE(stanza2);
+  // SignalStrategy should be disconnected in response to the second timeout.
+  fake_time_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  base::RunLoop().RunUntilIdle();
 
-  EXPECT_CALL(signal_strategy_, Disconnect());
-  heartbeat_sender_->ProcessResponse(false, nullptr, nullptr /* timeout */);
+  EXPECT_EQ(SignalStrategy::DISCONNECTED, signal_strategy_.GetState());
 }
 
 }  // namespace remoting
diff --git a/remoting/protocol/jingle_session_unittest.cc b/remoting/protocol/jingle_session_unittest.cc
index 8538c8d..d08c046 100644
--- a/remoting/protocol/jingle_session_unittest.cc
+++ b/remoting/protocol/jingle_session_unittest.cc
@@ -380,7 +380,7 @@
   // Verify that the client specified correct initiator value.
   ASSERT_GT(host_signal_strategy_->received_messages().size(), 0U);
   const buzz::XmlElement* initiate_xml =
-      host_signal_strategy_->received_messages().front();
+      host_signal_strategy_->received_messages().front().get();
   const buzz::XmlElement* jingle_element =
       initiate_xml->FirstNamed(buzz::QName("urn:xmpp:jingle:1", "jingle"));
   ASSERT_TRUE(jingle_element);
diff --git a/remoting/signaling/fake_signal_strategy.cc b/remoting/signaling/fake_signal_strategy.cc
index 33f3242..b52f745f 100644
--- a/remoting/signaling/fake_signal_strategy.cc
+++ b/remoting/signaling/fake_signal_strategy.cc
@@ -38,10 +38,6 @@
 
 FakeSignalStrategy::~FakeSignalStrategy() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  while (!received_messages_.empty()) {
-    delete received_messages_.front();
-    received_messages_.pop_front();
-  }
 }
 
 void FakeSignalStrategy::ConnectTo(FakeSignalStrategy* peer) {
@@ -53,10 +49,8 @@
     peer->SetPeerCallback(peer_callback);
   } else {
     peer->main_thread_->PostTask(
-        FROM_HERE,
-        base::Bind(&FakeSignalStrategy::SetPeerCallback,
-                   base::Unretained(peer),
-                   peer_callback));
+        FROM_HERE, base::Bind(&FakeSignalStrategy::SetPeerCallback,
+                              base::Unretained(peer), peer_callback));
   }
 }
 
@@ -72,18 +66,20 @@
 
 void FakeSignalStrategy::Connect() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  state_ = CONNECTED;
   for (auto& observer : listeners_)
     observer.OnSignalStrategyStateChange(CONNECTED);
 }
 
 void FakeSignalStrategy::Disconnect() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  state_ = DISCONNECTED;
   for (auto& observer : listeners_)
     observer.OnSignalStrategyStateChange(DISCONNECTED);
 }
 
 SignalStrategy::State FakeSignalStrategy::GetState() const {
-  return CONNECTED;
+  return state_;
 }
 
 SignalStrategy::Error FakeSignalStrategy::GetError() const {
@@ -163,7 +159,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   buzz::XmlElement* stanza_ptr = stanza.get();
-  received_messages_.push_back(stanza.release());
+  received_messages_.push_back(std::move(stanza));
 
   std::string to_error;
   SignalingAddress to =
diff --git a/remoting/signaling/fake_signal_strategy.h b/remoting/signaling/fake_signal_strategy.h
index e22a216..f6f87ac 100644
--- a/remoting/signaling/fake_signal_strategy.h
+++ b/remoting/signaling/fake_signal_strategy.h
@@ -32,7 +32,7 @@
   FakeSignalStrategy(const SignalingAddress& address);
   ~FakeSignalStrategy() override;
 
-  const std::list<buzz::XmlElement*>& received_messages() {
+  const std::vector<std::unique_ptr<buzz::XmlElement>>& received_messages() {
     return received_messages_;
   }
 
@@ -40,6 +40,8 @@
     send_delay_ = delay;
   }
 
+  void SetState(State state) const;
+
   // Connects current FakeSignalStrategy to receive messages from |peer|.
   void ConnectTo(FakeSignalStrategy* peer);
 
@@ -76,6 +78,8 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
 
+  State state_ = CONNECTED;
+
   SignalingAddress address_;
   PeerCallback peer_callback_;
   base::ObserverList<Listener, true> listeners_;
@@ -88,7 +92,7 @@
   std::unique_ptr<buzz::XmlElement> pending_stanza_;
 
   // All received messages, includes thouse still in |pending_messages_|.
-  std::list<buzz::XmlElement*> received_messages_;
+  std::vector<std::unique_ptr<buzz::XmlElement>> received_messages_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 622af1c..93cb992 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -15,15 +15,22 @@
 # use the ServiceTest framework, you must also include corresponding catalog
 # entries in the "services_unittests_catalog" target below.
 service_test("services_unittests") {
+  # If your service does not run on iOS, its tests should go in the !iOS
+  # section below. If you are unsure, contact blundell@chromium.org.
   deps = [
-    "//services/data_decoder:tests",
-    "//services/device:tests",
     "//services/identity:tests",
-    "//services/preferences:tests",
-    "//services/resource_coordinator:tests",
-    "//services/shape_detection:tests",
   ]
 
+  if (!is_ios) {
+    deps += [
+      "//services/data_decoder:tests",
+      "//services/device:tests",
+      "//services/preferences:tests",
+      "//services/resource_coordinator:tests",
+      "//services/shape_detection:tests",
+    ]
+  }
+
   if (use_aura) {
     deps += [
       "//services/ui/clipboard:tests",
@@ -38,7 +45,7 @@
       deps += [ "//services/ui/demo:tests" ]
     }
 
-    if (use_ozone || (!is_chromeos && !is_mac)) {
+    if (use_ozone || (!is_chromeos && !is_mac && !is_ios)) {
       deps += [ "//services/ui/ws:tests" ]
     }
   }
@@ -50,10 +57,12 @@
       # Some tests need to initialize V8.
       "//v8:v8_external_startup_data_assets",
     ]
-  } else {
-    # NOTE: We do not currently support standalone service binaries on Android,
-    # so any tests which use the ServiceTest framework to connect to standalone
-    # services must be added here.
+  }
+
+  if (!is_android && !is_ios) {
+    # NOTE: We do not currently support standalone service binaries on Android
+    # or iOS, so any tests which use the ServiceTest framework to connect to
+    # standalone services must be added here.
     deps += [ "//services/video_capture:tests" ]
   }
 
@@ -68,13 +77,19 @@
 
 catalog("services_unittests_catalog") {
   testonly = true
-  catalog_deps = [
-    "//services/device:tests_catalog",
-    "//services/identity:tests_catalog",
-    "//services/preferences:tests_catalog",
-    "//services/resource_coordinator:tests_catalog",
-    "//services/video_capture:tests_catalog",
-  ]
+
+  # The division between "all platforms" and "!iOS" here needs to match that
+  # for the main deps list in the services_unittests target above.
+  catalog_deps = [ "//services/identity:tests_catalog" ]
+
+  if (!is_ios) {
+    catalog_deps += [
+      "//services/device:tests_catalog",
+      "//services/preferences:tests_catalog",
+      "//services/resource_coordinator:tests_catalog",
+      "//services/video_capture:tests_catalog",
+    ]
+  }
 
   if (use_aura) {
     catalog_deps += [
@@ -87,7 +102,7 @@
       catalog_deps += [ "//services/ui/demo:tests_catalog" ]
     }
 
-    if (use_ozone || !is_chromeos) {
+    if (use_ozone || (!is_chromeos && !is_ios)) {
       catalog_deps += [ "//services/ui/ws:tests_catalog" ]
     }
   }
diff --git a/services/service_manager/BUILD.gn b/services/service_manager/BUILD.gn
index f8fd3d5..b2215bc 100644
--- a/services/service_manager/BUILD.gn
+++ b/services/service_manager/BUILD.gn
@@ -12,8 +12,15 @@
     ":service_manager",
     "//services/service_manager/background",
     "//services/service_manager/standalone",
-    "//services/service_manager/tests",
   ]
+
+  if (!is_ios) {
+    deps += [
+      # These tests heavily rely on service binaries, which are not supported on
+      # iOS.
+      "//services/service_manager/tests",
+    ]
+  }
 }
 
 source_set("service_manager") {
diff --git a/services/service_manager/background/background_service_manager.cc b/services/service_manager/background/background_service_manager.cc
index 891f3ca..50c4953 100644
--- a/services/service_manager/background/background_service_manager.cc
+++ b/services/service_manager/background/background_service_manager.cc
@@ -40,7 +40,7 @@
 }  // namespace
 
 BackgroundServiceManager::BackgroundServiceManager(
-    service_manager::ServiceProcessLauncher::Delegate* launcher_delegate,
+    service_manager::ServiceProcessLauncherDelegate* launcher_delegate,
     std::unique_ptr<base::Value> catalog_contents)
     : background_thread_("service_manager") {
   background_thread_.Start();
@@ -106,7 +106,7 @@
 }
 
 void BackgroundServiceManager::InitializeOnBackgroundThread(
-    service_manager::ServiceProcessLauncher::Delegate* launcher_delegate,
+    service_manager::ServiceProcessLauncherDelegate* launcher_delegate,
     std::unique_ptr<base::Value> catalog_contents) {
   context_ =
       base::MakeUnique<Context>(launcher_delegate, std::move(catalog_contents));
diff --git a/services/service_manager/background/background_service_manager.h b/services/service_manager/background/background_service_manager.h
index ba77b24..d5292046 100644
--- a/services/service_manager/background/background_service_manager.h
+++ b/services/service_manager/background/background_service_manager.h
@@ -14,7 +14,11 @@
 #include "services/service_manager/public/cpp/identity.h"
 #include "services/service_manager/public/interfaces/connector.mojom.h"
 #include "services/service_manager/public/interfaces/service.mojom.h"
+#include "services/service_manager/runner/host/service_process_launcher_delegate.h"
+
+#if !defined(OS_IOS)
 #include "services/service_manager/runner/host/service_process_launcher.h"
+#endif
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -32,7 +36,7 @@
 class BackgroundServiceManager {
  public:
   BackgroundServiceManager(
-      service_manager::ServiceProcessLauncher::Delegate* launcher_delegate,
+      service_manager::ServiceProcessLauncherDelegate* launcher_delegate,
       std::unique_ptr<base::Value> catalog_contents);
   ~BackgroundServiceManager();
 
@@ -57,7 +61,7 @@
 
  private:
   void InitializeOnBackgroundThread(
-      service_manager::ServiceProcessLauncher::Delegate* launcher_delegate,
+      service_manager::ServiceProcessLauncherDelegate* launcher_delegate,
       std::unique_ptr<base::Value> catalog_contents);
   void ShutDownOnBackgroundThread(base::WaitableEvent* done_event);
   void StartServiceOnBackgroundThread(const Identity& identity);
diff --git a/services/service_manager/embedder/main.cc b/services/service_manager/embedder/main.cc
index 7b49811..6d8ad13 100644
--- a/services/service_manager/embedder/main.cc
+++ b/services/service_manager/embedder/main.cc
@@ -79,14 +79,14 @@
 constexpr size_t kMaximumMojoMessageSize = 128 * 1024 * 1024;
 
 class ServiceProcessLauncherDelegateImpl
-    : public service_manager::ServiceProcessLauncher::Delegate {
+    : public service_manager::ServiceProcessLauncherDelegate {
  public:
   explicit ServiceProcessLauncherDelegateImpl(MainDelegate* main_delegate)
       : main_delegate_(main_delegate) {}
   ~ServiceProcessLauncherDelegateImpl() override {}
 
  private:
-  // service_manager::ServiceProcessLauncher::Delegate:
+  // service_manager::ServiceProcessLauncherDelegate:
   void AdjustCommandLineArgumentsForTarget(
       const service_manager::Identity& target,
       base::CommandLine* command_line) override {
diff --git a/services/service_manager/public/cpp/BUILD.gn b/services/service_manager/public/cpp/BUILD.gn
index a4544309..1e6a2f6 100644
--- a/services/service_manager/public/cpp/BUILD.gn
+++ b/services/service_manager/public/cpp/BUILD.gn
@@ -62,29 +62,27 @@
   defines = [ "SERVICE_MANAGER_PUBLIC_CPP_TYPES_IMPL" ]
 }
 
-if (!is_ios) {
-  static_library("service_test_support") {
-    testonly = true
-    sources = [
-      "service_test.cc",
-      "service_test.h",
-    ]
+static_library("service_test_support") {
+  testonly = true
+  sources = [
+    "service_test.cc",
+    "service_test.h",
+  ]
 
-    public_deps = [
-      ":cpp",
-      "//testing/gtest",
-    ]
+  public_deps = [
+    ":cpp",
+    "//testing/gtest",
+  ]
 
-    deps = [
-      "//base",
-      "//base/test:test_support",
-      "//mojo/edk/system",
-      "//mojo/public/cpp/bindings",
-      "//mojo/public/cpp/system",
-      "//services/service_manager/background:lib",
-      "//services/service_manager/public/interfaces",
-    ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//services/service_manager/background:lib",
+    "//services/service_manager/public/interfaces",
+  ]
 
-    data_deps = []
-  }
+  data_deps = []
 }
diff --git a/services/service_manager/public/cpp/test/BUILD.gn b/services/service_manager/public/cpp/test/BUILD.gn
index 981bf1f..ee7880c 100644
--- a/services/service_manager/public/cpp/test/BUILD.gn
+++ b/services/service_manager/public/cpp/test/BUILD.gn
@@ -21,12 +21,14 @@
     "//mojo/edk/system",
     "//services/catalog:lib",
     "//services/service_manager/background:lib",
-    "//services/service_manager/public/cpp/standalone_service",
   ]
 
   if (is_android) {
     deps += [ "//mojo/android:libsystem_java" ]
   }
+  if (!is_ios) {
+    deps += [ "//services/service_manager/public/cpp/standalone_service" ]
+  }
 }
 
 # NOTE: Don't depend on this target directly. Instead use the service_test
diff --git a/services/service_manager/runner/host/BUILD.gn b/services/service_manager/runner/host/BUILD.gn
index e38345a3..a25b25cc 100644
--- a/services/service_manager/runner/host/BUILD.gn
+++ b/services/service_manager/runner/host/BUILD.gn
@@ -9,15 +9,25 @@
 
 source_set("lib") {
   sources = [
-    "service_process_launcher.cc",
-    "service_process_launcher.h",
+    # These interfaces are shared between iOS and other platforms to minimize
+    # the amount of iddef'ing necessary to handle the fact that iOS does not
+    # support ServiceProcessLauncher.
+    "service_process_launcher_delegate.h",
+    "service_process_launcher_factory.h",
   ]
 
+  if (!is_ios) {
+    sources += [
+      # iOS does not support launching services in their own processes.
+      "service_process_launcher.cc",
+      "service_process_launcher.h",
+    ]
+  }
+
   deps = [
     "//base:base_static",
     "//base:i18n",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/standalone_service",
     "//services/service_manager/runner:init",
     "//services/service_manager/runner/common",
   ]
@@ -31,42 +41,50 @@
   if (is_linux && !is_android) {
     deps += [ "//sandbox/linux:sandbox_services" ]
   }
+
+  if (!is_ios) {
+    deps += [ "//services/service_manager/public/cpp/standalone_service" ]
+  }
 }
 
-source_set("unittests") {
-  testonly = true
-  sources = [
-    "service_process_launcher_unittest.cc",
-  ]
+if (!is_ios) {
+  # The below targets rely on the host_test_service service binary. Service
+  # binaries are not supported on iOS.
+  source_set("unittests") {
+    testonly = true
+    sources = [
+      "service_process_launcher_unittest.cc",
+    ]
 
-  deps = [
-    ":lib",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/edk/system",
-    "//services/service_manager",
-    "//services/service_manager/runner:init",
-    "//services/service_manager/runner/common",
-    "//testing/gtest",
-  ]
+    deps = [
+      ":lib",
+      "//base",
+      "//base/test:test_support",
+      "//mojo/edk/system",
+      "//services/service_manager",
+      "//services/service_manager/runner:init",
+      "//services/service_manager/runner/common",
+      "//testing/gtest",
+    ]
 
-  data_deps = [
-    ":host_test_service",
-  ]
-}
+    data_deps = [
+      ":host_test_service",
+    ]
+  }
 
-service("host_test_service") {
-  sources = [
-    "host_test_service_main.cc",
-  ]
+  service("host_test_service") {
+    sources = [
+      "host_test_service_main.cc",
+    ]
 
-  deps = [
-    "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp/standalone_service:main",
-  ]
-}
+    deps = [
+      "//mojo/public/cpp/system",
+      "//services/service_manager/public/cpp/standalone_service:main",
+    ]
+  }
 
-service_manifest("host_test_service_manifest") {
-  name = "host_test_service"
-  source = "host_test_service_manifest.json"
+  service_manifest("host_test_service_manifest") {
+    name = "host_test_service"
+    source = "host_test_service_manifest.json"
+  }
 }
diff --git a/services/service_manager/runner/host/service_process_launcher.cc b/services/service_manager/runner/host/service_process_launcher.cc
index 7142aba1..c247fa5 100644
--- a/services/service_manager/runner/host/service_process_launcher.cc
+++ b/services/service_manager/runner/host/service_process_launcher.cc
@@ -41,7 +41,7 @@
 
 ServiceProcessLauncher::ServiceProcessLauncher(
     base::TaskRunner* launch_process_runner,
-    Delegate* delegate,
+    ServiceProcessLauncherDelegate* delegate,
     const base::FilePath& service_path)
     : launch_process_runner_(launch_process_runner),
       delegate_(delegate),
diff --git a/services/service_manager/runner/host/service_process_launcher.h b/services/service_manager/runner/host/service_process_launcher.h
index 3afae0a..b8afbe4 100644
--- a/services/service_manager/runner/host/service_process_launcher.h
+++ b/services/service_manager/runner/host/service_process_launcher.h
@@ -18,6 +18,7 @@
 #include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "services/service_manager/public/interfaces/service_factory.mojom.h"
+#include "services/service_manager/runner/host/service_process_launcher_delegate.h"
 
 namespace base {
 class CommandLine;
@@ -41,23 +42,11 @@
  public:
   using ProcessReadyCallback = base::Callback<void(base::ProcessId)>;
 
-  class Delegate {
-   public:
-    // Called to adjust the commandline for launching the specified app.
-    // WARNING: this is called on a background thread.
-    virtual void AdjustCommandLineArgumentsForTarget(
-        const Identity& target,
-        base::CommandLine* command_line) = 0;
-
-   protected:
-    virtual ~Delegate() {}
-  };
-
   // |name| is just for debugging ease. We will spawn off a process so that it
   // can be sandboxed if |start_sandboxed| is true. |service_path| is a path to
   // the service executable we wish to start.
   ServiceProcessLauncher(base::TaskRunner* launch_process_runner,
-                         Delegate* delegate,
+                         ServiceProcessLauncherDelegate* delegate,
                          const base::FilePath& service_path);
   ~ServiceProcessLauncher();
 
@@ -75,7 +64,7 @@
   void DoLaunch(std::unique_ptr<base::CommandLine> child_command_line);
 
   scoped_refptr<base::TaskRunner> launch_process_runner_;
-  Delegate* delegate_ = nullptr;
+  ServiceProcessLauncherDelegate* delegate_ = nullptr;
   bool start_sandboxed_ = false;
   Identity target_;
   base::FilePath service_path_;
@@ -95,13 +84,6 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceProcessLauncher);
 };
 
-class ServiceProcessLauncherFactory {
- public:
-  virtual ~ServiceProcessLauncherFactory() {}
-  virtual std::unique_ptr<ServiceProcessLauncher> Create(
-      const base::FilePath& service_path) = 0;
-};
-
 }  // namespace service_manager
 
 #endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_SERVICE_PROCESS_LAUNCHER_H_
diff --git a/services/service_manager/runner/host/service_process_launcher_delegate.h b/services/service_manager/runner/host/service_process_launcher_delegate.h
new file mode 100644
index 0000000..2eee3db
--- /dev/null
+++ b/services/service_manager/runner/host/service_process_launcher_delegate.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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 SERVICES_SERVICE_MANAGER_RUNNER_HOST_SERVICE_PROCESS_LAUNCHER_DELEGATE_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_SERVICE_PROCESS_LAUNCHER_DELEGATE_H_
+
+namespace base {
+class CommandLine;
+}
+
+namespace service_manager {
+
+class Identity;
+
+class ServiceProcessLauncherDelegate {
+ public:
+  // Called to adjust the commandline for launching the specified app.
+  // WARNING: this is called on a background thread.
+  virtual void AdjustCommandLineArgumentsForTarget(
+      const Identity& target,
+      base::CommandLine* command_line) = 0;
+
+ protected:
+  virtual ~ServiceProcessLauncherDelegate() {}
+};
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_SERVICE_PROCESS_LAUNCHER_DELEGATE_H_
diff --git a/services/service_manager/runner/host/service_process_launcher_factory.h b/services/service_manager/runner/host/service_process_launcher_factory.h
new file mode 100644
index 0000000..ab11fd1a
--- /dev/null
+++ b/services/service_manager/runner/host/service_process_launcher_factory.h
@@ -0,0 +1,25 @@
+// Copyright 2014 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 SERVICES_SERVICE_MANAGER_RUNNER_HOST_SERVICE_PROCESS_LAUNCHER_FACTORY_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_SERVICE_PROCESS_LAUNCHER_FACTORY_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+
+namespace service_manager {
+
+class ServiceProcessLauncher;
+
+class ServiceProcessLauncherFactory {
+ public:
+  virtual ~ServiceProcessLauncherFactory() {}
+  virtual std::unique_ptr<ServiceProcessLauncher> Create(
+      const base::FilePath& service_path) = 0;
+};
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_SERVICE_PROCESS_LAUNCHER_FACTORY_H_
diff --git a/services/service_manager/runner/host/service_process_launcher_unittest.cc b/services/service_manager/runner/host/service_process_launcher_unittest.cc
index fb696bdc..642e27e 100644
--- a/services/service_manager/runner/host/service_process_launcher_unittest.cc
+++ b/services/service_manager/runner/host/service_process_launcher_unittest.cc
@@ -37,7 +37,7 @@
 }
 
 class ServiceProcessLauncherDelegateImpl
-    : public ServiceProcessLauncher::Delegate {
+    : public ServiceProcessLauncherDelegate {
  public:
   ServiceProcessLauncherDelegateImpl() {}
   ~ServiceProcessLauncherDelegateImpl() override {}
@@ -49,7 +49,7 @@
   }
 
  private:
-  // ServiceProcessLauncher::Delegate:
+  // ServiceProcessLauncherDelegate:
   void AdjustCommandLineArgumentsForTarget(
       const Identity& target,
       base::CommandLine* command_line) override {
diff --git a/services/service_manager/service_manager.cc b/services/service_manager/service_manager.cc
index 13d26c90..9d37a60 100644
--- a/services/service_manager/service_manager.cc
+++ b/services/service_manager/service_manager.cc
@@ -71,6 +71,15 @@
   return capabilities;
 }
 
+base::ProcessId GetCurrentPid() {
+#if defined(OS_IOS)
+  // iOS does not support base::Process.
+  return 0;
+#else
+  return base::Process::Current().Pid();
+#endif
+}
+
 // Generates a single set of interfaces that is the union of all interfaces
 // exposed by the target for the capabilities requested by the source.
 InterfaceSet GetInterfacesToExpose(const InterfaceProviderSpec& source_spec,
@@ -173,7 +182,7 @@
         weak_factory_(this) {
     if (identity_.name() == service_manager::mojom::kServiceName ||
         identity_.name() == catalog::mojom::kServiceName) {
-      pid_ = base::Process::Current().Pid();
+      pid_ = GetCurrentPid();
     }
     DCHECK_NE(mojom::kInvalidInstanceID, id_);
   }
@@ -249,6 +258,11 @@
   }
 
   bool StartWithFilePath(const base::FilePath& path) {
+#if defined(OS_IOS)
+    // iOS does not support launching services in their own processes.
+    NOTREACHED();
+    return false;
+#else
     DCHECK(!service_);
     DCHECK(!path.empty());
     runner_ = service_manager_->service_process_launcher_factory_->Create(path);
@@ -260,6 +274,7 @@
         base::Bind(&Instance::PIDAvailable, weak_factory_.GetWeakPtr()));
     StartWithService(std::move(service));
     return true;
+#endif
   }
 
   void BindPIDReceiver(mojom::PIDReceiverRequest request) {
@@ -583,10 +598,14 @@
   }
 
   void PIDAvailable(base::ProcessId pid) {
+#if !defined(OS_IOS)
+    // iOS does not support base::Process and simply passes 0 here, so elide
+    // this check on that platform.
     if (pid == base::kNullProcessId) {
       service_manager_->OnInstanceError(this);
       return;
     }
+#endif
     pid_ = pid;
   }
 
@@ -638,7 +657,9 @@
   Identity identity_;
   const InterfaceProviderSpecMap interface_provider_specs_;
   const bool allow_any_application_;
+#if !defined(OS_IOS)
   std::unique_ptr<ServiceProcessLauncher> runner_;
+#endif
   mojom::ServicePtr service_;
   mojo::Binding<mojom::PIDReceiver> pid_receiver_binding_;
   mojo::BindingSet<mojom::Connector> connectors_;
@@ -899,7 +920,7 @@
   if (!pid_receiver_request.is_pending()) {
     mojom::PIDReceiverPtr pid_receiver;
     pid_receiver_request = mojo::MakeRequest(&pid_receiver);
-    pid_receiver->SetPID(base::Process::Current().Pid());
+    pid_receiver->SetPID(GetCurrentPid());
   }
 
   params->set_source(identity);
diff --git a/services/service_manager/service_manager.h b/services/service_manager/service_manager.h
index 475ee1b..1547c33 100644
--- a/services/service_manager/service_manager.h
+++ b/services/service_manager/service_manager.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/process/process.h"
 #include "base/values.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
@@ -23,9 +24,13 @@
 #include "services/service_manager/public/interfaces/service.mojom.h"
 #include "services/service_manager/public/interfaces/service_factory.mojom.h"
 #include "services/service_manager/public/interfaces/service_manager.mojom.h"
-#include "services/service_manager/runner/host/service_process_launcher.h"
+#include "services/service_manager/runner/host/service_process_launcher_factory.h"
 #include "services/service_manager/service_overrides.h"
 
+#if !defined(OS_IOS)
+#include "services/service_manager/runner/host/service_process_launcher.h"
+#endif
+
 namespace catalog {
 class ManifestProvider;
 }
diff --git a/services/service_manager/standalone/BUILD.gn b/services/service_manager/standalone/BUILD.gn
index 7ec5b5e0..950a21b 100644
--- a/services/service_manager/standalone/BUILD.gn
+++ b/services/service_manager/standalone/BUILD.gn
@@ -15,15 +15,17 @@
   deps = [
     "//base",
     "//base/third_party/dynamic_annotations",
-    "//components/tracing:startup_tracing",
     "//mojo/common:common_base",
     "//mojo/edk/system",
     "//services/catalog:lib",
     "//services/service_manager",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/standalone_service",
     "//services/service_manager/runner/common",
     "//services/service_manager/runner/host:lib",
     "//url",
   ]
+
+  if (!is_ios) {
+    deps += [ "//services/service_manager/public/cpp/standalone_service" ]
+  }
 }
diff --git a/services/service_manager/standalone/DEPS b/services/service_manager/standalone/DEPS
index 3749b753..3fd6080b 100644
--- a/services/service_manager/standalone/DEPS
+++ b/services/service_manager/standalone/DEPS
@@ -1,4 +1,3 @@
 include_rules = [
-  "+components/tracing",
   "+services/catalog",
 ]
diff --git a/services/service_manager/standalone/context.cc b/services/service_manager/standalone/context.cc
index 6262aad0..3c94276 100644
--- a/services/service_manager/standalone/context.cc
+++ b/services/service_manager/standalone/context.cc
@@ -30,10 +30,14 @@
 #include "services/service_manager/connect_params.h"
 #include "services/service_manager/connect_util.h"
 #include "services/service_manager/runner/common/switches.h"
-#include "services/service_manager/runner/host/service_process_launcher.h"
+#include "services/service_manager/runner/host/service_process_launcher_factory.h"
 #include "services/service_manager/service_manager.h"
 #include "services/service_manager/switches.h"
 
+#if !defined(OS_IOS)
+#include "services/service_manager/runner/host/service_process_launcher.h"
+#endif
+
 #if defined(OS_MACOSX)
 #include "services/service_manager/public/cpp/standalone_service/mach_broker.h"
 #endif
@@ -45,14 +49,13 @@
 namespace service_manager {
 namespace {
 
+#if !defined(OS_IOS)
 // Used to ensure we only init once.
 class ServiceProcessLauncherFactoryImpl : public ServiceProcessLauncherFactory {
  public:
   ServiceProcessLauncherFactoryImpl(base::TaskRunner* launch_process_runner,
-                                    ServiceProcessLauncher::Delegate* delegate)
-      : launch_process_runner_(launch_process_runner),
-        delegate_(delegate) {
-  }
+                                    ServiceProcessLauncherDelegate* delegate)
+      : launch_process_runner_(launch_process_runner), delegate_(delegate) {}
 
  private:
    std::unique_ptr<ServiceProcessLauncher> Create(
@@ -62,8 +65,9 @@
   }
 
   base::TaskRunner* launch_process_runner_;
-  ServiceProcessLauncher::Delegate* delegate_;
+  ServiceProcessLauncherDelegate* delegate_;
 };
+#endif  // !defined(OS_IOS)
 
 void OnInstanceQuit(const std::string& name, const Identity& identity) {
   if (name == identity.name())
@@ -75,7 +79,7 @@
 }  // namespace
 
 Context::Context(
-    ServiceProcessLauncher::Delegate* service_process_launcher_delegate,
+    ServiceProcessLauncherDelegate* service_process_launcher_delegate,
     std::unique_ptr<base::Value> catalog_contents)
     : main_entry_time_(base::Time::Now()) {
   TRACE_EVENT0("service_manager", "Context::Context");
@@ -84,10 +88,15 @@
       kThreadPoolMaxThreads, "blocking_pool", base::TaskPriority::USER_VISIBLE);
 
   std::unique_ptr<ServiceProcessLauncherFactory>
-      service_process_launcher_factory =
-          base::MakeUnique<ServiceProcessLauncherFactoryImpl>(
-              blocking_pool_.get(),
-              service_process_launcher_delegate);
+      service_process_launcher_factory;
+
+// iOS does not support launching services in their own processes (and does
+// not build ServiceProcessLauncher).
+#if !defined(OS_IOS)
+  service_process_launcher_factory =
+      base::MakeUnique<ServiceProcessLauncherFactoryImpl>(
+          blocking_pool_.get(), service_process_launcher_delegate);
+#endif
   service_manager_.reset(
       new ServiceManager(std::move(service_process_launcher_factory),
                          std::move(catalog_contents), nullptr));
diff --git a/services/service_manager/standalone/context.h b/services/service_manager/standalone/context.h
index 6e9b91f..61c6ae7 100644
--- a/services/service_manager/standalone/context.h
+++ b/services/service_manager/standalone/context.h
@@ -11,7 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "services/service_manager/runner/host/service_process_launcher.h"
+#include "services/service_manager/runner/host/service_process_launcher_delegate.h"
 
 namespace base {
 class SequencedWorkerPool;
@@ -27,7 +27,7 @@
 // The "global" context for the service manager's main process.
 class Context {
  public:
-  Context(ServiceProcessLauncher::Delegate* launcher_delegate,
+  Context(ServiceProcessLauncherDelegate* launcher_delegate,
           std::unique_ptr<base::Value> catalog_content);
   ~Context();
 
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 2d4fb59..e8280055f 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -354,6 +354,41 @@
         },
         "test": "unit_tests"
       }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--additional-driver-flag",
+          "--enable-browser-side-navigation"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose",
+            "--results-json-override-with-build-property",
+            "build_number",
+            "buildnumber",
+            "--results-json-override-with-build-property",
+            "builder_name",
+            "buildername",
+            "--results-json-override-with-build-property",
+            "chromium_revision",
+            "got_revision_cp"
+          ],
+          "script": "//third_party/WebKit/Tools/Scripts/merge-layout-test-results"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "shards": 1
+        }
+      }
     ]
   },
   "CFI Linux": {
@@ -10137,7 +10172,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-filter-file=testing/buildbot/filters/fuchsia.base_unittests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.base_unittests.filter"
         ],
         "swarming": {
           "can_use_on_swarming_builders": false
@@ -10154,7 +10189,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-filter-file=testing/buildbot/filters/fuchsia.base_unittests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.base_unittests.filter"
         ],
         "swarming": {
           "can_use_on_swarming_builders": false
@@ -11041,6 +11076,44 @@
         },
         "test": "unit_tests"
       }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--additional-driver-flag",
+          "--site-per-process",
+          "--additional-expectations",
+          "src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process",
+          "http/tests"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose",
+            "--results-json-override-with-build-property",
+            "build_number",
+            "buildnumber",
+            "--results-json-override-with-build-property",
+            "builder_name",
+            "buildername",
+            "--results-json-override-with-build-property",
+            "chromium_revision",
+            "got_revision_cp"
+          ],
+          "script": "//third_party/WebKit/Tools/Scripts/merge-layout-test-results"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "shards": 1
+        }
+      }
     ]
   },
   "Site Isolation Win": {
@@ -11914,6 +11987,80 @@
       }
     ]
   },
+  "WebKit Linux layout_ng Dummy Builder": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--additional-driver-flag=--enable-blink-features=LayoutNG"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose",
+            "--results-json-override-with-build-property",
+            "build_number",
+            "buildnumber",
+            "--results-json-override-with-build-property",
+            "builder_name",
+            "buildername",
+            "--results-json-override-with-build-property",
+            "chromium_revision",
+            "got_revision_cp"
+          ],
+          "script": "//third_party/WebKit/Tools/Scripts/merge-layout-test-results"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "hard_timeout": 900,
+          "shards": 6
+        }
+      }
+    ]
+  },
+  "WebKit Linux slimming_paint_v2 Dummy Builder": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--additional-driver-flag=--enable-slimming-paint-v2"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose",
+            "--results-json-override-with-build-property",
+            "build_number",
+            "buildnumber",
+            "--results-json-override-with-build-property",
+            "builder_name",
+            "buildername",
+            "--results-json-override-with-build-property",
+            "chromium_revision",
+            "got_revision_cp"
+          ],
+          "script": "//third_party/WebKit/Tools/Scripts/merge-layout-test-results"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "hard_timeout": 900,
+          "shards": 6
+        }
+      }
+    ]
+  },
   "WebKit Mac - RandomOrder": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/filters/fuchsia.base_unittests.filter b/testing/buildbot/filters/fuchsia.base_unittests.filter
index 8bc6fd19..1a7c2a20 100644
--- a/testing/buildbot/filters/fuchsia.base_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.base_unittests.filter
@@ -109,6 +109,7 @@
 -MessageLoopForIOOnMainThread/FileDescriptorWatcherTest.WatchWritable/0
 -MessageLoopTest.FileDescriptorWatcherDoubleStop
 -MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop
+-MessageLoopTestTypeDefault.PostDelayedTask_Basic
 -NativeLibraryTest.LoadLibrary
 -NativeLibraryTest.LoadLibraryPreferOwnSymbols
 -ObserverListThreadSafeTest.AddObserverFromNotificationNotifyAll
@@ -173,10 +174,16 @@
 -ProcessUtilTest.LaunchProcess
 -ProcessUtilTest.PreExecHook
 -ProcessUtilTest.SpawnChild
+-RedirectionToTaskScheduler/SequencedWorkerPoolTest.AvoidsDeadlockOnShutdown/0
 -RedirectionToTaskScheduler/SequencedWorkerPoolTest.AvoidsDeadlockOnShutdownWithSequencedBlockingTasks/0
+-RedirectionToTaskScheduler/SequencedWorkerPoolTest.ContinueOnShutdown/0
+-RedirectionToTaskScheduler/SequencedWorkerPoolTest.DiscardOnShutdown/0
 -RedirectionToTaskScheduler/SequencedWorkerPoolTest.FlushForTesting/0
 -RedirectionToTaskScheduler/SequencedWorkerPoolTest.GetWorkerPoolAndSequenceTokenForCurrentThread/0
+-RedirectionToTaskScheduler/SequencedWorkerPoolTest.RunsTasksOnCurrentThread/0
 -RedirectionToTaskScheduler/SequencedWorkerPoolTest.ShutsDownCleanWithContinueOnShutdown/0
+-RedirectionToTaskScheduler/SequencedWorkerPoolTest.SkipOnShutdown/0
+-RedirectionToTaskScheduler/SequencedWorkerPoolTest.SpuriousWorkSignal/0
 -RTLTest.WrapPathWithLTRFormatting
 -SafeNumerics.SignedIntegerMath
 -SharedMemoryProcessTest.SharedMemoryAcrossProcesses
@@ -197,6 +204,15 @@
 -SysInfoTest.AmountOfFreeDiskSpace
 -SysInfoTest.AmountOfMem
 -SysInfoTest.AmountOfTotalDiskSpace
+-TaskSchedulerSingleThreadTaskRunnerManagerJoinTest.ConcurrentJoinExtraSkippedTask
+-TaskSchedulerSingleThreadTaskRunnerManagerStartTest.PostTaskBeforeStart
+-TaskSchedulerWorkerPoolCheckTlsReuse.CheckDetachedThreads
+-TaskSchedulerWorkerPoolHistogramTest.NumTasksBeforeDetach
+-TaskSchedulerWorkerPoolHistogramTest.NumTasksBetweenWaits
+-TaskSchedulerWorkerPoolHistogramTest.NumTasksBetweenWaitsWithDetach
+-TaskSchedulerWorkerPoolImplPostTaskBeforeStartTest.PostTasksBeforeStart
+-TaskSchedulerWorkerPoolStandbyPolicyTest.InitLazy
+-TaskSchedulerWorkerPoolStandbyPolicyTest.InitOne
 -TraceEventTestFixture.TestTraceFlush
 -TrackedObjectsTest.ReuseRetiredThreadData
 -TrackedTimeTest.TrackedTimerDuration
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index c71d597..0ac02b2 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -19903,7 +19903,11 @@
 crbug.com/591099 paint/clipath/clip-path-with-background-and-box-behind.html [ Failure Pass ]
 crbug.com/591099 paint/frames/frameset-with-stacking-context-and-not-stacking-context-children.html [ Failure ]
 crbug.com/591099 paint/frames/frameset-with-stacking-contexts.html [ Failure ]
-crbug.com/591099 paint/high-contrast-mode/text-on-backgrounds.html [ Failure ]
+crbug.com/591099 paint/high-contrast-mode/image-filter-all/gradient-invert.html [ Failure ]
+crbug.com/591099 paint/high-contrast-mode/image-filter-all/image-invert.html [ Failure ]
+crbug.com/591099 paint/high-contrast-mode/image-filter-all/text-on-backgrounds.html [ Failure ]
+crbug.com/591099 paint/high-contrast-mode/image-filter-none/gradient-noinvert.html [ Failure ]
+crbug.com/591099 paint/high-contrast-mode/image-filter-none/image-noinvert.html [ Failure ]
 crbug.com/591099 paint/images/animated-gif-last-frame-crash.html [ Crash ]
 crbug.com/591099 paint/inline/floating-inline.html [ Failure ]
 crbug.com/591099 paint/inline/focus-ring-under-absolute-with-relative-continuation.html [ Failure ]
@@ -23151,7 +23155,11 @@
 crbug.com/591099 virtual/disable-spinvalidation/paint/clipath/clip-path-with-background-and-box-behind.html [ Failure Pass ]
 crbug.com/591099 virtual/disable-spinvalidation/paint/frames/frameset-with-stacking-context-and-not-stacking-context-children.html [ Failure ]
 crbug.com/591099 virtual/disable-spinvalidation/paint/frames/frameset-with-stacking-contexts.html [ Failure ]
-crbug.com/591099 virtual/disable-spinvalidation/paint/high-contrast-mode/text-on-backgrounds.html [ Failure ]
+crbug.com/591099 virtual/disable-spinvalidation/paint/high-contrast-mode/image-filter-all/gradient-invert.html [ Failure ]
+crbug.com/591099 virtual/disable-spinvalidation/paint/high-contrast-mode/image-filter-all/image-invert.html [ Failure ]
+crbug.com/591099 virtual/disable-spinvalidation/paint/high-contrast-mode/image-filter-all/text-on-backgrounds.html [ Failure ]
+crbug.com/591099 virtual/disable-spinvalidation/paint/high-contrast-mode/image-filter-none/gradient-noinvert.html [ Failure ]
+crbug.com/591099 virtual/disable-spinvalidation/paint/high-contrast-mode/image-filter-none/image-noinvert.html [ Failure ]
 crbug.com/591099 virtual/disable-spinvalidation/paint/images/animated-gif-last-frame-crash.html [ Crash ]
 crbug.com/591099 virtual/disable-spinvalidation/paint/inline/floating-inline.html [ Failure ]
 crbug.com/591099 virtual/disable-spinvalidation/paint/inline/focus-ring-under-absolute-with-relative-continuation.html [ Failure ]
@@ -24491,7 +24499,11 @@
 crbug.com/591099 virtual/gpu/fast/canvas/webgl/webgl-texture-binding-preserved.html [ Failure ]
 crbug.com/591099 virtual/gpu/fast/canvas/webgl/webgl-viewport-parameters-preserved.html [ Failure ]
 crbug.com/591099 virtual/gpu/fast/canvas/zero-size-fill-rect.html [ Crash ]
-crbug.com/591099 virtual/high-contrast-mode/paint/high-contrast-mode/text-on-backgrounds.html [ Failure ]
+crbug.com/591099 virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/gradient-invert.html [ Failure ]
+crbug.com/591099 virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/image-invert.html [ Failure ]
+crbug.com/591099 virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/text-on-backgrounds.html [ Failure ]
+crbug.com/591099 virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/gradient-noinvert.html [ Failure ]
+crbug.com/591099 virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/image-noinvert.html [ Failure ]
 crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/floats/floats-wrap-top-below-inline-003r.xht [ Failure Pass ]
 crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Crash Failure Pass ]
 crbug.com/591099 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/height-114.xht [ Crash Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 75c7ecf6..322f5e3 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -107,7 +107,6 @@
 Bug(none) virtual/without-smil/ [ Skip ]
 
 Bug(none) paint/transparency/compositing-alpha-fold-crash.html [ Failure ]
-Bug(none) paint/invalidation/table/row-change-background-rowspan-cell.html [ Failure ]
 Bug(none) paint/invalidation/clip-flex-text.html [ Pass Failure ]
 Bug(none) virtual/new-remote-playback-pipeline/media/controls/video-controls-with-cast-rendering.html [ Failure ]
 Bug(none) virtual/new-remote-playback-pipeline/media/controls/video-overlay-cast-dark-rendering.html [ Failure ]
@@ -998,7 +997,6 @@
 Bug(none) fast/text/word-break.html [ Failure ]
 Bug(none) fast/writing-mode/border-radius-clipping-vertical-lr.html [ Failure ]
 Bug(none) fast/writing-mode/fieldsets.html [ Failure ]
-Bug(none) paint/invalidation/caret-contenteditable-content-after.html [ Failure ]
 Bug(none) paint/invalidation/composited-iframe-scroll-repaint.html [ Failure ]
 Bug(none) paint/invalidation/iframe-scroll-repaint.html [ Failure ]
 Bug(none) paint/invalidation/repaint-subrect-grid.html [ Failure ]
@@ -1006,63 +1004,40 @@
 Bug(none) paint/invalidation/single-line-cells-repeating-thead-break-inside-on-thead-only.html [ Crash ]
 Bug(none) paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1.html [ Failure ]
 Bug(none) paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-2.html [ Failure ]
-Bug(none) paint/invalidation/svg/js-late-mask-and-object-creation.svg [ Crash ]
-Bug(none) paint/invalidation/svg/js-late-mask-creation.svg [ Crash ]
 Bug(none) paint/invalidation/svg/repaint-moving-svg-and-div.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/resource-client-removal.svg [ Failure ]
 Bug(none) paint/invalidation/svg/use-detach.svg [ Failure ]
 Bug(none) paint/background/rounded-clip-fractional-offset.html [ Failure ]
 Bug(none) paint/clipath/clip-path-with-background-and-box-behind.html [ Failure ]
 Bug(none) paint/frames/frameset-with-stacking-context-and-not-stacking-context-children.html [ Failure ]
 Bug(none) paint/frames/frameset-with-stacking-contexts.html [ Failure ]
-Bug(none) paint/invalidation/4776765.html [ Failure ]
 Bug(none) paint/invalidation/absolute-position-change-containing-block.html [ Failure ]
 Bug(none) paint/invalidation/absolute-position-changed.html [ Failure ]
 Bug(none) paint/invalidation/absolute-position-moved.html [ Failure ]
 Bug(none) paint/invalidation/abspos-shift-image-incorrect-repaint.html [ Failure ]
-Bug(none) paint/invalidation/add-table-overpaint.html [ Failure ]
-Bug(none) paint/invalidation/align-items-change.html [ Failure ]
-Bug(none) paint/invalidation/align-self-change.html [ Failure ]
 Bug(none) paint/invalidation/animated-gif-transformed-offscreen.html [ Failure ]
-Bug(none) paint/invalidation/background-currentColor-repaint.html [ Failure ]
 Bug(none) paint/invalidation/background-image-paint-invalidation-large-abspos-div.html [ Failure ]
-Bug(none) paint/invalidation/background-image-paint-invalidation.html [ Failure ]
 Bug(none) paint/invalidation/background-misaligned.html [ Failure ]
 Bug(none) paint/invalidation/background-resize-height.html [ Failure ]
 Bug(none) paint/invalidation/backgroundSizeRepaint.html [ Failure ]
 Bug(none) paint/invalidation/block-layout-inline-children-float-positioned.html [ Failure ]
-Bug(none) paint/invalidation/block-no-inflow-children.html [ Failure ]
-Bug(none) paint/invalidation/block-shift-repaint.html [ Failure ]
 Bug(none) paint/invalidation/body-background-image.html [ Failure ]
 Bug(none) paint/invalidation/border-image-outset-add-repaint.html [ Failure ]
 Bug(none) paint/invalidation/border-image-outset-change-repaint.html [ Failure ]
-Bug(none) paint/invalidation/border-outline-0.html [ Failure ]
 Bug(none) paint/invalidation/border-radius-repaint-2.html [ Failure ]
 Bug(none) paint/invalidation/border-radius-repaint.html [ Failure ]
 Bug(none) paint/invalidation/border-repaint-glitch.html [ Failure ]
-Bug(none) paint/invalidation/box-inline-resize.html [ Failure ]
 Bug(none) paint/invalidation/box-sizing.html [ Failure ]
-Bug(none) paint/invalidation/bugzilla-3509.html [ Failure ]
 Bug(none) paint/invalidation/bugzilla-5699.html [ Failure ]
-Bug(none) paint/invalidation/bugzilla-6278.html [ Failure ]
-Bug(none) paint/invalidation/bugzilla-6388.html [ Failure ]
 Bug(none) paint/invalidation/bugzilla-6473.html [ Failure ]
-Bug(none) paint/invalidation/bugzilla-7235.html [ Failure ]
-Bug(none) paint/invalidation/canvas-composite-repaint-by-all-imagesource.html [ Failure ]
-Bug(none) paint/invalidation/canvas-putImageData.html [ Failure ]
-Bug(none) paint/invalidation/canvas-resize.html [ Failure ]
+crbug.com/732612 paint/invalidation/canvas-composite-repaint-by-all-imagesource.html [ Failure ]
+crbug.com/732612 paint/invalidation/canvas-putImageData.html [ Failure ]
+crbug.com/732612 paint/invalidation/canvas-resize.html [ Failure ]
 Bug(none) paint/invalidation/caret-invalidation-in-overflow-scroll.html [ Failure ]
-Bug(none) paint/invalidation/caret-outside-block.html [ Failure ]
 Bug(none) paint/invalidation/caret-subpixel.html [ Failure ]
 Bug(none) paint/invalidation/caret-with-composited-scroll.html [ Failure ]
-Bug(none) paint/invalidation/caret-with-transformation.html [ Failure ]
-Bug(none) paint/invalidation/change-text-content-and-background-color.html [ Failure ]
-Bug(none) paint/invalidation/change-transform.html [ Failure ]
 Bug(none) paint/invalidation/child-of-sub-pixel-offset-composited-layer.html [ Failure ]
 Bug(none) paint/invalidation/clip-path-constant-repaint.html [ Failure ]
-Bug(none) paint/invalidation/clip-with-layout-delta.html [ Failure ]
 Bug(none) paint/invalidation/clipped-overflow-visible-subtree.html [ Failure ]
-Bug(none) paint/invalidation/clipped-relative.html [ Failure ]
 Bug(none) paint/invalidation/column-rules-fixed-height.html [ Failure ]
 Bug(none) paint/invalidation/composited-overflow-with-borderbox-background.html [ Failure ]
 Bug(none) paint/invalidation/composited-overflow-with-local-background.html [ Failure ]
@@ -1121,25 +1096,15 @@
 Bug(none) paint/invalidation/compositing/updating-scrolling-container-and-content.html [ Failure ]
 Bug(none) paint/invalidation/compositing/updating-scrolling-container.html [ Failure ]
 Bug(none) paint/invalidation/compositing/updating-scrolling-content.html [ Failure ]
-Bug(none) paint/invalidation/containing-block-position-change.html [ Failure ]
-Bug(none) paint/invalidation/content-into-overflow.html [ Failure ]
 Bug(none) paint/invalidation/control-clip.html [ Failure ]
-Bug(none) paint/invalidation/crbug-371640-2.html [ Failure ]
-Bug(none) paint/invalidation/crbug-371640-3.html [ Failure ]
-Bug(none) paint/invalidation/crbug-371640-4.html [ Failure ]
-Bug(none) paint/invalidation/crbug-371640.html [ Failure ]
 Bug(none) paint/invalidation/create-layer-repaint.html [ Failure ]
 Bug(none) paint/invalidation/css-grid-layout/grid-item-z-index-change-repaint.html [ Failure ]
-Bug(none) paint/invalidation/delete-into-nested-block.html [ Failure ]
 Bug(none) paint/invalidation/destroy-composited-scrollbar.html [ Failure ]
 Bug(none) paint/invalidation/destroy-overlay-scrollbar.html [ Failure ]
 Bug(none) paint/invalidation/destroy-scrollbar.html [ Failure ]
-Bug(none) paint/invalidation/details-open-repaint.html [ Failure ]
 Bug(none) paint/invalidation/document-flipped-blocks-writing-mode-scroll.html [ Failure ]
 Bug(none) paint/invalidation/dont-invalidate-root-layer-when-composited-layer-becomes-visible.html [ Failure ]
-Bug(none) paint/invalidation/dynamic-table-vertical-alignment-change.html [ Failure ]
 Bug(none) paint/invalidation/empty-object-move-and-resize.html [ Failure ]
-Bug(none) paint/invalidation/erase-overflow.html [ Failure ]
 Bug(none) paint/invalidation/filter-invalidation-after-display.html [ Failure ]
 Bug(none) paint/invalidation/filter-invalidation-positioned-child.html [ Failure ]
 Bug(none) paint/invalidation/filter-invalidation-with-composited-container-change.html [ Failure ]
@@ -1185,43 +1150,16 @@
 Bug(none) paint/invalidation/flexbox/repaint-opacity-change.html [ Failure ]
 Bug(none) paint/invalidation/flexbox/repaint-rtl-column.html [ Failure ]
 Bug(none) paint/invalidation/flexbox/repaint.html [ Failure ]
-Bug(none) paint/invalidation/flexbox/scrollbars-changed.html [ Failure ]
 Bug(none) paint/invalidation/flipped-blocks-writing-mode-scroll.html [ Failure ]
-Bug(none) paint/invalidation/float-move-during-layout.html [ Failure ]
-Bug(none) paint/invalidation/float-overflow-right.html [ Failure ]
-Bug(none) paint/invalidation/float-overflow.html [ Failure ]
-Bug(none) paint/invalidation/focus-continuations.html [ Failure ]
-Bug(none) paint/invalidation/focus-enable-continuations.html [ Failure ]
-Bug(none) paint/invalidation/focus-ring-on-inline-continuation-move.html [ Failure ]
-Bug(none) paint/invalidation/forms/button-reset-focus-by-mouse-then-keydown.html [ Failure ]
-Bug(none) paint/invalidation/forms/range-focus-by-mouse-then-keydown.html [ Failure ]
-Bug(none) paint/invalidation/forms/submit-focus-by-mouse-then-keydown.html [ Failure ]
 Bug(none) paint/invalidation/full-viewport-repaint-for-background-attachment-fixed.html [ Failure ]
-Bug(none) paint/invalidation/gradients-em-stops-repaint.html [ Failure ]
-Bug(none) paint/invalidation/hover-pseudo-borders.html [ Failure ]
 Bug(none) paint/invalidation/iframe-display-block-to-display-none.html [ Failure ]
 Bug(none) paint/invalidation/iframe-display-none-to-display-block.html [ Failure ]
-Bug(none) paint/invalidation/iframe-scrollbar-hover.html [ Failure ]
 Bug(none) paint/invalidation/image-resize.html [ Failure ]
-Bug(none) paint/invalidation/inline-block-resize.html [ Failure ]
-Bug(none) paint/invalidation/inline-color-change.html [ Failure ]
 Bug(none) paint/invalidation/inline-focus.html [ Failure ]
 Bug(none) paint/invalidation/inline-outline-repaint-2.html [ Failure ]
-Bug(none) paint/invalidation/inline-outline-repaint.html [ Failure ]
-Bug(none) paint/invalidation/inline-overflow.html [ Failure ]
-Bug(none) paint/invalidation/inline-reflow.html [ Failure ]
-Bug(none) paint/invalidation/inline-relative-positioned.html [ Failure ]
 Bug(none) paint/invalidation/inline-style-change-in-scrolled-view.html [ Failure ]
-Bug(none) paint/invalidation/inline-vertical-lr-overflow.html [ Failure ]
-Bug(none) paint/invalidation/inline-vertical-rl-overflow.html [ Failure ]
-Bug(none) paint/invalidation/input-overflow-in-table.html [ Failure ]
-Bug(none) paint/invalidation/insert-frame.html [ Failure ]
-Bug(none) paint/invalidation/intermediate-layout-position-clip.html [ Failure ]
-Bug(none) paint/invalidation/intermediate-layout-position.html [ Failure ]
 Bug(none) paint/invalidation/invalidate-after-composited-scroll-of-window.html [ Failure ]
 Bug(none) paint/invalidation/invalidate-after-composited-scroll.html [ Failure ]
-Bug(none) paint/invalidation/invalidate-box-shadow-currentColor.html [ Failure ]
-Bug(none) paint/invalidation/invalidate-caret-before-text-node-update.html [ Failure ]
 Bug(none) paint/invalidation/invalidate-caret-in-composited-scrolling-container.html [ Failure ]
 Bug(none) paint/invalidation/invalidate-caret-in-non-composited-scrolling-container.html [ Failure ]
 Bug(none) paint/invalidation/invalidate-cell-in-row-with-offset.html [ Failure ]
@@ -1230,39 +1168,19 @@
 Bug(none) paint/invalidation/invalidate-paint-in-iframe-in-composited-layer.html [ Failure ]
 Bug(none) paint/invalidation/invalidation-after-opacity-change-subtree.html [ Failure ]
 Bug(none) paint/invalidation/invalidation-on-foreground-graphics-layer.html [ Failure ]
-Bug(none) paint/invalidation/japanese-rl-selection-clear.html [ Failure ]
 Bug(none) paint/invalidation/japanese-rl-selection-repaint.html [ Failure ]
 Bug(none) paint/invalidation/layer-hide-when-needs-layout.html [ Failure ]
 Bug(none) paint/invalidation/layout-state-only-positioned.html [ Failure ]
-Bug(none) paint/invalidation/layout-state-relative.html [ Failure ]
-Bug(none) paint/invalidation/layout-state-scrolloffset.html [ Failure ]
-Bug(none) paint/invalidation/layout-state-scrolloffset2.html [ Failure ]
 Bug(none) paint/invalidation/layout-state-scrolloffset3.html [ Failure ]
 Bug(none) paint/invalidation/layoutstate-invalid-invalidation-inline-relative-positioned.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-1.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-10.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-2.html [ Failure ]
 Bug(none) paint/invalidation/line-flow-with-floats-3.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-4.html [ Failure ]
 Bug(none) paint/invalidation/line-flow-with-floats-5.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-6.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-7.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-8.html [ Failure ]
-Bug(none) paint/invalidation/line-flow-with-floats-9.html [ Failure ]
-Bug(none) paint/invalidation/line-in-scrolled-clipped-block.html [ Failure ]
-Bug(none) paint/invalidation/line-overflow.html [ Failure ]
-Bug(none) paint/invalidation/lines-with-layout-delta.html [ Failure ]
-Bug(none) paint/invalidation/list-marker-2.html [ Failure ]
-Bug(none) paint/invalidation/make-children-non-inline.html [ Failure ]
 Bug(none) paint/invalidation/media-audio-no-spurious-repaints.html [ Failure ]
 Bug(none) paint/invalidation/mix-blend-mode-separate-stacking-context.html [ Failure ]
-Bug(none) paint/invalidation/multi-layout-one-frame.html [ Failure ]
 Bug(none) paint/invalidation/multicol-as-paint-container.html [ Failure ]
 Bug(none) paint/invalidation/multicol-nested.html [ Failure ]
-Bug(none) paint/invalidation/multicol-relpos-with-abspos.html [ Failure ]
 Bug(none) paint/invalidation/multicol-repaint.html [ Failure ]
 Bug(none) paint/invalidation/multicol-with-abspos-in-relpos.html [ Failure ]
-Bug(none) paint/invalidation/multicol-with-abspos.html [ Failure ]
 Bug(none) paint/invalidation/multicol-with-block.html [ Failure ]
 Bug(none) paint/invalidation/multicol-with-inline.html [ Failure ]
 Bug(none) paint/invalidation/multicol-with-overflowing-block-rl.html [ Failure ]
@@ -1274,31 +1192,20 @@
 Bug(none) paint/invalidation/offset-change-wrong-invalidation-with-float.html [ Failure ]
 Bug(none) paint/invalidation/opacity-change-on-overflow-float.html [ Failure ]
 Bug(none) paint/invalidation/outline-add-repaint.html [ Failure ]
-Bug(none) paint/invalidation/outline-change-invalidation.html [ Failure ]
 Bug(none) paint/invalidation/outline-change-repaint.html [ Failure ]
-Bug(none) paint/invalidation/outline-child-repaint.html [ Failure ]
-Bug(none) paint/invalidation/outline-clip-change.html [ Failure ]
-Bug(none) paint/invalidation/outline-continuations.html [ Failure ]
 Bug(none) paint/invalidation/overflow-auto-in-overflow-auto-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/overflow-changed-on-child-of-composited-layer.html [ Failure ]
-Bug(none) paint/invalidation/overflow-delete-line.html [ Failure ]
 Bug(none) paint/invalidation/overflow-flipped-writing-mode-block.html [ Failure ]
 Bug(none) paint/invalidation/overflow-flipped-writing-mode-table.html [ Failure ]
-Bug(none) paint/invalidation/overflow-hidden-in-overflow-hidden-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/overflow-hidden-yet-scrolled-with-custom-scrollbar.html [ Failure ]
 Bug(none) paint/invalidation/overflow-hidden-yet-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/overflow-hide.html [ Failure ]
-Bug(none) paint/invalidation/overflow-into-content.html [ Failure ]
 Bug(none) paint/invalidation/overflow-move-after-scroll.html [ Failure ]
 Bug(none) paint/invalidation/overflow-scroll-after-move.html [ Failure ]
 Bug(none) paint/invalidation/overflow-scroll-body-appear.html [ Failure ]
-Bug(none) paint/invalidation/overflow-scroll-composited-non-stacking-child.html [ Failure ]
 Bug(none) paint/invalidation/overflow-scroll-delete.html [ Failure ]
 Bug(none) paint/invalidation/overflow-scroll-in-overflow-scroll-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/overflow-scroll-local-background-text-color-change.html [ Failure ]
-Bug(none) paint/invalidation/overflow-show.html [ Failure ]
-Bug(none) paint/invalidation/padding-keeping-content-size.html [ Failure ]
-Bug(none) paint/invalidation/padding-keeping-visual-size.html [ Failure ]
 Bug(none) paint/invalidation/paged-with-overflowing-block-rl.html [ Failure ]
 Bug(none) paint/invalidation/paint-caret-in-div-with-negative-indent.html [ Failure ]
 Bug(none) paint/invalidation/paint-invalidation-with-opacity.html [ Failure ]
@@ -1306,108 +1213,60 @@
 Bug(none) paint/invalidation/percent-size-image-resize-container.html [ Failure ]
 Bug(none) paint/invalidation/position-change-keeping-geometry.html [ Failure ]
 Bug(none) paint/invalidation/positioned-document-element.html [ Failure ]
-Bug(none) paint/invalidation/positioned-great-grandparent-change-location.html [ Failure ]
-Bug(none) paint/invalidation/positioned-list-offset-change-repaint.html [ Failure ]
-Bug(none) paint/invalidation/push-block-with-first-line.html [ Failure ]
-Bug(none) paint/invalidation/quotes.html [ Failure ]
 Bug(none) paint/invalidation/reflection-invalidation-after-display.html [ Failure ]
 Bug(none) paint/invalidation/reflection-invalidation-positioned-child.html [ Failure ]
 Bug(none) paint/invalidation/reflection-redraw.html [ Failure ]
-Bug(none) paint/invalidation/reflection-repaint-test.html [ Failure ]
 Bug(none) paint/invalidation/relative-inline-positioned-movement-repaint.html [ Failure ]
 Bug(none) paint/invalidation/relative-margin-change-repaint.html [ Failure ]
 Bug(none) paint/invalidation/relative-positioned-movement-repaint.html [ Failure ]
 Bug(none) paint/invalidation/remove-block-after-layout.html [ Failure ]
-Bug(none) paint/invalidation/remove-inline-after-layout.html [ Failure ]
-Bug(none) paint/invalidation/remove-inline-block-descendant-of-flex.html [ Failure ]
 Bug(none) paint/invalidation/remove-inline-layer-after-layout.html [ Failure ]
-Bug(none) paint/invalidation/repaint-across-writing-mode-boundary.html [ Failure ]
+crbug.com/732612 paint/invalidation/repaint-across-writing-mode-boundary.html [ Failure ]
 Bug(none) paint/invalidation/repaint-composited-child-in-scrolled-container.html [ Failure ]
-Bug(none) paint/invalidation/repaint-descandant-on-ancestor-layer-move.html [ Failure ]
-Bug(none) paint/invalidation/repaint-during-scroll-with-zoom.html [ Failure ]
 Bug(none) paint/invalidation/repaint-during-scroll.html [ Failure ]
 Bug(none) paint/invalidation/repaint-resized-overflow.html [ Failure ]
-Bug(none) paint/invalidation/repaint-table-row-in-composited-document.html [ Failure ]
-Bug(none) paint/invalidation/replaced-clipped-positioned-not-wrong-incremental-repainting.html [ Failure ]
 Bug(none) paint/invalidation/requestAnimation-translation-leave-traces.html [ Failure ]
 Bug(none) paint/invalidation/resize-iframe-text.html [ Failure ]
 Bug(none) paint/invalidation/resize-scrollable-div.html [ Failure ]
-Bug(none) paint/invalidation/resize-scrollable-iframe.html [ Failure ]
 Bug(none) paint/invalidation/resize-skewed.html [ Failure ]
 Bug(none) paint/invalidation/resize-with-border-clipped.html [ Pass Failure ]
-Bug(none) paint/invalidation/resize-with-border.html [ Failure ]
-Bug(none) paint/invalidation/ruby-flipped-blocks.html [ Failure ]
 Bug(none) paint/invalidation/scroll-absolute-layer-with-reflection.html [ Failure ]
 Bug(none) paint/invalidation/scroll-descendant-with-cached-cliprects.html [ Failure ]
 Bug(none) paint/invalidation/scroll-fixed-layer-with-no-visible-content.html [ Failure ]
 Bug(none) paint/invalidation/scroll-fixed-layer-with-reflection.html [ Failure ]
 Bug(none) paint/invalidation/scroll-fixed-layer-with-transformed-parent-layer.html [ Failure ]
 Bug(none) paint/invalidation/scroll-fixed-reflected-layer.html [ Failure ]
-Bug(none) paint/invalidation/scroll-in-clipped-layer.html [ Failure ]
 Bug(none) paint/invalidation/scroll-in-fixed-layer.html [ Failure ]
 Bug(none) paint/invalidation/scroll-in-transformed-layer.html [ Failure ]
-Bug(none) paint/invalidation/scroll-inside-table-cell.html [ Failure ]
-Bug(none) paint/invalidation/scroll-relative-table-inside-table-cell.html [ Failure ]
 Bug(none) paint/invalidation/scroll-stacking-context-backface-visiblity-leaves-traces.html [ Failure ]
 Bug(none) paint/invalidation/scroll-with-transformed-parent-layer.html [ Failure ]
 Bug(none) paint/invalidation/scrollbar-damage-and-full-viewport-repaint.html [ Failure ]
-Bug(none) paint/invalidation/scrollbar-invalidation-on-resize-with-border.html [ Failure ]
-Bug(none) paint/invalidation/scrollbar-invalidation-on-resize.html [ Failure ]
 Bug(none) paint/invalidation/scrollbar-parts.html [ Failure ]
-Bug(none) paint/invalidation/search-field-cancel.html [ Failure ]
-Bug(none) paint/invalidation/select-option-background-color.html [ Failure ]
-Bug(none) paint/invalidation/selected-replaced.html [ Failure ]
 Bug(none) paint/invalidation/selection-after-delete.html [ Failure ]
 Bug(none) paint/invalidation/selection-after-remove.html [ Failure ]
-Bug(none) paint/invalidation/selection-change-in-iframe-with-relative-parent.html [ Failure ]
 Bug(none) paint/invalidation/selection-clear.html [ Failure ]
 Bug(none) paint/invalidation/selection-partial-invalidation-between-blocks.html [ Failure ]
 Bug(none) paint/invalidation/selection-rl.html [ Failure ]
 Bug(none) paint/invalidation/selection/invalidation-rect-includes-newline-for-rtl.html [ Failure ]
-Bug(none) paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-lr.html [ Failure ]
-Bug(none) paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-rl.html [ Failure ]
-Bug(none) paint/invalidation/selection/invalidation-rect-includes-newline.html [ Failure ]
 Bug(none) paint/invalidation/selection/invalidation-rect-with-br-includes-newline.html [ Failure ]
 Bug(none) paint/invalidation/selection/repaint-rect-for-vertical-writing-mode-with-positioned-root.html [ Failure ]
-Bug(none) paint/invalidation/selection/selection-in-composited-scrolling-container.html [ Failure ]
-Bug(none) paint/invalidation/selection/selection-in-non-composited-scrolling-container.html [ Failure ]
 Bug(none) paint/invalidation/selection/selection-within-composited-scroller.html [ Failure ]
-Bug(none) paint/invalidation/shift-relative-positioned-container-with-image-addition.html [ Failure ]
-Bug(none) paint/invalidation/shift-relative-positioned-container-with-image-removal.html [ Failure ]
-Bug(none) paint/invalidation/stacked-diacritics.html [ Failure ]
-Bug(none) paint/invalidation/stacking-context-lost.html [ Failure ]
 Bug(none) paint/invalidation/subpixel-offset-scaled-transform.html [ Failure ]
 Bug(none) paint/invalidation/subpixel-shadow-included-in-invalidation.html [ Failure ]
-Bug(none) paint/invalidation/subtree-layoutstate-transform.html [ Failure ]
 Bug(none) paint/invalidation/subtree-root-clip.html [ Failure ]
-Bug(none) paint/invalidation/subtree-root-clip-2.html [ Failure ]
-Bug(none) paint/invalidation/subtree-root-clip-3.html [ Failure ]
 Bug(none) paint/invalidation/subtree-root-skipped.html [ Failure ]
 Bug(none) paint/invalidation/svg/absolute-sized-content-with-resources.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/absolute-sized-document-no-scrollbars.svg [ Failure ]
-Bug(none) paint/invalidation/svg/add-background-property-on-root.html [ Failure ]
 Bug(none) paint/invalidation/svg/add-border-property-on-root.html [ Failure ]
 Bug(none) paint/invalidation/svg/add-outline-property-on-root.html [ Failure ]
-Bug(none) paint/invalidation/svg/animate-fill.svg [ Failure ]
 Bug(none) paint/invalidation/svg/animated-path-inside-transformed-html.xhtml [ Failure ]
 Bug(none) paint/invalidation/svg/animated-svg-as-image-transformed-offscreen.html [ Failure ]
-Bug(none) paint/invalidation/svg/append-text-node-to-tspan.html [ Failure ]
-Bug(none) paint/invalidation/svg/circle-move-invalidation.svg [ Failure ]
 Bug(none) paint/invalidation/svg/clip-path-child-changes.svg [ Failure ]
 Bug(none) paint/invalidation/svg/clip-path-href-changes.svg [ Failure ]
 Bug(none) paint/invalidation/svg/clip-path-id-changes.svg [ Failure ]
 Bug(none) paint/invalidation/svg/clip-path-units-changes.svg [ Failure ]
-Bug(none) paint/invalidation/svg/color-fill-currentColor-and-css.html [ Failure ]
 Bug(none) paint/invalidation/svg/container-repaint.svg [ Failure ]
 Bug(none) paint/invalidation/svg/deep-dynamic-updates.svg [ Failure ]
 Bug(none) paint/invalidation/svg/embedded-svg-size-changes-no-layout-triggers.html [ Failure ]
-Bug(none) paint/invalidation/svg/ems-display-none.svg [ Failure ]
-Bug(none) paint/invalidation/svg/exs-display-none.svg [ Failure ]
-Bug(none) paint/invalidation/svg/feImage-target-add-to-document.svg [ Failure ]
-Bug(none) paint/invalidation/svg/feImage-target-inline-style-change.svg [ Failure ]
-Bug(none) paint/invalidation/svg/feImage-target-reappend-to-document.svg [ Failure ]
-Bug(none) paint/invalidation/svg/feImage-target-style-change.svg [ Failure ]
-Bug(none) paint/invalidation/svg/filter-child-repaint.svg [ Failure ]
 Bug(none) paint/invalidation/svg/filter-refresh.svg [ Failure ]
 Bug(none) paint/invalidation/svg/foreign-object-repaint.svg [ Failure ]
 Bug(none) paint/invalidation/svg/hairline-stroke-squarecap.svg [ Failure ]
@@ -1415,156 +1274,70 @@
 Bug(none) paint/invalidation/svg/image-with-clip-path.svg [ Failure ]
 Bug(none) paint/invalidation/svg/inner-svg-change-viewBox-contract.svg [ Failure ]
 Bug(none) paint/invalidation/svg/inner-svg-change-viewBox.svg [ Failure ]
-Bug(none) paint/invalidation/svg/inner-svg-change-viewPort-relative.svg [ Failure ]
-Bug(none) paint/invalidation/svg/invalidate-on-child-layout.svg [ Failure ]
 Bug(none) paint/invalidation/svg/js-late-clipPath-and-object-creation.svg [ Failure ]
 Bug(none) paint/invalidation/svg/js-late-clipPath-creation.svg [ Failure ]
 Bug(none) paint/invalidation/svg/js-late-gradient-and-object-creation.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-late-gradient-creation.svg [ Failure ]
 Bug(none) paint/invalidation/svg/js-late-marker-and-object-creation.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-late-pattern-and-object-creation.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-late-pattern-creation.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-repaint-rect-on-path-with-stroke.svg [ Failure ]
 Bug(none) paint/invalidation/svg/js-update-bounce.svg [ Failure ]
 Bug(none) paint/invalidation/svg/js-update-container.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-update-image.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-update-polygon-changes.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-update-polygon-removal.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-update-style.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-update-transform-addition.svg [ Failure ]
-Bug(none) paint/invalidation/svg/js-update-transform-changes.svg [ Failure ]
-Bug(none) paint/invalidation/svg/marker-strokeWidth-changes.svg [ Failure ]
-Bug(none) paint/invalidation/svg/marker-viewBox-changes.svg [ Failure ]
 Bug(none) paint/invalidation/svg/mask-child-changes.svg [ Failure ]
 Bug(none) paint/invalidation/svg/mask-invalidation.svg [ Failure ]
-Bug(none) paint/invalidation/svg/modify-inserted-listitem.html [ Failure ]
-Bug(none) paint/invalidation/svg/modify-text-node-in-tspan.html [ Failure ]
-Bug(none) paint/invalidation/svg/modify-transferred-listitem-different-attr.html [ Failure ]
 Bug(none) paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-1.html [ Failure ]
 Bug(none) paint/invalidation/svg/nested-embedded-svg-size-changes-no-layout-triggers-2.html [ Failure ]
-Bug(none) paint/invalidation/svg/object-sizing-no-width-height-change-content-box-size.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/outline-offset-text.html [ Failure ]
 Bug(none) paint/invalidation/svg/paintorder-filtered.svg [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-content-with-resources.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-content.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-deep-shadow-tree-content.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-document-scrollbars.svg [ Failure ]
 Bug(none) paint/invalidation/svg/relative-sized-image.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-inner-svg.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-shadow-tree-content-with-symbol.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-shadow-tree-content.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-use-on-symbol.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/relative-sized-use-without-attributes-on-symbol.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/remove-background-property-on-root.html [ Failure ]
 Bug(none) paint/invalidation/svg/remove-border-property-on-root.html [ Failure ]
 Bug(none) paint/invalidation/svg/remove-outline-property-on-root.html [ Failure ]
-Bug(none) paint/invalidation/svg/remove-text-node-from-tspan.html [ Failure ]
-Bug(none) paint/invalidation/svg/remove-tspan-from-text.html [ Failure ]
 Bug(none) paint/invalidation/svg/repaint-in-scrolled-view.html [ Failure ]
-Bug(none) paint/invalidation/svg/repaint-non-scaling-stroke-text-decoration.html [ Failure ]
-Bug(none) paint/invalidation/svg/repaint-non-scaling-stroke-text.html [ Failure ]
-Bug(none) paint/invalidation/svg/repaint-on-image-bounds-change.svg [ Failure ]
-Bug(none) paint/invalidation/svg/repaint-paintorder.svg [ Failure ]
 Bug(none) paint/invalidation/svg/resize-svg-invalidate-children-2.html [ Failure ]
 Bug(none) paint/invalidation/svg/resize-svg-invalidate-children.html [ Failure ]
 Bug(none) paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem.html [ Failure ]
 Bug(none) paint/invalidation/svg/shape-transform-change.html [ Failure ]
-Bug(none) paint/invalidation/svg/stroke-opacity-update.svg [ Failure ]
-Bug(none) paint/invalidation/svg/svgsvgelement-repaint-children.html [ Failure ]
 Bug(none) paint/invalidation/svg/tabgroup.svg [ Failure ]
-Bug(none) paint/invalidation/svg/text-dom-removal.svg [ Failure ]
 Bug(none) paint/invalidation/svg/text-mask-update.svg [ Failure ]
-Bug(none) paint/invalidation/svg/text-pattern-update-2.html [ Failure ]
 Bug(none) paint/invalidation/svg/text-pattern-update.html [ Failure ]
 Bug(none) paint/invalidation/svg/text-repaint-including-stroke.svg [ Failure ]
 Bug(none) paint/invalidation/svg/text-rescale.html [ Failure ]
 Bug(none) paint/invalidation/svg/text-selection-text-05-t.svg [ Failure ]
 Bug(none) paint/invalidation/svg/text-viewbox-rescale.html [ Failure ]
 Bug(none) paint/invalidation/svg/text-xy-updates-SVGList.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/transform-changed-state.html [ Failure ]
 Bug(none) paint/invalidation/svg/transform-focus-ring-repaint.html [ Failure ]
 Bug(none) paint/invalidation/svg/transform-foreign-object.html [ Failure ]
 Bug(none) paint/invalidation/svg/transform-text-element.html [ Failure ]
-Bug(none) paint/invalidation/svg/tspan-dynamic-positioning.svg [ Failure ]
-Bug(none) paint/invalidation/svg/tspan-pattern-update.html [ Failure ]
 Bug(none) paint/invalidation/svg/use-clipped-hit.svg [ Failure ]
-Bug(none) paint/invalidation/svg/use-disappears-after-style-update.svg [ Failure ]
 Bug(none) paint/invalidation/svg/use-inherit-style.svg [ Failure ]
-Bug(none) paint/invalidation/svg/use-setAttribute-crash.svg [ Failure ]
 Bug(none) paint/invalidation/svg/window.svg [ Failure ]
 Bug(none) paint/invalidation/svg/zoom-coords-viewattr-01-b.svg [ Failure ]
 Bug(none) paint/invalidation/svg/zoom-foreignObject.svg [ Failure ]
-Bug(none) paint/invalidation/table-cell-move.html [ Failure ]
-Bug(none) paint/invalidation/table-cell-overflow.html [ Failure ]
-Bug(none) paint/invalidation/table-col-background-offset.html [ Failure ]
 Bug(none) paint/invalidation/table-col-background.html [ Failure ]
-Bug(none) paint/invalidation/table-outer-border.html [ Failure ]
 Bug(none) paint/invalidation/table-overflow-auto-in-overflow-auto-scrolled.html [ Failure ]
 Bug(none) paint/invalidation/table-overflow-scroll-in-overflow-scroll-scrolled.html [ Failure ]
-Bug(none) paint/invalidation/table-row.html [ Failure ]
-Bug(none) paint/invalidation/table-section-overflow.html [ Failure ]
-Bug(none) paint/invalidation/table-section-repaint.html [ Failure ]
-Bug(none) paint/invalidation/table-shrink-row-repaint.html [ Failure ]
 Bug(none) paint/invalidation/table-two-pass-layout-overpaint.html [ Failure ]
-Bug(none) paint/invalidation/table/border-collapse-change-collapse-to-separate.html [ Failure ]
-Bug(none) paint/invalidation/table/border-collapse-change-separate-to-collapse.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-69296.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-cell-append.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-change-cell-border-width.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-change-col-border-width.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-change-colgroup-border-width.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-change-row-border-width.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-change-table-border-width.html [ Failure ]
-Bug(none) paint/invalidation/table/cached-change-tbody-border-width.html [ Failure ]
-Bug(none) paint/invalidation/table/collapsed-border-cell-resize.html [ Failure ]
-Bug(none) paint/invalidation/table/composited-table-background-col-initial-empty.html [ Failure ]
-Bug(none) paint/invalidation/table/composited-table-background-col-span-initial-empty.html [ Failure ]
-Bug(none) paint/invalidation/table/composited-table-background-col-span.html [ Failure ]
-Bug(none) paint/invalidation/table/composited-table-background-col.html [ Failure ]
-Bug(none) paint/invalidation/table/composited-table-background-colgroup-initial-empty.html [ Failure ]
-Bug(none) paint/invalidation/table/composited-table-background-colgroup.html [ Failure ]
 Bug(none) paint/invalidation/table/composited-table-background-composited-row-initial-empty.html [ Failure ]
 Bug(none) paint/invalidation/table/composited-table-background-composited-row.html [ Failure ]
 Bug(none) paint/invalidation/table/composited-table-background-initial-empty.html [ Failure ]
-Bug(none) paint/invalidation/table/composited-table-background-section-composited-row-initial-empty.html [ Failure ]
 Bug(none) paint/invalidation/table/composited-table-background-section-composited-row.html [ Failure ]
 Bug(none) paint/invalidation/table/composited-table-background-section-initial-empty.html [ Failure ]
 Bug(none) paint/invalidation/table/composited-table-background-section.html [ Failure ]
 Bug(none) paint/invalidation/table/composited-table-background.html [ Failure ]
-Bug(none) paint/invalidation/table/resize-table-repaint-percent-size-cell.html [ Failure ]
-Bug(none) paint/invalidation/table/resize-table-repaint-vertical-align-cell.html [ Failure ]
-Bug(none) paint/invalidation/table/resize-table-row-repaint.html [ Failure ]
-Bug(none) paint/invalidation/text-append-dirty-lines.html [ Failure ]
 Bug(none) paint/invalidation/text-in-relative-positioned-inline.html [ Failure ]
-Bug(none) paint/invalidation/text-match-document-change.html [ Failure ]
-Bug(none) paint/invalidation/text-selection-rect-in-overflow-2.html [ Failure ]
-Bug(none) paint/invalidation/text-selection-rect-in-overflow.html [ Failure ]
 Bug(none) paint/invalidation/textarea-appearance-none-resize-handle.html [ Failure ]
 Bug(none) paint/invalidation/textarea-caret.html [ Failure ]
-Bug(none) paint/invalidation/textarea-resize-property-change.html [ Failure ]
-Bug(none) paint/invalidation/textarea-set-disabled.html [ Failure ]
 Bug(none) paint/invalidation/transform-absolute-child.html [ Failure ]
 Bug(none) paint/invalidation/transform-absolute-in-positioned-container.html [ Failure ]
-Bug(none) paint/invalidation/transform-disable-layoutstate.html [ Failure ]
 Bug(none) paint/invalidation/transform-inline-layered-child.html [ Failure ]
-Bug(none) paint/invalidation/transform-layout-repaint.html [ Failure ]
 Bug(none) paint/invalidation/transform-relative-position.html [ Failure ]
 Bug(none) paint/invalidation/transform-repaint-descendants.html [ Failure ]
 Bug(none) paint/invalidation/transform-replaced-shadows.html [ Failure ]
 Bug(none) paint/invalidation/transform-rotate-and-remove.html [ Failure ]
 Bug(none) paint/invalidation/transform-translate.html [ Failure ]
 Bug(none) paint/invalidation/vertical-align-length1.html [ Failure ]
-Bug(none) paint/invalidation/vertical-align-length2.html [ Failure ]
-Bug(none) paint/invalidation/vertical-align1.html [ Failure ]
-Bug(none) paint/invalidation/vertical-align2.html [ Failure ]
 Bug(none) paint/invalidation/vertical-overflow-child.html [ Failure ]
 Bug(none) paint/invalidation/vertical-overflow-parent.html [ Failure ]
 Bug(none) paint/invalidation/vertical-overflow-same.html [ Failure ]
-Bug(none) paint/invalidation/vertical-rl-as-paint-container.html [ Failure ]
 Bug(none) paint/invalidation/video-mute-repaint.html [ Failure ]
 Bug(none) paint/invalidation/video-unmute-repaint.html [ Failure ]
 Bug(none) paint/invalidation/view-background-from-body-2.html [ Failure ]
-Bug(none) paint/invalidation/viewport-gradient-background-html-resize.html [ Failure ]
 Bug(none) paint/invalidation/window-resize-background-image-fixed-centered-composited.html [ Failure ]
 Bug(none) paint/invalidation/window-resize-background-image-fixed-centered.html [ Failure ]
 Bug(none) paint/invalidation/window-resize-background-image-generated.html [ Failure ]
@@ -2078,8 +1851,10 @@
 crbug.com/730284 fast/replaced/border-radius-clip.html [ Failure ]
 
 # Paint property under-invalidation
+crbug.com/728913 paint/invalidation/svg/js-late-mask-and-object-creation.svg [ Crash ]
 crbug.com/728913 paint/invalidation/svg/mask-clip-target-transform.svg [ Crash ]
 crbug.com/728913 paint/invalidation/svg/resource-invalidate-on-target-update.svg [ Crash ]
+crbug.com/728913 paint/invalidation/svg/js-late-mask-creation.svg [ Crash ]
 
 # Check failed: layer_list_.empty() || *page_scale_factor == 1
 crbug.com/706066 compositing/background-color/background-color-outside-document.html [ Crash Timeout ]
@@ -2109,13 +1884,8 @@
 crbug.com/706066 paint/invalidation/compositing/page-scale-repaint.html [ Crash ]
 crbug.com/706066 paint/invalidation/relayout-fixed-position-after-scale.html [ Crash ]
 
-Bug(none) paint/invalidation/negative-shadow-box-shrink.html [ Failure ]
 Bug(none) paint/invalidation/svg/feImage-remove-target.svg [ Failure ]
-Bug(none) paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection.svg [ Failure ]
-Bug(none) paint/invalidation/svg/feImage-target-remove-from-document.svg [ Failure ]
 Bug(none) paint/invalidation/svg/js-update-stop-linked-gradient.svg [ Failure ]
-Bug(none) paint/invalidation/svg/pending-resource-after-removal.xhtml [ Failure ]
-Bug(none) paint/invalidation/svg/svg-background-partial-redraw.html [ Failure ]
 
 crbug.com/719721 virtual/threaded/animations/composited-animation-style-update.html [ Pass Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 46d4b9df..105225b 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -497,7 +497,12 @@
   },
   {
     "prefix": "high-contrast-mode",
-    "base": "paint/high-contrast-mode",
-    "args": ["--blink-settings=highContrastMode=3"]
+    "base": "paint/high-contrast-mode/image-filter-all",
+    "args": ["--blink-settings=highContrastMode=3,highContrastImagePolicy=0"]
+  },
+  {
+    "prefix": "high-contrast-mode",
+    "base": "paint/high-contrast-mode/image-filter-none",
+    "args": ["--blink-settings=highContrastMode=3,highContrastImagePolicy=1"]
   }
 ]
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js
index 73d266a..1256f567 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-called-during-success.js
@@ -1,22 +1,20 @@
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gatt => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
-        gatt.CALLS([
-          getPrimaryService('heart_rate')|
+        device.gatt.CALLS([
+          getPrimaryService('health_thermometer')|
           getPrimaryServices()|
-          getPrimaryServices('heart_rate')[UUID]
+          getPrimaryServices('health_thermometer')[UUID]
         ]),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      gatt.disconnect();
+      device.gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a FUNCTION_NAME call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
index db410ed..deb310d1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
@@ -1,21 +1,18 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer
-        .CALLS([
-          getPrimaryService('heart_rate')|
-          getPrimaryServices()|
-          getPrimaryServices('heart_rate')[UUID]])
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      return device.gatt.CALLS([
+        getPrimaryService('health_thermometer')|
+        getPrimaryServices()|
+        getPrimaryServices('health_thermometer')[UUID]])
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          gattServer.disconnect();
-          return gattServer.connect()
+          device.gatt.disconnect();
+          return device.gatt.connect()
             .then(() => services);
         });
     })
@@ -29,7 +26,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('body_sensor_location'),
+            service.getCharacteristic('measurement_interval'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -37,7 +34,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('body_sensor_location'),
+            service.getCharacteristics('measurement_interval'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
index f2bab5df..18e98f6a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
@@ -1,25 +1,21 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithKeyDown(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        promise = assert_promise_rejects_with_message(
-            gattServer.CALLS(
-                [getPrimaryService('health_thermometer') |
-                 getPrimaryServices() |
-                 getPrimaryServices('health_thermometer')[UUID]]),
-            new DOMException(
-                'GATT Server is disconnected. ' +
-                    'Cannot retrieve services. ' +
-                    '(Re)connect first with `device.gatt.connect`.',
-                'NetworkError'));
-        gattServer.disconnect();
-      })
-      .then(runGarbageCollection)
-      .then(() => promise);
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      promise = assert_promise_rejects_with_message(
+        device.gatt.CALLS([
+          getPrimaryService('health_thermometer') |
+          getPrimaryServices() |
+          getPrimaryServices('health_thermometer')[UUID]]),
+        new DOMException(
+          'GATT Server is disconnected. Cannot retrieve services. ' +
+          '(Re)connect first with `device.gatt.connect`.',
+          'NetworkError'));
+      device.gatt.disconnect();
+    })
+    .then(runGarbageCollection)
+    .then(() => promise);
 }, 'Garbage Collection ran during a FUNCTION_NAME call that succeeds. ' +
    'Should not crash.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
index 26ca8da..2dcc9ab6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
@@ -1,37 +1,38 @@
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let services1;
-      return gattServer
-        .CALLS([
-          getPrimaryService('heart_rate')|
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
+      let services_first_connection;
+      return device.gatt.CALLS([
+          getPrimaryService('health_thermometer')|
           getPrimaryServices()|
-          getPrimaryServices('heart_rate')[UUID]])
-        .then(services => services1 = services)
-        .then(() => gattServer.disconnect())
-        .then(() => gattServer.connect())
-        .then(() => gattServer.PREVIOUS_CALL)
-        .then(services2 => [services1, services2])
+          getPrimaryServices('health_thermometer')[UUID]])
+        .then(services => services_first_connection = services)
+        .then(() => device.gatt.disconnect())
+        .then(() => device.gatt.connect())
+        .then(() => device.gatt.PREVIOUS_CALL)
+        .then(services_second_connection => [
+          services_first_connection,
+          services_second_connection
+        ]);
     })
-    .then(services_arrays => {
+    .then(([services_first_connection, services_second_connection]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_connection = [].concat(services_first_connection);
+      services_second_connection = [].concat(services_second_connection);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_connection.length, services_second_connection.length);
 
-      let base_set = new Set(services_arrays.shift());
-      for (let services of services_arrays) {
-        services.forEach(service => assert_false(base_set.has(service)));
-      }
+      let first_connection_set = new Set(services_first_connection);
+      let second_connection_set = new Set(services_second_connection);
+
+      // The two sets should be disjoint.
+      let common_services = services_first_connection.filter(
+        val => second_connection_set.has(val));
+      assert_equals(common_services.length, 0);
+
     });
 }, 'Calls to FUNCTION_NAME after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
index 529bffb87..167e2c7e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
@@ -1,29 +1,32 @@
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => Promise.all([
-      gattServer.CALLS([
-        getPrimaryService('heart_rate')|
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => Promise.all([
+      device.gatt.CALLS([
+        getPrimaryService('health_thermometer')|
         getPrimaryServices()|
-        getPrimaryServices('heart_rate')[UUID]]),
-      gattServer.PREVIOUS_CALL]))
-    .then(services_arrays => {
+        getPrimaryServices('health_thermometer')[UUID]]),
+      device.gatt.PREVIOUS_CALL]))
+    .then(([services_first_call, services_second_call]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_call = [].concat(services_first_call);
+      services_second_call = [].concat(services_second_call);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_call.length, services_second_call.length);
 
-      let base_set = new Set(services_arrays[0]);
-      for (let services of services_arrays) {
-        services.forEach(service => assert_true(base_set.has(service)));
-      }
+      let first_call_set = new Set(services_first_call);
+      assert_equals(services_first_call.length, first_call_set.size);
+      let second_call_set = new Set(services_second_call);
+      assert_equals(services_second_call.length, second_call_set.size);
+
+      services_first_call.forEach(service => {
+        assert_true(second_call_set.has(service))
+      });
+
+      services_second_call.forEach(service => {
+        assert_true(first_call_set.has(service));
+      });
     });
 }, 'Calls to FUNCTION_NAME should return the same object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js
index 37bccfc3..f7de4d2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-present-service.js
@@ -5,18 +5,16 @@
                                   '\'optionalServices\' in requestDevice() ' +
                                   'options. https://goo.gl/HxfxSQ',
                                   'SecurityError');
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gatt => Promise.all([
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
-        gatt.CALLS([
+        device.gatt.CALLS([
           getPrimaryService(generic_access.alias)|
           getPrimaryServices(generic_access.alias)[UUID]
         ]), expected),
       assert_promise_rejects_with_message(
-        gatt.FUNCTION_NAME(generic_access.name), expected),
+        device.gatt.FUNCTION_NAME(generic_access.name), expected),
       assert_promise_rejects_with_message(
-        gatt.FUNCTION_NAME(generic_access.uuid), expected)]));
+        device.gatt.FUNCTION_NAME(generic_access.uuid), expected)]));
 }, 'Request for present service without permission. Reject with SecurityError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html
index 802a7d6..c2bd90f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.html
@@ -8,19 +8,17 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gatt => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
-        gatt.getPrimaryService('heart_rate'),
+        device.gatt.getPrimaryService('health_thermometer'),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      gatt.disconnect();
+      device.gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a getPrimaryService call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
index 0acff90..83741ef7 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
@@ -9,18 +9,15 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer
-        .getPrimaryService('heart_rate')
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      return device.gatt.getPrimaryService('health_thermometer')
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          gattServer.disconnect();
-          return gattServer.connect()
+          device.gatt.disconnect();
+          return device.gatt.connect()
             .then(() => services);
         });
     })
@@ -34,7 +31,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('body_sensor_location'),
+            service.getCharacteristic('measurement_interval'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -42,7 +39,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('body_sensor_location'),
+            service.getCharacteristics('measurement_interval'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
index f03e923..a6429fb1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
@@ -9,23 +9,19 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithKeyDown(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        promise = assert_promise_rejects_with_message(
-            gattServer.getPrimaryService('health_thermometer'),
-            new DOMException(
-                'GATT Server is disconnected. ' +
-                    'Cannot retrieve services. ' +
-                    '(Re)connect first with `device.gatt.connect`.',
-                'NetworkError'));
-        gattServer.disconnect();
-      })
-      .then(runGarbageCollection)
-      .then(() => promise);
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      promise = assert_promise_rejects_with_message(
+        device.gatt.getPrimaryService('health_thermometer'),
+        new DOMException(
+          'GATT Server is disconnected. Cannot retrieve services. ' +
+          '(Re)connect first with `device.gatt.connect`.',
+          'NetworkError'));
+      device.gatt.disconnect();
+    })
+    .then(runGarbageCollection)
+    .then(() => promise);
 }, 'Garbage Collection ran during a getPrimaryService call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
index 37c33542..92b3a1b8d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
@@ -8,35 +8,36 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let services1;
-      return gattServer
-        .getPrimaryService('heart_rate')
-        .then(services => services1 = services)
-        .then(() => gattServer.disconnect())
-        .then(() => gattServer.connect())
-        .then(() => gattServer.getPrimaryService('heart_rate'))
-        .then(services2 => [services1, services2])
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
+      let services_first_connection;
+      return device.gatt.getPrimaryService('health_thermometer')
+        .then(services => services_first_connection = services)
+        .then(() => device.gatt.disconnect())
+        .then(() => device.gatt.connect())
+        .then(() => device.gatt.getPrimaryService('health_thermometer'))
+        .then(services_second_connection => [
+          services_first_connection,
+          services_second_connection
+        ]);
     })
-    .then(services_arrays => {
+    .then(([services_first_connection, services_second_connection]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_connection = [].concat(services_first_connection);
+      services_second_connection = [].concat(services_second_connection);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_connection.length, services_second_connection.length);
 
-      let base_set = new Set(services_arrays.shift());
-      for (let services of services_arrays) {
-        services.forEach(service => assert_false(base_set.has(service)));
-      }
+      let first_connection_set = new Set(services_first_connection);
+      let second_connection_set = new Set(services_second_connection);
+
+      // The two sets should be disjoint.
+      let common_services = services_first_connection.filter(
+        val => second_connection_set.has(val));
+      assert_equals(common_services.length, 0);
+
     });
 }, 'Calls to getPrimaryService after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
index f046b52..3fc19c6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
@@ -8,28 +8,31 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => Promise.all([
-      gattServer.getPrimaryService('heart_rate'),
-      gattServer.getPrimaryService('heart_rate')]))
-    .then(services_arrays => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => Promise.all([
+      device.gatt.getPrimaryService('health_thermometer'),
+      device.gatt.getPrimaryService('health_thermometer')]))
+    .then(([services_first_call, services_second_call]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_call = [].concat(services_first_call);
+      services_second_call = [].concat(services_second_call);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_call.length, services_second_call.length);
 
-      let base_set = new Set(services_arrays[0]);
-      for (let services of services_arrays) {
-        services.forEach(service => assert_true(base_set.has(service)));
-      }
+      let first_call_set = new Set(services_first_call);
+      assert_equals(services_first_call.length, first_call_set.size);
+      let second_call_set = new Set(services_second_call);
+      assert_equals(services_second_call.length, second_call_set.size);
+
+      services_first_call.forEach(service => {
+        assert_true(second_call_set.has(service))
+      });
+
+      services_second_call.forEach(service => {
+        assert_true(first_call_set.has(service));
+      });
     });
 }, 'Calls to getPrimaryService should return the same object.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html
index 94fad05..2a92858 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-present-service.html
@@ -13,17 +13,15 @@
                                   '\'optionalServices\' in requestDevice() ' +
                                   'options. https://goo.gl/HxfxSQ',
                                   'SecurityError');
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gatt => Promise.all([
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
-        gatt.getPrimaryService(generic_access.alias), expected),
+        device.gatt.getPrimaryService(generic_access.alias), expected),
       assert_promise_rejects_with_message(
-        gatt.getPrimaryService(generic_access.name), expected),
+        device.gatt.getPrimaryService(generic_access.name), expected),
       assert_promise_rejects_with_message(
-        gatt.getPrimaryService(generic_access.uuid), expected)]));
+        device.gatt.getPrimaryService(generic_access.uuid), expected)]));
 }, 'Request for present service without permission. Reject with SecurityError.');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
index e23fc14..69338c2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
@@ -2,19 +2,19 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
-promise_test(function() {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => {
-      return device.gatt.connect()
-        .then(gattServer => Promise.all([
-          gattServer.getPrimaryService(generic_access.alias),
-          gattServer.getPrimaryService(generic_access.name),
-          gattServer.getPrimaryService(generic_access.uuid)]))
+promise_test(() => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
+      return Promise.all([
+          device.gatt.getPrimaryService(generic_access.alias),
+          device.gatt.getPrimaryService(generic_access.name),
+          device.gatt.getPrimaryService(generic_access.uuid)])
         .then(services => {
           services.forEach(service => {
             assert_equals(service.uuid, generic_access.uuid,
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html
index 898cd7c..6989bf26 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/two-iframes-from-same-origin.html
@@ -2,49 +2,62 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
-  "use strict";
-  var firstIframe = true;
-  var iframe1 = document.createElement('iframe');
-  iframe1.src = '../../../resources/bluetooth/heart-rate-two-iframes.html';
-  iframe1.id = 'iframe1';
-  var iframe2 = document.createElement('iframe');
-  iframe2.src = '../../../resources/bluetooth/heart-rate-two-iframes.html';
-  iframe2.id = 'iframe2';
-  async_test(test => {
-    window.onmessage = messageEvent => test.step(() => {
-      if (messageEvent.data === 'Ready') {
-        if (firstIframe) {
-          callWithKeyDown(() => {
-            iframe1.contentWindow.postMessage('Iframe1RequestAndConnect', '*');
-          });
-        } else {
-          callWithKeyDown(() => {
-            iframe2.contentWindow.postMessage('Iframe2RequestAndConnect', '*');
-          });
-        }
-        firstIframe = false;
-      } else if (messageEvent.data === 'Iframe1Connected') {
+"use strict";
+var firstIframe = true;
+var iframe1 = document.createElement('iframe');
+iframe1.src ='../../../resources/bluetooth/health-thermometer-two-iframes.html';
+iframe1.id = 'iframe1';
+var iframe2 = document.createElement('iframe');
+iframe2.src = '../../../resources/bluetooth/health-thermometer-two-iframes.html';
+iframe2.id = 'iframe2';
+async_test(test => {
+  window.onmessage = messageEvent => test.step(() => {
+    if (messageEvent.data === 'Ready') {
+      if (firstIframe) {
         callWithKeyDown(() => {
-          iframe1.contentWindow.postMessage('Iframe1TryAccessGenericAccessService', '*');
+          iframe1.contentWindow.postMessage('Iframe1RequestAndConnect', '*');
         });
-      } else if (messageEvent.data === 'Iframe1AccessGenericAccessServiceFailed') {
-        document.body.appendChild(iframe2);
-      } else if (messageEvent.data === 'Iframe2Connected') {
-        callWithKeyDown(() => {
-          iframe1.contentWindow.postMessage('TestIframe1HasGenericAccessService', '*');
-        });
-      }
-      else if (messageEvent.data === 'DoneTest') {
-        test.done();
       } else {
-        assert_unreached('iframe sent invalid data: ' + messageEvent.data);
+        callWithKeyDown(() => {
+          iframe2.contentWindow.postMessage('Iframe2RequestAndConnect', '*');
+        });
       }
-    });
-
-    setBluetoothFakeAdapter('HeartRateAdapter')
-      .then(() => {
-        document.body.appendChild(iframe1);
+      firstIframe = false;
+    } else if (messageEvent.data === 'Iframe1Connected') {
+      callWithKeyDown(() => {
+        iframe1.contentWindow.postMessage('Iframe1TryAccessGenericAccessService', '*');
       });
-  }, 'Two iframes in the same origin should be able to access each other\'s services');
+    } else if (messageEvent.data === 'Iframe1AccessGenericAccessServiceFailed') {
+      document.body.appendChild(iframe2);
+    } else if (messageEvent.data === 'Iframe2Connected') {
+      callWithKeyDown(() => {
+        iframe1.contentWindow.postMessage('TestIframe1HasGenericAccessService', '*');
+      });
+    }
+    else if (messageEvent.data === 'DoneTest') {
+      test.done();
+    } else {
+      assert_unreached('iframe sent invalid data: ' + messageEvent.data);
+    }
+  });
+
+  return setUpPreconnectedDevice({
+      address: '09:09:09:09:09:09',
+      name: 'Health Thermometer',
+      knownServiceUUIDs: ['generic_access', 'health_thermometer'],
+    })
+    .then(fake_peripheral => {
+      return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
+        .then(() => fake_peripheral.addFakeService({uuid: 'generic_access'}))
+        .then(() => fake_peripheral.addFakeService({uuid: 'health_thermometer'}))
+        .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
+          code: HCI_SUCCESS}))
+    })
+    .then(() => {
+      document.body.appendChild(iframe1);
+    });
+}, 'Two iframes in the same origin should be able to access each other\'s services');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
index 65c8b2f..5f72453 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
@@ -2,17 +2,16 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('BlocklistTestAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['device_information']}],
-      optionalServices: ['human_interface_device']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => assert_promise_rejects_with_message(
-      gattServer.getPrimaryServices('human_interface_device'),
-
+  return getHIDDevice({
+      filters: [{services: ['battery_service']}],
+      optionalServices: ['human_interface_device']})
+    .then(([device]) => assert_promise_rejects_with_message(
+      device.gatt.getPrimaryServices('human_interface_device'),
       new DOMException('Origin is not allowed to access the service. ' +
                        'Tip: Add the service UUID to \'optionalServices\' ' +
                        'in requestDevice() options. https://goo.gl/HxfxSQ',
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
index e894ab5..ca39ae68 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
@@ -2,27 +2,27 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('BlocklistTestAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['device_information']}],
+  return getHIDDevice({
+      filters: [{services: ['battery_service']}],
       optionalServices: [
-        blocklist_test_service_uuid, 'device_information', 'generic_access',
-       'heart_rate', 'human_interface_device']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryServices())
+        'generic_access',
+        'human_interface_device'
+      ]})
+    .then(([device]) => device.gatt.getPrimaryServices())
     .then(services => {
-      assert_equals(services.length, 4);
-      assert_equals(services[0].uuid,
-                    BluetoothUUID.getService(blocklist_test_service_uuid));
-      assert_equals(services[1].uuid,
-                    BluetoothUUID.getService('device_information'));
-      assert_equals(services[2].uuid,
-                    BluetoothUUID.getService('generic_access'));
-      assert_equals(services[3].uuid,
-                    BluetoothUUID.getService('heart_rate'));
+      assert_equals(services.length, 2);
+      let uuid_set = new Set(services.map(s => s.uuid));
+
+      assert_equals(uuid_set.size, 2);
+      assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
+      assert_true(uuid_set.has(BluetoothUUID.getService('battery_service')));
+      assert_false(
+        uuid_set.has(BluetoothUUID.getService('human_interface_device')));
     });
 }, 'Request for services. Does not return blocklisted service.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html
index e05bbe0..5069b35 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.html
@@ -8,19 +8,17 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gatt => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
-        gatt.getPrimaryServices('heart_rate'),
+        device.gatt.getPrimaryServices('health_thermometer'),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      gatt.disconnect();
+      device.gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a getPrimaryServices call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html
index fd9a868..003aa676 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.html
@@ -8,19 +8,17 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gatt => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
       let promise = assert_promise_rejects_with_message(
-        gatt.getPrimaryServices(),
+        device.gatt.getPrimaryServices(),
         new DOMException('GATT Server is disconnected. ' +
                          'Cannot retrieve services. ' +
                          '(Re)connect first with `device.gatt.connect`.',
                          'NetworkError'));
-      gatt.disconnect();
+      device.gatt.disconnect();
       return promise;
     });
 }, 'disconnect() called during a getPrimaryServices call that succeeds. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
index 19ccb5a..8450d8e0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
@@ -9,18 +9,15 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer
-        .getPrimaryServices('heart_rate')
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      return device.gatt.getPrimaryServices('health_thermometer')
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          gattServer.disconnect();
-          return gattServer.connect()
+          device.gatt.disconnect();
+          return device.gatt.connect()
             .then(() => services);
         });
     })
@@ -34,7 +31,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('body_sensor_location'),
+            service.getCharacteristic('measurement_interval'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -42,7 +39,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('body_sensor_location'),
+            service.getCharacteristics('measurement_interval'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
index 1506c3d..45a160a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
@@ -9,18 +9,15 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer
-        .getPrimaryServices()
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      return device.gatt.getPrimaryServices()
         // Convert to array if necessary.
         .then(s => {
           let services = [].concat(s);
-          gattServer.disconnect();
-          return gattServer.connect()
+          device.gatt.disconnect();
+          return device.gatt.connect()
             .then(() => services);
         });
     })
@@ -34,7 +31,7 @@
           'InvalidStateError');
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristic('body_sensor_location'),
+            service.getCharacteristic('measurement_interval'),
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
@@ -42,7 +39,7 @@
             error));
         promises = promises.then(() =>
           assert_promise_rejects_with_message(
-            service.getCharacteristics('body_sensor_location'),
+            service.getCharacteristics('measurement_interval'),
             error));
       }
       return promises;
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
index ff3978c..23c33e0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
@@ -9,23 +9,19 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithKeyDown(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        promise = assert_promise_rejects_with_message(
-            gattServer.getPrimaryServices('health_thermometer'),
-            new DOMException(
-                'GATT Server is disconnected. ' +
-                    'Cannot retrieve services. ' +
-                    '(Re)connect first with `device.gatt.connect`.',
-                'NetworkError'));
-        gattServer.disconnect();
-      })
-      .then(runGarbageCollection)
-      .then(() => promise);
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      promise = assert_promise_rejects_with_message(
+        device.gatt.getPrimaryServices('health_thermometer'),
+        new DOMException(
+          'GATT Server is disconnected. Cannot retrieve services. ' +
+          '(Re)connect first with `device.gatt.connect`.',
+          'NetworkError'));
+      device.gatt.disconnect();
+    })
+    .then(runGarbageCollection)
+    .then(() => promise);
 }, 'Garbage Collection ran during a getPrimaryServices call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
index 66813e4..eb9b69ee 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
@@ -9,23 +9,19 @@
 'use strict';
 promise_test(() => {
   let promise;
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithKeyDown(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        promise = assert_promise_rejects_with_message(
-            gattServer.getPrimaryServices(),
-            new DOMException(
-                'GATT Server is disconnected. ' +
-                    'Cannot retrieve services. ' +
-                    '(Re)connect first with `device.gatt.connect`.',
-                'NetworkError'));
-        gattServer.disconnect();
-      })
-      .then(runGarbageCollection)
-      .then(() => promise);
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => {
+      promise = assert_promise_rejects_with_message(
+        device.gatt.getPrimaryServices(),
+        new DOMException(
+          'GATT Server is disconnected. Cannot retrieve services. ' +
+          '(Re)connect first with `device.gatt.connect`.',
+          'NetworkError'));
+      device.gatt.disconnect();
+    })
+    .then(runGarbageCollection)
+    .then(() => promise);
 }, 'Garbage Collection ran during a getPrimaryServices call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
index e903a517..cdd6a8f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
@@ -8,35 +8,36 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let services1;
-      return gattServer
-        .getPrimaryServices('heart_rate')
-        .then(services => services1 = services)
-        .then(() => gattServer.disconnect())
-        .then(() => gattServer.connect())
-        .then(() => gattServer.getPrimaryServices('heart_rate'))
-        .then(services2 => [services1, services2])
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
+      let services_first_connection;
+      return device.gatt.getPrimaryServices('health_thermometer')
+        .then(services => services_first_connection = services)
+        .then(() => device.gatt.disconnect())
+        .then(() => device.gatt.connect())
+        .then(() => device.gatt.getPrimaryServices('health_thermometer'))
+        .then(services_second_connection => [
+          services_first_connection,
+          services_second_connection
+        ]);
     })
-    .then(services_arrays => {
+    .then(([services_first_connection, services_second_connection]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_connection = [].concat(services_first_connection);
+      services_second_connection = [].concat(services_second_connection);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_connection.length, services_second_connection.length);
 
-      let base_set = new Set(services_arrays.shift());
-      for (let services of services_arrays) {
-        services.forEach(service => assert_false(base_set.has(service)));
-      }
+      let first_connection_set = new Set(services_first_connection);
+      let second_connection_set = new Set(services_second_connection);
+
+      // The two sets should be disjoint.
+      let common_services = services_first_connection.filter(
+        val => second_connection_set.has(val));
+      assert_equals(common_services.length, 0);
+
     });
 }, 'Calls to getPrimaryServices after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
index 1afe652..1c7d192 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
@@ -8,35 +8,36 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let services1;
-      return gattServer
-        .getPrimaryServices()
-        .then(services => services1 = services)
-        .then(() => gattServer.disconnect())
-        .then(() => gattServer.connect())
-        .then(() => gattServer.getPrimaryServices())
-        .then(services2 => [services1, services2])
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => {
+      let services_first_connection;
+      return device.gatt.getPrimaryServices()
+        .then(services => services_first_connection = services)
+        .then(() => device.gatt.disconnect())
+        .then(() => device.gatt.connect())
+        .then(() => device.gatt.getPrimaryServices())
+        .then(services_second_connection => [
+          services_first_connection,
+          services_second_connection
+        ]);
     })
-    .then(services_arrays => {
+    .then(([services_first_connection, services_second_connection]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_connection = [].concat(services_first_connection);
+      services_second_connection = [].concat(services_second_connection);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_connection.length, services_second_connection.length);
 
-      let base_set = new Set(services_arrays.shift());
-      for (let services of services_arrays) {
-        services.forEach(service => assert_false(base_set.has(service)));
-      }
+      let first_connection_set = new Set(services_first_connection);
+      let second_connection_set = new Set(services_second_connection);
+
+      // The two sets should be disjoint.
+      let common_services = services_first_connection.filter(
+        val => second_connection_set.has(val));
+      assert_equals(common_services.length, 0);
+
     });
 }, 'Calls to getPrimaryServices after a disconnection should return a ' +
    'different object.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
index e6231c3..0b50d54d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
@@ -8,28 +8,31 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => Promise.all([
-      gattServer.getPrimaryServices('heart_rate'),
-      gattServer.getPrimaryServices('heart_rate')]))
-    .then(services_arrays => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => Promise.all([
+      device.gatt.getPrimaryServices('health_thermometer'),
+      device.gatt.getPrimaryServices('health_thermometer')]))
+    .then(([services_first_call, services_second_call]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_call = [].concat(services_first_call);
+      services_second_call = [].concat(services_second_call);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_call.length, services_second_call.length);
 
-      let base_set = new Set(services_arrays[0]);
-      for (let services of services_arrays) {
-        services.forEach(service => assert_true(base_set.has(service)));
-      }
+      let first_call_set = new Set(services_first_call);
+      assert_equals(services_first_call.length, first_call_set.size);
+      let second_call_set = new Set(services_second_call);
+      assert_equals(services_second_call.length, second_call_set.size);
+
+      services_first_call.forEach(service => {
+        assert_true(second_call_set.has(service))
+      });
+
+      services_second_call.forEach(service => {
+        assert_true(first_call_set.has(service));
+      });
     });
 }, 'Calls to getPrimaryServices should return the same object.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
index 00f859b..242ddf6c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
@@ -8,28 +8,31 @@
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => Promise.all([
-      gattServer.getPrimaryServices(),
-      gattServer.getPrimaryServices()]))
-    .then(services_arrays => {
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => Promise.all([
+      device.gatt.getPrimaryServices(),
+      device.gatt.getPrimaryServices()]))
+    .then(([services_first_call, services_second_call]) => {
       // Convert to arrays if necessary.
-      for (let i = 0; i < services_arrays.length; i++) {
-        services_arrays[i] = [].concat(services_arrays[i]);
-      }
+      services_first_call = [].concat(services_first_call);
+      services_second_call = [].concat(services_second_call);
 
-      for (let i = 1; i < services_arrays.length; i++) {
-        assert_equals(services_arrays[0].length, services_arrays[i].length);
-      }
+      assert_equals(services_first_call.length, services_second_call.length);
 
-      let base_set = new Set(services_arrays[0]);
-      for (let services of services_arrays) {
-        services.forEach(service => assert_true(base_set.has(service)));
-      }
+      let first_call_set = new Set(services_first_call);
+      assert_equals(services_first_call.length, first_call_set.size);
+      let second_call_set = new Set(services_second_call);
+      assert_equals(services_second_call.length, second_call_set.size);
+
+      services_first_call.forEach(service => {
+        assert_true(second_call_set.has(service))
+      });
+
+      services_second_call.forEach(service => {
+        assert_true(first_call_set.has(service));
+      });
     });
 }, 'Calls to getPrimaryServices should return the same object.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html
index dd42f46..bd8fd430 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.html
@@ -13,17 +13,15 @@
                                   '\'optionalServices\' in requestDevice() ' +
                                   'options. https://goo.gl/HxfxSQ',
                                   'SecurityError');
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gatt => Promise.all([
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => Promise.all([
       assert_promise_rejects_with_message(
-        gatt.getPrimaryServices(generic_access.alias), expected),
+        device.gatt.getPrimaryServices(generic_access.alias), expected),
       assert_promise_rejects_with_message(
-        gatt.getPrimaryServices(generic_access.name), expected),
+        device.gatt.getPrimaryServices(generic_access.name), expected),
       assert_promise_rejects_with_message(
-        gatt.getPrimaryServices(generic_access.uuid), expected)]));
+        device.gatt.getPrimaryServices(generic_access.uuid), expected)]));
 }, 'Request for present service without permission. Reject with SecurityError.');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
index 4b27bbb..73409c8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
@@ -2,27 +2,26 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
-promise_test(function() {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => Promise.all([
-      gattServer.getPrimaryServices(heart_rate.alias),
-      gattServer.getPrimaryServices(heart_rate.name),
-      gattServer.getPrimaryServices(heart_rate.uuid)]))
+promise_test(() => {
+  return getHealthThermometerDevice({
+    filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => Promise.all([
+      device.gatt.getPrimaryServices(health_thermometer.alias),
+      device.gatt.getPrimaryServices(health_thermometer.name),
+      device.gatt.getPrimaryServices(health_thermometer.uuid)]))
     .then(services_arrays => {
       services_arrays.forEach(services => {
         assert_equals(services.length, 2);
-        assert_equals(services[0].uuid,
-                      BluetoothUUID.getService('heart_rate'));
-        assert_equals(services[1].uuid,
-                      BluetoothUUID.getService('heart_rate'));
-        assert_true(services[0].isPrimary);
-        assert_true(services[1].isPrimary);
+        services.forEach(service => {
+          assert_equals(service.uuid,
+                        BluetoothUUID.getService('health_thermometer'));
+          assert_true(service.isPrimary);
+        });
       });
     });
-}, 'Request for services. Should return right services');
+}, 'Request for services. Should return right number of services.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
index 64c06041..79706f6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
@@ -2,26 +2,27 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(function() {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}],
-      optionalServices: ['generic_access']}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryServices())
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}],
+      optionalServices: ['generic_access']})
+    .then(([device]) => device.gatt.getPrimaryServices())
     .then(services => {
+      // Expect three service instances.
       assert_equals(services.length, 3);
-      assert_equals(services[0].uuid,
-                    BluetoothUUID.getService('generic_access'));
-      assert_equals(services[1].uuid,
-                    BluetoothUUID.getService('heart_rate'));
-      assert_equals(services[2].uuid,
-                    BluetoothUUID.getService('heart_rate'));
-      assert_true(services[0].isPrimary);
-      assert_true(services[1].isPrimary);
-      assert_true(services[2].isPrimary);
+      services.forEach(s => assert_true(s.isPrimary));
+
+      let uuid_set = new Set(services.map(s => s.uuid));
+      // Two of the expected services are 'health_thermometer', so
+      // only 2 unique UUIDs.
+      assert_equals(uuid_set.size, 2);
+
+      assert_true(uuid_set.has(BluetoothUUID.getService('generic_access')));
+      assert_true(uuid_set.has(BluetoothUUID.getService('health_thermometer')));
     });
 }, 'Find all services in a device.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html
index 4056825..be9e995 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-from-2-services.html
@@ -2,16 +2,16 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryServices('heart_rate'))
-    .then(services => {
-      assert_equals(services[0].device, services[1].device);
+  return getHealthThermometerDevice({
+     filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => device.gatt.getPrimaryServices('health_thermometer'))
+    .then(([service1, service2]) => {
+      assert_equals(service1.device, service2.device);
     });
 }, "Same parent device returned from multiple services.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html
index 91388573..1029c3ef 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/device-same-object.html
@@ -2,14 +2,14 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
+<script src="../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('heart_rate'))
+  return getHealthThermometerDevice({
+      filters: [{services: ['health_thermometer']}]})
+    .then(([device]) => device.gatt.getPrimaryService('health_thermometer'))
     .then(service => {
       assert_equals(service.device, service.device);
     });
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/4776765-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/4776765-expected.txt
new file mode 100644
index 0000000..d7af0c1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/4776765-expected.txt
@@ -0,0 +1,58 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [7, 43, 786, 62],
+          "reason": "geometry"
+        },
+        {
+          "object": "Caret",
+          "rect": [8, 84, 1, 19],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [8, 64, 1, 19],
+          "reason": "caret"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='div'",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/add-table-overpaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/add-table-overpaint-expected.txt
new file mode 100644
index 0000000..6bd32049
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/add-table-overpaint-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [2, 314, 152, 152],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/align-items-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/align-items-change-expected.txt
new file mode 100644
index 0000000..5166dec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/align-items-change-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='item'",
+          "rect": [100, 52, 100, 300],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item'",
+          "rect": [0, 52, 100, 300],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='item'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='item'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/align-self-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/align-self-change-expected.txt
new file mode 100644
index 0000000..5166dec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/align-self-change-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='item'",
+          "rect": [100, 52, 100, 300],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='item'",
+          "rect": [0, 52, 100, 300],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='item'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='item'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/background-currentColor-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/background-currentColor-repaint-expected.txt
new file mode 100644
index 0000000..e3a0c79
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/background-currentColor-repaint-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='div2'",
+          "rect": [8, 108, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='div1'",
+          "rect": [8, 8, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='div1'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='div2'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/background-image-paint-invalidation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/background-image-paint-invalidation-expected.txt
new file mode 100644
index 0000000..a97db5c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/background-image-paint-invalidation-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [808, 2016],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 0, 808, 2016],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-no-inflow-children-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-no-inflow-children-expected.txt
new file mode 100644
index 0000000..05093c3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-no-inflow-children-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'La la la la'",
+          "rect": [0, 0, 63, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'La la la'",
+          "rect": [0, 0, 48, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'La la la la'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-shift-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-shift-repaint-expected.txt
new file mode 100644
index 0000000..df0d08a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-shift-repaint-expected.txt
@@ -0,0 +1,135 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='half'",
+          "rect": [8, 353, 60, 38],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='half'",
+          "rect": [8, 338, 60, 38],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='red half'",
+          "rect": [8, 248, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='green half'",
+          "rect": [8, 218, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='red half'",
+          "rect": [8, 218, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='green half'",
+          "rect": [8, 188, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='green half'",
+          "rect": [8, 158, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='green half'",
+          "rect": [8, 128, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='red half'",
+          "rect": [8, 128, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='red half'",
+          "rect": [8, 98, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='top' class='blue half'",
+          "rect": [8, 98, 60, 30],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='green half'",
+          "rect": [8, 68, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='green half'",
+          "rect": [8, 38, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='red half'",
+          "rect": [8, 38, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='red half'",
+          "rect": [8, 8, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='div1' class='blue half'",
+          "rect": [8, 8, 60, 30],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='div1' class='blue half'",
+      "reason": "disappeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='red half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='green half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='top' class='blue half'",
+      "reason": "disappeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='red half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='green half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='green half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='red half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='half'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/border-outline-0-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/border-outline-0-expected.txt
new file mode 100644
index 0000000..6965a08
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/border-outline-0-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='border-image'",
+          "rect": [10, 10, 200, 200],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='border-image'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/box-inline-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/box-inline-resize-expected.txt
new file mode 100644
index 0000000..1f81cbe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/box-inline-resize-expected.txt
@@ -0,0 +1,58 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Chromium'",
+          "rect": [40, 107, 113, 27],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'Chromium'",
+          "rect": [8, 107, 113, 27],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage IMG id='foo'",
+          "rect": [8, 88, 32, 32],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage IMG id='foo'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow H2",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Chromium'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-3509-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-3509-expected.txt
new file mode 100644
index 0000000..9a36f9b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-3509-expected.txt
@@ -0,0 +1,46 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='im'",
+          "rect": [11, 131, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [11, 131, 4, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [11, 131, 4, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='im'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-6278-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-6278-expected.txt
new file mode 100644
index 0000000..bb36407
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-6278-expected.txt
@@ -0,0 +1,199 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [10, 138, 292, 160],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'Curabitur pretium, quam quis semper'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Phasellus vehicula, sem at posuere vehicula,'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'augue nibh molestie nisl, nec ullamcorper'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'fringilla orci nibh sed neque. Quisque eu nulla'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'lacus ante vulputate pede.'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'laoreet ac, laoreet non, suscipit sed, sapien.'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'malesuada, est libero feugiat libero, vel'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'non nisi molestie accumsan. Etiam tellus urna,'",
+          "rect": [10, 138, 292, 159],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [10, 303, 292, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [10, 138, 242, 200],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [10, 343, 242, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD id='col1'",
+          "rect": [10, 353, 242, 40],
+          "reason": "incremental"
+        },
+        {
+          "object": "InlineTextBox 'Curabitur pretium, quam quis semper'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Phasellus vehicula, sem at posuere'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Quisque eu nulla non nisi molestie'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'ac, laoreet non, suscipit sed, sapien.'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'accumsan. Etiam tellus urna, laoreet'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'malesuada, est libero feugiat libero,'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'nec ullamcorper lacus ante vulputate'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'pede.'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'vehicula, augue nibh molestie nisl,'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'vel fringilla orci nibh sed neque.'",
+          "rect": [9, 138, 236, 199],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutTableCell TD id='col1'",
+          "rect": [252, 138, 50, 215],
+          "reason": "incremental"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD id='col1'",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Curabitur pretium, quam quis semper'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'malesuada, est libero feugiat libero,'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'vel fringilla orci nibh sed neque.'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Quisque eu nulla non nisi molestie'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'accumsan. Etiam tellus urna, laoreet'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'ac, laoreet non, suscipit sed, sapien.'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Phasellus vehicula, sem at posuere'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'vehicula, augue nibh molestie nisl,'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'nec ullamcorper lacus ante vulputate'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'pede.'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD id='target'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-6388-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-6388-expected.txt
new file mode 100644
index 0000000..e4bdf2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-6388-expected.txt
@@ -0,0 +1,42 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [8, 136, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [8, 136, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 136, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='outside'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-7235-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-7235-expected.txt
new file mode 100644
index 0000000..7397a7eb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/bugzilla-7235-expected.txt
@@ -0,0 +1,58 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='t'",
+          "rect": [8, 176, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [8, 156, 4, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [8, 156, 4, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline (relative positioned) SPAN id='p'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='t'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-contenteditable-content-after-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-contenteditable-content-after-expected.txt
new file mode 100644
index 0000000..c34103f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-contenteditable-content-after-expected.txt
@@ -0,0 +1,221 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='editor'",
+          "rect": [7, 47, 786, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='editor'",
+          "rect": [7, 47, 786, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='editor'",
+          "rect": [7, 47, 786, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='editor'",
+          "rect": [7, 47, 786, 22],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'abc'",
+          "rect": [8, 48, 22, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'ab'",
+          "rect": [8, 48, 15, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'ab'",
+          "rect": [8, 48, 15, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [30, 48, 8, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [23, 48, 8, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [23, 48, 8, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [15, 48, 8, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [15, 48, 8, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a'",
+          "rect": [8, 48, 8, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'a'",
+          "rect": [8, 48, 8, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [8, 48, 8, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "Caret",
+          "rect": [8, 48, 1, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "Caret",
+          "rect": [8, 48, 1, 20],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [30, 48, 1, 19],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [23, 48, 1, 19],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [23, 48, 1, 19],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [15, 48, 1, 19],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [15, 48, 1, 19],
+          "reason": "caret"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='editor'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'a'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTextFragment (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'ab'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutTextFragment (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'abc'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutTextFragment (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-outside-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-outside-block-expected.txt
new file mode 100644
index 0000000..9e743f41
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-outside-block-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "Caret",
+          "rect": [791, 8, 1, 19],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "Caret",
+      "reason": "caret"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-with-transformation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-with-transformation-expected.txt
new file mode 100644
index 0000000..954a7d6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/caret-with-transformation-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "Caret",
+          "rect": [337, 206, 11, 18],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [42, 36, 11, 18],
+          "reason": "caret"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "Caret",
+      "reason": "caret"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/change-text-content-and-background-color-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/change-text-content-and-background-color-expected.txt
new file mode 100644
index 0000000..92593029
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/change-text-content-and-background-color-expected.txt
@@ -0,0 +1,50 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl (positioned) INPUT id='input'",
+          "rect": [8, 8, 244, 68],
+          "reason": "style change"
+        },
+        {
+          "object": "InlineTextBox 'NEW'",
+          "rect": [30, 30, 46, 23],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'OLD'",
+          "rect": [30, 30, 41, 23],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTextControl (positioned) INPUT id='input'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'NEW'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/change-transform-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/change-transform-expected.txt
new file mode 100644
index 0000000..2e83406
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/change-transform-expected.txt
@@ -0,0 +1,39 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='square'",
+          "rect": [10, 10, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='overlay'",
+          "rect": [35, 35, 50, 50],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='overlay'",
+          "rect": [35, 35, 50, 50],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='square'",
+          "rect": [35, 35, 50, 50],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='square'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip-with-layout-delta-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip-with-layout-delta-expected.txt
new file mode 100644
index 0000000..6dc22e41
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip-with-layout-delta-expected.txt
@@ -0,0 +1,75 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV",
+          "rect": [108, 8, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV",
+          "rect": [108, 8, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 8, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 8, 4, 19],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutBlockFlow SPAN id='t1'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutBlockFlow SPAN id='t2' class='blue'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clipped-relative-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clipped-relative-expected.txt
new file mode 100644
index 0000000..570bf580
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clipped-relative-expected.txt
@@ -0,0 +1,45 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutImage IMG",
+          "rect": [8, 74, 204, 232],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage IMG",
+          "rect": [8, 74, 94, 232],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV id='i'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='inner'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage IMG",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/containing-block-position-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/containing-block-position-change-expected.txt
new file mode 100644
index 0000000..ba171650
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/containing-block-position-change-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [158, 74, 50, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [100, 74, 50, 50],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV id='t'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/content-into-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/content-into-overflow-expected.txt
new file mode 100644
index 0000000..5f9191a3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/content-into-overflow-expected.txt
@@ -0,0 +1,51 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [8, 158, 106, 106],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [8, 8, 106, 106],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 308, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target3'",
+          "rect": [8, 388, 100, 20],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target3'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-2-expected.txt
new file mode 100644
index 0000000..8a690c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-2-expected.txt
@@ -0,0 +1,54 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='keep' class='item'",
+          "rect": [348, 88, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='to_remove' class='item'",
+          "rect": [348, 88, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='keep' class='item'",
+          "rect": [88, 88, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='container'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='keep_outer' class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) (floating) DIV id='keep_inner' class='inner'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='abs_pos'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='keep' class='item'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-3-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-3-expected.txt
new file mode 100644
index 0000000..cde8904e5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-3-expected.txt
@@ -0,0 +1,50 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='keep_child'",
+          "rect": [508, 208, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='remove_child'",
+          "rect": [508, 88, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='keep_child'",
+          "rect": [408, 208, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='container'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='keep_outer' class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV id='keep_inner'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='abs_pos'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='keep_child'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-4-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-4-expected.txt
new file mode 100644
index 0000000..f4e420eb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-4-expected.txt
@@ -0,0 +1,73 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='keep' class='item'",
+          "rect": [408, 88, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='to_remove' class='item'",
+          "rect": [408, 88, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='keep' class='item'",
+          "rect": [208, 88, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [408, 88, 4, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [408, 88, 4, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [208, 88, 4, 19],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) (floating) DIV id='keep_outer' class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='abs_pos'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) (floating) DIV id='keep_inner' class='inner'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='keep' class='item'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-expected.txt
new file mode 100644
index 0000000..39382ba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/crbug-371640-expected.txt
@@ -0,0 +1,77 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='keep' class='item'",
+          "rect": [348, 88, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='to_remove' class='item'",
+          "rect": [348, 88, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='keep' class='item'",
+          "rect": [88, 88, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [348, 88, 4, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [348, 88, 4, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox '\u00A0'",
+          "rect": [88, 88, 4, 19],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='container'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='keep_outer' class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) (floating) DIV id='keep_inner' class='inner'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='abs_pos'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='keep' class='item'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/delete-into-nested-block-expected.txt
new file mode 100644
index 0000000..3a64a27
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/delete-into-nested-block-expected.txt
@@ -0,0 +1,60 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'three'",
+          "rect": [8, 167, 31, 20],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'three'",
+          "rect": [8, 127, 31, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'two'",
+          "rect": [8, 147, 28, 20],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'one'",
+          "rect": [8, 127, 27, 20],
+          "reason": "disappeared"
+        },
+        {
+          "object": "Caret",
+          "rect": [8, 127, 1, 20],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='one'",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'three'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/details-open-repaint-expected.txt
new file mode 100644
index 0000000..4b89491
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/details-open-repaint-expected.txt
@@ -0,0 +1,71 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [8, 16, 351, 78],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'This test passes if the arrow rotates to the open position.'",
+          "rect": [8, 16, 351, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'summary'",
+          "rect": [24, 52, 59, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutDetailsMarker DIV id='details-marker'",
+          "rect": [8, 56, 11, 11],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutDetailsMarker DIV id='details-marker'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='details-content'",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTextControl INPUT",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/dynamic-table-vertical-alignment-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/dynamic-table-vertical-alignment-change-expected.txt
new file mode 100644
index 0000000..4e521bb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/dynamic-table-vertical-alignment-change-expected.txt
@@ -0,0 +1,44 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [808, 585],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (floating) DIV id='target'",
+          "rect": [11, 75, 100, 100],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='target'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/erase-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/erase-overflow-expected.txt
new file mode 100644
index 0000000..78178c0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/erase-overflow-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='square'",
+          "rect": [8, 108, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='square'",
+          "rect": [8, 8, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target' class='square'",
+          "rect": [8, 8, 100, 100],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target' class='square'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/flexbox/scrollbars-changed-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/flexbox/scrollbars-changed-expected.txt
new file mode 100644
index 0000000..ed4a5c3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/flexbox/scrollbars-changed-expected.txt
@@ -0,0 +1,50 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "VerticalScrollbar",
+          "rect": [185, 0, 15, 100],
+          "reason": "scroll control"
+        },
+        {
+          "object": "InlineTextBox 'a'",
+          "rect": [0, 5, 15, 15],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'z'",
+          "rect": [0, 5, 15, 15],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='dynamic' class='content'",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'z'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-move-during-layout-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-move-during-layout-expected.txt
new file mode 100644
index 0000000..fdf4e56
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-move-during-layout-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [8, 210, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [8, 110, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='s'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-overflow-expected.txt
new file mode 100644
index 0000000..2527c0a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-overflow-expected.txt
@@ -0,0 +1,1122 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 537, 62, 37],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 531, 62, 37],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 459, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 453, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 420, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 414, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [61, 249, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [61, 243, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [61, 210, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [61, 204, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 171, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 165, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 132, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 126, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 93, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 87, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 54, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 48, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 15, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [61, 9, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [61, 366, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [61, 360, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [61, 327, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [61, 321, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [61, 288, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [61, 282, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 426, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 420, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 216, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 210, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 177, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 171, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 138, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 132, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 99, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 93, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 60, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 54, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 21, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 15, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [69, 374, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [69, 368, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [69, 335, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [69, 329, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [69, 296, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [69, 290, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [70, 375, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [70, 369, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [70, 336, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [70, 330, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [70, 297, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [70, 291, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [67, 138, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [67, 132, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [65, 60, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [65, 54, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [65, 21, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [65, 15, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 543, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 537, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 255, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 249, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 216, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 210, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 177, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 171, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 99, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [59, 93, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 543, 32, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [67, 537, 32, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [66, 299, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [66, 293, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [63, 23, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [63, 17, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 545, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 539, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 257, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 251, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 218, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 212, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 179, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 173, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 140, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [59, 134, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [58, 377, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [58, 371, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [58, 338, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [58, 332, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [57, 101, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [57, 95, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [57, 62, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [57, 56, 16, 17],
+          "reason": "geometry"
+        }
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow (floating) DIV",
+      "position": [59, 426],
+      "bounds": [64, 97],
+      "contentsOpaque": false,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [2, 72, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [2, 66, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 78, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 72, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 39, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 33, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [0, 78, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [0, 72, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [0, 39, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [0, 33, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [0, 0, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [0, -6, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [0, 80, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [0, 74, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [0, 41, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [0, 35, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [0, 2, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [0, -4, 16, 17],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTable TABLE class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTable TABLE class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTable TABLE class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-overflow-right-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-overflow-right-expected.txt
new file mode 100644
index 0000000..8d49645
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/float-overflow-right-expected.txt
@@ -0,0 +1,1122 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 537, 62, 37],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 531, 62, 37],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 459, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 453, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 420, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 414, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [677, 249, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [677, 243, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [677, 210, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+          "rect": [677, 204, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 171, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 165, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 132, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 126, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 93, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 87, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 54, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 48, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 15, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [677, 9, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 366, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 360, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 327, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 321, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 288, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 282, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 426, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 420, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 216, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 210, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 177, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 171, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 138, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 132, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 99, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 93, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 60, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 54, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 21, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 15, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [689, 374, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [689, 368, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [689, 335, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [689, 329, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [689, 296, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [689, 290, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 543, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 537, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 255, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 249, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 216, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 210, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 177, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 171, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 99, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 93, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [695, 60, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [695, 54, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [695, 21, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [695, 15, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [693, 138, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [693, 132, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [690, 375, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [690, 369, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [690, 336, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [690, 330, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [690, 297, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [690, 291, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [701, 543, 32, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [701, 537, 32, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [727, 101, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [727, 95, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [727, 62, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [727, 56, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [726, 377, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [726, 371, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [726, 338, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [726, 332, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 545, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 539, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 257, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 251, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 218, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 212, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 179, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 173, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 140, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 134, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [721, 23, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [721, 17, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [718, 299, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [718, 293, 16, 17],
+          "reason": "geometry"
+        }
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow (floating) DIV",
+      "position": [677, 426],
+      "bounds": [64, 97],
+      "contentsOpaque": false,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [0, 72, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outer'",
+          "rect": [0, 66, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [6, 78, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [6, 72, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [6, 39, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [6, 33, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [24, 78, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [24, 72, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [24, 39, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [24, 33, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [24, 0, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [24, -6, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [48, 80, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [48, 74, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [48, 41, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [48, 35, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [48, 2, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [48, -4, 16, 17],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutDeprecatedFlexibleBox DIV class='outer box'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTable TABLE class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTable TABLE class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTable TABLE class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'x'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-continuations-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-continuations-expected.txt
new file mode 100644
index 0000000..5e16de52
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-continuations-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutInline SPAN id='outer'",
+          "rect": [7, 87, 88, 22],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='outer'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-enable-continuations-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-enable-continuations-expected.txt
new file mode 100644
index 0000000..eacd06df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-enable-continuations-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutInline SPAN id='outer'",
+          "rect": [7, 87, 88, 22],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='outer'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-ring-on-inline-continuation-move-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-ring-on-inline-continuation-move-expected.txt
new file mode 100644
index 0000000..69df868
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/focus-ring-on-inline-continuation-move-expected.txt
@@ -0,0 +1,54 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutInline SPAN",
+          "rect": [7, 7, 102, 182],
+          "reason": "outline"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV id='block'",
+          "rect": [8, 88, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV id='block'",
+          "rect": [8, 8, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "outline"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV id='block'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/button-reset-focus-by-mouse-then-keydown-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/button-reset-focus-by-mouse-then-keydown-expected.txt
new file mode 100644
index 0000000..0bea246
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/button-reset-focus-by-mouse-then-keydown-expected.txt
@@ -0,0 +1,45 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutButton BUTTON",
+          "rect": [7, 7, 52, 24],
+          "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'Reset'",
+          "rect": [16, 11, 34, 16],
+          "reason": "subtree"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutButton BUTTON",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "subtree"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "subtree"
+    },
+    {
+      "object": "InlineTextBox 'Reset'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/range-focus-by-mouse-then-keydown-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/range-focus-by-mouse-then-keydown-expected.txt
new file mode 100644
index 0000000..2678298
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/range-focus-by-mouse-then-keydown-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSlider INPUT",
+          "rect": [9, 9, 131, 23],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='thumb'",
+          "rect": [69, 10, 11, 21],
+          "reason": "subtree"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSlider INPUT",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutFlexibleBox DIV",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='track'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='thumb'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/submit-focus-by-mouse-then-keydown-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/submit-focus-by-mouse-then-keydown-expected.txt
new file mode 100644
index 0000000..6a0e4d64
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/forms/submit-focus-by-mouse-then-keydown-expected.txt
@@ -0,0 +1,45 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutButton INPUT",
+          "rect": [7, 7, 59, 24],
+          "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'Submit'",
+          "rect": [16, 11, 41, 16],
+          "reason": "subtree"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutButton INPUT",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "subtree"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "subtree"
+    },
+    {
+      "object": "InlineTextBox 'Submit'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/gradients-em-stops-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/gradients-em-stops-repaint-expected.txt
new file mode 100644
index 0000000..d626117
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/gradients-em-stops-repaint-expected.txt
@@ -0,0 +1,68 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='box4' class='box'",
+          "rect": [344, 18, 302, 122],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='box3' class='box'",
+          "rect": [18, 18, 302, 122],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='indicator'",
+          "rect": [345, 19, 240, 20],
+          "reason": "style change"
+        },
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [330, 135, 4, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [330, 135, 4, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='em-units'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='box3' class='box'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='box4' class='box'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='indicator'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/hover-pseudo-borders-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/hover-pseudo-borders-expected.txt
new file mode 100644
index 0000000..0ce7114
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/hover-pseudo-borders-expected.txt
@@ -0,0 +1,57 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) \u003Cpseudo:after\u003E",
+          "rect": [138, 8, 100, 100],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) \u003Cpseudo:after\u003E",
+          "rect": [138, 8, 100, 100],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) \u003Cpseudo:after\u003E",
+          "rect": [138, 8, 100, 100],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='hitregion'",
+          "rect": [8, 8, 100, 100],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='hitregion'",
+          "rect": [8, 8, 100, 100],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='hitregion'",
+          "rect": [8, 8, 100, 100],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='hitregion'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) \u003Cpseudo:after\u003E",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTextFragment (anonymous)",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-scrollbar-hover-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-scrollbar-hover-expected.txt
new file mode 100644
index 0000000..27072d5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-scrollbar-hover-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "VerticalScrollbar",
+          "rect": [187, 102, 15, 200],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-block-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-block-resize-expected.txt
new file mode 100644
index 0000000..ae762b58
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-block-resize-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [8, 8, 100, 100],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-color-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-color-change-expected.txt
new file mode 100644
index 0000000..593506e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-color-change-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineFlowBox",
+          "rect": [8, 72, 38, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [8, 72, 38, 19],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutInline SPAN id='target'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-outline-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-outline-repaint-expected.txt
new file mode 100644
index 0000000..f27bb30f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-outline-repaint-expected.txt
@@ -0,0 +1,95 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutInline SPAN id='test'",
+          "rect": [5, 173, 95, 45],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [8, 176, 89, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Lorem ipsum'",
+          "rect": [8, 176, 89, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'dolor sit amet\u00A0'",
+          "rect": [8, 176, 89, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [8, 176, 88, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Lorem ipsum'",
+          "rect": [8, 176, 88, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'dolor\u00A0'",
+          "rect": [8, 176, 88, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "Caret",
+          "rect": [45, 196, 1, 19],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    },
+    {
+      "object": "LayoutInline SPAN id='test'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'Lorem ipsum'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'dolor\u00A0'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-overflow-expected.txt
new file mode 100644
index 0000000..b960409
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-overflow-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'A\u00A0\u00A0B'",
+          "rect": [33, 33, 250, 100],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'A\u00A0\u00A0B'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-reflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-reflow-expected.txt
new file mode 100644
index 0000000..ae45675
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-reflow-expected.txt
@@ -0,0 +1,218 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'A A A A A AA AA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'A A'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AA AA AA A A A'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AA AA AAA AAA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AAA AAA AAA AA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AAA AAAA AAAA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAA AAAA AAAA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAA AAAAA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAAA AAAAA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAAA AAAAAAA'",
+          "rect": [0, 0, 300, 200],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'A A A A A'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'A A A A'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AA AA AA A'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AA AA AA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AA AAA AAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAA AAA AA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAA AAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAA AAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAA AAAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAA AAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'AAAAAAA'",
+          "rect": [0, 0, 200, 300],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'A A A A A'",
+          "rect": [0, 300, 180, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'A A A A A'",
+          "rect": [0, 300, 180, 20],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='div1' class='container'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'A A A A A AA AA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AA AA AAA AAA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AAA AAAA AAAA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AAAAA AAAAA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AAAAAA AAAAAAA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AAAAAA AAAAA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AAAAA AAAA AAAA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AAA AAA AAA AA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'AA AA AA A A A'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'A A'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='div2' class='container'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'A A A A A'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-relative-positioned-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-relative-positioned-expected.txt
new file mode 100644
index 0000000..d495092
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-relative-positioned-expected.txt
@@ -0,0 +1,46 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'a'",
+          "rect": [8, 88, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'p'",
+          "rect": [8, 88, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [8, 88, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'a'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-vertical-lr-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-vertical-lr-overflow-expected.txt
new file mode 100644
index 0000000..b1984167
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-vertical-lr-overflow-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'A\u00A0\u00A0B'",
+          "rect": [33, 33, 100, 250],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'A\u00A0\u00A0B'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-vertical-rl-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-vertical-rl-overflow-expected.txt
new file mode 100644
index 0000000..85460b7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/inline-vertical-rl-overflow-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'A\u00A0\u00A0B'",
+          "rect": [667, 33, 100, 250],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'A\u00A0\u00A0B'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/input-overflow-in-table-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/input-overflow-in-table-expected.txt
new file mode 100644
index 0000000..de89491
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/input-overflow-in-table-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='test'",
+          "rect": [7, 7, 102, 22],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='test'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/insert-frame-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/insert-frame-expected.txt
new file mode 100644
index 0000000..63554274
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/insert-frame-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 104, 104],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutIFrame IFRAME",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/intermediate-layout-position-clip-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/intermediate-layout-position-clip-expected.txt
new file mode 100644
index 0000000..70001d8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/intermediate-layout-position-clip-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [8, 8, 40, 20],
+          "reason": "incremental"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "incremental"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/intermediate-layout-position-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/intermediate-layout-position-expected.txt
new file mode 100644
index 0000000..a1cfad5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/intermediate-layout-position-expected.txt
@@ -0,0 +1,37 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 172, 46, 20],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 74, 46, 20],
+          "reason": "incremental"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutTableCell TD id='cell'",
+      "reason": "incremental"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/invalidate-box-shadow-currentColor-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/invalidate-box-shadow-currentColor-expected.txt
new file mode 100644
index 0000000..de6ee5a00
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/invalidate-box-shadow-currentColor-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineFlowBox",
+          "rect": [8, 8, 29, 24],
+          "reason": "style change"
+        },
+        {
+          "object": "InlineTextBox 'Text'",
+          "rect": [8, 8, 29, 19],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutInline SPAN id='target'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'Text'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
new file mode 100644
index 0000000..0da1bc5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
@@ -0,0 +1,43 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='root' class='editing'",
+          "rect": [7, 7, 786, 20],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox '1'",
+          "rect": [8, 8, 10, 18],
+          "reason": "disappeared"
+        },
+        {
+          "object": "Caret",
+          "rect": [18, 8, 1, 18],
+          "reason": "caret"
+        },
+        {
+          "object": "Caret",
+          "rect": [8, 8, 1, 18],
+          "reason": "caret"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='root' class='editing'",
+      "reason": "full"
+    },
+    {
+      "object": "Caret",
+      "reason": "caret"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/japanese-rl-selection-clear-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/japanese-rl-selection-clear-expected.txt
new file mode 100644
index 0000000..21a86590
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/japanese-rl-selection-clear-expected.txt
@@ -0,0 +1,145 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox '\u3042\u3063\u305F\u304B\u5FD8\u308C\u3066\u3057\u307E\u3063\u305F\u7D4C\u9A13\u306F\u3042\u308A\u307E\u3059\u304B'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u3057\u305F\u30A6\u30A7\u30D6\u30DA\u30FC\u30B8\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u304B\u3089\u3082\u691C\u7D22'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u3059\u3002\u8A2A\u554F\u3057\u305F\u30A6\u30A7\u30D6\u30DA\u30FC\u30B8\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u304B'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u305B\u3063\u304B\u304F\u898B\u3064\u3051\u305F\u3059'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u305B\u3063\u304B\u304F\u898B\u3064\u3051\u305F\u3059\u3070\u3089\u3057\u3044\u8A18\u4E8B\u304C\u3069\u3053\u306B'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u3063\u305F\u7D4C\u9A13\u306F\u3042\u308A\u307E\u3059\u304B \u306A\u3089\u30BF\u30A4\u30C8\u30EB\u3068\u30A2'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u3064\u3051\u305F\u3059\u3070\u3089\u3057\u3044\u8A18\u4E8B\u304C\u3069\u3053\u306B\u3042\u3063\u305F\u304B\u5FD8'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u306A\u3089\u30BF\u30A4\u30C8\u30EB\u3068\u30A2\u30C9\u30EC\u30B9\u3060\u3051\u3067\u306A\u304F\u3001\u8A2A\u554F'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u304B\u3089\u3082\u691C\u7D22\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u3070\u3089\u3057\u3044\u8A18\u4E8B\u304C\u3069\u3053\u306B\u3042\u3063\u305F\u304B\u5FD8\u308C\u3066\u3057\u307E'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u3089\u3082\u691C\u7D22\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u305B\u3063\u304B\u304F\u898B'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u308C\u3066\u3057\u307E\u3063\u305F\u7D4C\u9A13\u306F\u3042\u308A\u307E\u3059\u304B \u306A\u3089\u30BF\u30A4'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u30C8\u30EB\u3068\u30A2\u30C9\u30EC\u30B9\u3060\u3051\u3067\u306A\u304F\u3001\u8A2A\u554F'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\u30C9\u30EC\u30B9\u3060\u3051\u3067\u306A\u304F\u3001\u8A2A\u554F\u3057\u305F\u30A6\u30A7\u30D6\u30DA\u30FC\u30B8'",
+          "rect": [441, 123, 336, 404],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u305B\u3063\u304B\u304F\u898B\u3064\u3051\u305F\u3059\u3070\u3089\u3057\u3044\u8A18\u4E8B\u304C\u3069\u3053\u306B'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3042\u3063\u305F\u304B\u5FD8\u308C\u3066\u3057\u307E\u3063\u305F\u7D4C\u9A13\u306F\u3042\u308A\u307E\u3059\u304B'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u306A\u3089\u30BF\u30A4\u30C8\u30EB\u3068\u30A2\u30C9\u30EC\u30B9\u3060\u3051\u3067\u306A\u304F\u3001\u8A2A\u554F'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3057\u305F\u30A6\u30A7\u30D6\u30DA\u30FC\u30B8\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u304B\u3089\u3082\u691C\u7D22'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u305B\u3063\u304B\u304F\u898B\u3064\u3051\u305F\u3059'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3070\u3089\u3057\u3044\u8A18\u4E8B\u304C\u3069\u3053\u306B\u3042\u3063\u305F\u304B\u5FD8\u308C\u3066\u3057\u307E'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3063\u305F\u7D4C\u9A13\u306F\u3042\u308A\u307E\u3059\u304B \u306A\u3089\u30BF\u30A4\u30C8\u30EB\u3068\u30A2'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u30C9\u30EC\u30B9\u3060\u3051\u3067\u306A\u304F\u3001\u8A2A\u554F\u3057\u305F\u30A6\u30A7\u30D6\u30DA\u30FC\u30B8'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u304B\u3089\u3082\u691C\u7D22\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3059\u3002\u8A2A\u554F\u3057\u305F\u30A6\u30A7\u30D6\u30DA\u30FC\u30B8\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u304B'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3089\u3082\u691C\u7D22\u3059\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002\u305B\u3063\u304B\u304F\u898B'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u3064\u3051\u305F\u3059\u3070\u3089\u3057\u3044\u8A18\u4E8B\u304C\u3069\u3053\u306B\u3042\u3063\u305F\u304B\u5FD8'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u308C\u3066\u3057\u307E\u3063\u305F\u7D4C\u9A13\u306F\u3042\u308A\u307E\u3059\u304B \u306A\u3089\u30BF\u30A4'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\u30C8\u30EB\u3068\u30A2\u30C9\u30EC\u30B9\u3060\u3051\u3067\u306A\u304F\u3001\u8A2A\u554F'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-relative-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-relative-expected.txt
new file mode 100644
index 0000000..7ad4b37
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-relative-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [8, 152, 38, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'FAIL'",
+          "rect": [8, 152, 35, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-scrolloffset-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-scrolloffset-expected.txt
new file mode 100644
index 0000000..82eb0fb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-scrolloffset-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'before'",
+          "rect": [100, 120, 40, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'after'",
+          "rect": [100, 120, 29, 19],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'after'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-scrolloffset2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-scrolloffset2-expected.txt
new file mode 100644
index 0000000..bc04889
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/layout-state-scrolloffset2-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'before'",
+          "rect": [102, 122, 40, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'after'",
+          "rect": [102, 122, 29, 19],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'after'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-1-expected.txt
new file mode 100644
index 0000000..737163d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-1-expected.txt
@@ -0,0 +1,148 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'The chief difficulty Alice found at first was in managing'",
+          "rect": [14, 80, 406, 119],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'away,\n'",
+          "rect": [14, 80, 406, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'but generally, just as she had got its'",
+          "rect": [14, 80, 406, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+          "rect": [14, 80, 406, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'flamingo: she succeeded in getting its body tucked'",
+          "rect": [14, 80, 406, 119],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'hanging down,\n'",
+          "rect": [14, 80, 406, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+          "rect": [14, 80, 406, 119],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'her\n'",
+          "rect": [14, 80, 406, 119],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'neck nicely straightened\n'",
+          "rect": [14, 80, 406, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'out, and was going to give the'",
+          "rect": [14, 80, 406, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'away,\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'but generally, just as she had got its'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'hanging down,\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'neck nicely straightened\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'out, and was going to give the'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV id='pinkFloat'",
+          "rect": [378, 138, 70, 30],
+          "reason": "incremental"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='pinkFloat'",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The chief difficulty Alice found at first was in managing'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'flamingo: she succeeded in getting its body tucked'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'away,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hanging down,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'but generally, just as she had got its'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'neck nicely straightened\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'out, and was going to give the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-10-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-10-expected.txt
new file mode 100644
index 0000000..9023441
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-10-expected.txt
@@ -0,0 +1,484 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' was in a furious passion, and went\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'about once in a minute.\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'about, and shouting \u2018Off with his head!\u2019 or \u2018Off with'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019 about once in a minute.\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'was in a furious passion, and went\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Queen'",
+          "rect": [14, 440, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the'",
+          "rect": [14, 440, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 440, 66, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 363, 48, 65],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 374, 48, 64],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'begin again, it was very'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'unrolled itself, and was'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'this, there was generally'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'she wanted to send the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soldiers were always'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the ground, Alice'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'difficult'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'game indeed.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The players all played at once without waiting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for turns,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a very short time '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Queen'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' was in a furious passion, and went\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'stamping'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'about, and shouting \u2018Off with his head!\u2019 or \u2018Off with'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her head!\u2019 about once in a minute.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'yet'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'become of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'here; the great\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-2-expected.txt
new file mode 100644
index 0000000..695a637
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-2-expected.txt
@@ -0,0 +1,774 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow P",
+          "rect": [8, 74, 418, 526],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 520, 408, 80],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 521, 408, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 241, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox ' twist itself round and'",
+          "rect": [14, 181, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'could not help bursting out\n'",
+          "rect": [14, 181, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'head down, and was going to\n'",
+          "rect": [14, 181, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'laughing: and when she had got its'",
+          "rect": [14, 181, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'look up in her face, with\n'",
+          "rect": [14, 181, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'such a puzzled expression that she'",
+          "rect": [14, 181, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox ' twist itself round and'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'could not help bursting out\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'head down, and was going to\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'laughing: and when she had got its'",
+          "rect": [14, 180, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'look up in her face, with\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'such a puzzled expression that she'",
+          "rect": [14, 180, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 361, 356, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 361, 356, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'The chief difficulty Alice found at first was in managing'",
+          "rect": [14, 80, 355, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'away,\n'",
+          "rect": [14, 80, 355, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'but generally, just as she had got its'",
+          "rect": [14, 80, 355, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+          "rect": [14, 80, 355, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'flamingo: she succeeded in getting its body tucked'",
+          "rect": [14, 80, 355, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'hanging down,\n'",
+          "rect": [14, 80, 355, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+          "rect": [14, 80, 355, 120],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'her\n'",
+          "rect": [14, 80, 355, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'neck nicely straightened\n'",
+          "rect": [14, 80, 355, 120],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'out, and was going to give the'",
+          "rect": [14, 80, 355, 120],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'neck nicely straightened\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'out, and was going to give the'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'about once in a minute.\n'",
+          "rect": [14, 461, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019'",
+          "rect": [14, 461, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+          "rect": [14, 461, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and'",
+          "rect": [14, 461, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'was in a furious passion, and went\n'",
+          "rect": [14, 461, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'about once in a minute.\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019'",
+          "rect": [14, 460, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and'",
+          "rect": [14, 460, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'was in a furious passion, and went\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 421, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 421, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 421, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 421, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 401, 304, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 441, 66, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 440, 66, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='greenFloat'",
+          "rect": [372, 404, 48, 81],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='greenFloat'",
+          "rect": [372, 403, 48, 81],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 364, 48, 65],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 363, 48, 65],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'would'",
+          "rect": [235, 180, 45, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'would'",
+          "rect": [238, 180, 40, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [485, 0, 15, 600],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutBlockFlow P",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The chief difficulty Alice found at first was in managing'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'flamingo: she succeeded in getting its body tucked'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'away,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hanging down,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'but generally, just as she had got its'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'neck nicely straightened\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'out, and was going to give the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline I id='would'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'would'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' twist itself round and'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'look up in her face, with\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'such a puzzled expression that she'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'could not help bursting out\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'laughing: and when she had got its'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'head down, and was going to\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'begin again, it was very'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'unrolled itself, and was'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'this, there was generally'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'she wanted to send the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soldiers were always'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the ground, Alice'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'difficult'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'game indeed.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The players all played at once without waiting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) SPAN id='greenFloat'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for turns,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a very short time '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the Queen'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'was in a furious passion, and went\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'stamping about, and'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her head!\u2019'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'about once in a minute.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'yet'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'become of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'here; the great\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-4-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-4-expected.txt
new file mode 100644
index 0000000..b129a12
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-4-expected.txt
@@ -0,0 +1,459 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019 about once'",
+          "rect": [14, 460, 406, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'in a minute.\n'",
+          "rect": [14, 460, 406, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+          "rect": [14, 460, 406, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and'",
+          "rect": [14, 460, 406, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'was in a furious passion, and went\n'",
+          "rect": [14, 460, 406, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'about once in a minute.\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'was in a furious passion, and went\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 440, 66, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 440, 66, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='greenFloat'",
+          "rect": [372, 403, 48, 81],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'begin again, it was very'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'unrolled itself, and was'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'this, there was generally'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'she wanted to send the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soldiers were always'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the ground, Alice'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'difficult'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'game indeed.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The players all played at once without waiting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) SPAN id='greenFloat'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for turns,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a very short time '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the Queen'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'was in a furious passion, and went\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'stamping about, and'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her head!\u2019 about once'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'in a minute.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'yet'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'become of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'here; the great\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-6-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-6-expected.txt
new file mode 100644
index 0000000..76cccca3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-6-expected.txt
@@ -0,0 +1,210 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 240, 407, 139],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 363, 48, 65],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'foo'",
+          "rect": [27, 363, 22, 20],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'begin again, it was very'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'unrolled itself, and was'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'this, there was generally'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'she wanted to send the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soldiers were always'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the ground, Alice'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'foo'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'difficult'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'game indeed.\n'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-7-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-7-expected.txt
new file mode 100644
index 0000000..cf5eca5b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-7-expected.txt
@@ -0,0 +1,97 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [298, 440, 70, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 440, 66, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for turns,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a very short time '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='theQueen'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'the Queen'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-8-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-8-expected.txt
new file mode 100644
index 0000000..0ddeb010
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-8-expected.txt
@@ -0,0 +1,666 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow P",
+          "rect": [8, 74, 418, 526],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' twist itself round and'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' twist itself round and'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'could not help bursting out\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'could not help bursting out\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'head down, and was going to\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'head down, and was going to\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'laughing: and when she had got its'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'laughing: and when she had got its'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'look up in her face, with\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'look up in her face, with\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'such a puzzled expression that she'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'such a puzzled expression that she'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult game'",
+          "rect": [14, 360, 406, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'indeed.\n'",
+          "rect": [14, 360, 406, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' was in a furious'",
+          "rect": [13, 440, 358, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019 about once in a'",
+          "rect": [13, 440, 358, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'minute.\n'",
+          "rect": [13, 440, 358, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'passion, and went\n'",
+          "rect": [13, 440, 358, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and shouting \u2018Off'",
+          "rect": [13, 440, 358, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'with his head!\u2019 or \u2018Off with\n'",
+          "rect": [13, 440, 358, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'The chief difficulty Alice found at first was in managing'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'The chief difficulty Alice found at first was in managing'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'away,\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'away,\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'but generally, just as she had got its'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'but generally, just as she had got its'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'flamingo: she succeeded in getting its body tucked'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'flamingo: she succeeded in getting its body tucked'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'hanging down,\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hanging down,\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'her\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'her\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'neck nicely straightened\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'neck nicely straightened\n'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'out, and was going to give the'",
+          "rect": [14, 80, 355, 119],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'out, and was going to give the'",
+          "rect": [14, 80, 355, 119],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'about once in a minute.\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'was in a furious passion, and went\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 400, 354, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'and in\n'",
+          "rect": [14, 400, 354, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,'",
+          "rect": [14, 400, 354, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting for the hedgehogs;'",
+          "rect": [14, 400, 354, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting\n'",
+          "rect": [14, 400, 297, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [184, 440, 68, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 440, 66, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 363, 48, 65],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'would'",
+          "rect": [238, 180, 40, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'would'",
+          "rect": [238, 180, 40, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow P",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The chief difficulty Alice found at first was in managing'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'flamingo: she succeeded in getting its body tucked'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'away,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'comfortably enough, under her arm, with its legs'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hanging down,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'but generally, just as she had got its'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'neck nicely straightened\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'out, and was going to give the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog a blow with its head, it\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'would'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' twist itself round and'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'look up in her face, with\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'such a puzzled expression that she'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'could not help bursting out\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'laughing: and when she had got its'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'head down, and was going to\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'begin again, it was very'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'unrolled itself, and was'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'this, there was generally'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'she wanted to send the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soldiers were always'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the ground, Alice'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'difficult game'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'indeed.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The players all played at once without waiting\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for turns,'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'quarrelling all the while, and fighting for the hedgehogs;'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'and in\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a very short time '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the Queen'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' was in a furious'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'passion, and went\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'stamping about, and shouting \u2018Off'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'with his head!\u2019 or \u2018Off with\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her head!\u2019 about once in a'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'minute.\n'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-9-expected.txt
new file mode 100644
index 0000000..14686e05
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -0,0 +1,602 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'become of\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'here; the great\n'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 520, 408, 80],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'yet'",
+          "rect": [13, 520, 408, 80],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'and was in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'begin again, it was very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'ground, Alice soon came to the conclusion that it was a very'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'in the act of crawling away: besides all\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'she wanted to send the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'she'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soldiers were always'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'soldiers'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'soon came to the conclusion that it was a very\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the ground, Alice'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'there was generally a ridge or furrow in the way wherever\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'this, there was generally'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'this,'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself, and was'",
+          "rect": [13, 240, 407, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'unrolled itself,'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'wanted to send the hedgehog to, and, as the doubled-up\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'were always getting up and walking off to other parts of\n'",
+          "rect": [13, 240, 407, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' twist itself round and'",
+          "rect": [14, 180, 407, 79],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'could not help bursting out\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'could not help bursting out\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'head down, and was going to\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'head down, and was going to\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'laughing: and when she had got its'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'laughing: and when she had got its'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'look up in her face, with\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'look up in her face, with\n'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'such a puzzled expression that she'",
+          "rect": [14, 180, 407, 79],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'such a puzzled expression that she'",
+          "rect": [14, 180, 407, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'game indeed.\n'",
+          "rect": [65, 360, 356, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' was in a furious passion, and went\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'about once in a minute.\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'about, and shouting \u2018Off with his head!\u2019 or \u2018Off with'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019 about once in a minute.\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'her head!\u2019'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'shouting \u2018Off with his head!\u2019 or \u2018Off with\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping about, and'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'stamping'",
+          "rect": [14, 460, 355, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'was in a furious passion, and went\n'",
+          "rect": [14, 460, 355, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Queen'",
+          "rect": [14, 440, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the'",
+          "rect": [14, 440, 355, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [14, 420, 355, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a very short time '",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'for turns,\n'",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+          "rect": [65, 420, 304, 39],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'The players all played at once without waiting'",
+          "rect": [65, 400, 304, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'difficult game indeed.\n'",
+          "rect": [65, 380, 141, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the Queen'",
+          "rect": [302, 440, 66, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 383, 48, 65],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+          "rect": [14, 363, 48, 65],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) SPAN id='yellowFloat'",
+          "rect": [372, 243, 48, 49],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' twist itself round and'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'look up in her face, with\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'such a puzzled expression that she'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'could not help bursting out\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'laughing: and when she had got its'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'head down, and was going to\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) SPAN id='yellowFloat'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'begin again, it was very'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'provoking to find that the hedgehog had\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'unrolled itself,'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'and was in the act of crawling away: besides all\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'this,'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'there was generally a ridge or furrow in the way wherever\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'she'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'wanted to send the hedgehog to, and, as the doubled-up\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'soldiers'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'were always getting up and walking off to other parts of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'ground, Alice soon came to the conclusion that it was a very'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (floating) SPAN id='blueFloat'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'difficult game indeed.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'The players all played at once without waiting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for turns,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'quarrelling all the while, and fighting'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'for the hedgehogs; and in\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a very short time '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'the'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Queen'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' was in a furious passion, and went\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'stamping'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'about, and shouting \u2018Off with his head!\u2019 or \u2018Off with'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'her head!\u2019 about once in a minute.\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Alice began to feel very uneasy: to be sure, she had not as\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'yet'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'had any dispute with the Queen, but she knew that it might'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'happen any minute, \u2018and then,\u2019 thought she, \u2018what would'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'become of\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'me? They\u2019re dreadfully fond of beheading people'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'here; the great\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'wonder is, that there\u2018s any one left alive!\u2019'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-in-scrolled-clipped-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-in-scrolled-clipped-block-expected.txt
new file mode 100644
index 0000000..262f9d47
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-in-scrolled-clipped-block-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox '                         FAIL     .'",
+          "rect": [8, 8, 100, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox '                         PASS     .'",
+          "rect": [8, 8, 100, 19],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox '                         PASS     .'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-overflow-expected.txt
new file mode 100644
index 0000000..4ada816
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/line-overflow-expected.txt
@@ -0,0 +1,165 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox ' eleifend'",
+          "rect": [7, 122, 197, 99],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'convallis.'",
+          "rect": [7, 122, 197, 99],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'lacus, at sagittis eros leo'",
+          "rect": [7, 122, 197, 99],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'pulvinar velit. Integer'",
+          "rect": [7, 122, 197, 99],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'sollicitudin nisi ut urna blandit'",
+          "rect": [7, 122, 197, 99],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' eleifend lacus,'",
+          "rect": [8, 142, 195, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'at sagittis eros leo pulvinar'",
+          "rect": [8, 142, 195, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'urna blandit convallis.'",
+          "rect": [8, 142, 195, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'velit. Integer sollicitudin nisi ut'",
+          "rect": [8, 142, 195, 79],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Cras faucibus. Nunc'",
+          "rect": [8, 82, 192, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'adipiscing, enim in scelerisque'",
+          "rect": [8, 82, 192, 59],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'augue '",
+          "rect": [8, 82, 192, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'augue'",
+          "rect": [8, 82, 192, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'convallis,\n'",
+          "rect": [8, 82, 192, 59],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'convallis,\n'",
+          "rect": [8, 82, 192, 59],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineFlowBox",
+          "rect": [113, 92, 36, 49],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'purus'",
+          "rect": [113, 122, 36, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'purus'",
+          "rect": [37, 142, 36, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Cras faucibus. Nunc'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'adipiscing, enim in scelerisque'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'convallis,\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'augue '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='t'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'purus'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' eleifend'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'lacus, at sagittis eros leo'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'pulvinar velit. Integer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'sollicitudin nisi ut urna blandit'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'convallis.'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/lines-with-layout-delta-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/lines-with-layout-delta-expected.txt
new file mode 100644
index 0000000..56b95e7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/lines-with-layout-delta-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [8, 58, 38, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'FAIL'",
+          "rect": [8, 58, 35, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (floating) DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/list-marker-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/list-marker-2-expected.txt
new file mode 100644
index 0000000..a65e73f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/list-marker-2-expected.txt
@@ -0,0 +1,46 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutImage IMG id='target'",
+          "rect": [48, 100, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutListMarker (anonymous)",
+          "rect": [30, 185, 7, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutListMarker (anonymous)",
+          "rect": [30, 135, 7, 19],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutListItem LI",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutListMarker (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage IMG id='target'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/make-children-non-inline-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/make-children-non-inline-expected.txt
new file mode 100644
index 0000000..f8211a9a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/make-children-non-inline-expected.txt
@@ -0,0 +1,270 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'about all the stuff'",
+          "rect": [8, 264, 111, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'about all the stuff'",
+          "rect": [8, 164, 111, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'write a book'",
+          "rect": [8, 144, 80, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'write a book'",
+          "rect": [8, 144, 80, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'more words.'",
+          "rect": [8, 104, 79, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'more words.'",
+          "rect": [8, 104, 79, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'that comes'",
+          "rect": [8, 284, 67, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'that comes'",
+          "rect": [8, 184, 67, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'the break.'",
+          "rect": [8, 324, 62, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'the break.'",
+          "rect": [8, 224, 62, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'I could'",
+          "rect": [8, 124, 45, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'I could'",
+          "rect": [8, 124, 45, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'words,'",
+          "rect": [8, 84, 43, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'words,'",
+          "rect": [8, 84, 43, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Word,'",
+          "rect": [8, 64, 39, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Word,'",
+          "rect": [8, 64, 39, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'after'",
+          "rect": [8, 304, 29, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'after'",
+          "rect": [8, 204, 29, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [8, 164, 10, 100],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'Word,'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'words,'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'more words.'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'I could'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'write a book'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'about all the stuff'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'that comes'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'after'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'the break.'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multi-layout-one-frame-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multi-layout-one-frame-expected.txt
new file mode 100644
index 0000000..181ae38
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multi-layout-one-frame-expected.txt
@@ -0,0 +1,72 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [162, 8, 158, 22],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'PASSED'",
+          "rect": [168, 11, 53, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASSED'",
+          "rect": [10, 11, 53, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'FAILED'",
+          "rect": [168, 11, 46, 16],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'FAILED'",
+          "rect": [10, 11, 46, 16],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'PASSED'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'PASSED'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multicol-relpos-with-abspos-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multicol-relpos-with-abspos-expected.txt
new file mode 100644
index 0000000..4375e70c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multicol-relpos-with-abspos-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='target'",
+          "rect": [618, 28, 20, 20],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multicol-with-abspos-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multicol-with-abspos-expected.txt
new file mode 100644
index 0000000..5953db4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/multicol-with-abspos-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [0, 580, 80, 20],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='target'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/negative-shadow-box-shrink-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/negative-shadow-box-shrink-expected.txt
new file mode 100644
index 0000000..56af3fdf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/negative-shadow-box-shrink-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='outer'",
+          "rect": [100, 60, 640, 240],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='inner2'",
+          "rect": [100, 200, 600, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='outer'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-change-invalidation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-change-invalidation-expected.txt
new file mode 100644
index 0000000..5d5346f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-change-invalidation-expected.txt
@@ -0,0 +1,37 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow A id='link'",
+          "rect": [43, 83, 754, 30],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutListMarker (anonymous)",
+          "rect": [30, 88, 7, 19],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow A id='link'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutListMarker (anonymous)",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-child-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-child-repaint-expected.txt
new file mode 100644
index 0000000..e001acc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-child-repaint-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='outlined'",
+          "rect": [0, 240, 220, 50],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='outlined'",
+          "rect": [0, 140, 220, 50],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='container'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='outlined'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-clip-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-clip-change-expected.txt
new file mode 100644
index 0000000..ef9ece2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-clip-change-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) A id='link' class='updated'",
+          "rect": [48, 108, 90, 25],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Lorem Ipsum'",
+          "rect": [48, 108, 86, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) A id='link' class='updated'",
+      "reason": "subtree"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "subtree"
+    },
+    {
+      "object": "InlineTextBox 'Lorem Ipsum'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-continuations-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-continuations-expected.txt
new file mode 100644
index 0000000..6cfda16
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/outline-continuations-expected.txt
@@ -0,0 +1,96 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutInline SPAN id='outer'",
+          "rect": [16, 108, 90, 63],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'CONTENTS'",
+          "rect": [18, 150, 86, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'CONTENTS'",
+          "rect": [18, 150, 86, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'CONTENTS'",
+          "rect": [18, 110, 86, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'CONTENTS'",
+          "rect": [18, 110, 86, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'CONTENTS'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'CONTENTS'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline SPAN id='outer'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-delete-line-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-delete-line-expected.txt
new file mode 100644
index 0000000..13da917
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-delete-line-expected.txt
@@ -0,0 +1,51 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Lorem ipsu'",
+          "rect": [8, 74, 72, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [8, 74, 46, 36],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Lorem'",
+          "rect": [8, 74, 46, 36],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'ipsum'",
+          "rect": [8, 74, 46, 36],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='dv'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'Lorem ipsu'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-hidden-in-overflow-hidden-scrolled-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-hidden-in-overflow-hidden-scrolled-expected.txt
new file mode 100644
index 0000000..13fdaf9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-hidden-in-overflow-hidden-scrolled-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='innerWrapper'",
+          "rect": [8, 18, 100, 190],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='innerWrapper'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='red'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='ucp' class='green'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-into-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-into-content-expected.txt
new file mode 100644
index 0000000..f5c68cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-into-content-expected.txt
@@ -0,0 +1,47 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [8, 158, 106, 106],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [8, 8, 106, 106],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 308, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target3'",
+          "rect": [8, 388, 100, 20],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "disappeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-scroll-composited-non-stacking-child-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-scroll-composited-non-stacking-child-expected.txt
new file mode 100644
index 0000000..917cf6cc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-scroll-composited-non-stacking-child-expected.txt
@@ -0,0 +1,61 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='scroller'",
+          "rect": [18, 60, 310, 200],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='scroller'",
+          "rect": [18, 60, 310, 200],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='back'",
+          "rect": [93, 75, 180, 100],
+          "reason": "subtree"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [308, 65, 15, 175],
+          "reason": "scroll control"
+        }
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='icon'",
+      "bounds": [40, 40],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='scroller'",
+      "reason": "subtree"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='list'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='commit'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='back'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-show-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-show-expected.txt
new file mode 100644
index 0000000..20e3233
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-show-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 2016],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='relative green'",
+          "rect": [108, 108, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='overflowParent' class='absolute green'",
+          "rect": [8, 8, 100, 100],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='overflowParent' class='absolute green'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/padding-keeping-content-size-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/padding-keeping-content-size-expected.txt
new file mode 100644
index 0000000..f419e23
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/padding-keeping-content-size-expected.txt
@@ -0,0 +1,47 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='target2' class='outer'",
+          "rect": [0, 200, 140, 140],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='target1' class='outer'",
+          "rect": [0, 0, 140, 140],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='inner'",
+          "rect": [20, 220, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='inner'",
+          "rect": [0, 200, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='target1' class='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='target2' class='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='inner'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/padding-keeping-visual-size-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/padding-keeping-visual-size-expected.txt
new file mode 100644
index 0000000..6758b023
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/padding-keeping-visual-size-expected.txt
@@ -0,0 +1,42 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='target2' class='outer'",
+          "rect": [0, 200, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='inner'",
+          "rect": [0, 200, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='target1' class='outer'",
+          "rect": [0, 0, 100, 100],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='target1' class='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='target2' class='outer'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='inner'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/positioned-great-grandparent-change-location-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/positioned-great-grandparent-change-location-expected.txt
new file mode 100644
index 0000000..0821600c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/positioned-great-grandparent-change-location-expected.txt
@@ -0,0 +1,63 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [100, 200, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [100, 100, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'Target'",
+          "rect": [100, 200, 40, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'Target'",
+          "rect": [100, 100, 40, 19],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='great-grandparent'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Target'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/positioned-list-offset-change-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/positioned-list-offset-change-repaint-expected.txt
new file mode 100644
index 0000000..9c1bda0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/positioned-list-offset-change-repaint-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutImage IMG",
+          "rect": [8, 64, 214, 232],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage IMG",
+          "rect": [8, 64, 114, 232],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) UL id='list'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutListItem (positioned) LI",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutListMarker (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage IMG",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/push-block-with-first-line-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/push-block-with-first-line-expected.txt
new file mode 100644
index 0000000..b07897e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/push-block-with-first-line-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "RootInlineBox",
+          "rect": [8, 68, 784, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "RootInlineBox",
+          "rect": [8, 8, 784, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'JOCULAR'",
+          "rect": [8, 68, 140, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'JOCULAR'",
+          "rect": [8, 8, 140, 20],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='spacer'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='test'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'JOCULAR'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/quotes-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/quotes-expected.txt
new file mode 100644
index 0000000..ba0c56e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/quotes-expected.txt
@@ -0,0 +1,97 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'quote 2'",
+          "rect": [17, 28, 47, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'quote 2'",
+          "rect": [16, 28, 47, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\u003E'",
+          "rect": [64, 28, 9, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox '\u003C'",
+          "rect": [8, 28, 9, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox '}'",
+          "rect": [63, 28, 8, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '{'",
+          "rect": [8, 28, 8, 19],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutInline Q id='q2' class='q-changed'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutInline \u003Cpseudo:before\u003E",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutQuote (anonymous)",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTextFragment (anonymous)",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '{'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'quote 2'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutInline \u003Cpseudo:after\u003E",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutQuote (anonymous)",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTextFragment (anonymous)",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '}'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/reflection-repaint-test-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/reflection-repaint-test-expected.txt
new file mode 100644
index 0000000..832b3bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/reflection-repaint-test-expected.txt
@@ -0,0 +1,46 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [22, 50, 226, 167],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [23, 51, 72, 110],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'FAIL'",
+          "rect": [23, 51, 69, 109],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/remove-inline-after-layout-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/remove-inline-after-layout-expected.txt
new file mode 100644
index 0000000..8efc46d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/remove-inline-after-layout-expected.txt
@@ -0,0 +1,69 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'This span should disappear.\n'",
+          "rect": [112, 193, 179, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [290, 108, 101, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [112, 108, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 108, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [108, 193, 4, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [108, 193, 4, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/remove-inline-block-descendant-of-flex-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/remove-inline-block-descendant-of-flex-expected.txt
new file mode 100644
index 0000000..905cffd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/remove-inline-block-descendant-of-flex-expected.txt
@@ -0,0 +1,54 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='inline-block-2' class='item'",
+          "rect": [0, 200, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='inline-block-1' class='item'",
+          "rect": [0, 100, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='inline-block-2' class='item'",
+          "rect": [0, 100, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inline-block-2' class='item'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-descandant-on-ancestor-layer-move-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-descandant-on-ancestor-layer-move-expected.txt
new file mode 100644
index 0000000..a053359
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-descandant-on-ancestor-layer-move-expected.txt
@@ -0,0 +1,63 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV",
+          "rect": [428, 38, 300, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV",
+          "rect": [28, 38, 300, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'There should only be one copy of this text.'",
+          "rect": [428, 38, 270, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'There should only be one copy of this text.'",
+          "rect": [28, 38, 270, 19],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='container'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'There should only be one copy of this text.'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt
new file mode 100644
index 0000000..2841872
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-during-scroll-with-zoom-expected.txt
@@ -0,0 +1,72 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutView #document",
+          "rect": [3, 65, 235, 235],
+          "reason": "subtree"
+        },
+        {
+          "object": "HorizontalScrollbar",
+          "rect": [3, 300, 235, 15],
+          "reason": "scroll control"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [3, 65, 225, 225],
+          "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'scroll me'",
+          "rect": [3, 65, 55, 17],
+          "reason": "subtree"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [238, 65, 15, 235],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "subtree"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "subtree"
+    },
+    {
+      "object": "InlineTextBox 'scroll me'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-table-row-in-composited-document-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-table-row-in-composited-document-expected.txt
new file mode 100644
index 0000000..fd3d3d44
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-table-row-in-composited-document-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [256, 306, 204, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [256, 156, 204, 104],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD id='target'",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
new file mode 100644
index 0000000..23b7f3f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
@@ -0,0 +1,45 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutImage IMG",
+          "rect": [8, 8, 194, 232],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage IMG",
+          "rect": [8, 8, 114, 232],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV id='shiftMe'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) (floating) DIV class='imgContainer'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage IMG",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/resize-scrollable-iframe-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/resize-scrollable-iframe-expected.txt
new file mode 100644
index 0000000..8a6e599
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/resize-scrollable-iframe-expected.txt
@@ -0,0 +1,77 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutView #document",
+          "rect": [8, 193, 285, 200],
+          "reason": "incremental"
+        },
+        {
+          "object": "HorizontalScrollbar",
+          "rect": [8, 393, 285, 15],
+          "reason": "scroll control"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [93, 108, 200, 285],
+          "reason": "incremental"
+        },
+        {
+          "object": "HorizontalScrollbar",
+          "rect": [8, 193, 85, 15],
+          "reason": "scroll control"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [293, 108, 15, 285],
+          "reason": "scroll control"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [93, 108, 15, 85],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutIFrame IFRAME id='iframe'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "geometry"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "geometry"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/resize-with-border-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/resize-with-border-expected.txt
new file mode 100644
index 0000000..f4ed6995
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/resize-with-border-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='div'",
+          "rect": [100, 100, 420, 220],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='div'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/ruby-flipped-blocks-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/ruby-flipped-blocks-expected.txt
new file mode 100644
index 0000000..545d1c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/ruby-flipped-blocks-expected.txt
@@ -0,0 +1,84 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'b'",
+          "rect": [8, 28, 20, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'b'",
+          "rect": [8, 28, 20, 20],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'a'",
+          "rect": [8, 8, 20, 20],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'a'",
+          "rect": [8, 8, 20, 20],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'c'",
+          "rect": [28, 33, 10, 10],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'a'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutRubyRun (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'c'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutRubyBase (anonymous)",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'b'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-in-clipped-layer-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-in-clipped-layer-expected.txt
new file mode 100644
index 0000000..e224742
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-in-clipped-layer-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 2016],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='relative green'",
+          "rect": [100, 150, 100, 100],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='moveMe' class='absolute clipped'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='relative green'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='absolute red'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-inside-table-cell-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-inside-table-cell-expected.txt
new file mode 100644
index 0000000..d449ac9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-inside-table-cell-expected.txt
@@ -0,0 +1,51 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableCell (relative positioned) TD id='cellToScroll' class='relative'",
+          "rect": [312, 112, 454, 469],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutTableCell (relative positioned) TD id='cellToScroll' class='relative'",
+          "rect": [312, 112, 454, 469],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='absolute green'",
+          "rect": [314, 114, 435, 450],
+          "reason": "appeared"
+        },
+        {
+          "object": "HorizontalScrollbar",
+          "rect": [314, 564, 435, 15],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell (relative positioned) TD id='cellToScroll' class='relative'",
+      "reason": "subtree"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='relative red'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='absolute green'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-relative-table-inside-table-cell-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-relative-table-inside-table-cell-expected.txt
new file mode 100644
index 0000000..8c10c7f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scroll-relative-table-inside-table-cell-expected.txt
@@ -0,0 +1,51 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1566, 1781],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableCell (relative positioned) TD id='cellToScroll' class='relative'",
+          "rect": [1112, 1312, 454, 469],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutTableCell (relative positioned) TD id='cellToScroll' class='relative'",
+          "rect": [1112, 1312, 454, 469],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='absolute green'",
+          "rect": [1114, 1314, 435, 450],
+          "reason": "appeared"
+        },
+        {
+          "object": "HorizontalScrollbar",
+          "rect": [1114, 1764, 435, 15],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell (relative positioned) TD id='cellToScroll' class='relative'",
+      "reason": "subtree"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='relative red'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='absolute green'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scrollbar-invalidation-on-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scrollbar-invalidation-on-resize-expected.txt
new file mode 100644
index 0000000..986851e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scrollbar-invalidation-on-resize-expected.txt
@@ -0,0 +1,51 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='scrollable'",
+          "rect": [8, 50, 300, 100],
+          "reason": "full"
+        },
+        {
+          "object": "HorizontalScrollbar",
+          "rect": [8, 135, 285, 15],
+          "reason": "scroll control"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [293, 50, 15, 85],
+          "reason": "scroll control"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [93, 50, 15, 85],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='scrollable'",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='scrollable'",
+      "reason": "geometry"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scrollbar-invalidation-on-resize-with-border-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scrollbar-invalidation-on-resize-with-border-expected.txt
new file mode 100644
index 0000000..06fda6f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/scrollbar-invalidation-on-resize-with-border-expected.txt
@@ -0,0 +1,56 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='scrollable'",
+          "rect": [8, 50, 320, 120],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='scrollable'",
+          "rect": [8, 50, 320, 120],
+          "reason": "geometry"
+        },
+        {
+          "object": "HorizontalScrollbar",
+          "rect": [18, 145, 285, 15],
+          "reason": "scroll control"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [303, 60, 15, 85],
+          "reason": "scroll control"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [103, 60, 15, 85],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='scrollable'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV id='scrollable'",
+      "reason": "geometry"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/search-field-cancel-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/search-field-cancel-expected.txt
new file mode 100644
index 0000000..6bbd037
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/search-field-cancel-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='inner-editor'",
+          "rect": [11, 47, 58, 16],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'some text'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/select-option-background-color-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/select-option-background-color-expected.txt
new file mode 100644
index 0000000..279f4414
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/select-option-background-color-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutListBox SELECT",
+          "rect": [1, 37, 13, 17],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow OPTION id='option'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selected-replaced-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selected-replaced-expected.txt
new file mode 100644
index 0000000..09eec4b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selected-replaced-expected.txt
@@ -0,0 +1,37 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
+          "rect": [8, 132, 214, 232],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
+          "rect": [8, 52, 214, 232],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (anonymous)",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection-change-in-iframe-with-relative-parent-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection-change-in-iframe-with-relative-parent-expected.txt
new file mode 100644
index 0000000..f177df55
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection-change-in-iframe-with-relative-parent-expected.txt
@@ -0,0 +1,117 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Bazz'",
+          "rect": [18, 238, 40, 10],
+          "reason": "selection"
+        },
+        {
+          "object": "InlineTextBox 'Bar'",
+          "rect": [18, 218, 30, 10],
+          "reason": "selection"
+        },
+        {
+          "object": "InlineTextBox 'Foo'",
+          "rect": [18, 198, 30, 10],
+          "reason": "selection"
+        },
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [58, 238, 10, 10],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [48, 218, 10, 10],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [48, 198, 10, 10],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "selection"
+    },
+    {
+      "object": "InlineTextBox 'Foo'",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "selection"
+    },
+    {
+      "object": "InlineTextBox 'Bar'",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "selection"
+    },
+    {
+      "object": "InlineTextBox 'Bazz'",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-expected.txt
new file mode 100644
index 0000000..ef88878
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'xx'",
+          "rect": [8, 8, 48, 33],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'y'",
+          "rect": [8, 8, 48, 33],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='container'",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'xx'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'y'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-lr-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-lr-expected.txt
new file mode 100644
index 0000000..52f24c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-lr-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'xx'",
+          "rect": [8, 8, 33, 48],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'y'",
+          "rect": [8, 8, 33, 48],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='container'",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'xx'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'y'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-rl-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-rl-expected.txt
new file mode 100644
index 0000000..f4abc4b0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/invalidation-rect-includes-newline-for-vertical-rl-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'xx'",
+          "rect": [7, 8, 33, 48],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'y'",
+          "rect": [7, 8, 33, 48],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='container'",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'xx'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'y'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..0f6d9e5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -0,0 +1,62 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl INPUT id='target'",
+          "rect": [7, 7, 66, 24],
+          "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [10, 11, 59, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [10, 11, 59, 16],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutTextControl INPUT id='target'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "subtree"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "subtree"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "subtree"
+    },
+    {
+      "object": "InlineTextBox 'test test test'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..e67738b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
@@ -0,0 +1,62 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl INPUT id='target'",
+          "rect": [7, 7, 66, 24],
+          "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [10, 11, 60, 16],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [10, 11, 60, 16],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "selection"
+    },
+    {
+      "object": "LayoutTextControl INPUT id='target'",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "subtree"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "subtree"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "subtree"
+    },
+    {
+      "object": "InlineTextBox 'test test test'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
new file mode 100644
index 0000000..9595e5f0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
@@ -0,0 +1,79 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [785, 836],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 742, 785, 94],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutIFrame IFRAME id='iframe'",
+          "rect": [8, 92, 732, 94],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutImage IMG",
+          "rect": [58, 236, 489, 537],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage IMG",
+          "rect": [58, 142, 489, 537],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutIFrame IFRAME id='iframe'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='relative'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='relative paddingTop'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage IMG",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
new file mode 100644
index 0000000..24890f1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
@@ -0,0 +1,59 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [785, 742],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 742, 785, 99],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutIFrame IFRAME id='iframe'",
+          "rect": [8, 92, 732, 94],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutImage IMG",
+          "rect": [58, 241, 489, 537],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage IMG",
+          "rect": [58, 142, 489, 537],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='relative'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='relative paddingTop'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutImage IMG",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacked-diacritics-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacked-diacritics-expected.txt
new file mode 100644
index 0000000..11fb4a3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacked-diacritics-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox '\u1EA6\u1EA4\u1EAA\u1EA8\u1EB0'",
+          "rect": [19, 168, 130, 41],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='stacked'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacking-context-lost-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacking-context-lost-expected.txt
new file mode 100644
index 0000000..a87502f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacking-context-lost-expected.txt
@@ -0,0 +1,31 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV id='outer'",
+      "position": [278, 278],
+      "bounds": [100, 100],
+      "contentsOpaque": false,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV id='outer'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV id='outer'",
+      "reason": "background"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-layoutstate-transform-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-layoutstate-transform-expected.txt
new file mode 100644
index 0000000..01ebe44
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-layoutstate-transform-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='layoutroot'",
+          "rect": [28, 48, 200, 200],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [128, 48, 100, 200],
+          "reason": "incremental"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV class='layoutroot'",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "incremental"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-root-clip-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-root-clip-2-expected.txt
new file mode 100644
index 0000000..3e9d2376
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-root-clip-2-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [8, 8, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [8, 8, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (floating) DIV",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-root-clip-3-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-root-clip-3-expected.txt
new file mode 100644
index 0000000..e73e25c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/subtree-root-clip-3-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [15, 60, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [15, 60, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/absolute-sized-document-no-scrollbars-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/absolute-sized-document-no-scrollbars-expected.txt
new file mode 100644
index 0000000..47647db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/absolute-sized-document-no-scrollbars-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [-1, -1, 578, 434],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/add-background-property-on-root-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/add-background-property-on-root-expected.txt
new file mode 100644
index 0000000..375dd5a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/add-background-property-on-root-expected.txt
@@ -0,0 +1,37 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 8, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [8, 8, 100, 100],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "background"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/animate-fill-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/animate-fill-expected.txt
new file mode 100644
index 0000000..478016e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/animate-fill-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect id='rect'",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect id='rect'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/append-text-node-to-tspan-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/append-text-node-to-tspan-expected.txt
new file mode 100644
index 0000000..70aab79
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/append-text-node-to-tspan-expected.txt
@@ -0,0 +1,150 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PA'",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PA'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'SS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan id='modify'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PA'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'SS'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/circle-move-invalidation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/circle-move-invalidation-expected.txt
new file mode 100644
index 0000000..1fc3749
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/circle-move-invalidation-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox '\n'",
+          "rect": [8, 295, 552, 55],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGEllipse circle id='c1'",
+          "rect": [196, 196, 76, 76],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGEllipse circle id='c1'",
+          "rect": [46, 46, 76, 76],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGEllipse circle id='c1'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/color-fill-currentColor-and-css-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/color-fill-currentColor-and-css-expected.txt
new file mode 100644
index 0000000..4b194ec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/color-fill-currentColor-and-css-expected.txt
@@ -0,0 +1,38 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGEllipse circle",
+          "rect": [8, 8, 80, 80],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGEllipse circle",
+          "rect": [8, 8, 80, 80],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGEllipse circle",
+          "rect": [8, 8, 80, 80],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGEllipse circle",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGEllipse circle",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/ems-display-none-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/ems-display-none-expected.txt
new file mode 100644
index 0000000..6f7bdfd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/ems-display-none-expected.txt
@@ -0,0 +1,72 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [50, 54, 572, 86],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGText text id='text'",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan id='tspan'",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'Two lines of text should be visible.'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'Two lines of text should be visible.'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/exs-display-none-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/exs-display-none-expected.txt
new file mode 100644
index 0000000..03cb8fbe8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/exs-display-none-expected.txt
@@ -0,0 +1,72 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [50, 50, 572, 82],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGText text id='text'",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan id='tspan'",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'Two lines of text should be visible.'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'Two lines of text should be visible.'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-add-to-document-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
new file mode 100644
index 0000000..906fa2d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
new file mode 100644
index 0000000..33b1e3c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
new file mode 100644
index 0000000..906fa2d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
new file mode 100644
index 0000000..aad1a02
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
new file mode 100644
index 0000000..33b1e3c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-style-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-style-change-expected.txt
new file mode 100644
index 0000000..906fa2d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/feImage-target-style-change-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 110, 110],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/filter-child-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/filter-child-repaint-expected.txt
new file mode 100644
index 0000000..74d764c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/filter-child-repaint-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGContainer g",
+          "rect": [0, 0, 106, 106],
+          "reason": "SVG resource change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGRect rect id='poke'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/inner-svg-change-viewPort-relative-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/inner-svg-change-viewPort-relative-expected.txt
new file mode 100644
index 0000000..9c90f2cb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/inner-svg-change-viewPort-relative-expected.txt
@@ -0,0 +1,67 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [0, 0, 200, 200],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'right-aligned text'",
+          "rect": [93, 85, 107, 19],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [180, 60, 20, 20],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [80, 60, 20, 20],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGViewportContainer svg id='inner'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGText text",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'right-aligned text'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/invalidate-on-child-layout-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/invalidate-on-child-layout-expected.txt
new file mode 100644
index 0000000..58eb6ab4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/invalidate-on-child-layout-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGContainer g",
+          "rect": [40, 40, 120, 120],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGContainer g",
+          "rect": [0, 0, 110, 110],
+          "reason": "SVG resource change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGEllipse circle id='circle'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-gradient-creation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-gradient-creation-expected.txt
new file mode 100644
index 0000000..e69fd79
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-gradient-creation-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Gradient on fill'",
+          "rect": [33, 22, 697, 196],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Gradient on fill'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-pattern-and-object-creation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-pattern-and-object-creation-expected.txt
new file mode 100644
index 0000000..f529ab1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-pattern-and-object-creation-expected.txt
@@ -0,0 +1,72 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg id='svg-root'",
+          "rect": [0, 15, 682, 365],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g id='content'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGText text",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'Pattern on fill'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGText text",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'Pattern on stroke'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGText text",
+      "reason": "appeared"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'Pattern on fill/stroke'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-pattern-creation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-pattern-creation-expected.txt
new file mode 100644
index 0000000..ecfaf2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-late-pattern-creation-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Pattern on fill'",
+          "rect": [33, 22, 622, 196],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Pattern on fill'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-repaint-rect-on-path-with-stroke-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-repaint-rect-on-path-with-stroke-expected.txt
new file mode 100644
index 0000000..de92cb9a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-repaint-rect-on-path-with-stroke-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath path id='path'",
+          "rect": [199, 49, 102, 52],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='path'",
+          "rect": [49, 49, 102, 52],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGPath path id='path'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-image-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-image-expected.txt
new file mode 100644
index 0000000..182ba04
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-image-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [0, 0, 75, 75],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGImage image id='image'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-polygon-changes-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-polygon-changes-expected.txt
new file mode 100644
index 0000000..c6e87f28
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-polygon-changes-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath polygon id='polygon'",
+          "rect": [264, 218, 164, 107],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGPath polygon id='polygon'",
+          "rect": [264, 208, 164, 107],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGPath polygon id='polygon'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-polygon-removal-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-polygon-removal-expected.txt
new file mode 100644
index 0000000..c9bdce0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-polygon-removal-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath polygon id='polygon'",
+          "rect": [264, 258, 165, 67],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGPath polygon id='polygon'",
+          "rect": [264, 218, 164, 107],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGPath polygon id='polygon'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-style-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-style-expected.txt
new file mode 100644
index 0000000..292b460
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-style-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 186, 158, 58],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 126, 158, 58],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 66, 158, 58],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [6, 6, 158, 58],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-transform-addition-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-transform-addition-expected.txt
new file mode 100644
index 0000000..8d4ff95a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-transform-addition-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [264, 218, 164, 107],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGPath polygon id='polygon'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-transform-changes-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-transform-changes-expected.txt
new file mode 100644
index 0000000..8d4ff95a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/js-update-transform-changes-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [264, 218, 164, 107],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGPath polygon id='polygon'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/marker-strokeWidth-changes-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/marker-strokeWidth-changes-expected.txt
new file mode 100644
index 0000000..a7772f8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/marker-strokeWidth-changes-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath path id='path'",
+          "rect": [121, 126, 68, 68],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGPath path id='path'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/marker-viewBox-changes-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/marker-viewBox-changes-expected.txt
new file mode 100644
index 0000000..c970d09
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/marker-viewBox-changes-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath path id='go'",
+          "rect": [89, 94, 100, 100],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGPath path id='go'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-inserted-listitem-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-inserted-listitem-expected.txt
new file mode 100644
index 0000000..59a1417
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-inserted-listitem-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect id='move'",
+          "rect": [28, 38, 10, 10],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGRect rect id='move'",
+          "rect": [18, 18, 10, 10],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect id='move'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-text-node-in-tspan-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-text-node-in-tspan-expected.txt
new file mode 100644
index 0000000..5e55498
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-text-node-in-tspan-expected.txt
@@ -0,0 +1,137 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PA'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan id='modify'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
new file mode 100644
index 0000000..3a643b0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 85, 23],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 85, 23],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 80, 23],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 80, 23],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' Y Z'",
+          "rect": [58, 45, 50, 26],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'X'",
+          "rect": [58, 45, 50, 26],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'X Y Z'",
+          "rect": [58, 45, 50, 16],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text id='target'",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'A'",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' B C'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGText text id='source'",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'X Y Z'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/object-sizing-no-width-height-change-content-box-size-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/object-sizing-no-width-height-change-content-box-size-expected.txt
new file mode 100644
index 0000000..b6ce25f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/object-sizing-no-width-height-change-content-box-size-expected.txt
@@ -0,0 +1,59 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 8, 402, 402],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutEmbeddedObject object",
+          "rect": [9, 9, 400, 400],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [9, 9, 400, 400],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGEllipse circle",
+          "rect": [169, 169, 80, 80],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutEmbeddedObject object",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGEllipse circle",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/outline-offset-text-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/outline-offset-text-expected.txt
new file mode 100644
index 0000000..74f96179
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/outline-offset-text-expected.txt
@@ -0,0 +1,51 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Foo'",
+          "rect": [103, 26, 160, 113],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [103, 26, 160, 113],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'Foo'",
+          "rect": [63, 26, 160, 113],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGText text",
+          "rect": [63, 26, 160, 113],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Foo'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/pending-resource-after-removal-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/pending-resource-after-removal-expected.txt
new file mode 100644
index 0000000..9d42caf3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/pending-resource-after-removal-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect id='shape'",
+          "rect": [0, 0, 200, 200],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGRect rect id='shape'",
+          "rect": [0, 0, 200, 200],
+          "reason": "SVG resource change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect id='shape'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGRect rect id='shape'",
+      "reason": "SVG resource change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-content-expected.txt
new file mode 100644
index 0000000..ac4e786
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-content-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 72, 402, 402],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGRect rect id='targetRect'",
+          "rect": [9, 73, 400, 400],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRect rect id='targetRect'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-content-with-resources-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-content-with-resources-expected.txt
new file mode 100644
index 0000000..d99dcae
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-content-with-resources-expected.txt
@@ -0,0 +1,46 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 72, 402, 402],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGEllipse circle",
+          "rect": [47, 111, 324, 324],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGEllipse circle",
+          "rect": [8, 154, 170, 238],
+          "reason": "SVG resource change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGEllipse circle",
+      "reason": "SVG resource change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-deep-shadow-tree-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-deep-shadow-tree-content-expected.txt
new file mode 100644
index 0000000..2b2fd71d9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-deep-shadow-tree-content-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 72, 402, 402],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGRect rect id='targetRect'",
+          "rect": [209, 273, 200, 200],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer g id='targetUse'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRect rect id='targetRect'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
new file mode 100644
index 0000000..c4977ee
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [6, 2, 788, 595],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-inner-svg-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-inner-svg-expected.txt
new file mode 100644
index 0000000..1d20b6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-inner-svg-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 52, 402, 402],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-shadow-tree-content-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-shadow-tree-content-expected.txt
new file mode 100644
index 0000000..c988ee54
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-shadow-tree-content-expected.txt
@@ -0,0 +1,62 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 72, 402, 402],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGRect rect id='targetRect'",
+          "rect": [209, 273, 200, 200],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutSVGRect rect id='targetRect1'",
+          "rect": [9, 73, 200, 200],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRect rect id='targetRect'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer g id='targetUse'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRect rect id='targetRect1'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-shadow-tree-content-with-symbol-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-shadow-tree-content-with-symbol-expected.txt
new file mode 100644
index 0000000..2668abaa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-shadow-tree-content-with-symbol-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 72, 402, 402],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-use-on-symbol-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-use-on-symbol-expected.txt
new file mode 100644
index 0000000..0fb6d08
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-use-on-symbol-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 52, 402, 402],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-use-without-attributes-on-symbol-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-use-without-attributes-on-symbol-expected.txt
new file mode 100644
index 0000000..0fb6d08
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/relative-sized-use-without-attributes-on-symbol-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow div id='contentBox'",
+          "rect": [8, 52, 402, 402],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow div id='contentBox'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-background-property-on-root-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-background-property-on-root-expected.txt
new file mode 100644
index 0000000..3914539
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-background-property-on-root-expected.txt
@@ -0,0 +1,37 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 8, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGRoot svg",
+          "rect": [8, 8, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "background"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutSVGRoot svg",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-text-node-from-tspan-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-text-node-from-tspan-expected.txt
new file mode 100644
index 0000000..4d8de5f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-text-node-from-tspan-expected.txt
@@ -0,0 +1,115 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'FAIL'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 380],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 180, 310, 260],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 180, 310, 260],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 180, 310, 260],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan id='modify'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-tspan-from-text-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-tspan-from-text-expected.txt
new file mode 100644
index 0000000..b7e0895a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/remove-tspan-from-text-expected.txt
@@ -0,0 +1,107 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 260],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 260],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox ' '",
+          "rect": [10, 60, 310, 260],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'FAIL'",
+          "rect": [10, 60, 310, 260],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 260],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 260],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 260],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [10, 60, 310, 260],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text id='text'",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox ' '",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-non-scaling-stroke-text-decoration-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-non-scaling-stroke-text-decoration-expected.txt
new file mode 100644
index 0000000..933a84d3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-non-scaling-stroke-text-decoration-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Hello'",
+          "rect": [9, 13, 48, 28],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGText text id='t'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'Hello'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-non-scaling-stroke-text-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-non-scaling-stroke-text-expected.txt
new file mode 100644
index 0000000..933a84d3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-non-scaling-stroke-text-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Hello'",
+          "rect": [9, 13, 48, 28],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGText text id='t'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'Hello'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-on-image-bounds-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-on-image-bounds-change-expected.txt
new file mode 100644
index 0000000..3eb554d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-on-image-bounds-change-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGImage image id='target'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGImage image id='target'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-paintorder-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-paintorder-expected.txt
new file mode 100644
index 0000000..f33c38b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/repaint-paintorder-expected.txt
@@ -0,0 +1,67 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath polygon",
+          "rect": [456, 165, 134, 134],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGPath polygon id='t3'",
+          "rect": [319, 165, 134, 134],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGPath polygon",
+          "rect": [183, 165, 134, 134],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutSVGPath polygon",
+          "rect": [47, 165, 134, 134],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer use id='t1'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGViewportContainer svg id='poly'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGPath polygon",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGContainer use id='t2'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGViewportContainer svg id='poly'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGPath polygon",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGPath polygon id='t3'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutSVGPath polygon",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/resource-client-removal-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/resource-client-removal-expected.txt
new file mode 100644
index 0000000..6f91ebb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/resource-client-removal-expected.txt
@@ -0,0 +1,250 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGPath path id='hp'",
+          "rect": [0, 0, 100, 100],
+          "reason": "SVG resource change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGContainer g id='inneruse'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGPath path id='hp'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/stroke-opacity-update-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/stroke-opacity-update-expected.txt
new file mode 100644
index 0000000..b0a9dd1b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/stroke-opacity-update-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGEllipse circle",
+          "rect": [-1, -1, 205, 205],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGEllipse circle",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/svg-background-partial-redraw-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/svg-background-partial-redraw-expected.txt
new file mode 100644
index 0000000..460deab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/svg-background-partial-redraw-expected.txt
@@ -0,0 +1,18 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV id='revealer'",
+          "rect": [9, 153, 200, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/svgsvgelement-repaint-children-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/svgsvgelement-repaint-children-expected.txt
new file mode 100644
index 0000000..bdaec06d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/svgsvgelement-repaint-children-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGPath path id='path'",
+          "rect": [8, 8, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGPath path id='path'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-dom-removal-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-dom-removal-expected.txt
new file mode 100644
index 0000000..f63b372
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-dom-removal-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'This should not be visible'",
+          "rect": [49, 109, 164, 21],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [-1, -1, 22, 22],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g id='objectsToRemove'",
+      "reason": "disappeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-2-expected.txt
new file mode 100644
index 0000000..836a6e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/text-pattern-update-2-expected.txt
@@ -0,0 +1,70 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'X'",
+          "rect": [8, 8, 300, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "InlineTextBox 'Y'",
+          "rect": [8, 8, 300, 100],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "InlineTextBox 'Z'",
+          "rect": [8, 8, 300, 100],
+          "reason": "SVG resource change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "InlineTextBox 'X'",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "InlineTextBox 'Y'",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "InlineTextBox 'Z'",
+      "reason": "SVG resource change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/transform-changed-state-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/transform-changed-state-expected.txt
new file mode 100644
index 0000000..dbb4b40
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/transform-changed-state-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [108, 108, 100, 100],
+          "reason": "full"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [108, 8, 100, 100],
+          "reason": "full"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGContainer g",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRect rect",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-dynamic-positioning-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-dynamic-positioning-expected.txt
new file mode 100644
index 0000000..0ea105b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-dynamic-positioning-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'This text should be at visible at 200,200'",
+          "rect": [200, 185, 251, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'This text should be at visible at 200,200'",
+          "rect": [20, 5, 251, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGText text",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGTSpan tspan id='ts'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'This text should be at visible at 200,200'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-pattern-update-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-pattern-update-expected.txt
new file mode 100644
index 0000000..66dd3ba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/tspan-pattern-update-expected.txt
@@ -0,0 +1,36 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Y'",
+          "rect": [8, 8, 300, 100],
+          "reason": "SVG resource change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGTSpan tspan",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGInlineText #text",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "InlineTextBox 'Y'",
+      "reason": "SVG resource change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/use-disappears-after-style-update-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
new file mode 100644
index 0000000..96aadba0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
@@ -0,0 +1,42 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGContainer use",
+          "rect": [50, 10, 36, 36],
+          "reason": "SVG resource change"
+        },
+        {
+          "object": "LayoutSVGRect rect id='rect'",
+          "rect": [10, 10, 30, 30],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutSVGRect rect id='rect'",
+          "rect": [10, 10, 30, 30],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutSVGRect rect id='rect'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGContainer use",
+      "reason": "SVG resource change"
+    },
+    {
+      "object": "LayoutSVGRect rect id='rect'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/use-setAttribute-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/use-setAttribute-crash-expected.txt
new file mode 100644
index 0000000..5e6cf162
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/svg/use-setAttribute-crash-expected.txt
@@ -0,0 +1,44 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutSVGEllipse svg:circle id='circle'",
+          "rect": [27, 27, 62, 62],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow body",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutSVGRoot svg:svg id='svg'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGContainer svg:use id='use'",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutSVGViewportContainer svg id='symbol'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutSVGEllipse svg:circle id='circle'",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-cell-move-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-cell-move-expected.txt
new file mode 100644
index 0000000..48a42bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-cell-move-expected.txt
@@ -0,0 +1,62 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 326, 60, 60],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 266, 60, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [8, 206, 60, 60],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD id='s'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-cell-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-cell-overflow-expected.txt
new file mode 100644
index 0000000..31c1983a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-cell-overflow-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [111, 9, 100, 100],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-col-background-offset-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-col-background-offset-expected.txt
new file mode 100644
index 0000000..0f441572
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-col-background-offset-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY",
+          "rect": [8, 108, 210, 104],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCol COL id='col'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-outer-border-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-outer-border-expected.txt
new file mode 100644
index 0000000..c1767010
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-outer-border-expected.txt
@@ -0,0 +1,95 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 132, 149],
+          "reason": "appeared"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 132, 149],
+          "reason": "appeared"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 132, 149],
+          "reason": "disappeared"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 132, 149],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 75, 132, 82],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 75, 132, 82],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 8, 132, 82],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 8, 132, 82],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE id='table' class='green'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "appeared"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "appeared"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-row-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-row-expected.txt
new file mode 100644
index 0000000..c1cb78b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-row-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableRow TR id='target' class='green'",
+          "rect": [8, 10, 106, 100],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableRow TR id='target' class='green'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-section-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-section-overflow-expected.txt
new file mode 100644
index 0000000..69c61737
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-section-overflow-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [33, 58, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [33, 8, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-section-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-section-repaint-expected.txt
new file mode 100644
index 0000000..0b2a8f3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-section-repaint-expected.txt
@@ -0,0 +1,199 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 308, 60, 83],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 308, 60, 83],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='red half'",
+          "rect": [8, 248, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='green half'",
+          "rect": [8, 218, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='red half'",
+          "rect": [8, 218, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='green half'",
+          "rect": [8, 188, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='green half'",
+          "rect": [8, 158, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='green half'",
+          "rect": [8, 128, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='red half'",
+          "rect": [8, 128, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='top' class='blue half'",
+          "rect": [8, 98, 60, 30],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutTableCell TD class='red half'",
+          "rect": [8, 98, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='green half'",
+          "rect": [8, 68, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='green half'",
+          "rect": [8, 38, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD class='red half'",
+          "rect": [8, 38, 60, 30],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow CAPTION id='caption1' class='blue half'",
+          "rect": [8, 8, 60, 30],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutTableCell TD class='red half'",
+          "rect": [8, 8, 60, 30],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow CAPTION id='caption1' class='blue half'",
+      "reason": "disappeared"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='red half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='green half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "disappeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='top' class='blue half'",
+      "reason": "disappeared"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='red half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='green half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='green half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='red half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD class='half'",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-shrink-row-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-shrink-row-repaint-expected.txt
new file mode 100644
index 0000000..9441ca19
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table-shrink-row-repaint-expected.txt
@@ -0,0 +1,513 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [785, 850],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 850, 785, 200],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutTableCell TD id='resizeMe'",
+          "rect": [8, 112, 769, 210],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 982, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 922, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 862, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 802, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 782, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 742, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 722, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 682, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 662, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 622, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 602, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 562, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 542, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 502, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 482, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 442, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 422, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 382, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 362, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 322, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 302, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 242, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 182, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [8, 122, 769, 60],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '13'",
+          "rect": [13, 1002, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '12'",
+          "rect": [13, 942, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '11'",
+          "rect": [13, 882, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '10'",
+          "rect": [13, 822, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '13'",
+          "rect": [13, 802, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '12'",
+          "rect": [13, 742, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '11'",
+          "rect": [13, 682, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '10'",
+          "rect": [13, 622, 16, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '9'",
+          "rect": [13, 762, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '8'",
+          "rect": [13, 702, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '7'",
+          "rect": [13, 642, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '6'",
+          "rect": [13, 582, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '9'",
+          "rect": [13, 562, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '5'",
+          "rect": [13, 522, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '8'",
+          "rect": [13, 502, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '4'",
+          "rect": [13, 462, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '7'",
+          "rect": [13, 442, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '3'",
+          "rect": [13, 402, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '6'",
+          "rect": [13, 382, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '2'",
+          "rect": [13, 342, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '5'",
+          "rect": [13, 322, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '4'",
+          "rect": [13, 262, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '3'",
+          "rect": [13, 202, 8, 19],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox '2'",
+          "rect": [13, 142, 8, 19],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutTableCell TD id='resizeMe'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '2'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '3'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '4'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '5'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '6'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '7'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '8'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '9'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '10'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '11'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '12'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox '13'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/border-collapse-change-collapse-to-separate-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/border-collapse-change-collapse-to-separate-expected.txt
new file mode 100644
index 0000000..89964ff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/border-collapse-change-collapse-to-separate-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 212, 108],
+          "reason": "disappeared"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 212, 108],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [118, 10, 108, 108],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [10, 10, 106, 108],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE id='table'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
new file mode 100644
index 0000000..b26b4f97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
@@ -0,0 +1,71 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 212, 108],
+          "reason": "appeared"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 212, 108],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [118, 10, 108, 108],
+          "reason": "disappeared"
+        },
+        {
+          "object": "LayoutTableCell TD",
+          "rect": [10, 10, 106, 108],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE id='table'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-69296-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-69296-expected.txt
new file mode 100644
index 0000000..52524a8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-69296-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableRow TR id='row1'",
+          "rect": [20, 20, 160, 51],
+          "reason": "appeared"
+        },
+        {
+          "object": "LayoutTableRow TR id='row1'",
+          "rect": [20, 20, 160, 51],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableRow TR id='row1'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableRow TR id='row1'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-cell-append-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-cell-append-expected.txt
new file mode 100644
index 0000000..3650eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-cell-append-expected.txt
@@ -0,0 +1,61 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 120, 56],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 120, 56],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR id='row'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "appeared"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "appeared"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-cell-border-width-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-cell-border-width-expected.txt
new file mode 100644
index 0000000..5d1824ef
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-cell-border-width-expected.txt
@@ -0,0 +1,61 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 54],
+          "reason": "style change"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 54],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD id='foo'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD id='foo'",
+      "reason": "style change"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-col-border-width-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-col-border-width-expected.txt
new file mode 100644
index 0000000..9f47ff7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-col-border-width-expected.txt
@@ -0,0 +1,103 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 113, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 113, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 113, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 113, 104],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCol COL id='col'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
new file mode 100644
index 0000000..e90ca8dab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
@@ -0,0 +1,137 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 167, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 167, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 167, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 167, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 167, 104],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 167, 104],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCol COLGROUP id='colgroup'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-row-border-width-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-row-border-width-expected.txt
new file mode 100644
index 0000000..ccc668e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-row-border-width-expected.txt
@@ -0,0 +1,65 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 60, 103],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 60, 103],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR id='row'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-table-border-width-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-table-border-width-expected.txt
new file mode 100644
index 0000000..3399b2c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-table-border-width-expected.txt
@@ -0,0 +1,44 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 60, 54],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE id='tbl'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-tbody-border-width-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
new file mode 100644
index 0000000..9de89c9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
@@ -0,0 +1,141 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 153],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 153],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 153],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 153],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 153],
+          "reason": "geometry"
+        },
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 114, 153],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY id='tbody'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/collapsed-border-cell-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/collapsed-border-cell-resize-expected.txt
new file mode 100644
index 0000000..2e8b30a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/collapsed-border-cell-resize-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "CollapsedBorderValues",
+          "rect": [8, 8, 104, 204],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "CollapsedBorderValues",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-expected.txt
new file mode 100644
index 0000000..01f85057
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-expected.txt
@@ -0,0 +1,35 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY",
+          "rect": [8, 28, 186, 134],
+          "reason": "style change"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD",
+      "position": [96, 118],
+      "bounds": [10, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCol COL id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-initial-empty-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-initial-empty-expected.txt
new file mode 100644
index 0000000..3bc837f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-initial-empty-expected.txt
@@ -0,0 +1,35 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY",
+          "rect": [8, 28, 186, 134],
+          "reason": "appeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD",
+      "position": [96, 118],
+      "bounds": [10, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCol COL id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-span-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-span-expected.txt
new file mode 100644
index 0000000..9928d6e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-span-expected.txt
@@ -0,0 +1,42 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY",
+          "rect": [8, 28, 186, 134],
+          "reason": "style change"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD",
+      "position": [96, 118],
+      "bounds": [10, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    },
+    {
+      "name": "LayoutTableCell TD",
+      "position": [157, 118],
+      "bounds": [9, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCol COL id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-span-initial-empty-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-span-initial-empty-expected.txt
new file mode 100644
index 0000000..a62327a6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-col-span-initial-empty-expected.txt
@@ -0,0 +1,42 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY",
+          "rect": [8, 28, 186, 134],
+          "reason": "appeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD",
+      "position": [96, 118],
+      "bounds": [10, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    },
+    {
+      "name": "LayoutTableCell TD",
+      "position": [157, 118],
+      "bounds": [9, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCol COL id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-colgroup-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-colgroup-expected.txt
new file mode 100644
index 0000000..f255d22
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-colgroup-expected.txt
@@ -0,0 +1,35 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY",
+          "rect": [8, 28, 186, 134],
+          "reason": "style change"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD id='target'",
+      "position": [96, 118],
+      "bounds": [10, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCol COLGROUP id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-colgroup-initial-empty-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-colgroup-initial-empty-expected.txt
new file mode 100644
index 0000000..2b77eef1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-colgroup-initial-empty-expected.txt
@@ -0,0 +1,35 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY",
+          "rect": [8, 28, 186, 134],
+          "reason": "appeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD id='target'",
+      "position": [96, 118],
+      "bounds": [10, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutTableCol COLGROUP id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-section-composited-row-initial-empty-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-section-composited-row-initial-empty-expected.txt
new file mode 100644
index 0000000..7c729cd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/composited-table-background-section-composited-row-initial-empty-expected.txt
@@ -0,0 +1,45 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true
+    },
+    {
+      "name": "LayoutTableSection TBODY id='target'",
+      "position": [8, 28],
+      "bounds": [186, 134],
+      "contentsOpaque": false,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection TBODY id='target'",
+          "rect": [0, 0, 186, 134],
+          "reason": "appeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableRow TR",
+      "position": [34, 118],
+      "bounds": [132, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    },
+    {
+      "name": "LayoutTableCell TD",
+      "position": [96, 118],
+      "bounds": [10, 19],
+      "contentsOpaque": false,
+      "drawsContent": true
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableSection TBODY id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-repaint-percent-size-cell-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-repaint-percent-size-cell-expected.txt
new file mode 100644
index 0000000..d48ab11
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-repaint-percent-size-cell-expected.txt
@@ -0,0 +1,90 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableRow TR",
+          "rect": [0, 292, 106, 236],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTableRow TR",
+          "rect": [0, 100, 106, 190],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutTableRow TR",
+          "rect": [0, 102, 106, 46],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'ROW2'",
+          "rect": [3, 400, 46, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'ROW1'",
+          "rect": [3, 161, 46, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'ROW2'",
+          "rect": [3, 115, 46, 19],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'ROW1'",
+          "rect": [3, 66, 46, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'ROW1'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableRow TR",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'ROW2'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-repaint-vertical-align-cell-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-repaint-vertical-align-cell-expected.txt
new file mode 100644
index 0000000..c9da9a68
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-repaint-vertical-align-cell-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'MIDDLE'",
+          "rect": [3, 280, 63, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'MIDDLE'",
+          "rect": [3, 90, 63, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'MIDDLE'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-row-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-row-repaint-expected.txt
new file mode 100644
index 0000000..16e0b7c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/resize-table-row-repaint-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'MIDDLE'",
+          "rect": [3, 192, 63, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'MIDDLE'",
+          "rect": [3, 142, 63, 19],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableCell TD",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'MIDDLE'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/row-change-background-rowspan-cell-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/row-change-background-rowspan-cell-expected.txt
new file mode 100644
index 0000000..cd4392aa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/table/row-change-background-rowspan-cell-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableRow TR id='target'",
+          "rect": [8, 298, 110, 106],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTableRow TR id='target'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-append-dirty-lines-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-append-dirty-lines-expected.txt
new file mode 100644
index 0000000..6643170
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-append-dirty-lines-expected.txt
@@ -0,0 +1,92 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Curabitur a velit'",
+          "rect": [7, 44, 757, 139],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'Curabitur a velit.'",
+          "rect": [7, 44, 757, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse placerat. Morbi tristique. Mauris eu lacus sed felis'",
+          "rect": [7, 44, 757, 139],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'fermentum ut, tortor. Sed rhoncus. Quisque enim metus, luctus tincidunt, vestibulum eu, vestibulum eu, libero. Mauris'",
+          "rect": [7, 44, 757, 139],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'feugiat molestie, mi lorem bibendum leo, ac gravida orci nunc nec nulla. Nunc nunc lorem, rhoncus et, rutrum ac,'",
+          "rect": [7, 44, 757, 139],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'laoreet feugiat. Phasellus mollis pulvinar mi. Etiam ut neque sed eros egestas laoreet. Vestibulum ullamcorper, nulla non'",
+          "rect": [7, 44, 757, 139],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'pellentesque cursus. Proin vitae nulla. Vivamus in ipsum. Etiam mi. Nam malesuada purus in sem. Sed eget elit vel erat'",
+          "rect": [7, 44, 757, 139],
+          "reason": "full"
+        },
+        {
+          "object": "InlineTextBox 'sagittis aliquam nunc. Nullam pharetra molestie eros. Donec tempus purus ut ligula. Phasellus non nisl. Etiam eu mauris.'",
+          "rect": [7, 44, 757, 139],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'sagittis aliquam nunc. Nullam pharetra molestie eros. Donec tempus purus ut ligula. Phasellus non nisl. Etiam eu mauris.'",
+          "rect": [7, 44, 757, 139],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse placerat. Morbi tristique. Mauris eu lacus sed felis'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'pellentesque cursus. Proin vitae nulla. Vivamus in ipsum. Etiam mi. Nam malesuada purus in sem. Sed eget elit vel erat'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'laoreet feugiat. Phasellus mollis pulvinar mi. Etiam ut neque sed eros egestas laoreet. Vestibulum ullamcorper, nulla non'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'feugiat molestie, mi lorem bibendum leo, ac gravida orci nunc nec nulla. Nunc nunc lorem, rhoncus et, rutrum ac,'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'fermentum ut, tortor. Sed rhoncus. Quisque enim metus, luctus tincidunt, vestibulum eu, vestibulum eu, libero. Mauris'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'sagittis aliquam nunc. Nullam pharetra molestie eros. Donec tempus purus ut ligula. Phasellus non nisl. Etiam eu mauris.'",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'Curabitur a velit.'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-match-document-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-match-document-change-expected.txt
new file mode 100644
index 0000000..c14d6b7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-match-document-change-expected.txt
@@ -0,0 +1,59 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Find-in-page 'findme', then click here)'",
+          "rect": [18, 130, 251, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'To be changed: findme (Manual testing:'",
+          "rect": [18, 130, 251, 39],
+          "reason": "disappeared"
+        },
+        {
+          "object": "InlineTextBox 'After change'",
+          "rect": [18, 130, 82, 19],
+          "reason": "appeared"
+        },
+        {
+          "object": "VerticalScrollbar",
+          "rect": [295, 102, 15, 400],
+          "reason": "scroll control"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='to-be-changed'",
+      "reason": "full"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "full"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "appeared"
+    },
+    {
+      "object": "InlineTextBox 'After change'",
+      "reason": "appeared"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-selection-rect-in-overflow-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-selection-rect-in-overflow-2-expected.txt
new file mode 100644
index 0000000..fcea650
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-selection-rect-in-overflow-2-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Should have blue, not gray, highlight'",
+          "rect": [18, 18, 234, 19],
+          "reason": "selection"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "selection"
+    },
+    {
+      "object": "InlineTextBox 'Should have blue, not gray, highlight'",
+      "reason": "selection"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-selection-rect-in-overflow-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-selection-rect-in-overflow-expected.txt
new file mode 100644
index 0000000..ce4ca344
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/text-selection-rect-in-overflow-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Should have green background'",
+          "rect": [8, 8, 198, 19],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "geometry"
+    },
+    {
+      "object": "InlineTextBox 'Should have green background'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/textarea-resize-property-change-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/textarea-resize-property-change-expected.txt
new file mode 100644
index 0000000..d005d50
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/textarea-resize-property-change-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl (positioned) TEXTAREA id='textarea'",
+          "rect": [0, 50, 106, 106],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTextControl (positioned) TEXTAREA id='textarea'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/textarea-set-disabled-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/textarea-set-disabled-expected.txt
new file mode 100644
index 0000000..380908f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/textarea-set-disabled-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl TEXTAREA",
+          "rect": [8, 8, 106, 106],
+          "reason": "subtree"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTextControl TEXTAREA",
+      "reason": "subtree"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='inner-editor'",
+      "reason": "subtree"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform-disable-layoutstate-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform-disable-layoutstate-expected.txt
new file mode 100644
index 0000000..18f64154
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform-disable-layoutstate-expected.txt
@@ -0,0 +1,33 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [58, 138, 500, 63],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [58, 256, 500, 45],
+          "reason": "incremental"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform-layout-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform-layout-repaint-expected.txt
new file mode 100644
index 0000000..58c43aab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform-layout-repaint-expected.txt
@@ -0,0 +1,46 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [40, 50, 208, 118],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'PASS'",
+          "rect": [52, 51, 43, 32],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'FAIL'",
+          "rect": [52, 51, 40, 31],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "full"
+    },
+    {
+      "object": "InlineTextBox 'PASS'",
+      "reason": "full"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align-length2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align-length2-expected.txt
new file mode 100644
index 0000000..3105b9a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align-length2-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [300, 0, 200, 200],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [0, 0, 200, 200],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [200, 80, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [200, 50, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='other'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='other'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align1-expected.txt
new file mode 100644
index 0000000..b60671b0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align1-expected.txt
@@ -0,0 +1,60 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [20, 0, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [120, 80, 20, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [120, 33, 20, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [0, 80, 20, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [0, 33, 20, 20],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='other'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='other'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align2-expected.txt
new file mode 100644
index 0000000..bfaa692
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-align2-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [300, 0, 200, 200],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV class='other'",
+          "rect": [0, 0, 200, 200],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [200, 147, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV id='target'",
+          "rect": [200, 100, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "geometry"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='other'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV id='target'",
+      "reason": "geometry"
+    },
+    {
+      "object": "LayoutBlockFlow DIV class='other'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-rl-as-paint-container-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-rl-as-paint-container-expected.txt
new file mode 100644
index 0000000..763c25e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/vertical-rl-as-paint-container-expected.txt
@@ -0,0 +1,62 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true
+    },
+    {
+      "name": "LayoutBlockFlow DIV id='target'",
+      "position": [528, 8],
+      "bounds": [80, 340],
+      "contentsOpaque": false,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "InlineTextBox 'Lorem ipsum dolor'",
+          "rect": [0, 0, 80, 340],
+          "reason": "style change"
+        },
+        {
+          "object": "InlineTextBox 'adipiscing elit.'",
+          "rect": [0, 0, 80, 340],
+          "reason": "style change"
+        },
+        {
+          "object": "InlineTextBox 'consectetur'",
+          "rect": [0, 0, 80, 340],
+          "reason": "style change"
+        },
+        {
+          "object": "InlineTextBox 'sit amet,'",
+          "rect": [0, 0, 80, 340],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'Lorem ipsum dolor'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'sit amet,'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'consectetur'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox 'adipiscing elit.'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/viewport-gradient-background-html-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/viewport-gradient-background-html-resize-expected.txt
new file mode 100644
index 0000000..a37c5fa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/viewport-gradient-background-html-resize-expected.txt
@@ -0,0 +1,28 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "background"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "background"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.png b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.png
new file mode 100644
index 0000000..1d18cd5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.txt b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert.html b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert.html
new file mode 100644
index 0000000..7f9d0a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/gradient-invert.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner)
+    testRunner.dumpAsTextWithPixelResults();
+</script>
+<style>
+  .gradient {
+    background-image: linear-gradient(white 0%, blue 100%);
+    width: 400px;
+    height: 300px;
+  }
+</style>
+<div class="gradient"></div>
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert-expected.png b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert-expected.png
new file mode 100644
index 0000000..2ce1c60
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert-expected.txt b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert.html b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert.html
new file mode 100644
index 0000000..8a47cfb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/image-invert.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner)
+    testRunner.dumpAsTextWithPixelResults();
+</script>
+<img src="../../../images/resources/png-simple.png">
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds-expected.png b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds-expected.png
rename to third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds-expected.txt b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds-expected.txt
rename to third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.txt
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds.html b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/text-on-backgrounds.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds.html
rename to third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-all/text-on-backgrounds.html
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.png b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.png
new file mode 100644
index 0000000..1d18cd5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.txt b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert.html b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert.html
new file mode 100644
index 0000000..7f9d0a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/gradient-noinvert.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner)
+    testRunner.dumpAsTextWithPixelResults();
+</script>
+<style>
+  .gradient {
+    background-image: linear-gradient(white 0%, blue 100%);
+    width: 400px;
+    height: 300px;
+  }
+</style>
+<div class="gradient"></div>
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.png b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.png
new file mode 100644
index 0000000..2ce1c60
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.txt b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert.html b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert.html
new file mode 100644
index 0000000..8a47cfb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/high-contrast-mode/image-filter-none/image-noinvert.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner)
+    testRunner.dumpAsTextWithPixelResults();
+</script>
+<img src="../../../images/resources/png-simple.png">
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
index c8dea3d6..86581b8 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
@@ -40,6 +40,11 @@
   name: 'heart_rate',
   uuid: '0000180d-0000-1000-8000-00805f9b34fb'
 };
+var health_thermometer = {
+  alias: 0x1809,
+  name: 'health_thermometer',
+  uuid: '00001809-0000-1000-8000-00805f9b34fb'
+};
 var body_sensor_location = {
   alias: 0x2a38,
   name: 'body_sensor_location',
@@ -455,14 +460,18 @@
 // corresponding FakePeripheral.
 // The simulated device is called 'Health Thermometer' it has two known service
 // UUIDs: 'generic_access' and 'health_thermometer'. The device has been
-// connected to and its services have been discovered.
-// TODO(crbug.com/719816): Add services, characteristics and descriptors,
-// and discover all the attributes.
+// connected to and its services are ready to be discovered.
+// TODO(crbug.com/719816): Add characteristics and descriptors.
 function getHealthThermometerDevice(options) {
   return getDiscoveredHealthThermometerDevice(options)
     .then(([device, fake_peripheral]) => {
       return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
         .then(() => device.gatt.connect())
+        .then(() => fake_peripheral.addFakeService({uuid: 'generic_access'}))
+        .then(() => fake_peripheral.addFakeService({
+          uuid: 'health_thermometer'}))
+        .then(() => fake_peripheral.addFakeService({
+          uuid: 'health_thermometer'}))
         .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
           code: HCI_SUCCESS}))
         .then(() => [device, fake_peripheral]);
@@ -521,6 +530,42 @@
     });
 }
 
+// Returns a BluetoothDevice discovered using |options| and its
+// corresponding FakePeripheral.
+// The simulated device is called 'HID Device' it has three known service
+// UUIDs: 'generic_access', 'battery_service', 'human_interface_device'. The
+// device has been connected to and its services are ready to be discovered.
+// TODO(crbug.com/719816): Add characteristics and descriptors.
+function getHIDDevice(options) {
+  return setUpPreconnectedDevice({
+      address: '10:10:10:10:10:10',
+      name: 'HID Device',
+      knownServiceUUIDs: [
+        'generic_access',
+        'battery_service',
+        'human_interface_device'
+      ],
+    })
+    .then(fake_peripheral => {
+      return requestDeviceWithKeyDown(options)
+        .then(device => {
+          return fake_peripheral
+            .setNextGATTConnectionResponse({
+              code: HCI_SUCCESS})
+            .then(() => device.gatt.connect())
+            .then(() => fake_peripheral.addFakeService({
+              uuid: 'generic_access'}))
+            .then(() => fake_peripheral.addFakeService({
+              uuid: 'battery_service'}))
+            .then(() => fake_peripheral.addFakeService({
+              uuid: 'human_interface_device'}))
+            .then(() => fake_peripheral.setNextGATTDiscoveryResponse({
+              code: HCI_SUCCESS}))
+            .then(() => [device, fake_peripheral]);
+        });
+    });
+};
+
 // Similar to getHealthThermometerDevice() except the device
 // is not connected and thus its services have not been
 // discovered.
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/heart-rate-two-iframes.html b/third_party/WebKit/LayoutTests/resources/bluetooth/health-thermometer-two-iframes.html
similarity index 61%
rename from third_party/WebKit/LayoutTests/resources/bluetooth/heart-rate-two-iframes.html
rename to third_party/WebKit/LayoutTests/resources/bluetooth/health-thermometer-two-iframes.html
index 91f7a98..d5d38fd 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/heart-rate-two-iframes.html
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/health-thermometer-two-iframes.html
@@ -1,25 +1,25 @@
 <!DOCTYPE html>
 <script>
-'use restrict';
-  let device;
-  window.onmessage = messageEvent => {
-    if (messageEvent.data === 'Iframe1RequestAndConnect') {
-      navigator.bluetooth.requestDevice({
-        filters: [{services: ['heart_rate']}]
+'use strict';
+let device;
+window.onmessage = messageEvent => {
+  if (messageEvent.data === 'Iframe1RequestAndConnect') {
+    navigator.bluetooth.requestDevice({
+        filters: [{services: ['health_thermometer']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
-        // iframe1 can access heart_rate service.
-        return gattServer.getPrimaryService('heart_rate');
+        // iframe1 can access health_thermometer service.
+        return gattServer.getPrimaryService('health_thermometer');
       }).then(() => {
         parent.postMessage('Iframe1Connected', '*');
       }).catch(err => {
         console.error(err);
         parent.postMessage('FAIL: ' + err, '*');
       });
-    } else if (messageEvent.data === 'Iframe1TryAccessGenericAccessService') {
-      navigator.bluetooth.requestDevice({
-        filters: [{services: ['heart_rate']}]
+  } else if (messageEvent.data === 'Iframe1TryAccessGenericAccessService') {
+    navigator.bluetooth.requestDevice({
+        filters: [{services: ['health_thermometer']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
@@ -28,26 +28,26 @@
       }).catch(err => {
         parent.postMessage('Iframe1AccessGenericAccessServiceFailed', '*');
       });
-    } else if (messageEvent.data === 'Iframe2RequestAndConnect') {
-      navigator.bluetooth.requestDevice({
+  } else if (messageEvent.data === 'Iframe2RequestAndConnect') {
+    navigator.bluetooth.requestDevice({
         filters: [{services: ['generic_access']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
-        // Since iframe1 can access heart_rate service, and iframe2 has the
+        // Since iframe1 can access health_thermometer service, and iframe2 has the
         // same origin as iframe1, iframe2 should also be able to access
-        // heart_rate service.
+        // health_thermometer service.
         return Promise.all([gattServer.getPrimaryService('generic_access'),
-          gattServer.getPrimaryService('heart_rate')]);
+                            gattServer.getPrimaryService('health_thermometer')]);
       }).then(() => {
         parent.postMessage('Iframe2Connected', '*');
       }).catch(err => {
         console.error(err);
         parent.postMessage('FAIL: ' + err, '*');
       });
-    } else if (messageEvent.data === 'TestIframe1HasGenericAccessService') {
-      navigator.bluetooth.requestDevice({
-        filters: [{services: ['heart_rate']}]
+  } else if (messageEvent.data === 'TestIframe1HasGenericAccessService') {
+    navigator.bluetooth.requestDevice({
+        filters: [{services: ['health_thermometer']}]
       })
       .then(device => device.gatt.connect())
       .then(gattServer => {
@@ -61,7 +61,7 @@
         console.error(err);
         parent.postMessage('FAIL: ' + err, '*');
       });
-    }
-  };
-  parent.postMessage("Ready", "*");
+  }
+};
+parent.postMessage("Ready", "*");
 </script>
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
index 7b6cd79..648ea2b 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/web-bluetooth-test.js
@@ -150,6 +150,7 @@
   class FakePeripheral {
     constructor(address, fake_central_ptr) {
       this.address = address;
+      this.services_ = [];
       this.fake_central_ptr_ = fake_central_ptr;
     }
 
@@ -166,6 +167,24 @@
       if (success !== true) throw 'setNextGATTConnectionResponse failed.';
     }
 
+    // Adds a fake GATT Service with |uuid| to be discovered when discovering
+    // the peripheral's GATT Attributes. Returns a FakeRemoteGATTService
+    // corresponding to this service. |uuid| should be a BluetoothServiceUUIDs
+    // https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
+    async addFakeService({uuid}) {
+      let {service_id} = await this.fake_central_ptr_.addFakeService(
+        this.address, {uuid: BluetoothUUID.getService(uuid)});
+
+      if (service_id === null) throw 'addFakeService failed';
+
+      let fake_service = new FakeRemoteGATTService(
+        service_id, this.address, this.fake_central_ptr_);
+
+      this.services_.push(fake_service);
+
+      return fake_service;
+    }
+
     // Sets the next GATT Discovery request response for peripheral with
     // |address| to |code|. |code| could be an HCI Error Code from
     // BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a number outside that
@@ -191,5 +210,13 @@
     }
   }
 
+  class FakeRemoteGATTService {
+    constructor(service_id, peripheral_address, fake_central_ptr) {
+      this.service_id_ = service_id;
+      this.peripheral_address_ = peripheral_address;
+      this.fake_central_ptr_ = fake_central_ptr;
+    }
+  }
+
   navigator.bluetooth.test = new FakeBluetooth();
 })();
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssMatrixComponent.html b/third_party/WebKit/LayoutTests/typedcssom/cssMatrixComponent.html
index 0669296..80c5c1c2 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssMatrixComponent.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssMatrixComponent.html
@@ -3,78 +3,125 @@
 <script src="../resources/testharnessreport.js"></script>
 
 <script>
-var values = [
-  {input: new CSSMatrixComponent(new DOMMatrixReadOnly([0, 0, 0, 0, 0, 0])),
-    a: 0, b: 0, c: 0, d: 0, e: 0, f: 0,
-    is2D: true, cssText: "matrix(0, 0, 0, 0, 0, 0)"},
-  {input: new CSSMatrixComponent(new DOMMatrixReadOnly([2, 4, 6, 8, 10, 12])),
-    a: 2, b: 4, c: 6, d: 8, e: 10, f: 12,
-    is2D: true, cssText: "matrix(2, 4, 6, 8, 10, 12)"},
-  {input: new CSSMatrixComponent(new DOMMatrixReadOnly([-2, -4, -6, -8, -10, -12])),
-    a: -2, b: -4, c: -6, d: -8, e: -10, f: -12,
-    is2D: true, cssText: "matrix(-2, -4, -6, -8, -10, -12)"},
-  {input: new CSSMatrixComponent(new DOMMatrixReadOnly([1.1, -2.2, 3.3, -4.4, 5.5, 0.6])),
-    a: 1.1, b: -2.2, c: 3.3, d: -4.4, e: 5.5, f: 0.6,
-    is2D: true, cssText: "matrix(1.1, -2.2, 3.3, -4.4, 5.5, 0.6)"},
-  {input: new CSSMatrixComponent(new DOMMatrixReadOnly([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])),
-    m11: 0, m12: 0, m13: 0, m14: 0, m21: 0, m22: 0, m23: 0, m24: 0,
-    m31: 0, m32: 0, m33: 0, m34: 0, m41: 0, m42: 0, m43: 0, m44: 0,
-    is2D: false, cssText: "matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)"},
-  {input: new CSSMatrixComponent(new DOMMatrixReadOnly([11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44])),
-    m11: 11, m12: 12, m13: 13, m14: 14, m21: 21, m22: 22, m23: 23, m24: 24,
-    m31: 31, m32: 32, m33: 33, m34: 34, m41: 41, m42: 42, m43: 43, m44: 44,
-    is2D: false, cssText: "matrix3d(11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44)"},
-  {input: new CSSMatrixComponent(new DOMMatrixReadOnly([1.1, 1.2, -13, -1.4, 2, 0, -2, 4, 3.1, 3, 3, 3.4, -4.1, 42, 43, 4.4])),
-    m11: 1.1, m12: 1.2, m13: -13, m14: -1.4, m21: 2, m22: 0, m23: -2, m24: 4,
-    m31: 3.1, m32: 3, m33: 3, m34: 3.4, m41: -4.1, m42: 42, m43: 43, m44: 4.4,
-    is2D: false, cssText: "matrix3d(1.1, 1.2, -13, -1.4, 2, 0, -2, 4, 3.1, 3, 3, 3.4, -4.1, 42, 43, 4.4)"}
+var testParams = [
+{
+  input: new CSSMatrixComponent(new DOMMatrixReadOnly([0, 0, 0, 0, 0, 0])),
+  a: 0, b: 0, c: 0, d: 0, e: 0, f: 0,
+  is2D: true,
+  cssText: "matrix(0, 0, 0, 0, 0, 0)"
+},
+{
+  input: new CSSMatrixComponent(new DOMMatrixReadOnly([2, 4, 6, 8, 10, 12])),
+  a: 2, b: 4, c: 6, d: 8, e: 10, f: 12,
+  is2D: true,
+  cssText: "matrix(2, 4, 6, 8, 10, 12)"
+},
+{
+  input: new CSSMatrixComponent(
+      new DOMMatrixReadOnly([-2, -4, -6, -8, -10, -12])),
+  a: -2, b: -4, c: -6, d: -8, e: -10, f: -12,
+  is2D: true,
+  cssText: "matrix(-2, -4, -6, -8, -10, -12)"
+},
+{
+  input: new CSSMatrixComponent(
+      new DOMMatrixReadOnly([1.1, -2.2, 3.3, -4.4, 5.5, 0.6])),
+  a: 1.1, b: -2.2, c: 3.3, d: -4.4, e: 5.5, f: 0.6,
+  is2D: true,
+  cssText: "matrix(1.1, -2.2, 3.3, -4.4, 5.5, 0.6)"
+},
+{
+  input: new CSSMatrixComponent(
+      new DOMMatrixReadOnly(
+          [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])),
+  m11: 0, m12: 0, m13: 0, m14: 0, m21: 0, m22: 0, m23: 0, m24: 0,
+  m31: 0, m32: 0, m33: 0, m34: 0, m41: 0, m42: 0, m43: 0, m44: 0,
+  is2D: false,
+  cssText: "matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)"
+},
+{
+  input: new CSSMatrixComponent(
+      new DOMMatrixReadOnly(
+          [11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44])),
+  m11: 11, m12: 12, m13: 13, m14: 14, m21: 21, m22: 22, m23: 23, m24: 24,
+  m31: 31, m32: 32, m33: 33, m34: 34, m41: 41, m42: 42, m43: 43, m44: 44,
+  is2D: false,
+  cssText: "matrix3d(11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44)"
+},
+{
+  input: new CSSMatrixComponent(
+      new DOMMatrixReadOnly(
+          [1.1, 1.2, -13, -1.4, 2, 0, -2, 4, 3.1, 3, 3, 3.4, -4.1, 42, 43, 4.4])),
+  m11: 1.1, m12: 1.2, m13: -13, m14: -1.4, m21: 2, m22: 0, m23: -2, m24: 4,
+  m31: 3.1, m32: 3, m33: 3, m34: 3.4, m41: -4.1, m42: 42, m43: 43, m44: 4.4,
+  is2D: false,
+  cssText: "matrix3d(1.1, 1.2, -13, -1.4, 2, 0, -2, 4, 3.1, 3, 3, 3.4, -4.1, 42, 43, 4.4)"
+}
 ];
 
 var attributeValues2D = ["a", "b", "c", "d", "e", "f"];
-var attributeValues3D =  ["m11", "m12", "m13", "m14", "m21", "m22", "m23", "m24",
-  "m31", "m32", "m33", "m34", "m41", "m42", "m43", "m44"];
+var attributeValues3D =  [
+  "m11",
+  "m12",
+  "m13",
+  "m14",
+  "m21",
+  "m22",
+  "m23",
+  "m24",
+  "m31",
+  "m32",
+  "m33",
+  "m34",
+  "m41",
+  "m42",
+  "m43",
+  "m44"
+];
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    var attributeValues = values[i].is2D ? attributeValues2D : attributeValues3D;
+for (let params of testParams) {
+  test(() => {
+    var attributeValues = params.is2D ? attributeValues2D : attributeValues3D;
     for (var j = 0; j < attributeValues.length; ++j) {
       var attribute = attributeValues[j];
-      assert_equals(values[i].input.matrix[attribute], values[i][attribute]);
+      assert_equals(params.input.matrix[attribute], params[attribute]);
     }
-  }
-}, "Test that the (a, ... , f) and (m11, ... , m44) attributes for CSSMatrixComponent are correct.");
+  }, "(a, ... , f) and (m11, ... , m44) attributes are correct for " +
+      params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.is2D(), values[i].is2D);
-  }
-}, "Test that the is2D values for CSSMatrixComponent are correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.is2D(), params.is2D);
+  }, "is2D value is correct for " + params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.toString(), values[i].cssText);
-  }
-}, "Test that the toString for CSSMatrixComponent is correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.toString(), params.cssText);
+  }, "toString is correct for " + params.cssText);
+}
 
-test(function() {
-  assert_throws(new TypeError(), function() { new CSSMatrixComponent() });
-  assert_throws(new TypeError(), function() { new CSSMatrixComponent(0) });
-  assert_throws(new TypeError(), function() { new CSSMatrixComponent("string") });
-  assert_throws(new TypeError(), function() { new CSSMatrixComponent(null) });
-  assert_throws(new TypeError(), function() { new CSSMatrixComponent(undefined) });
-}, "Test that invalid number of arguments for CSSMatrixComponent throws an exception.");
+test(() => {
+  assert_throws(new TypeError(), () => { new CSSMatrixComponent(); });
+  assert_throws(new TypeError(), () => { new CSSMatrixComponent(0); });
+  assert_throws(new TypeError(), () => { new CSSMatrixComponent("string") });
+  assert_throws(new TypeError(), () => { new CSSMatrixComponent(null); });
+  assert_throws(new TypeError(), () => { new CSSMatrixComponent(undefined); });
+}, "invalid number of arguments to the constructor throws");
 
-test(function() {
-  var attributeValues = attributeValues2D.concat(attributeValues3D);
-  for (var i = 0; i < values.length; ++i) {
-    var inputAsMatrix = values[i].input.asMatrix();
+for (let params of testParams) {
+  test(() => {
+    var attributeValues = attributeValues2D.concat(attributeValues3D);
+    var inputAsMatrix = params.input.asMatrix();
     for (var j = 0; j < attributeValues.length; ++j) {
       var attribute = attributeValues[j];
-      assert_equals(inputAsMatrix[attribute], values[i].input[attribute]);
+      assert_equals(inputAsMatrix[attribute], params.input[attribute]);
     }
-    assert_equals(inputAsMatrix.is2D(), values[i].input.is2D());
-    assert_equals(inputAsMatrix.toString(), values[i].input.toString());
-  }
-}, "Test that asMatrix has all the same properties as the original CSSMatrixComponent.");
+    assert_equals(inputAsMatrix.is2D(), params.input.is2D());
+    assert_equals(inputAsMatrix.toString(), params.input.toString());
+  }, "asMatrix has all the same properties as the original " +
+  "CSSMatrixComponent for " + params.cssText);
+}
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssRotation-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/cssRotation-expected.txt
index bf6dfe0..a4cc31b5 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssRotation-expected.txt
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssRotation-expected.txt
@@ -1,7 +1,39 @@
 This is a testharness.js-based test.
-FAIL Test that the (x, y, z, angle) values for CSSRotation are correct. assert_equals: expected (number) 0 but got (undefined) undefined
-PASS Test that the is2D values for CSSRotation is correct. 
-FAIL Test that toString values for CSSRotation is correct. assert_equals: expected "rotate(0deg)" but got ""
-FAIL Test that asMatrix is constructed correctly for CSSRotation. Cannot read property 'is2D' of null
+PASS x, y, z and angle values are correct for rotate(0deg) 
+PASS x, y, z and angle values are correct for rotate(10deg) 
+PASS x, y, z and angle values are correct for rotate(-21deg) 
+PASS x, y, z and angle values are correct for rotate(3.2deg) 
+PASS x, y, z and angle values are correct for rotate3d(0, 0, 1, 90deg) 
+PASS x, y, z and angle values are correct for rotate3d(2.7, -3, 4.4, 0deg) 
+PASS x, y, z and angle values are correct for rotate3d(2, 3, 4, 10deg) 
+PASS x, y, z and angle values are correct for rotate3d(2, 3.7, -4, -1.2deg) 
+PASS x, y, z and angle values are correct for rotate3d(1, 0, 0, 0.5turn) 
+PASS is2D value is correct for rotate(0deg) 
+PASS is2D value is correct for rotate(10deg) 
+PASS is2D value is correct for rotate(-21deg) 
+PASS is2D value is correct for rotate(3.2deg) 
+PASS is2D value is correct for rotate3d(0, 0, 1, 90deg) 
+PASS is2D value is correct for rotate3d(2.7, -3, 4.4, 0deg) 
+PASS is2D value is correct for rotate3d(2, 3, 4, 10deg) 
+PASS is2D value is correct for rotate3d(2, 3.7, -4, -1.2deg) 
+PASS is2D value is correct for rotate3d(1, 0, 0, 0.5turn) 
+FAIL toString value is correct for rotate(0deg) assert_equals: expected "rotate(0deg)" but got ""
+FAIL toString value is correct for rotate(10deg) assert_equals: expected "rotate(10deg)" but got ""
+FAIL toString value is correct for rotate(-21deg) assert_equals: expected "rotate(-21deg)" but got ""
+FAIL toString value is correct for rotate(3.2deg) assert_equals: expected "rotate(3.2deg)" but got ""
+FAIL toString value is correct for rotate3d(0, 0, 1, 90deg) assert_equals: expected "rotate3d(0, 0, 1, 90deg)" but got ""
+FAIL toString value is correct for rotate3d(2.7, -3, 4.4, 0deg) assert_equals: expected "rotate3d(2.7, -3, 4.4, 0deg)" but got ""
+FAIL toString value is correct for rotate3d(2, 3, 4, 10deg) assert_equals: expected "rotate3d(2, 3, 4, 10deg)" but got ""
+FAIL toString value is correct for rotate3d(2, 3.7, -4, -1.2deg) assert_equals: expected "rotate3d(2, 3.7, -4, -1.2deg)" but got ""
+FAIL toString value is correct for rotate3d(1, 0, 0, 0.5turn) assert_equals: expected "rotate3d(1, 0, 0, 0.5turn)" but got ""
+PASS toMatrix works for rotate(0deg) 
+FAIL toMatrix works for rotate(10deg) assert_approx_equals: expected 0.9848077 +/- 0.000001 but got 1
+FAIL toMatrix works for rotate(-21deg) assert_approx_equals: expected 0.9335804 +/- 0.000001 but got 1
+FAIL toMatrix works for rotate(3.2deg) assert_approx_equals: expected 0.9984407 +/- 0.000001 but got 1
+FAIL toMatrix works for rotate3d(0, 0, 1, 90deg) assert_equals: is2D expected false but got true
+FAIL toMatrix works for rotate3d(2.7, -3, 4.4, 0deg) assert_equals: is2D expected false but got true
+FAIL toMatrix works for rotate3d(2, 3, 4, 10deg) assert_equals: is2D expected false but got true
+FAIL toMatrix works for rotate3d(2, 3.7, -4, -1.2deg) assert_equals: is2D expected false but got true
+FAIL toMatrix works for rotate3d(1, 0, 0, 0.5turn) assert_equals: is2D expected false but got true
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssRotation.html b/third_party/WebKit/LayoutTests/typedcssom/cssRotation.html
index bb4a4f1..d4e14ed9 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssRotation.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssRotation.html
@@ -1,94 +1,134 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
+<script src="resources/comparisons.js"></script>
 
 <script>
 var EPSILON = 1e-6; // float epsilon
-function angle(deg) {
-  return new CSSUnitValue(deg, 'deg');
-}
 
-var values = [
-  {input: new CSSRotation(angle(0)), angle: 0, x: 0, y: 0, z: 1,
-    is2D: true, cssText: "rotate(0deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([1, 0, 0, 1, 0, 0]))},
-  {input: new CSSRotation(angle(10)), angle: 10, x: 0, y: 0, z: 1,
-    is2D: true, cssText: "rotate(10deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([0.9848077, 0.1736481, -0.1736481, 0.9848077, 0, 0]))},
-  {input: new CSSRotation(angle(-21)), angle: -21, x: 0, y: 0, z: 1,
+var testParams = [
+  {
+    input: new CSSRotation(new CSSUnitValue(0, 'deg')),
+    angle: 0,
+    x: 0,
+    y: 0,
+    z: 1,
+    is2D: true,
+    cssText: "rotate(0deg)",
+    asMatrix: new DOMMatrixReadOnly([1, 0, 0, 1, 0, 0])
+  },
+  {
+    input: new CSSRotation(new CSSUnitValue(10, 'deg')),
+    angle: 10,
+    x: 0,
+    y: 0,
+    z: 1,
+    is2D: true,
+    cssText: "rotate(10deg)",
+    asMatrix: new DOMMatrixReadOnly([0.9848077, 0.1736481, -0.1736481, 0.9848077, 0, 0])
+  },
+  {
+    input: new CSSRotation(new CSSUnitValue(-21, 'deg')),
+    angle: -21,
+    x: 0,
+    y: 0,
+    z: 1,
     is2D: true, cssText: "rotate(-21deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([0.9335804, -0.3583679, 0.3583679, 0.9335804, 0, 0]))},
-  {input: new CSSRotation(angle(3.2)), angle: 3.2, x: 0, y: 0, z: 1,
-    is2D: true, cssText: "rotate(3.2deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([0.9984407, 0.0558215, -0.0558215, 0.9984407, 0, 0]))},
-  {input: new CSSRotation(0, 0, 1, angle(90)), angle: 90, x: 0, y: 0, z: 1,
+    asMatrix: new DOMMatrixReadOnly([0.9335804, -0.3583679, 0.3583679, 0.9335804, 0, 0])
+  },
+  {
+    input: new CSSRotation(new CSSUnitValue(3.2, 'deg')),
+    angle: 3.2,
+    x: 0,
+    y: 0,
+    z: 1,
+    is2D: true,
+    cssText: "rotate(3.2deg)",
+    asMatrix: new DOMMatrixReadOnly([0.9984407, 0.0558215, -0.0558215, 0.9984407, 0, 0])
+  },
+  {
+    input: new CSSRotation(0, 0, 1, new CSSUnitValue(90, 'deg')),
+    angle: 90,
+    x: 0,
+    y: 0,
+    z: 1,
     is2D: false, cssText: "rotate3d(0, 0, 1, 90deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]))},
-  {input: new CSSRotation(2.7, -3, 4.4, angle(0)), angle: 0, x: 2.7, y: -3, z: 4.4,
-    is2D: false, cssText: "rotate3d(2.7, -3, 4.4, 0deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]))},
-  {input: new CSSRotation(2, 3, 4, angle(10)), angle: 10, x: 2, y: 3, z: 4,
-    is2D: false, cssText: "rotate3d(2, 3, 4, 10deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([0.9869032, 0.1321258, -0.0925460, 0, -0.1258394,
-      0.9895225, 0.0707777, 0, 0.1009279, -0.0582048, 0.9931896, 0, 0, 0, 0, 1]))},
-  {input: new CSSRotation(2, 3.7, -4, angle(-1.2)), angle: -1.2, x: 2, y: 3.7, z: -4,
-    is2D: false, cssText: "rotate3d(2, 3.7, -4, -1.2deg)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([0.9998067, 0.01448049, 0.0132978, 0, -0.0143841,
-    0.9998698, -0.0073125, 0, -0.0134019, 0.0071198, 0.9998848, 0, 0, 0, 0, 1]))},
-  {input: new CSSRotation(1, 0, 0, new CSSUnitValue(0.5, 'turn')), angle: 180, x: 1, y: 0, z: 0,
-    is2D: false, cssText: "rotate3d(1, 0, 0, 0.5turn)",
-    asMatrix: new CSSMatrixComponent(new DOMMatrixReadOnly([1, 0, 0, 0, 0, -1, 1.2246467991473532e-16, 0, 0,
-    -1.2246467991473532e-16, -1, 0, 0, 0, 0, 1]))}
+    asMatrix: new DOMMatrixReadOnly([0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
+  },
+  {
+    input: new CSSRotation(2.7, -3, 4.4, new CSSUnitValue(0, 'deg')),
+    angle: 0,
+    x: 2.7,
+    y: -3,
+    z: 4.4,
+    is2D: false,
+    cssText: "rotate3d(2.7, -3, 4.4, 0deg)",
+    asMatrix: new DOMMatrixReadOnly([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
+  },
+  {
+    input: new CSSRotation(2, 3, 4, new CSSUnitValue(10, 'deg')),
+    angle: 10,
+    x: 2,
+    y: 3,
+    z: 4,
+    is2D: false,
+    cssText: "rotate3d(2, 3, 4, 10deg)",
+    asMatrix: new DOMMatrixReadOnly([0.9869032, 0.1321258, -0.0925460, 0, -0.1258394,
+      0.9895225, 0.0707777, 0, 0.1009279, -0.0582048, 0.9931896, 0, 0, 0, 0, 1])
+  },
+  {
+    input: new CSSRotation(2, 3.7, -4, new CSSUnitValue(-1.2, 'deg')),
+    angle: -1.2,
+    x: 2,
+    y: 3.7,
+    z: -4,
+    is2D: false,
+    cssText: "rotate3d(2, 3.7, -4, -1.2deg)",
+    asMatrix: new DOMMatrixReadOnly([0.9998067, 0.01448049, 0.0132978, 0, -0.0143841,
+    0.9998698, -0.0073125, 0, -0.0134019, 0.0071198, 0.9998848, 0, 0, 0, 0, 1])
+  },
+  {
+    input: new CSSRotation(1, 0, 0, new CSSUnitValue(0.5, 'turn')),
+    angle: 0.5,
+    x: 1,
+    y: 0,
+    z: 0,
+    is2D: false,
+    cssText: "rotate3d(1, 0, 0, 0.5turn)",
+    asMatrix: new DOMMatrixReadOnly([1, 0, 0, 0, 0, -1, 1.2246467991473532e-16, 0, 0,
+    -1.2246467991473532e-16, -1, 0, 0, 0, 0, 1])
+  }
 ];
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.angle.degrees, values[i].angle);
-    assert_equals(values[i].input.x, values[i].x);
-    assert_equals(values[i].input.y, values[i].y);
-    assert_equals(values[i].input.z, values[i].z);
-  }
-}, "Test that the (x, y, z, angle) values for CSSRotation are correct.");
-
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.is2D(), values[i].is2D);
-  }
-}, "Test that the is2D values for CSSRotation is correct.");
-
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.toString(), values[i].cssText);
-  }
-}, "Test that toString values for CSSRotation is correct.");
-
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    var input = values[i].input;
-    var inputAsMatrix = input.asMatrix();
-    assert_equals(inputAsMatrix.is2D(), input.is2D());
-    var expectedMatrix = values[i].asMatrix;
-    for (var attribute in expectedMatrix) {
-      if (attribute == "matrix") {
-        assert_matrix_approx_equals(inputAsMatrix[attribute], expectedMatrix[attribute]);
-      } else if (attribute != "toString") {
-        // Due to the complex trigonometric calculations required for a CSSRotation matrix,
-        // the 6 significant figures of each value in the toString might be different.
-        // Hence, do not check toString.
-        assert_equals(inputAsMatrix[attribute], expectedMatrix[attribute]);
-      }
-    }
-  }
-}, "Test that asMatrix is constructed correctly for CSSRotation.");
-
-function assert_array_approx_equals(actual, expected) {
-  for (var i = 0; i < actual.length; i++) {
-    assert_approx_equals(actual[i], expected[i], EPSILON);
-  }
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.angle.value, params.angle);
+    assert_equals(params.input.x, params.x);
+    assert_equals(params.input.y, params.y);
+    assert_equals(params.input.z, params.z);
+  }, "x, y, z and angle values are correct for " + params.cssText);
 }
 
-function assert_matrix_approx_equals(actual, expected) {
-  assert_array_approx_equals(actual.toFloat64Array(), expected.toFloat64Array());
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.is2D(), params.is2D);
+  }, "is2D value is correct for " + params.cssText);
+}
+
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.toString(), params.cssText);
+  }, "toString value is correct for " + params.cssText);
+}
+
+for (let params of testParams) {
+  var input = params.input;
+  test(() => {
+    var transformValue = new CSSTransformValue([input]);
+    var result = transformValue.toMatrix();
+    assert_equals(result.is2D, input.is2D(), 'is2D');
+    assert_matrix_approx_equals(result, params.asMatrix);
+  }, "toMatrix works for " + params.cssText);
 }
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssScale.html b/third_party/WebKit/LayoutTests/typedcssom/cssScale.html
index 01075f0..45daef7 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssScale.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssScale.html
@@ -1,52 +1,81 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
+<script src="resources/comparisons.js"></script>
 
 <script>
 var EPSILON = 1e-6; // float epsilon
-var values = [
-  {input: new CSSScale(0, 0), x: 0, y: 0, z: 1, is2D: true,
-    cssText: "scale(0, 0)"},
-  {input: new CSSScale(1, 2), x: 1, y: 2, z: 1, is2D: true,
-    cssText: "scale(1, 2)"},
-  {input: new CSSScale(-2, -4), x: -2, y: -4, z: 1, is2D: true,
-    cssText: "scale(-2, -4)"},
-  {input: new CSSScale(3.4, 2.7), x: 3.4, y: 2.7, z: 1, is2D: true,
-    cssText: "scale(3.4, 2.7)"},
-  {input: new CSSScale(0, 0, 0), x: 0, y: 0, z: 0, is2D: false,
-    cssText: "scale3d(0, 0, 0)"},
-  {input: new CSSScale(1, 2, 3), x: 1, y: 2, z: 3, is2D: false,
-    cssText: "scale3d(1, 2, 3)"},
-  {input: new CSSScale(3.5, -2.7, -2), x: 3.5, y: -2.7, z: -2, is2D: false,
-    cssText: "scale3d(3.5, -2.7, -2)"}
+var testParams = [
+  {
+    input: new CSSScale(0, 0),
+    x: 0, y: 0, z: 1,
+    is2D: true,
+    cssText: "scale(0, 0)"
+  },
+  {
+    input: new CSSScale(1, 2),
+    x: 1, y: 2, z: 1,
+    is2D: true,
+    cssText: "scale(1, 2)"
+  },
+  {
+    input: new CSSScale(-2, -4),
+    x: -2, y: -4, z: 1,
+    is2D: true,
+    cssText: "scale(-2, -4)"
+  },
+  {
+    input: new CSSScale(3.4, 2.7),
+    x: 3.4, y: 2.7, z: 1,
+    is2D: true,
+    cssText: "scale(3.4, 2.7)"
+  },
+  {
+    input: new CSSScale(0, 0, 0),
+    x: 0, y: 0, z: 0,
+    is2D: false,
+    cssText: "scale3d(0, 0, 0)"
+  },
+  {
+    input: new CSSScale(1, 2, 3),
+    x: 1, y: 2, z: 3,
+    is2D: false,
+    cssText: "scale3d(1, 2, 3)"
+  },
+  {
+    input: new CSSScale(3.5, -2.7, -2),
+    x: 3.5, y: -2.7, z: -2,
+    is2D: false,
+    cssText: "scale3d(3.5, -2.7, -2)"
+  }
 ];
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.x, values[i].x);
-    assert_equals(values[i].input.y, values[i].y);
-    assert_equals(values[i].input.z, values[i].z);
-  }
-}, "Test that the (x, y, z) values for CSSScale are correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.x, params.x);
+    assert_equals(params.input.y, params.y);
+    assert_equals(params.input.z, params.z);
+  }, "x, y, and z values are correct for " + params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.is2D(), values[i].is2D);
-  }
-}, "Test that the is2D values for CSSScale is correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.is2D(), params.is2D);
+  }, "is2D value is correct for " + params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.toString(), values[i].cssText);
-  }
-}, "Test that the toString for CSSScale is correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.toString(), params.cssText);
+  }, "toString is correct for " + params.cssText);
+}
 
-test(function() {
+test(() => {
   assert_throws(new TypeError(), () => { new CSSScale(); });
   assert_throws(new TypeError(), () => { new CSSScale(1); });
-}, "Test that invalid number of arguments for CSSScale throws an exception.");
+}, "Invalid number of arguments to constructor throws an exception.");
 
-test(function() {
+test(() => {
   assert_throws(new TypeError(), () => { new CSSScale(NaN, 0); });
   assert_throws(new TypeError(), () => { new CSSScale(0, NaN); });
   assert_throws(new TypeError(), () => { new CSSScale(NaN, NaN); });
@@ -64,34 +93,32 @@
   assert_throws(new TypeError(), () => { new CSSScale(0, 0, Infinity); });
   assert_throws(new TypeError(), () => { new CSSScale(0, 0, -Infinity); });
   assert_throws(new TypeError(), () => { new CSSScale(0, 0, undefined); });
-  assert_throws(new TypeError(), () => { new CSSScale(undefined, undefined, 0); });
+  assert_throws(new TypeError(), () => {
+    new CSSScale(undefined, undefined, 0);
+  });
   assert_throws(new TypeError(), () => { new CSSScale(NaN, undefined, 0); });
   assert_throws(new TypeError(), () => { new CSSScale(NaN, 0, NaN); });
   assert_throws(new TypeError(), () => { new CSSScale(0, "hello", "world"); });
   assert_throws(new TypeError(), () => { new CSSScale(0, {}, {}); });
   assert_throws(new TypeError(), () => { new CSSScale({}, {}, {}); });
   assert_throws(new TypeError(), () => { new CSSScale(NaN, NaN, NaN); });
-}, "Test that invalid input throws an exception.");
+}, "Invalid input throws an exception.");
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    var input = values[i].input;
+for (let params of testParams) {
+  test(() => {
+    var input = params.input;
     var inputAsMatrix = input.asMatrix();
     assert_equals(inputAsMatrix.is2D(), input.is2D());
 
-    var expectedMatrix = input.is2D() ? new CSSMatrixComponent(new DOMMatrixReadOnly([input.x, 0, 0, input.y, 0, 0])) :
-        new CSSMatrixComponent(new DOMMatrixReadOnly([input.x, 0, 0, 0, 0, input.y, 0, 0, 0, 0, input.z, 0, 0, 0, 0, 1]));
-    for (var attribute in expectedMatrix) {
-      if (attribute == "matrix") {
-        assert_matrix_approx_equals(inputAsMatrix[attribute], expectedMatrix[attribute]);
-      } else {
-        assert_equals(inputAsMatrix[attribute], expectedMatrix[attribute]);
-      }
-    }
-  }
-}, "Test that asMatrix is constructed correctly for CSSScale.");
+    var expectedMatrix = input.is2D() ?
+        new DOMMatrixReadOnly([input.x, 0, 0, input.y, 0, 0]) :
+        new DOMMatrixReadOnly(
+          [input.x, 0, 0, 0, 0, input.y, 0, 0, 0, 0, input.z, 0, 0, 0, 0, 1]);
+    assert_matrix_approx_equals(inputAsMatrix.matrix, expectedMatrix, EPSILON);
+  }, "asMatrix is constructed correctly for " + params.cssText);
+}
 
-test(function() {
+test(() => {
   var actual = new CSSScale(0, 0, 0);
   actual.x = 1;
   actual.y = 2;
@@ -99,15 +126,6 @@
   assert_equals(actual.x, 1);
   assert_equals(actual.y, 2);
   assert_equals(actual.z, 3);
-}, "Test that x, y, z are mutable attributes.");
+}, "x, y, z are mutable attributes.");
 
-function assert_array_approx_equals(actual, expected) {
-  for (var i = 0; i < actual.length; i++) {
-    assert_approx_equals(actual[i], expected[i], EPSILON);
-  }
-}
-
-function assert_matrix_approx_equals(actual, expected) {
-  assert_array_approx_equals(actual.toFloat64Array(), expected.toFloat64Array());
-}
 </script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssSkew-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/cssSkew-expected.txt
index fc7db33..f2d679d 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssSkew-expected.txt
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssSkew-expected.txt
@@ -1,10 +1,30 @@
 This is a testharness.js-based test.
-FAIL (ax, ay) values for CSSSkew are correct. assert_approx_equals: expected a number but got a "undefined"
-PASS is2D values for CSSSkew are correct. 
-FAIL toString for CSSSkew is correct. assert_equals: expected "skew(0deg, 0deg)" but got ""
-PASS Invalid arguments for CSSSkew throws an exception. 
-FAIL asMatrix is constructed correctly for CSSSkew. Cannot read property 'is2D' of null
-PASS Setting ax and ay for CSSSkew with valid CSSUnitValues 
+PASS (ax, ay) value is correct for skew(0deg, 0deg) 
+PASS (ax, ay) value is correct for skew(1deg, 2deg) 
+PASS (ax, ay) value is correct for skew(-2deg, -4deg) 
+PASS (ax, ay) value is correct for skew(3.4deg, 2.7deg) 
+PASS (ax, ay) value is correct for skew(1rad, 0deg) 
+PASS (ax, ay) value is correct for skew(0deg, 1rad) 
+PASS is2D value is correct for skew(0deg, 0deg) 
+PASS is2D value is correct for skew(1deg, 2deg) 
+PASS is2D value is correct for skew(-2deg, -4deg) 
+PASS is2D value is correct for skew(3.4deg, 2.7deg) 
+PASS is2D value is correct for skew(1rad, 0deg) 
+PASS is2D value is correct for skew(0deg, 1rad) 
+FAIL toString is correct for skew(0deg, 0deg) assert_equals: expected "skew(0deg, 0deg)" but got ""
+FAIL toString is correct for skew(1deg, 2deg) assert_equals: expected "skew(1deg, 2deg)" but got ""
+FAIL toString is correct for skew(-2deg, -4deg) assert_equals: expected "skew(-2deg, -4deg)" but got ""
+FAIL toString is correct for skew(3.4deg, 2.7deg) assert_equals: expected "skew(3.4deg, 2.7deg)" but got ""
+FAIL toString is correct for skew(1rad, 0deg) assert_equals: expected "skew(1rad, 0deg)" but got ""
+FAIL toString is correct for skew(0deg, 1rad) assert_equals: expected "skew(0deg, 1rad)" but got ""
+PASS Invalid arguments to constructor throws an exception. 
+FAIL asMatrix is constructed correctly for skew(0deg, 0deg) Cannot read property 'is2D' of null
+FAIL asMatrix is constructed correctly for skew(1deg, 2deg) Cannot read property 'is2D' of null
+FAIL asMatrix is constructed correctly for skew(-2deg, -4deg) Cannot read property 'is2D' of null
+FAIL asMatrix is constructed correctly for skew(3.4deg, 2.7deg) Cannot read property 'is2D' of null
+FAIL asMatrix is constructed correctly for skew(1rad, 0deg) Cannot read property 'is2D' of null
+FAIL asMatrix is constructed correctly for skew(0deg, 1rad) Cannot read property 'is2D' of null
+PASS Setting ax and ay with valid CSSUnitValues 
 PASS Setting ax with invalid values 
 PASS Setting ay with invalid values 
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssSkew.html b/third_party/WebKit/LayoutTests/typedcssom/cssSkew.html
index 70b35a6..25ce2514e 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssSkew.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssSkew.html
@@ -5,79 +5,111 @@
 <script>
 var EPSILON = 1e-6; // float epsilon
 
-function angle(deg) { return new CSSUnitValue(deg, 'deg'); }
-
-function tanDegrees(degrees) {
-  var radians = degrees * Math.PI / 180;
-  return Math.tan(radians);
-}
-
-function assert_array_approx_equals(actual, expected) {
-  for (var i = 0; i < actual.length; i++) {
-    assert_approx_equals(actual[i], expected[i], EPSILON);
+function tanUnitValue(unitValue) {
+  if (unitValue.unit == 'deg') {
+    var radians = unitValue.value * Math.PI / 180;
+    return Math.tan(radians);
   }
+  if (unitValue.unit = 'rad') {
+    return Math.tan(unitValue.value);
+  }
+  // We've only used degrees and radians in this test.
+  throw Error('Test bug: helper function tanUnitValue needs updating.');
 }
 
-function assert_matrix_approx_equals(actual, expected) {
-  assert_array_approx_equals(actual.toFloat64Array(), expected.toFloat64Array());
-}
-
-var values = [
-  {input: new CSSSkew(angle(0), angle(0)), ax: 0, ay: 0, cssText: "skew(0deg, 0deg)"},
-  {input: new CSSSkew(angle(1), angle(2)), ax: 1, ay: 2, cssText: "skew(1deg, 2deg)"},
-  {input: new CSSSkew(angle(-2), angle(-4)), ax: -2, ay: -4, cssText: "skew(-2deg, -4deg)"},
-  {input: new CSSSkew(angle(3.4), angle(2.7)), ax: 3.4, ay: 2.7, cssText: "skew(3.4deg, 2.7deg)"},
-  {input: new CSSSkew(new CSSUnitValue(1, 'rad'), angle(0)), ax: 57.2957795, ay: 0, cssText: "skew(1rad, 0deg)"},
-  {input: new CSSSkew(angle(0), new CSSUnitValue(1, 'rad')), ax: 0, ay: 57.2957795, cssText: "skew(0deg, 1rad)"}
+var testParams = [
+  {
+    input: new CSSSkew(new CSSUnitValue(0, 'deg'), new CSSUnitValue(0, 'deg')),
+    ax: new CSSUnitValue(0, 'deg'),
+    ay: new CSSUnitValue(0, 'deg'),
+    cssText: "skew(0deg, 0deg)"
+   },
+  {
+    input: new CSSSkew(new CSSUnitValue(1, 'deg'), new CSSUnitValue(2, 'deg')),
+    ax: new CSSUnitValue(1, 'deg'),
+    ay: new CSSUnitValue(2, 'deg'),
+    cssText: "skew(1deg, 2deg)"},
+  {
+    input: new CSSSkew(new CSSUnitValue(-2, 'deg'), new CSSUnitValue(-4, 'deg')),
+    ax: new CSSUnitValue(-2, 'deg'),
+    ay: new CSSUnitValue(-4, 'deg'),
+    cssText: "skew(-2deg, -4deg)"
+  },
+  {
+    input: new CSSSkew(
+        new CSSUnitValue(3.4, 'deg'), new CSSUnitValue(2.7, 'deg')),
+    ax: new CSSUnitValue(3.4, 'deg'),
+    ay: new CSSUnitValue(2.7, 'deg'),
+    cssText: "skew(3.4deg, 2.7deg)"
+  },
+  {
+    input: new CSSSkew(new CSSUnitValue(1, 'rad'), new CSSUnitValue(0, 'deg')),
+    ax: new CSSUnitValue(1, 'rad'),
+    ay: new CSSUnitValue(0, 'deg'),
+    cssText: "skew(1rad, 0deg)"
+  },
+  {
+    input: new CSSSkew(new CSSUnitValue(0, 'deg'), new CSSUnitValue(1, 'rad')),
+    ax: new CSSUnitValue(0, 'deg'),
+    ay: new CSSUnitValue(1, 'rad'),
+    cssText: "skew(0deg, 1rad)"
+  }
 ];
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_approx_equals(values[i].input.ax.degrees, values[i].ax, EPSILON);
-    assert_approx_equals(values[i].input.ay.degrees, values[i].ay, EPSILON);
-  }
-}, "(ax, ay) values for CSSSkew are correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_approx_equals(params.input.ax.value, params.ax.value, EPSILON);
+    assert_equals(params.input.ax.unit, params.ax.unit);
+    assert_approx_equals(params.input.ay.value, params.ay.value, EPSILON);
+    assert_equals(params.input.ay.unit, params.ay.unit);
+  }, "(ax, ay) value is correct for " + params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_true(values[i].input.is2D());
-  }
-}, "is2D values for CSSSkew are correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_true(params.input.is2D());
+  }, "is2D value is correct for " + params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.toString(), values[i].cssText);
-  }
-}, "toString for CSSSkew is correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.toString(), params.cssText);
+  }, "toString is correct for " + params.cssText);
+}
 
-test(function() {
-  assert_throws(new TypeError(), function() { new CSSSkew(); });
-  assert_throws(new TypeError(), function() { new CSSSkew(null); });
-  assert_throws(new TypeError(), function() { new CSSSkew(1); });
-  assert_throws(new TypeError(), function() { new CSSSkew('1'); });
-  assert_throws(new TypeError(), function() { new CSSSkew(angle(1)); });
-}, "Invalid arguments for CSSSkew throws an exception.");
+test(() => {
+  assert_throws(new TypeError(), () => { new CSSSkew(); });
+  assert_throws(new TypeError(), () => { new CSSSkew(null); });
+  assert_throws(new TypeError(), () => { new CSSSkew(1); });
+  assert_throws(new TypeError(), () => { new CSSSkew('1'); });
+  assert_throws(new TypeError(), () => {
+    new CSSSkew(new CSSUnitValue(1, 'deg'));
+  });
+}, "Invalid arguments to constructor throws an exception.");
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    var input = values[i].input;
+for (let params of testParams) {
+  test(() => {
+    var input = params.input;
     var inputAsMatrix = input.asMatrix();
     assert_true(inputAsMatrix.is2D());
-    var tanAx = tanDegrees(input.ax.degrees);
-    var tanAy = tanDegrees(input.ay.degrees);
-    var expectedMatrix = new CSSMatrixComponent(new DOMMatrixReadOnly([1, tanAy, tanAx, 1, 0, 0]));
+    var tanAx = tanUnitValue(input.ax.degrees);
+    var tanAy = tanUnitValue(input.ay.degrees);
+    var expectedMatrix = new CSSMatrixComponent(
+        new DOMMatrixReadOnly([1, tanAy, tanAx, 1, 0, 0]));
     for (var attribute in expectedMatrix) {
       if (attribute == "matrix") {
-        assert_matrix_approx_equals(inputAsMatrix[attribute], expectedMatrix[attribute]);
+        assert_matrix_approx_equals(
+            inputAsMatrix[attribute], expectedMatrix[attribute], epsilon);
       } else {
         assert_equals(inputAsMatrix[attribute], expectedMatrix[attribute]);
       }
     }
-  }
-}, "asMatrix is constructed correctly for CSSSkew.");
+  }, "asMatrix is constructed correctly for " + params.cssText);
+}
 
 test(function() {
-  var skew = new CSSSkew(new CSSUnitValue(1, 'deg'), new CSSUnitValue(2, 'deg'));
+  var skew = new CSSSkew(
+      new CSSUnitValue(1, 'deg'), new CSSUnitValue(2, 'deg'));
   skew.ax = new CSSUnitValue(3, 'deg');
   skew.ay = new CSSUnitValue(3, 'rad');
 
@@ -85,7 +117,7 @@
   assert_equals(skew.ay.value, 3);
   assert_equals(skew.ax.unit, 'deg');
   assert_equals(skew.ay.unit, 'rad');
-}, "Setting ax and ay for CSSSkew with valid CSSUnitValues");
+}, "Setting ax and ay with valid CSSUnitValues");
 
 for (let a of ['ax', 'ay']) {
   test(() => {
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssTransformValue.html b/third_party/WebKit/LayoutTests/typedcssom/cssTransformValue.html
index 69119fb..b0287b84 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssTransformValue.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssTransformValue.html
@@ -1,16 +1,12 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
+<script src="resources/comparisons.js"></script>
 
 <script>
 var EPSILON = 1e-6; // float epsilon
 
 test(function() {
-  var transformValueObject = new CSSTransformValue();
-  assert_equals(transformValueObject.constructor.name, CSSTransformValue.name);
-}, "A CSSTransformValue object can be constructed");
-
-test(function() {
   var transformArray = [
     new CSSScale(2, 2),
     new CSSMatrixComponent(new DOMMatrixReadOnly([1, 1, 1, 1, 1, 1])),
@@ -18,17 +14,18 @@
   ];
   var transformValue = new CSSTransformValue(transformArray);
   assert_true(transformValue.is2D);
-}, "is2D returns true for transformValues containing only 2D transformComponents");
+}, "is2D is true for transformValues containing only 2D components");
 
 test(function() {
   var transformArray = [
     new CSSScale(2, 2),
-    new CSSMatrixComponent(new DOMMatrixReadOnly([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])),
+    new CSSMatrixComponent(new DOMMatrixReadOnly(
+        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])),
     new CSSScale(5, 6)
   ];
   var transformValue = new CSSTransformValue(transformArray);
   assert_false(transformValue.is2D);
-}, "is2D returns false for transformValues containing both 2D and 3D transformComponents");
+}, "is2D is false for transformValues containing both 2D and 3D components");
 
 test(function() {
   var transformArray = [
@@ -43,7 +40,7 @@
   assert_equals(newTransformArray[0].constructor.name, CSSScale.name);
   assert_equals(newTransformArray[1].constructor.name, CSSMatrixComponent.name);
   assert_equals(newTransformArray[2].constructor.name, CSSScale.name);
-}, "CSSTransformValue can iterate through all its all its transformComponent members");
+}, "Can iterate through transformComponent members");
 
 test(function() {
   var transformArray = [new CSSScale(2,2)];
@@ -52,12 +49,17 @@
   var expectedMatrix = new DOMMatrix();
   expectedMatrix.scaleSelf(2, 2);
 
-  assert_matrix_approx_equals(transformValue.toMatrix(), expectedMatrix);
+  assert_matrix_approx_equals(
+      transformValue.toMatrix(), expectedMatrix, EPSILON);
 }, "toMatrix() returns DOMMatrix Object - single CSSTransformComponent");
 
 test(function() {
   var transformMatrix = new DOMMatrixReadOnly([1,1,1,1,1,1]);
-  var transformArray = [new CSSScale(2,2) , new CSSMatrixComponent(transformMatrix), new CSSScale(5,6)];
+  var transformArray = [
+    new CSSScale(2,2),
+    new CSSMatrixComponent(transformMatrix),
+    new CSSScale(5,6)
+  ];
   var transformValue = new CSSTransformValue(transformArray);
 
   var expectedMatrix = new DOMMatrix();
@@ -65,17 +67,8 @@
   expectedMatrix.multiplySelf(transformMatrix);
   expectedMatrix.scaleSelf(5, 6);
 
-  assert_matrix_approx_equals(transformValue.toMatrix(), expectedMatrix);
-},  "toMatrix() returns DOMMatrix Object - multiple CSSTransformComponent");
-
-function assert_array_approx_equals(actual, expected) {
-  for (var i = 0; i < actual.length; i++) {
-    assert_approx_equals(actual[i], expected[i], EPSILON);
-  }
-}
-
-function assert_matrix_approx_equals(actual, expected) {
-  assert_array_approx_equals(actual.toFloat64Array(), expected.toFloat64Array());
-}
+  assert_matrix_approx_equals(
+      transformValue.toMatrix(), expectedMatrix, EPSILON);
+},  "toMatrix() returns DOMMatrix Object - multiple CSSTransformComponents");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssTranslation.html b/third_party/WebKit/LayoutTests/typedcssom/cssTranslation.html
index d3dfec0..7cd366a 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssTranslation.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssTranslation.html
@@ -13,20 +13,32 @@
 var simplePercent = new CSSUnitValue(10, "percent");
 // var calcPercent = new CSSCalcValue({px: 1, percent: 2.2});
 
-var values = [
+var testParams = [
   // 2D CSSTranslation Transform Components
-  {input: new CSSTranslation(simpleLength, simpleLength),
-    x: simpleLength, y: simpleLength, is2D: true},
-  {input: new CSSTranslation(decimalLength, negativeLength),
-    x: decimalLength, y: negativeLength, is2D: true},
+  {
+    input: new CSSTranslation(simpleLength, simpleLength),
+    x: simpleLength, y: simpleLength,
+    is2D: true,
+    cssText: "translate(0px, 0px)"
+  },
+  {
+    input: new CSSTranslation(decimalLength, negativeLength),
+    x: decimalLength, y: negativeLength,
+    is2D: true,
+    cssText: "translate(1.1px, -2.2em)"
+  },
   // {input: new CSSTranslation(negativeLength, calcLengthPx),
   //   x: negativeLength, y: calcLengthPx, is2D: true},
   // {input: new CSSTranslation(calcLengthPx, negativeLength),
   //   x: calcLengthPx, y: negativeLength, is2D: true},
   // {input: new CSSTranslation(calcLengthPx, calcLength),
   //   x: calcLengthPx, y: calcLength, is2D: true},
-  {input: new CSSTranslation(simplePercent, simpleLength),
-    x: simplePercent, y: simpleLength, is2D: true},
+  {
+    input: new CSSTranslation(simplePercent, simpleLength),
+    x: simplePercent, y: simpleLength,
+    is2D: true,
+    cssText: "translate(10%, 0px)"
+  },
   // {input: new CSSTranslation(calcLengthPx, simplePercent),
   //   x: calcLengthPx, y: simplePercent, is2D: true},
   // {input: new CSSTranslation(calcPercent, calcLength),
@@ -35,63 +47,73 @@
   //   x: simplePercent, y: calcPercent, is2D: true},
 
   // 3D CSSTranslation Transform Components
-  {input: new CSSTranslation(simpleLength, simpleLength, simpleLength),
-    x: simpleLength, y: simpleLength, z: simpleLength, is2D: false},
-  {input: new CSSTranslation(simpleLength, decimalLength, negativeLength),
-    x: simpleLength, y: decimalLength, z: negativeLength, is2D: false},
+  {
+    input: new CSSTranslation(simpleLength, simpleLength, simpleLength),
+    x: simpleLength, y: simpleLength, z: simpleLength,
+    is2D: false,
+    cssText: "translate3d(0px, 0px, 0px)"
+  },
+  {
+    input: new CSSTranslation(simpleLength, decimalLength, negativeLength),
+    x: simpleLength, y: decimalLength, z: negativeLength,
+    is2D: false,
+    cssText: "translate3d(0px, 1.1px, -2.2em)"
+  },
   // {input: new CSSTranslation(simpleLength, simpleLength, calcLengthPx),
   //   x: simpleLength, y: simpleLength, z: calcLengthPx, is2D: false},
   // {input: new CSSTranslation(calcLengthPx, calcLength, calcLength),
   //   x: calcLengthPx, y: calcLength, z: calcLength, is2D: false},
-  {input: new CSSTranslation(simplePercent, decimalLength, simpleLength),
-    x: simplePercent, y: decimalLength, z: simpleLength, is2D: false},
+  {
+    input: new CSSTranslation(simplePercent, decimalLength, simpleLength),
+    x: simplePercent, y: decimalLength, z: simpleLength,
+    is2D: false,
+    cssText: "translate3d(10%, 1.1px, 0px)"
+  },
   // {input: new CSSTranslation(simpleLength, calcPercent, decimalLength),
   //   x: simpleLength, y: calcPercent, z: decimalLength, is2D: false},
   // {input: new CSSTranslation(calcPercent, simplePercent, calcLength),
   //   x: calcPercent, y: simplePercent, z: calcLength, is2D: false}
 ];
 
-function expectedCssString(obj) {
-  var cssText = obj.is2D ? "translate(" : "translate3d(";
-  cssText += obj.x.toString() + ", " + obj.y.toString();
-  if (!obj.is2D)
-    cssText += ", " + obj.z.toString();
-  cssText += ")";
-  return cssText;
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.x, params.x);
+    assert_equals(params.input.y, params.y);
+    if (params.is2D) {
+      assert_equals(params.input.z, null);
+    } else {
+      assert_equals(params.input.z, params.z);
+    }
+  }, "x, y, and z values are correct for " + params.cssText);
 }
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.x, values[i].x);
-    assert_equals(values[i].input.y, values[i].y);
-    if (values[i].is2D)
-      assert_equals(values[i].input.z, null);
-    else
-      assert_equals(values[i].input.z, values[i].z);
-  }
-}, "Test that the (x, y, z) values for CSSTranslation are correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.is2D(), params.is2D);
+  }, "is2D value is correct for " + params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.is2D(), values[i].is2D);
-  }
-}, "Test that the is2D values for CSSTranslation is correct.");
+for (let params of testParams) {
+  test(() => {
+    assert_equals(params.input.toString(), params.cssText);
+  }, "toString value is correct for " + params.cssText);
+}
 
-test(function() {
-  for (var i = 0; i < values.length; ++i) {
-    assert_equals(values[i].input.toString(), expectedCssString(values[i]));
-  }
-}, "Test that toString values for CSSTranslation is correct.");
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new CSSTranslation(simpleLength, simpleLength, simplePercent);
+  });
+  // assert_throws(null, () => {
+  //  new CSSTranslation(simpleLength, simpleLength, calcPercent);
+  // });
+  assert_throws(new TypeError(), () => {
+    new CSSTranslation(simplePercent, simplePercent, simplePercent);
+  });
+}, "Constructor throws when z component contains percent.");
 
-test(function() {
-  assert_throws(new TypeError(), function() { new CSSTranslation(simpleLength, simpleLength, simplePercent); });
-  // assert_throws(null, function() { new CSSTranslation(simpleLength, simpleLength, calcPercent); });
-  assert_throws(new TypeError(), function() { new CSSTranslation(simplePercent, simplePercent, simplePercent); });
-}, "Test that CSSTranslation constructor throws when z component contains percent.");
-
-test(function() {
-  assert_throws(new TypeError(), function() { new CSSTranslation(); });
-  assert_throws(new TypeError(), function() { new CSSTranslation(simpleLength); });
-}, "Test that invalid number of arguments for CSSTranslation throws an exception.");
+test(() => {
+  assert_throws(new TypeError(), () => { new CSSTranslation(); });
+  assert_throws(new TypeError(), () => { new CSSTranslation(simpleLength); });
+}, "Invalid number of arguments to constructor throws an exception.");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/cssURLImageValue.html b/third_party/WebKit/LayoutTests/typedcssom/cssURLImageValue.html
index df636fd..060dc306 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/cssURLImageValue.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/cssURLImageValue.html
@@ -9,9 +9,16 @@
 
 <script>
 
-// list of available image properties
-var imageProperties = ["background-image", "border-image-source", "list-style-image", "content", "shape-outside"];
+var supportedCSSProperties = [
+  "background-image",
+  "border-image-source",
+  "list-style-image",
+  "content",
+  "shape-outside"
+];
 
+// Temporary helper function since CSSURLImageValue doesn't support relative
+// URLs yet. This can be removed once it does.
 function url() {
   var c = document.location.href.split('/');
   c[c.length - 1] = 'resources/1x1-green.png';
@@ -22,7 +29,8 @@
   return "data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=";
 }
 
-function assertCorrectURLImageValue(image, expectedUrl, expectedWidth, expectedHeight, expectedRatio) {
+function assertLoadedDotImageValue(
+    image, expectedUrl, expectedWidth, expectedHeight, expectedRatio) {
   assert_equals(image.constructor.name, CSSURLImageValue.name);
   assert_equals(image.url, expectedUrl);
   assert_equals(image.state, "loaded");
@@ -34,20 +42,16 @@
 test(function() {
   var image = new CSSURLImageValue(url());
   assert_equals(image.state, "unloaded");
-}, "Can construct a new CSSURLImageValue object with url");
+}, "State is unloaded after construction.");
 
 {
-  var test1 = async_test("Set available properties as CSSURLImageValue using URL");
+  var test1 = async_test("URL image changes state when loaded");
   var url1 = url();
 
   var imageValue1 = new CSSURLImageValue(url1);
 
-  for (var i = 0; i < imageProperties.length; ++i) {
-    if (imageProperties[i] == 'content') // content accepts a list of value
-      testImage1.styleMap.set(imageProperties[i], [imageValue1]);
-    else
-      testImage1.styleMap.set(imageProperties[i], imageValue1);
-  }
+  // Set it on an element so it can be loaded.
+  testImage1.styleMap.set(supportedCSSProperties[0], imageValue1);
 
   // add an Image object to know if the image has been loaded
   var image1 = new Image();
@@ -66,17 +70,12 @@
 }
 
 {
-  var test2 = async_test("Set available properties as CSSURLImageValue using base64 image");
+  var test2 = async_test("Base-64 image changes state when loaded");
   var url2 = base64Url();
 
   var imageValue2 = new CSSURLImageValue(url2);
-
-  for (var i = 0; i < imageProperties.length; ++i) {
-    if (imageProperties[i] == 'content') // content accepts a list of value
-      testImage2.styleMap.set(imageProperties[i], [imageValue2]);
-    else
-      testImage2.styleMap.set(imageProperties[i], imageValue2);
-  }
+  // Set it on an element so it can be loaded.
+  testImage2.styleMap.set(supportedCSSProperties[0], imageValue2);
 
   // add an Image object to know if the image has been loaded
   var image2 = new Image();
@@ -95,7 +94,7 @@
 }
 
 {
-  var test3 = async_test("Invalid Image will have get error state");
+  var test3 = async_test("Invalid Image will have error state after loading");
   var url3 = document.location.href;
   var imageValue3 = new CSSURLImageValue(url3);
 
@@ -117,17 +116,23 @@
   };
 }
 
-test(function() {
-  var url4 = base64Url();
+for (let property of supportedCSSProperties) {
+  test(function() {
+    testImage4.style[property] = 'url(' + base64Url() + ')';
 
-  for (var i = 0; i < imageProperties.length; ++i) {
-    testImage4.style[imageProperties[i]] = 'url(' + url4 + ')';
-  }
-
-  for (var i = 0; i < imageProperties.length; ++i) {
-    assertCorrectURLImageValue(getComputedStyleMap(testImage4).get(imageProperties[i]), url4, 1, 1, 1);
-    assertCorrectURLImageValue(testImage4.styleMap.get(imageProperties[i]), url4, 1, 1, 1);
-  }
-}, "Getting CSSURLImageValue from StyleMap");
+    assertLoadedDotImageValue(
+      getComputedStyleMap(testImage4).get(property),
+      base64Url(),
+      1,
+      1,
+      1);
+    assertLoadedDotImageValue(
+      testImage4.styleMap.get(property),
+      base64Url(),
+      1,
+      1,
+      1);
+}, "Getting CSSURLImageValue from StyleMap for " + property);
+    }
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/resources/comparisons.js b/third_party/WebKit/LayoutTests/typedcssom/resources/comparisons.js
new file mode 100644
index 0000000..70f867a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/typedcssom/resources/comparisons.js
@@ -0,0 +1,14 @@
+function assert_array_approx_equals(actual, expected, epsilon) {
+  for (var i = 0; i < actual.length; i++) {
+    assert_approx_equals(actual[i], expected[i], 1e-6);
+  }
+}
+
+/**
+ * Compares two instances of DOMMatrix.
+ */
+function assert_matrix_approx_equals(actual, expected, epsilon) {
+  assert_array_approx_equals(
+      actual.toFloat64Array(), expected.toFloat64Array(), epsilon);
+}
+
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/README.txt b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/README.txt
similarity index 69%
copy from third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/README.txt
copy to third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/README.txt
index c351a618..3eca2a3 100644
--- a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/README.txt
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/README.txt
@@ -1,3 +1,3 @@
 # This suite runs the tests in LayoutTests/paint/high-contrast-mode
-# with --blink-settings="highContrastMode=3"
+# with --blink-settings="highContrastMode=3,highContrastImagePolicy=0"
 # See the virtual_test_suites() method in Tools/Scripts/webkitpy/layout_tests/port/base.py.
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.png b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.png
new file mode 100644
index 0000000..ef36b5bc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.txt b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/gradient-invert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/image-invert-expected.png b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/image-invert-expected.png
new file mode 100644
index 0000000..0b738657
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/image-invert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/image-invert-expected.txt b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/image-invert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/image-invert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/text-on-backgrounds-expected.png b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/text-on-backgrounds-expected.png
rename to third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds-expected.txt b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.txt
similarity index 100%
copy from third_party/WebKit/LayoutTests/paint/high-contrast-mode/text-on-backgrounds-expected.txt
copy to third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-all/text-on-backgrounds-expected.txt
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/README.txt b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/README.txt
similarity index 69%
rename from third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/README.txt
rename to third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/README.txt
index c351a618..e1fcc9f4 100644
--- a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/README.txt
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/README.txt
@@ -1,3 +1,3 @@
 # This suite runs the tests in LayoutTests/paint/high-contrast-mode
-# with --blink-settings="highContrastMode=3"
+# with --blink-settings="highContrastMode=3,highContrastImagePolicy=1"
 # See the virtual_test_suites() method in Tools/Scripts/webkitpy/layout_tests/port/base.py.
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.png b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.png
new file mode 100644
index 0000000..f3564146
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.txt b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/gradient-noinvert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.png b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.png
new file mode 100644
index 0000000..e601838c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.txt b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/high-contrast-mode/paint/high-contrast-mode/image-filter-none/image-noinvert-expected.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
index 6749cd178..7cd3e57c 100755
--- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
+++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -27,15 +27,16 @@
     # Aligns like double
     'ScaleTransformOperation', 'RotateTransformOperation', 'TranslateTransformOperation', 'double',
     # Aligns like a pointer (can be 32 or 64 bits)
-    'StyleMotionData', 'TransformOperations', 'Vector<CSSPropertyID>', 'GridPosition', 'AtomicString',
-    'DataRef', 'RefPtr', 'DataPersistent', 'Persistent', 'std::unique_ptr',
+    'NamedGridLinesMap', 'OrderedNamedGridLines', 'NamedGridAreaMap', 'StyleMotionData', 'TransformOperations',
+    'Vector<CSSPropertyID>', 'Vector<GridTrackSize>', 'GridPosition', 'AtomicString',
+    'RefPtr', 'DataPersistent', 'Persistent', 'std::unique_ptr',
     'Vector<String>', 'Font', 'FillLayer', 'NinePieceImage',
     # Aligns like float
     'TransformOrigin', 'ScrollPadding', 'ScrollSnapMargin', 'LengthBox', 'LengthSize', 'FloatSize',
     'LengthPoint', 'Length', 'TextSizeAdjust', 'TabSize', 'float',
     # Aligns like int
     'ScrollSnapType', 'ScrollSnapAlign', 'BorderValue', 'StyleColor', 'Color', 'LayoutUnit',
-    'LineClampValue', 'OutlineValue', 'unsigned', 'int',
+    'LineClampValue', 'OutlineValue', 'unsigned', 'size_t', 'int',
     # Aligns like short
     'unsigned short', 'short',
     # Aligns like char
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
index 41e6f5a..48e80b2 100644
--- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
+++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -1047,13 +1047,13 @@
 inline CSSIdentifierValue::CSSIdentifierValue(EUserDrag e)
     : CSSValue(kIdentifierClass) {
   switch (e) {
-    case DRAG_AUTO:
+    case EUserDrag::kAuto:
       value_id_ = CSSValueAuto;
       break;
-    case DRAG_NONE:
+    case EUserDrag::kNone:
       value_id_ = CSSValueNone;
       break;
-    case DRAG_ELEMENT:
+    case EUserDrag::kElement:
       value_id_ = CSSValueElement;
       break;
     default:
@@ -1065,17 +1065,17 @@
 inline EUserDrag CSSIdentifierValue::ConvertTo() const {
   switch (value_id_) {
     case CSSValueAuto:
-      return DRAG_AUTO;
+      return EUserDrag::kAuto;
     case CSSValueNone:
-      return DRAG_NONE;
+      return EUserDrag::kNone;
     case CSSValueElement:
-      return DRAG_ELEMENT;
+      return EUserDrag::kElement;
     default:
       break;
   }
 
   NOTREACHED();
-  return DRAG_AUTO;
+  return EUserDrag::kAuto;
 }
 
 template <>
@@ -1885,10 +1885,10 @@
 inline CSSIdentifierValue::CSSIdentifierValue(ETransformStyle3D e)
     : CSSValue(kIdentifierClass) {
   switch (e) {
-    case kTransformStyle3DFlat:
+    case ETransformStyle3D::kFlat:
       value_id_ = CSSValueFlat;
       break;
-    case kTransformStyle3DPreserve3D:
+    case ETransformStyle3D::kPreserve3D:
       value_id_ = CSSValuePreserve3d;
       break;
   }
@@ -1898,15 +1898,15 @@
 inline ETransformStyle3D CSSIdentifierValue::ConvertTo() const {
   switch (value_id_) {
     case CSSValueFlat:
-      return kTransformStyle3DFlat;
+      return ETransformStyle3D::kFlat;
     case CSSValuePreserve3d:
-      return kTransformStyle3DPreserve3D;
+      return ETransformStyle3D::kPreserve3D;
     default:
       break;
   }
 
   NOTREACHED();
-  return kTransformStyle3DFlat;
+  return ETransformStyle3D::kFlat;
 }
 
 template <>
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 9cb415d55..9b7dcf2 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -114,9 +114,7 @@
     // Can only be set if the field template is storage_only.
     wrapper_pointer_name: {
       valid_type: "str",
-      // TODO(shend): Remove DataRef once we generate all of
-      // StyleRareNonInheritedData.
-      valid_values: ["RefPtr", "Persistent", "std::unique_ptr", "DataPersistent", "DataRef"],
+      valid_values: ["RefPtr", "Persistent", "std::unique_ptr", "DataPersistent"],
     },
 
     // - keywords: ["keyword1", "keyword2"]
@@ -1279,6 +1277,10 @@
       api_class: "CSSPropertyAPIGridAutoLine",
       converter: "ConvertGridTrackSizeList",
       runtime_flag: "CSSGridLayout",
+      type_name: "Vector<GridTrackSize>",
+      field_template: "storage_only",
+      default_value: "Vector<GridTrackSize>(1, GridTrackSize(Length(kAuto)))",
+      field_group: "rare-non-inherited->grid",
     },
     {
       name: "grid-auto-flow",
@@ -1287,12 +1289,20 @@
       converter: "ConvertGridAutoFlow",
       runtime_flag: "CSSGridLayout",
       type_name: "GridAutoFlow",
+      field_template: "storage_only",
+      default_value: "kAutoFlowRow",
+      field_size: 4, // TODO(shend): Make this use "kGridAutoFlowBits".
+      field_group: "rare-non-inherited->grid",
     },
     {
       name: "grid-auto-rows",
       api_class: "CSSPropertyAPIGridAutoLine",
       converter: "ConvertGridTrackSizeList",
       runtime_flag: "CSSGridLayout",
+      type_name: "Vector<GridTrackSize>",
+      field_template: "storage_only",
+      default_value: "Vector<GridTrackSize>(1, GridTrackSize(Length(kAuto)))",
+      field_group: "rare-non-inherited->grid",
     },
     {
       name: "grid-column-end",
@@ -1308,6 +1318,10 @@
       name: "grid-column-gap",
       converter: "ConvertLength",
       runtime_flag: "CSSGridLayout",
+      type_name: "Length",
+      field_template: "storage_only",
+      default_value: "Length(kFixed)",
+      field_group: "rare-non-inherited->grid",
     },
     {
       name: "grid-column-start",
@@ -1333,6 +1347,10 @@
       name: "grid-row-gap",
       converter: "ConvertLength",
       runtime_flag: "CSSGridLayout",
+      type_name: "Length",
+      field_template: "storage_only",
+      default_value: "Length(kFixed)",
+      field_group: "rare-non-inherited->grid",
     },
     {
       name: "grid-row-start",
@@ -1355,12 +1373,20 @@
       api_class: "CSSPropertyAPIGridTemplateLine",
       custom_all: true,
       runtime_flag: "CSSGridLayout",
+      field_template: "storage_only",
+      type_name: "Vector<GridTrackSize>",
+      field_group: "rare-non-inherited->grid",
+      default_value: "Vector<GridTrackSize>()",
     },
     {
       name: "grid-template-rows",
       api_class: "CSSPropertyAPIGridTemplateLine",
       custom_all: true,
       runtime_flag: "CSSGridLayout",
+      field_template: "storage_only",
+      type_name: "Vector<GridTrackSize>",
+      field_group: "rare-non-inherited->grid",
+      default_value: "Vector<GridTrackSize>()",
     },
     {
       name: "height",
@@ -1402,6 +1428,10 @@
       inherited: true,
       name_for_methods: "RespectImageOrientation",
       runtime_flag: "ImageOrientation",
+      field_template: "primitive",
+      type_name: "bool",
+      default_value: "false",
+      field_group: "rare-inherited",
     },
     {
       name: "isolation",
@@ -2581,7 +2611,7 @@
       name_for_methods: "TransformStyle3D",
       field_template: "storage_only",
       field_group: "rare-non-inherited",
-      default_value: "kTransformStyle3DFlat",
+      default_value: "ETransformStyle3D::kFlat",
       field_size: 1,
     },
     {
@@ -2749,7 +2779,7 @@
       name: "-webkit-box-flex",
       api_class: true,
       type_name: "float",
-      field_template: "storage_only",
+      field_template: "primitive",
       default_value: "0.0f",
       field_group: "rare-non-inherited->deprecated-flexible-box",
     },
@@ -2758,7 +2788,7 @@
       api_class: true,
       api_methods: ["parseSingleValue"],
       type_name: "unsigned",
-      field_template: "storage_only",
+      field_template: "primitive",
       default_value: "1",
       field_group: "rare-non-inherited->deprecated-flexible-box",
     },
@@ -3163,7 +3193,7 @@
       field_template: "storage_only",
       type_name: "EUserDrag",
       field_group: "rare-non-inherited",
-      default_value: "DRAG_AUTO",
+      default_value: "EUserDrag::kAuto",
       field_size: 2,
     },
     {
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
index 6758297..726f330 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
+++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
@@ -3290,7 +3290,7 @@
     }
     case CSSPropertyTransformStyle:
       return CSSIdentifierValue::Create(
-          (style.TransformStyle3D() == kTransformStyle3DPreserve3D)
+          (style.TransformStyle3D() == ETransformStyle3D::kPreserve3D)
               ? CSSValuePreserve3d
               : CSSValueFlat);
     case CSSPropertyTransitionDelay:
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5 b/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5
index 57759f4..5bb20ac 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5
+++ b/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5
@@ -53,7 +53,7 @@
                 "word-break", "overflow-wrap", "-webkit-line-break", 
                 "-webkit-text-security", "hyphens", "HyphenationLimitBefore", 
                 "HyphenationLimitAfter", "-webkit-hyphenate-character", 
-                "RespectImageOrientation", "-webkit-ruby-position", 
+                "image-orientation", "-webkit-ruby-position", 
                 "TextEmphasisMark", "TextEmphasisPosition", 
                 "TextEmphasisCustomMark", "text-justify", "text-orientation", 
                 "text-combine-upright", "tab-size", "text-size-adjust", 
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5 b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
index 8514f2b33..34172182 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
+++ b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
@@ -481,15 +481,6 @@
       field_size: 1,
       field_group: "rare-inherited",
     },
-    {
-      name: "RespectImageOrientation",
-      inherited: true,
-      field_template: "storage_only",
-      type_name: "bool",
-      default_value: "false",
-      field_size: 1,
-      field_group: "rare-inherited",
-    },
     // Though position: sticky is not itself an inherited property, being a
     // descendent of a sticky element changes some document lifecycle logic.
     {
@@ -856,7 +847,7 @@
     },
     {
       name: "WillChangeProperties",
-      field_template: "storage_only",
+      field_template: "external",
       type_name: "Vector<CSSPropertyID>",
       field_group: "rare-non-inherited->will-change",
       default_value: "Vector<CSSPropertyID>()",
@@ -864,19 +855,17 @@
     },
     {
       name: "WillChangeContents",
-      field_template: "storage_only",
-      type_name: "unsigned",
+      field_template: "primitive",
+      type_name: "bool",
       field_group: "rare-non-inherited->will-change",
       default_value: "false",
-      field_size: 1,
     },
     {
       name: "WillChangeScrollPosition",
-      field_template: "storage_only",
-      type_name: "unsigned",
+      field_template: "primitive",
+      type_name: "bool",
       field_group: "rare-non-inherited->will-change",
       default_value: "false",
-      field_size: 1,
     },
     {
       name: "ColumnRule",
@@ -950,16 +939,137 @@
       default_value: "Length()",
       include_paths: ["core/style/ScrollSnap.h"],
     },
-    // TODO(shend): These are subgroups and should be removed when we can
-    // generate them propertly.
     {
-      name: "GridData",
+      name: "NamedGridColumnLines",
       field_template: "storage_only",
-      type_name: "StyleGridData",
-      field_group: "rare-non-inherited",
-      default_value: "",
-      wrapper_pointer_name: "DataRef",
-      include_paths: ["core/style/StyleGridData.h"],
+      type_name: "NamedGridLinesMap",
+      field_group: "rare-non-inherited->grid",
+      default_value: "NamedGridLinesMap()",
+      include_paths: ["core/style/NamedGridLinesMap.h"],
+    },
+    {
+      name: "NamedGridRowLines",
+      field_template: "storage_only",
+      type_name: "NamedGridLinesMap",
+      field_group: "rare-non-inherited->grid",
+      default_value: "NamedGridLinesMap()",
+      include_paths: ["core/style/NamedGridLinesMap.h"],
+    },
+    {
+      name: "OrderedNamedGridColumnLines",
+      field_template: "storage_only",
+      type_name: "OrderedNamedGridLines",
+      field_group: "rare-non-inherited->grid",
+      default_value: "OrderedNamedGridLines()",
+      include_paths: ["core/style/OrderedNamedGridLines.h"],
+    },
+    {
+      name: "OrderedNamedGridRowLines",
+      field_template: "storage_only",
+      type_name: "OrderedNamedGridLines",
+      field_group: "rare-non-inherited->grid",
+      default_value: "OrderedNamedGridLines()",
+      include_paths: ["core/style/OrderedNamedGridLines.h"],
+    },
+    {
+      name: "AutoRepeatNamedGridColumnLines",
+      field_template: "storage_only",
+      type_name: "NamedGridLinesMap",
+      field_group: "rare-non-inherited->grid",
+      default_value: "NamedGridLinesMap()",
+      include_paths: ["core/style/NamedGridLinesMap.h"],
+    },
+    {
+      name: "AutoRepeatNamedGridRowLines",
+      field_template: "storage_only",
+      type_name: "NamedGridLinesMap",
+      field_group: "rare-non-inherited->grid",
+      default_value: "NamedGridLinesMap()",
+      include_paths: ["core/style/NamedGridLinesMap.h"],
+    },
+    {
+      name: "AutoRepeatOrderedNamedGridColumnLines",
+      field_template: "storage_only",
+      type_name: "OrderedNamedGridLines",
+      field_group: "rare-non-inherited->grid",
+      default_value: "OrderedNamedGridLines()",
+      include_paths: ["core/style/OrderedNamedGridLines.h"],
+    },
+    {
+      name: "AutoRepeatOrderedNamedGridRowLines",
+      field_template: "storage_only",
+      type_name: "OrderedNamedGridLines",
+      field_group: "rare-non-inherited->grid",
+      default_value: "OrderedNamedGridLines()",
+      include_paths: ["core/style/OrderedNamedGridLines.h"],
+    },
+    {
+      name: "NamedGridArea",
+      field_template: "storage_only",
+      type_name: "NamedGridAreaMap",
+      field_group: "rare-non-inherited->grid",
+      default_value: "NamedGridAreaMap()",
+      include_paths: ["core/style/GridArea.h"]
+    },
+    {
+      name: "NamedGridAreaRowCount",
+      field_template: "storage_only",
+      type_name: "size_t",
+      field_group: "rare-non-inherited->grid",
+      default_value: "0",
+    },
+    {
+      name: "NamedGridAreaColumnCount",
+      field_template: "storage_only",
+      type_name: "size_t",
+      field_group: "rare-non-inherited->grid",
+      default_value: "0",
+    },
+    {
+      name: "GridAutoRepeatColumns",
+      field_template: "storage_only",
+      type_name: "Vector<GridTrackSize>",
+      field_group: "rare-non-inherited->grid",
+      default_value: "Vector<GridTrackSize>()",
+      include_paths: ["platform/wtf/Vector.h", "core/style/GridTrackSize.h"],
+    },
+    {
+      name: "GridAutoRepeatRows",
+      field_template: "storage_only",
+      type_name: "Vector<GridTrackSize>",
+      field_group: "rare-non-inherited->grid",
+      default_value: "Vector<GridTrackSize>()",
+      include_paths: ["platform/wtf/Vector.h", "core/style/GridTrackSize.h"],
+    },
+    {
+      name: "AutoRepeatColumnsInsertionPoint",
+      field_template: "storage_only",
+      type_name: "size_t",
+      field_group: "rare-non-inherited->grid",
+      default_value: "0",
+    },
+    {
+      name: "AutoRepeatRowsInsertionPoint",
+      field_template: "storage_only",
+      type_name: "size_t",
+      field_group: "rare-non-inherited->grid",
+      default_value: "0",
+    },
+    {
+      name: "AutoRepeatColumnsType",
+      field_template: "storage_only",
+      type_name: "AutoRepeatType",
+      field_group: "rare-non-inherited->grid",
+      field_size: 3,
+      default_value: "kNoAutoRepeat",
+    },
+    {
+      name: "AutoRepeatRowsType",
+      field_template: "storage_only",
+      type_name: "AutoRepeatType",
+      field_group: "rare-non-inherited->grid",
+      field_size: 3,
+      default_value: "kNoAutoRepeat",
     },
   ],
 }
diff --git a/third_party/WebKit/Source/core/dom/Node.cpp b/third_party/WebKit/Source/core/dom/Node.cpp
index 1cf63d47..f0992ff 100644
--- a/third_party/WebKit/Source/core/dom/Node.cpp
+++ b/third_party/WebKit/Source/core/dom/Node.cpp
@@ -1066,7 +1066,7 @@
     // We allow selections to begin within an element that has
     // -webkit-user-select: none set, but if the element is draggable then
     // dragging should take priority over selection.
-    if (style.UserDrag() == DRAG_ELEMENT &&
+    if (style.UserDrag() == EUserDrag::kElement &&
         style.UserSelect() == EUserSelect::kNone)
       return false;
   }
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index 2dc6209..5b8387f0 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -3293,6 +3293,8 @@
         high_contrast_settings.mode = settings->GetHighContrastMode();
         high_contrast_settings.grayscale = settings->GetHighContrastGrayscale();
         high_contrast_settings.contrast = settings->GetHighContrastContrast();
+        high_contrast_settings.image_policy =
+            settings->GetHighContrastImagePolicy();
         graphics_context.SetHighContrast(high_contrast_settings);
       }
 
diff --git a/third_party/WebKit/Source/core/frame/Settings.json5 b/third_party/WebKit/Source/core/frame/Settings.json5
index 798ae0f..c2fd7ecd 100644
--- a/third_party/WebKit/Source/core/frame/Settings.json5
+++ b/third_party/WebKit/Source/core/frame/Settings.json5
@@ -948,6 +948,11 @@
       name: "highContrastContrast",
       initial: 0,
       type: "double",
+    },
+    {
+      name: "highContrastImagePolicy",
+      initial: "HighContrastImagePolicy::kFilterAll",
+      type: "HighContrastImagePolicy",
     }
   ],
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutImage.cpp b/third_party/WebKit/Source/core/layout/LayoutImage.cpp
index 0f9627c3..7ab5f843 100644
--- a/third_party/WebKit/Source/core/layout/LayoutImage.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutImage.cpp
@@ -69,9 +69,9 @@
                                  const ComputedStyle* old_style) {
   LayoutReplaced::StyleDidChange(diff, old_style);
 
-  RespectImageOrientationEnum old_orientation =
-      old_style ? old_style->RespectImageOrientation()
-                : ComputedStyle::InitialRespectImageOrientation();
+  bool old_orientation = old_style
+                             ? old_style->RespectImageOrientation()
+                             : ComputedStyle::InitialRespectImageOrientation();
   if (Style() && Style()->RespectImageOrientation() != old_orientation)
     IntrinsicSizeChanged();
 }
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index c43598b..8b48ce2 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -3075,6 +3075,7 @@
   high_contrast_settings.mode = settings->GetHighContrastMode();
   high_contrast_settings.grayscale = settings->GetHighContrastGrayscale();
   high_contrast_settings.contrast = settings->GetHighContrastContrast();
+  high_contrast_settings.image_policy = settings->GetHighContrastImagePolicy();
   context.SetHighContrast(high_contrast_settings);
 
   if (paint_info.paint_layer->GetCompositingState() !=
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp
index e412535..dcb80ee 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp
@@ -91,7 +91,7 @@
   if (style.HasInlineTransform())
     reasons |= kCompositingReasonInlineTransform;
 
-  if (style.UsedTransformStyle3D() == kTransformStyle3DPreserve3D)
+  if (style.UsedTransformStyle3D() == ETransformStyle3D::kPreserve3D)
     reasons |= kCompositingReasonPreserve3DWith3DDescendants;
 
   if (style.HasPerspective())
diff --git a/third_party/WebKit/Source/core/page/DragController.cpp b/third_party/WebKit/Source/core/page/DragController.cpp
index fafb0d4..6aea704 100644
--- a/third_party/WebKit/Source/core/page/DragController.cpp
+++ b/third_party/WebKit/Source/core/page/DragController.cpp
@@ -803,7 +803,7 @@
     }
     if (node->IsElementNode()) {
       EUserDrag drag_mode = layout_object->Style()->UserDrag();
-      if (drag_mode == DRAG_NONE)
+      if (drag_mode == EUserDrag::kNone)
         continue;
       // Even if the image is part of a selection, we always only drag the image
       // in this case.
@@ -813,7 +813,7 @@
         return node;
       }
       // Other draggable elements are considered unselectable.
-      if (drag_mode == DRAG_ELEMENT) {
+      if (drag_mode == EUserDrag::kElement) {
         candidate_drag_type = kDragSourceActionDHTML;
         break;
       }
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index ceda017..df813d4 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -355,7 +355,7 @@
   if (box.HasLayer() && box.Layer()->Has3DTransformedDescendant()) {
     if (style.HasPerspective())
       compositing_reasons |= kCompositingReasonPerspectiveWith3DDescendants;
-    if (style.UsedTransformStyle3D() == kTransformStyle3DPreserve3D)
+    if (style.UsedTransformStyle3D() == ETransformStyle3D::kPreserve3D)
       compositing_reasons |= kCompositingReasonPreserve3DWith3DDescendants;
   }
 
diff --git a/third_party/WebKit/Source/core/style/BUILD.gn b/third_party/WebKit/Source/core/style/BUILD.gn
index 94579e8f..69a18f5 100644
--- a/third_party/WebKit/Source/core/style/BUILD.gn
+++ b/third_party/WebKit/Source/core/style/BUILD.gn
@@ -74,8 +74,6 @@
     "StyleFilterData.h",
     "StyleGeneratedImage.cpp",
     "StyleGeneratedImage.h",
-    "StyleGridData.cpp",
-    "StyleGridData.h",
     "StyleImage.cpp",
     "StyleImage.h",
     "StyleInheritedVariables.cpp",
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index fd04bfa..c4c5603 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -121,8 +121,6 @@
 
 ALWAYS_INLINE ComputedStyle::ComputedStyle()
     : ComputedStyleBase(), RefCounted<ComputedStyle>() {
-  // TODO(shend): Generate these.
-  rare_non_inherited_data_.Access()->grid_data_.Init();
   svg_style_.Init();
 }
 
@@ -824,7 +822,7 @@
   // ComputedStyle::HasGroupingProperty().
   // This is legacy behavior that is left ambiguous in the official specs.
   // See crbug.com/663650 for more details."
-  if (TransformStyle3D() == kTransformStyle3DPreserve3D) {
+  if (TransformStyle3D() == ETransformStyle3D::kPreserve3D) {
     SetIsStackingContext(true);
     return;
   }
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index e985a32..d6e7dfe 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -51,7 +51,6 @@
 #include "core/style/StyleContentAlignmentData.h"
 #include "core/style/StyleDifference.h"
 #include "core/style/StyleFilterData.h"
-#include "core/style/StyleGridData.h"
 #include "core/style/StyleImage.h"
 #include "core/style/StyleInheritedVariables.h"
 #include "core/style/StyleOffsetRotation.h"
@@ -780,27 +779,6 @@
     SET_NESTED_VAR(rare_non_inherited_data_, flexible_box_data_, flex_wrap_, w);
   }
 
-  // -webkit-box-flex
-  static float InitialBoxFlex() { return 0.0f; }
-  float BoxFlex() const {
-    return rare_non_inherited_data_->deprecated_flexible_box_data_->box_flex_;
-  }
-  void SetBoxFlex(float f) {
-    SET_NESTED_VAR(rare_non_inherited_data_, deprecated_flexible_box_data_,
-                   box_flex_, f);
-  }
-
-  // -webkit-box-flex-group
-  static unsigned InitialBoxFlexGroup() { return 1; }
-  unsigned BoxFlexGroup() const {
-    return rare_non_inherited_data_->deprecated_flexible_box_data_
-        ->box_flex_group_;
-  }
-  void SetBoxFlexGroup(unsigned fg) {
-    SET_NESTED_VAR(rare_non_inherited_data_, deprecated_flexible_box_data_,
-                   box_flex_group_, fg);
-  }
-
   // -webkit-box-align
   // For valid values of box-align see
   // http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#alignment
@@ -1004,18 +982,6 @@
                    lengths);
   }
 
-  // image-orientation
-  static RespectImageOrientationEnum InitialRespectImageOrientation() {
-    return kDoNotRespectImageOrientation;
-  }
-  RespectImageOrientationEnum RespectImageOrientation() const {
-    return static_cast<RespectImageOrientationEnum>(
-        RespectImageOrientationInternal());
-  }
-  void SetRespectImageOrientation(RespectImageOrientationEnum v) {
-    SetRespectImageOrientationInternal(v);
-  }
-
   // mix-blend-mode
   static WebBlendMode InitialBlendMode() { return kWebBlendModeNormal; }
   WebBlendMode BlendMode() const {
@@ -1232,14 +1198,15 @@
 
   // transform-style (aka -webkit-transform-style)
   static ETransformStyle3D InitialTransformStyle3D() {
-    return kTransformStyle3DFlat;
+    return ETransformStyle3D::kFlat;
   }
   ETransformStyle3D TransformStyle3D() const {
     return static_cast<ETransformStyle3D>(
         rare_non_inherited_data_->transform_style_3d_);
   }
   void SetTransformStyle3D(ETransformStyle3D b) {
-    SET_VAR(rare_non_inherited_data_, transform_style_3d_, b);
+    SET_VAR(rare_non_inherited_data_, transform_style_3d_,
+            static_cast<unsigned>(b));
   }
 
   // -webkit-transform-origin-x
@@ -1618,30 +1585,6 @@
     SetVerticalAlignLengthInternal(length);
   }
 
-  // will-change
-  const Vector<CSSPropertyID>& WillChangeProperties() const {
-    return rare_non_inherited_data_->will_change_data_->will_change_properties_;
-  }
-  bool WillChangeContents() const {
-    return rare_non_inherited_data_->will_change_data_->will_change_contents_;
-  }
-  bool WillChangeScrollPosition() const {
-    return rare_non_inherited_data_->will_change_data_
-        ->will_change_scroll_position_;
-  }
-  void SetWillChangeProperties(const Vector<CSSPropertyID>& properties) {
-    SET_NESTED_VAR(rare_non_inherited_data_, will_change_data_,
-                   will_change_properties_, properties);
-  }
-  void SetWillChangeContents(bool b) {
-    SET_NESTED_VAR(rare_non_inherited_data_, will_change_data_,
-                   will_change_contents_, b);
-  }
-  void SetWillChangeScrollPosition(bool b) {
-    SET_NESTED_VAR(rare_non_inherited_data_, will_change_data_,
-                   will_change_scroll_position_, b);
-  }
-
   // z-index
   int ZIndex() const { return ZIndexInternal(); }
   bool HasAutoZIndex() const { return HasAutoZIndexInternal(); }
@@ -1796,12 +1739,12 @@
   }
 
   // -webkit-user-drag
-  static EUserDrag InitialUserDrag() { return DRAG_AUTO; }
+  static EUserDrag InitialUserDrag() { return EUserDrag::kAuto; }
   EUserDrag UserDrag() const {
     return static_cast<EUserDrag>(rare_non_inherited_data_->user_drag_);
   }
   void SetUserDrag(EUserDrag d) {
-    SET_VAR(rare_non_inherited_data_, user_drag_, d);
+    SET_VAR(rare_non_inherited_data_, user_drag_, static_cast<unsigned>(d));
   }
 
   // caret-color
@@ -3132,7 +3075,8 @@
                 .IsEmpty();
   }
   ETransformStyle3D UsedTransformStyle3D() const {
-    return HasGroupingProperty() ? kTransformStyle3DFlat : TransformStyle3D();
+    return HasGroupingProperty() ? ETransformStyle3D::kFlat
+                                 : TransformStyle3D();
   }
   // Returns whether the transform operations for |otherStyle| differ from the
   // operations for this style instance. Note that callers may want to also
@@ -3144,7 +3088,7 @@
            other.rare_non_inherited_data_->transform_data_;
   }
   bool Preserves3D() const {
-    return UsedTransformStyle3D() != kTransformStyle3DFlat;
+    return UsedTransformStyle3D() != ETransformStyle3D::kFlat;
   }
   enum ApplyTransformOrigin {
     kIncludeTransformOrigin,
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
index c381ef9..70e54f6d 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -167,7 +167,7 @@
 
 // CSS3 User Drag Values
 
-enum EUserDrag { DRAG_AUTO, DRAG_NONE, DRAG_ELEMENT };
+enum class EUserDrag { kAuto, kNone, kElement };
 
 // CSS3 Image Values
 enum EResize { RESIZE_NONE, RESIZE_BOTH, RESIZE_HORIZONTAL, RESIZE_VERTICAL };
@@ -222,7 +222,7 @@
   return a = a | b;
 }
 
-enum ETransformStyle3D { kTransformStyle3DFlat, kTransformStyle3DPreserve3D };
+enum class ETransformStyle3D { kFlat, kPreserve3D };
 
 enum OffsetRotationType { kOffsetRotationAuto, kOffsetRotationFixed };
 
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleTest.cpp b/third_party/WebKit/Source/core/style/ComputedStyleTest.cpp
index 47c93a97..537485f 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyleTest.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyleTest.cpp
@@ -70,11 +70,11 @@
 
 TEST(ComputedStyleTest, Preserve3dForceStackingContext) {
   RefPtr<ComputedStyle> style = ComputedStyle::Create();
-  style->SetTransformStyle3D(kTransformStyle3DPreserve3D);
+  style->SetTransformStyle3D(ETransformStyle3D::kPreserve3D);
   style->SetOverflowX(EOverflow::kHidden);
   style->SetOverflowY(EOverflow::kHidden);
   style->UpdateIsStackingContext(false, false);
-  EXPECT_EQ(kTransformStyle3DFlat, style->UsedTransformStyle3D());
+  EXPECT_EQ(ETransformStyle3D::kFlat, style->UsedTransformStyle3D());
   EXPECT_TRUE(style->IsStackingContext());
 }
 
diff --git a/third_party/WebKit/Source/core/style/DataEquivalency.h b/third_party/WebKit/Source/core/style/DataEquivalency.h
index 2308e3c..f233462 100644
--- a/third_party/WebKit/Source/core/style/DataEquivalency.h
+++ b/third_party/WebKit/Source/core/style/DataEquivalency.h
@@ -15,8 +15,6 @@
 template <typename T>
 class Member;
 template <typename T>
-class DataRef;
-template <typename T>
 class DataPersistent;
 
 template <typename T>
@@ -48,13 +46,6 @@
   return DataEquivalent(a.get(), b.get());
 }
 
-// TODO(shend): Remove this once all subgroups of StyleRareNonInheritedData are
-// generated
-template <typename T>
-bool DataEquivalent(const DataRef<T>& a, const DataRef<T>& b) {
-  return DataEquivalent(a.Get(), b.Get());
-}
-
 template <typename T>
 bool DataEquivalent(const DataPersistent<T>& a, const DataPersistent<T>& b) {
   return DataEquivalent(a.Get(), b.Get());
diff --git a/third_party/WebKit/Source/core/style/MemberCopy.h b/third_party/WebKit/Source/core/style/MemberCopy.h
index 4fef934..efe0c2a 100644
--- a/third_party/WebKit/Source/core/style/MemberCopy.h
+++ b/third_party/WebKit/Source/core/style/MemberCopy.h
@@ -34,13 +34,6 @@
   return v;
 }
 
-// TODO(shend): Remove this once all subgroups of StyleRareNonInheritedData are
-// generated
-template <typename T>
-DataRef<T> MemberCopy(const DataRef<T>& v) {
-  return v;
-}
-
 inline Persistent<ContentData> MemberCopy(const Persistent<ContentData>& v) {
   return v ? v->Clone() : nullptr;
 }
diff --git a/third_party/WebKit/Source/core/style/StyleGridData.cpp b/third_party/WebKit/Source/core/style/StyleGridData.cpp
deleted file mode 100644
index fdab2220..0000000
--- a/third_party/WebKit/Source/core/style/StyleGridData.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "core/style/StyleGridData.h"
-
-#include "core/style/ComputedStyle.h"
-
-namespace blink {
-
-StyleGridData::StyleGridData()
-    : grid_template_columns_(ComputedStyle::InitialGridTemplateColumns()),
-      grid_template_rows_(ComputedStyle::InitialGridTemplateRows()),
-      named_grid_column_lines_(ComputedStyle::InitialNamedGridColumnLines()),
-      named_grid_row_lines_(ComputedStyle::InitialNamedGridRowLines()),
-      ordered_named_grid_column_lines_(
-          ComputedStyle::InitialOrderedNamedGridColumnLines()),
-      ordered_named_grid_row_lines_(
-          ComputedStyle::InitialOrderedNamedGridRowLines()),
-      auto_repeat_named_grid_column_lines_(
-          ComputedStyle::InitialNamedGridColumnLines()),
-      auto_repeat_named_grid_row_lines_(
-          ComputedStyle::InitialNamedGridRowLines()),
-      auto_repeat_ordered_named_grid_column_lines_(
-          ComputedStyle::InitialOrderedNamedGridColumnLines()),
-      auto_repeat_ordered_named_grid_row_lines_(
-          ComputedStyle::InitialOrderedNamedGridRowLines()),
-      grid_auto_rows_(ComputedStyle::InitialGridAutoRows()),
-      grid_auto_columns_(ComputedStyle::InitialGridAutoColumns()),
-      named_grid_area_(ComputedStyle::InitialNamedGridArea()),
-      named_grid_area_row_count_(ComputedStyle::InitialNamedGridAreaCount()),
-      named_grid_area_column_count_(ComputedStyle::InitialNamedGridAreaCount()),
-      grid_column_gap_(ComputedStyle::InitialGridColumnGap()),
-      grid_row_gap_(ComputedStyle::InitialGridRowGap()),
-      grid_auto_repeat_columns_(ComputedStyle::InitialGridAutoRepeatTracks()),
-      grid_auto_repeat_rows_(ComputedStyle::InitialGridAutoRepeatTracks()),
-      auto_repeat_columns_insertion_point_(
-          ComputedStyle::InitialGridAutoRepeatInsertionPoint()),
-      auto_repeat_rows_insertion_point_(
-          ComputedStyle::InitialGridAutoRepeatInsertionPoint()),
-      grid_auto_flow_(ComputedStyle::InitialGridAutoFlow()),
-      auto_repeat_columns_type_(ComputedStyle::InitialGridAutoRepeatType()),
-      auto_repeat_rows_type_(ComputedStyle::InitialGridAutoRepeatType()) {}
-
-StyleGridData::StyleGridData(const StyleGridData& o)
-    : RefCounted<StyleGridData>(),
-      grid_template_columns_(o.grid_template_columns_),
-      grid_template_rows_(o.grid_template_rows_),
-      named_grid_column_lines_(o.named_grid_column_lines_),
-      named_grid_row_lines_(o.named_grid_row_lines_),
-      ordered_named_grid_column_lines_(o.ordered_named_grid_column_lines_),
-      ordered_named_grid_row_lines_(o.ordered_named_grid_row_lines_),
-      auto_repeat_named_grid_column_lines_(
-          o.auto_repeat_named_grid_column_lines_),
-      auto_repeat_named_grid_row_lines_(o.auto_repeat_named_grid_row_lines_),
-      auto_repeat_ordered_named_grid_column_lines_(
-          o.auto_repeat_ordered_named_grid_column_lines_),
-      auto_repeat_ordered_named_grid_row_lines_(
-          o.auto_repeat_ordered_named_grid_row_lines_),
-      grid_auto_rows_(o.grid_auto_rows_),
-      grid_auto_columns_(o.grid_auto_columns_),
-      named_grid_area_(o.named_grid_area_),
-      named_grid_area_row_count_(o.named_grid_area_row_count_),
-      named_grid_area_column_count_(o.named_grid_area_column_count_),
-      grid_column_gap_(o.grid_column_gap_),
-      grid_row_gap_(o.grid_row_gap_),
-      grid_auto_repeat_columns_(o.grid_auto_repeat_columns_),
-      grid_auto_repeat_rows_(o.grid_auto_repeat_rows_),
-      auto_repeat_columns_insertion_point_(
-          o.auto_repeat_columns_insertion_point_),
-      auto_repeat_rows_insertion_point_(o.auto_repeat_rows_insertion_point_),
-      grid_auto_flow_(o.grid_auto_flow_),
-      auto_repeat_columns_type_(o.auto_repeat_columns_type_),
-      auto_repeat_rows_type_(o.auto_repeat_rows_type_) {}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/style/StyleGridData.h b/third_party/WebKit/Source/core/style/StyleGridData.h
deleted file mode 100644
index 15707ae..0000000
--- a/third_party/WebKit/Source/core/style/StyleGridData.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef StyleGridData_h
-#define StyleGridData_h
-
-#include "core/style/ComputedStyleConstants.h"
-#include "core/style/GridArea.h"
-#include "core/style/GridTrackSize.h"
-#include "core/style/NamedGridLinesMap.h"
-#include "core/style/OrderedNamedGridLines.h"
-#include "platform/wtf/PassRefPtr.h"
-#include "platform/wtf/RefCounted.h"
-#include "platform/wtf/Vector.h"
-
-namespace blink {
-
-class StyleGridData : public RefCounted<StyleGridData> {
- public:
-  static PassRefPtr<StyleGridData> Create() {
-    return AdoptRef(new StyleGridData);
-  }
-  PassRefPtr<StyleGridData> Copy() const {
-    return AdoptRef(new StyleGridData(*this));
-  }
-
-  bool operator==(const StyleGridData& o) const {
-    return grid_template_columns_ == o.grid_template_columns_ &&
-           grid_template_rows_ == o.grid_template_rows_ &&
-           grid_auto_flow_ == o.grid_auto_flow_ &&
-           grid_auto_rows_ == o.grid_auto_rows_ &&
-           grid_auto_columns_ == o.grid_auto_columns_ &&
-           named_grid_column_lines_ == o.named_grid_column_lines_ &&
-           named_grid_row_lines_ == o.named_grid_row_lines_ &&
-           ordered_named_grid_column_lines_ ==
-               o.ordered_named_grid_column_lines_ &&
-           ordered_named_grid_row_lines_ == o.ordered_named_grid_row_lines_ &&
-           auto_repeat_named_grid_column_lines_ ==
-               o.auto_repeat_named_grid_column_lines_ &&
-           auto_repeat_named_grid_row_lines_ ==
-               o.auto_repeat_named_grid_row_lines_ &&
-           auto_repeat_ordered_named_grid_column_lines_ ==
-               o.auto_repeat_ordered_named_grid_column_lines_ &&
-           auto_repeat_ordered_named_grid_row_lines_ ==
-               o.auto_repeat_ordered_named_grid_row_lines_ &&
-           named_grid_area_ == o.named_grid_area_ &&
-           named_grid_area_ == o.named_grid_area_ &&
-           named_grid_area_row_count_ == o.named_grid_area_row_count_ &&
-           named_grid_area_column_count_ == o.named_grid_area_column_count_ &&
-           grid_column_gap_ == o.grid_column_gap_ &&
-           grid_row_gap_ == o.grid_row_gap_ &&
-           grid_auto_repeat_columns_ == o.grid_auto_repeat_columns_ &&
-           grid_auto_repeat_rows_ == o.grid_auto_repeat_rows_ &&
-           auto_repeat_columns_insertion_point_ ==
-               o.auto_repeat_columns_insertion_point_ &&
-           auto_repeat_rows_insertion_point_ ==
-               o.auto_repeat_rows_insertion_point_ &&
-           auto_repeat_columns_type_ == o.auto_repeat_columns_type_ &&
-           auto_repeat_rows_type_ == o.auto_repeat_rows_type_;
-  }
-
-  bool operator!=(const StyleGridData& o) const { return !(*this == o); }
-
-  Vector<GridTrackSize> grid_template_columns_;
-  Vector<GridTrackSize> grid_template_rows_;
-
-  NamedGridLinesMap named_grid_column_lines_;
-  NamedGridLinesMap named_grid_row_lines_;
-
-  // In order to reconstruct the original named grid line order, we can't rely
-  // on NamedGridLinesMap as it loses the position if multiple grid lines are
-  // set on a single track.
-  OrderedNamedGridLines ordered_named_grid_column_lines_;
-  OrderedNamedGridLines ordered_named_grid_row_lines_;
-
-  NamedGridLinesMap auto_repeat_named_grid_column_lines_;
-  NamedGridLinesMap auto_repeat_named_grid_row_lines_;
-  OrderedNamedGridLines auto_repeat_ordered_named_grid_column_lines_;
-  OrderedNamedGridLines auto_repeat_ordered_named_grid_row_lines_;
-
-  Vector<GridTrackSize> grid_auto_rows_;
-  Vector<GridTrackSize> grid_auto_columns_;
-
-  NamedGridAreaMap named_grid_area_;
-  // Because m_namedGridArea doesn't store the unnamed grid areas, we need to
-  // keep track of the explicit grid size defined by both named and unnamed grid
-  // areas.
-  size_t named_grid_area_row_count_;
-  size_t named_grid_area_column_count_;
-
-  Length grid_column_gap_;
-  Length grid_row_gap_;
-
-  Vector<GridTrackSize> grid_auto_repeat_columns_;
-  Vector<GridTrackSize> grid_auto_repeat_rows_;
-
-  size_t auto_repeat_columns_insertion_point_;
-  size_t auto_repeat_rows_insertion_point_;
-
-  unsigned grid_auto_flow_ : kGridAutoFlowBits;
-  unsigned auto_repeat_columns_type_ : 3;
-  unsigned auto_repeat_rows_type_ : 3;
-
- private:
-  StyleGridData();
-  StyleGridData(const StyleGridData&);
-};
-
-}  // namespace blink
-
-#endif  // StyleGridData_h
diff --git a/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp b/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp
index e31408b..bc24324 100644
--- a/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp
+++ b/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp
@@ -10,6 +10,7 @@
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
 #include "core/dom/ExecutionContext.h"
+#include "core/dom/UserGestureIndicator.h"
 #include "modules/push_messaging/PushController.h"
 #include "modules/push_messaging/PushError.h"
 #include "modules/push_messaging/PushPermissionStatusCallbacks.h"
@@ -76,11 +77,13 @@
                                "Document is detached from window."));
     PushController::ClientFrom(document->GetFrame())
         .Subscribe(registration_->WebRegistration(), web_options,
+                   UserGestureIndicator::ProcessingUserGestureThreadSafe(),
                    WTF::MakeUnique<PushSubscriptionCallbacks>(resolver,
                                                               registration_));
   } else {
     PushProvider()->Subscribe(
         registration_->WebRegistration(), web_options,
+        UserGestureIndicator::ProcessingUserGestureThreadSafe(),
         WTF::MakeUnique<PushSubscriptionCallbacks>(resolver, registration_));
   }
 
diff --git a/third_party/WebKit/Source/platform/graphics/Gradient.cpp b/third_party/WebKit/Source/platform/graphics/Gradient.cpp
index af08ed4..477657d 100644
--- a/third_party/WebKit/Source/platform/graphics/Gradient.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Gradient.cpp
@@ -104,12 +104,20 @@
     // 0.0 comes through cleanly and people aren't likely to want a gradient
     // with a stop at (0 + epsilon).
     pos.push_back(WebCoreFloatToSkScalar(0));
-    colors.push_back(MakeSkColor(stops_.front().color));
+    if (color_filter_) {
+      colors.push_back(
+          color_filter_->filterColor(MakeSkColor(stops_.front().color)));
+    } else {
+      colors.push_back(MakeSkColor(stops_.front().color));
+    }
   }
 
   for (const auto& stop : stops_) {
     pos.push_back(WebCoreFloatToSkScalar(stop.stop));
-    colors.push_back(MakeSkColor(stop.color));
+    if (color_filter_)
+      colors.push_back(color_filter_->filterColor(MakeSkColor(stop.color)));
+    else
+      colors.push_back(MakeSkColor(stop.color));
   }
 
   // Copy the last stop to 1.0 if needed. See comment above about this float
@@ -160,7 +168,10 @@
 
 void Gradient::ApplyToFlags(PaintFlags& flags, const SkMatrix& local_matrix) {
   if (!cached_shader_ ||
-      local_matrix != cached_shader_->sk_shader()->getLocalMatrix()) {
+      local_matrix != cached_shader_->sk_shader()->getLocalMatrix() ||
+      flags.getColorFilter() != color_filter_.get()) {
+    color_filter_ = flags.refColorFilter();
+    flags.setColorFilter(nullptr);
     cached_shader_ = CreateShaderInternal(local_matrix);
   }
 
diff --git a/third_party/WebKit/Source/platform/graphics/Gradient.h b/third_party/WebKit/Source/platform/graphics/Gradient.h
index b7a1c6a5..19f952a 100644
--- a/third_party/WebKit/Source/platform/graphics/Gradient.h
+++ b/third_party/WebKit/Source/platform/graphics/Gradient.h
@@ -113,6 +113,8 @@
   std::unique_ptr<PaintShader> CreateShaderInternal(
       const SkMatrix& local_matrix);
 
+  sk_sp<SkColorFilter> color_filter_;
+
   void SortStopsIfNecessary();
   void FillSkiaStops(ColorBuffer&, OffsetBuffer&) const;
 
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp
index 11df85a..e60f829 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp
@@ -795,6 +795,8 @@
   image_flags.setColor(SK_ColorBLACK);
   image_flags.setFilterQuality(ComputeFilterQuality(image, dest, src));
   image_flags.setAntiAlias(ShouldAntialias());
+  if (ShouldApplyHighContrastFilterToImage(*image))
+    image_flags.setColorFilter(high_contrast_filter_);
   image->Draw(canvas_, image_flags, dest, src, should_respect_image_orientation,
               Image::kClampImageToSourceRect);
   paint_controller_.SetImagePainted();
@@ -1320,6 +1322,15 @@
   return nullptr;
 }
 
+bool GraphicsContext::ShouldApplyHighContrastFilterToImage(
+    const Image& image) const {
+  if (!high_contrast_filter_)
+    return false;
+
+  return high_contrast_settings_.image_policy ==
+         HighContrastImagePolicy::kFilterAll;
+}
+
 Color GraphicsContext::ApplyHighContrastFilter(const Color& input) const {
   if (!high_contrast_filter_)
     return input;
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsContext.h b/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
index 5c0725af..04d3678 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
@@ -438,6 +438,7 @@
 
   const SkMetaData& MetaData() const { return meta_data_; }
 
+  bool ShouldApplyHighContrastFilterToImage(const Image&) const;
   Color ApplyHighContrastFilter(const Color& input) const;
   PaintFlags ApplyHighContrastFilter(const PaintFlags* input) const;
 
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
index 4e99bba..846be66 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
@@ -58,9 +58,11 @@
   GraphicsLayerTest() {
     clip_layer_ = WTF::WrapUnique(new FakeGraphicsLayer(&client_));
     scroll_elasticity_layer_ = WTF::WrapUnique(new FakeGraphicsLayer(&client_));
+    page_scale_layer_ = WTF::WrapUnique(new FakeGraphicsLayer(&client_));
     graphics_layer_ = WTF::WrapUnique(new FakeGraphicsLayer(&client_));
     clip_layer_->AddChild(scroll_elasticity_layer_.get());
-    scroll_elasticity_layer_->AddChild(graphics_layer_.get());
+    scroll_elasticity_layer_->AddChild(page_scale_layer_.get());
+    page_scale_layer_->AddChild(graphics_layer_.get());
     graphics_layer_->PlatformLayer()->SetScrollClipLayer(
         clip_layer_->PlatformLayer());
     platform_layer_ = graphics_layer_->PlatformLayer();
@@ -68,9 +70,9 @@
     DCHECK(layer_tree_view_);
     layer_tree_view_->SetRootLayer(*clip_layer_->PlatformLayer());
     layer_tree_view_->RegisterViewportLayers(
-        scroll_elasticity_layer_->PlatformLayer(), clip_layer_->PlatformLayer(),
-        clip_layer_->PlatformLayer(), nullptr, graphics_layer_->PlatformLayer(),
-        nullptr);
+        scroll_elasticity_layer_->PlatformLayer(),
+        page_scale_layer_->PlatformLayer(), clip_layer_->PlatformLayer(),
+        nullptr, graphics_layer_->PlatformLayer(), nullptr);
     layer_tree_view_->SetViewportSize(WebSize(1, 1));
   }
 
@@ -84,6 +86,7 @@
  protected:
   WebLayer* platform_layer_;
   std::unique_ptr<FakeGraphicsLayer> graphics_layer_;
+  std::unique_ptr<FakeGraphicsLayer> page_scale_layer_;
   std::unique_ptr<FakeGraphicsLayer> scroll_elasticity_layer_;
   std::unique_ptr<FakeGraphicsLayer> clip_layer_;
 
diff --git a/third_party/WebKit/Source/platform/graphics/HighContrastSettings.h b/third_party/WebKit/Source/platform/graphics/HighContrastSettings.h
index 0d5ad2f..237bfe4 100644
--- a/third_party/WebKit/Source/platform/graphics/HighContrastSettings.h
+++ b/third_party/WebKit/Source/platform/graphics/HighContrastSettings.h
@@ -16,10 +16,18 @@
   kInvertLightness,
 };
 
+enum class HighContrastImagePolicy {
+  // Apply high-contrast filter to all images.
+  kFilterAll,
+  // Never apply high-contrast filter to any images.
+  kFilterNone,
+};
+
 struct HighContrastSettings {
   HighContrastMode mode = HighContrastMode::kOff;
   bool grayscale = false;
   float contrast = 0.0;  // Valid range from -1.0 to 1.0
+  HighContrastImagePolicy image_policy = HighContrastImagePolicy::kFilterAll;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h b/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h
index da93df9..51f4f8d 100644
--- a/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h
+++ b/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h
@@ -23,6 +23,7 @@
   // Ownership of the callbacks is transferred to the client.
   virtual void Subscribe(WebServiceWorkerRegistration*,
                          const WebPushSubscriptionOptions&,
+                         bool user_gesture,
                          std::unique_ptr<WebPushSubscriptionCallbacks>) = 0;
 };
 
diff --git a/third_party/WebKit/public/platform/modules/push_messaging/WebPushProvider.h b/third_party/WebKit/public/platform/modules/push_messaging/WebPushProvider.h
index 3256746..3cb28c8 100644
--- a/third_party/WebKit/public/platform/modules/push_messaging/WebPushProvider.h
+++ b/third_party/WebKit/public/platform/modules/push_messaging/WebPushProvider.h
@@ -31,6 +31,7 @@
   // Does not take ownership of the WebServiceWorkerRegistration.
   virtual void Subscribe(WebServiceWorkerRegistration*,
                          const WebPushSubscriptionOptions&,
+                         bool user_gesture,
                          std::unique_ptr<WebPushSubscriptionCallbacks>) = 0;
 
   // Takes ownership of the WebPushSubscriptionCallbacks.
diff --git a/tools/clang/scripts/run_tool.py b/tools/clang/scripts/run_tool.py
index ce4888c..5f3fefa6 100755
--- a/tools/clang/scripts/run_tool.py
+++ b/tools/clang/scripts/run_tool.py
@@ -188,6 +188,13 @@
 
 def main():
   parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--options-file',
+      help='optional file to read options from')
+  args, argv = parser.parse_known_args()
+  if args.options_file:
+    argv = open(args.options_file).read().split()
+
   parser.add_argument('--tool', required=True, help='clang tool to run')
   parser.add_argument('--all', action='store_true')
   parser.add_argument(
@@ -208,7 +215,7 @@
   parser.add_argument(
       '--tool-args', nargs='*',
       help='optional arguments passed to the tool')
-  args = parser.parse_args()
+  args = parser.parse_args(argv)
 
   os.environ['PATH'] = '%s%s%s' % (
       os.path.abspath(os.path.join(
diff --git a/ui/accessibility/DEPS b/ui/accessibility/DEPS
index f3b2cb5..de7564a 100644
--- a/ui/accessibility/DEPS
+++ b/ui/accessibility/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+third_party/iaccessible2",
+  "+third_party/skia",
   "+ui/aura",
   "+ui/base",
   "+ui/base/l10n",
diff --git a/ui/accessibility/platform/ax_fake_caret_win.cc b/ui/accessibility/platform/ax_fake_caret_win.cc
index 8b645dc..6dc98e5 100644
--- a/ui/accessibility/platform/ax_fake_caret_win.cc
+++ b/ui/accessibility/platform/ax_fake_caret_win.cc
@@ -53,6 +53,11 @@
   return data_;
 }
 
+const ui::AXTreeData& AXFakeCaretWin::GetTreeData() const {
+  CR_DEFINE_STATIC_LOCAL(ui::AXTreeData, empty_data, ());
+  return empty_data;
+}
+
 gfx::NativeWindow AXFakeCaretWin::GetTopLevelWidget() {
   return nullptr;
 }
diff --git a/ui/accessibility/platform/ax_fake_caret_win.h b/ui/accessibility/platform/ax_fake_caret_win.h
index d2a5bba..732e596 100644
--- a/ui/accessibility/platform/ax_fake_caret_win.h
+++ b/ui/accessibility/platform/ax_fake_caret_win.h
@@ -11,6 +11,7 @@
 #include "base/win/scoped_comptr.h"
 #include "ui/accessibility/ax_export.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
@@ -34,6 +35,7 @@
  private:
   // |AXPlatformNodeDelegate| members.
   const AXNodeData& GetData() const override;
+  const ui::AXTreeData& GetTreeData() const override;
   gfx::NativeWindow GetTopLevelWidget() override;
   gfx::NativeViewAccessible GetParent() override;
   int GetChildCount() override;
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 4e830355..953172e 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -213,4 +213,79 @@
   return delegate_->AccessibilityPerformAction(action_data);
 }
 
+bool AXPlatformNodeBase::IsTextOnlyObject() const {
+  return GetData().role == ui::AX_ROLE_STATIC_TEXT ||
+         GetData().role == ui::AX_ROLE_LINE_BREAK ||
+         GetData().role == ui::AX_ROLE_INLINE_TEXT_BOX;
+}
+
+bool AXPlatformNodeBase::IsNativeTextControl() const {
+  const std::string& html_tag = GetStringAttribute(ui::AX_ATTR_HTML_TAG);
+  if (html_tag == "input") {
+    std::string input_type;
+    if (!GetData().GetHtmlAttribute("type", &input_type))
+      return true;
+    return input_type.empty() || input_type == "email" ||
+           input_type == "password" || input_type == "search" ||
+           input_type == "tel" || input_type == "text" || input_type == "url" ||
+           input_type == "number";
+  }
+  return html_tag == "textarea";
+}
+
+bool AXPlatformNodeBase::IsSimpleTextControl() const {
+  // Time fields, color wells and spinner buttons might also use text fields as
+  // constituent parts, but they are not considered text fields as a whole.
+  switch (GetData().role) {
+    case ui::AX_ROLE_COMBO_BOX:
+    case ui::AX_ROLE_SEARCH_BOX:
+      return true;
+    case ui::AX_ROLE_TEXT_FIELD:
+      return !GetData().HasState(ui::AX_STATE_RICHLY_EDITABLE);
+    default:
+      return false;
+  }
+}
+
+// Indicates if this object is at the root of a rich edit text control.
+bool AXPlatformNodeBase::IsRichTextControl() {
+  gfx::NativeViewAccessible parent_accessible = GetParent();
+  AXPlatformNodeBase* parent = FromNativeViewAccessible(parent_accessible);
+  if (!parent)
+    return false;
+
+  return GetData().HasState(ui::AX_STATE_RICHLY_EDITABLE) &&
+         (!parent || !parent->GetData().HasState(ui::AX_STATE_RICHLY_EDITABLE));
+}
+
+base::string16 AXPlatformNodeBase::GetInnerText() {
+  if (IsTextOnlyObject())
+    return GetString16Attribute(ui::AX_ATTR_NAME);
+
+  base::string16 text;
+  for (int i = 0; i < GetChildCount(); ++i) {
+    gfx::NativeViewAccessible child_accessible = ChildAtIndex(i);
+    AXPlatformNodeBase* child = FromNativeViewAccessible(child_accessible);
+    if (!child)
+      continue;
+
+    text += child->GetInnerText();
+  }
+  return text;
+}
+
+bool AXPlatformNodeBase::IsRangeValueSupported() const {
+  switch (GetData().role) {
+    case ui::AX_ROLE_PROGRESS_INDICATOR:
+    case ui::AX_ROLE_SLIDER:
+    case ui::AX_ROLE_SPIN_BUTTON:
+    case ui::AX_ROLE_SCROLL_BAR:
+      return true;
+    case ui::AX_ROLE_SPLITTER:
+      return GetData().HasState(ui::AX_STATE_FOCUSABLE);
+    default:
+      return false;
+  }
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index 4e3c3b0..17f53c22 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -68,6 +68,16 @@
   AXPlatformNodeBase();
   ~AXPlatformNodeBase() override;
 
+  bool IsTextOnlyObject() const;
+  bool IsNativeTextControl() const;
+  bool IsSimpleTextControl() const;
+  bool IsRichTextControl();
+  bool IsRangeValueSupported() const;
+
+  // |GetInnerText| recursively includes all the text from descendants such as
+  // text found in any embedded object.
+  base::string16 GetInnerText();
+
   // Cast a gfx::NativeViewAccessible to an AXPlatformNodeBase if it is one,
   // or return NULL if it's not an instance of this class.
   static AXPlatformNodeBase* FromNativeViewAccessible(
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index 6960a44..11cf9cec 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -14,6 +14,7 @@
 
 struct AXActionData;
 struct AXNodeData;
+struct AXTreeData;
 class AXPlatformNode;
 
 // An object that wants to be accessible should derive from this class.
@@ -36,6 +37,9 @@
   // is mostly to implement support for walking the accessibility tree.
   virtual const AXNodeData& GetData() const = 0;
 
+  // Get the accessibility tree data for this node.
+  virtual const ui::AXTreeData& GetTreeData() const = 0;
+
   // Get the window the node is contained in.
   virtual gfx::NativeWindow GetTopLevelWidget() = 0;
 
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 7fd3d70e..5cb972b 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -10,13 +10,17 @@
 
 #include "base/containers/hash_tables.h"
 #include "base/lazy_instance.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/win/scoped_comptr.h"
 #include "base/win/scoped_variant.h"
 #include "third_party/iaccessible2/ia2_api_all.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/accessibility/ax_text_utils.h"
+#include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 #include "ui/base/win/atl_module.h"
@@ -536,7 +540,86 @@
 STDMETHODIMP AXPlatformNodeWin::get_accValue(VARIANT var_id, BSTR* value) {
   AXPlatformNodeWin* target;
   COM_OBJECT_VALIDATE_VAR_ID_1_ARG_AND_GET_TARGET(var_id, value, target);
-  return target->GetStringAttributeAsBstr(ui::AX_ATTR_VALUE, value);
+
+  // get_accValue() has two sets of special cases depending on the node's role.
+  // The first set apply without regard for the nodes |value| attribute. That is
+  // the nodes value attribute isn't consider for the first set of special
+  // cases. For example, if the node role is AX_ROLE_COLOR_WELL, we do not care
+  // at all about the node's AX_ATTR_VALUE attribute. The second set of special
+  // cases only apply if the value attribute for the node is empty.  That is, if
+  // AX_ATTR_VALUE is empty, we do something special.
+
+  base::string16 result;
+
+  //
+  // Color Well special case (Use AX_ATTR_COLOR_VALUE)
+  //
+  if (target->GetData().role == ui::AX_ROLE_COLOR_WELL) {
+    unsigned int color = static_cast<unsigned int>(target->GetIntAttribute(
+        ui::AX_ATTR_COLOR_VALUE));  // todo, why the static cast?
+
+    unsigned int red = SkColorGetR(color);
+    unsigned int green = SkColorGetG(color);
+    unsigned int blue = SkColorGetB(color);
+    base::string16 value_text;
+    value_text = base::UintToString16(red * 100 / 255) + L"% red " +
+                 base::UintToString16(green * 100 / 255) + L"% green " +
+                 base::UintToString16(blue * 100 / 255) + L"% blue";
+    *value = SysAllocString(value_text.c_str());
+    DCHECK(*value);
+    return S_OK;
+  }
+
+  //
+  // Document special case (Use the document's url)
+  //
+  if (target->GetData().role == ui::AX_ROLE_ROOT_WEB_AREA ||
+      target->GetData().role == ui::AX_ROLE_WEB_AREA) {
+    result = base::UTF8ToUTF16(target->delegate_->GetTreeData().url);
+    *value = SysAllocString(result.c_str());
+    DCHECK(*value);
+    return S_OK;
+  }
+
+  //
+  // Links (Use AX_ATTR_URL)
+  //
+  if (target->GetData().role == ui::AX_ROLE_LINK ||
+      target->GetData().role == ui::AX_ROLE_IMAGE_MAP_LINK) {
+    result = target->GetString16Attribute(ui::AX_ATTR_URL);
+    *value = SysAllocString(result.c_str());
+    DCHECK(*value);
+    return S_OK;
+  }
+
+  // After this point, the role based special cases should test for an empty
+  // result.
+
+  result = target->GetString16Attribute(ui::AX_ATTR_VALUE);
+
+  //
+  // RangeValue (Use AX_ATTR_VALUE_FOR_RANGE)
+  //
+  if (result.empty() && target->IsRangeValueSupported()) {
+    float fval;
+    if (target->GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) {
+      result = base::UTF8ToUTF16(base::DoubleToString(fval));
+      *value = SysAllocString(result.c_str());
+      DCHECK(*value);
+      return S_OK;
+    }
+  }
+
+  // Last resort (Use innerText)
+  if (result.empty() &&
+      (target->IsSimpleTextControl() || target->IsRichTextControl()) &&
+      !target->IsNativeTextControl()) {
+    result = target->GetInnerText();
+  }
+
+  *value = SysAllocString(result.c_str());
+  DCHECK(*value);
+  return S_OK;
 }
 
 STDMETHODIMP AXPlatformNodeWin::put_accValue(VARIANT var_id,
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index 10ab594..2276640 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -76,6 +76,10 @@
   return node_->data();
 }
 
+const ui::AXTreeData& TestAXNodeWrapper::GetTreeData() const {
+  return tree_->data();
+}
+
 gfx::NativeWindow TestAXNodeWrapper::GetTopLevelWidget() {
   return nullptr;
 }
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.h b/ui/accessibility/platform/test_ax_node_wrapper.h
index 4d3f2b9..52b96cb3 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.h
+++ b/ui/accessibility/platform/test_ax_node_wrapper.h
@@ -30,6 +30,7 @@
 
   // AXPlatformNodeDelegate.
   const AXNodeData& GetData() const override;
+  const ui::AXTreeData& GetTreeData() const override;
   gfx::NativeWindow GetTopLevelWidget() override;
   gfx::NativeViewAccessible GetParent() override;
   int GetChildCount() override;
diff --git a/ui/android/java/src/org/chromium/ui/base/EventForwarder.java b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
index 9f832c7..0997a52c 100644
--- a/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
+++ b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
@@ -155,9 +155,35 @@
                 || eventAction == MotionEvent.ACTION_POINTER_UP;
     }
 
+    /**
+     * @see View#onHoverEvent(MotionEvent)
+     */
+    public boolean onHoverEvent(MotionEvent event) {
+        TraceEvent.begin("onHoverEvent");
+        try {
+            return sendNativeMouseEvent(event);
+        } finally {
+            TraceEvent.end("onHoverEvent");
+        }
+    }
+
+    /**
+     * @see View#onMouseEvent(MotionEvent)
+     */
     public boolean onMouseEvent(MotionEvent event) {
         TraceEvent.begin("sendMouseEvent");
+        try {
+            return sendNativeMouseEvent(event);
+        } finally {
+            TraceEvent.end("sendMouseEvent");
+        }
+    }
 
+    /**
+     * Sends mouse event to native. Hover event is also converted to mouse event,
+     * only differentiated by an internal flag.
+     */
+    private boolean sendNativeMouseEvent(MotionEvent event) {
         assert mNativeEventForwarder != 0;
 
         MotionEvent offsetEvent = createOffsetMotionEvent(event);
@@ -211,7 +237,6 @@
             return true;
         } finally {
             offsetEvent.recycle();
-            TraceEvent.end("sendMouseEvent");
         }
     }
 
diff --git a/ui/ozone/demo/ozone_demo.cc b/ui/ozone/demo/ozone_demo.cc
index 33bebf53..d14c60cc 100644
--- a/ui/ozone/demo/ozone_demo.cc
+++ b/ui/ozone/demo/ozone_demo.cc
@@ -199,6 +199,10 @@
 }
 
 bool RendererFactory::Initialize() {
+  ui::OzonePlatform::InitParams params;
+  params.single_process = true;
+  ui::OzonePlatform::InitializeForGPU(params);
+
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(kDisableGpu) && gl::init::InitializeGLOneOff() &&
       gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get(),
diff --git a/ui/views/accessibility/native_view_accessibility_auralinux.cc b/ui/views/accessibility/native_view_accessibility_auralinux.cc
index 89b42737..a5a06d4d 100644
--- a/ui/views/accessibility/native_view_accessibility_auralinux.cc
+++ b/ui/views/accessibility/native_view_accessibility_auralinux.cc
@@ -72,6 +72,11 @@
 
   const ui::AXNodeData& GetData() const override { return data_; }
 
+  const ui::AXTreeData& GetTreeData() const override {
+    CR_DEFINE_STATIC_LOCAL(ui::AXTreeData, empty_data, ());
+    return empty_data;
+  }
+
   gfx::NativeWindow GetTopLevelWidget() override { return nullptr; }
 
   gfx::NativeViewAccessible GetParent() override {
diff --git a/ui/views/accessibility/native_view_accessibility_base.cc b/ui/views/accessibility/native_view_accessibility_base.cc
index 300e86a0..49a06e54 100644
--- a/ui/views/accessibility/native_view_accessibility_base.cc
+++ b/ui/views/accessibility/native_view_accessibility_base.cc
@@ -97,6 +97,11 @@
   return data_;
 }
 
+const ui::AXTreeData& NativeViewAccessibilityBase::GetTreeData() const {
+  CR_DEFINE_STATIC_LOCAL(ui::AXTreeData, empty_data, ());
+  return empty_data;
+}
+
 int NativeViewAccessibilityBase::GetChildCount() {
   int child_count = view_->child_count();
 
diff --git a/ui/views/accessibility/native_view_accessibility_base.h b/ui/views/accessibility/native_view_accessibility_base.h
index fb0d0fe..af299c6 100644
--- a/ui/views/accessibility/native_view_accessibility_base.h
+++ b/ui/views/accessibility/native_view_accessibility_base.h
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/gfx/native_widget_types.h"
@@ -38,6 +39,7 @@
 
   // ui::AXPlatformNodeDelegate
   const ui::AXNodeData& GetData() const override;
+  const ui::AXTreeData& GetTreeData() const override;
   int GetChildCount() override;
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
   gfx::NativeWindow GetTopLevelWidget() override;