Remove testRunner.layoutAndPaintAsyncThen()

Now use two requestAnimationFrames instead. This reduces
dependencies of layout tests to testRunner API, making it possible
to convert them into wpt tests.

Change-Id: If9d4c82626128fb0def80eba52c85f818c2f31d2
Reviewed-on: https://chromium-review.googlesource.com/c/1395193
Commit-Queue: Xianzhu Wang <[email protected]>
Reviewed-by: Philip Rogers <[email protected]>
Reviewed-by: Antoine Labour <[email protected]>
Cr-Commit-Position: refs/heads/master@{#620925}
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index f39f7cb..cbe23e1 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -428,22 +428,6 @@
   return false;
 }
 
-void LayerTreeView::LayoutAndPaintAsync(base::OnceClosure callback) {
-  DCHECK(layout_and_paint_async_callback_.is_null());
-  layout_and_paint_async_callback_ = std::move(callback);
-
-  if (CompositeIsSynchronous()) {
-    // The LayoutAndPaintAsyncCallback is invoked in WillCommit, which is
-    // dispatched after layout and paint for all compositing modes.
-    const bool raster = false;
-    layer_tree_host_->GetTaskRunnerProvider()->MainThreadTaskRunner()->PostTask(
-        FROM_HERE, base::BindOnce(&LayerTreeView::SynchronouslyComposite,
-                                  weak_factory_.GetWeakPtr(), raster, nullptr));
-  } else {
-    layer_tree_host_->SetNeedsCommit();
-  }
-}
-
 void LayerTreeView::SetLayerTreeFrameSink(
     std::unique_ptr<cc::LayerTreeFrameSink> layer_tree_frame_sink) {
   if (!layer_tree_frame_sink) {
@@ -453,14 +437,8 @@
   layer_tree_host_->SetLayerTreeFrameSink(std::move(layer_tree_frame_sink));
 }
 
-void LayerTreeView::InvokeLayoutAndPaintCallback() {
-  if (!layout_and_paint_async_callback_.is_null())
-    std::move(layout_and_paint_async_callback_).Run();
-}
-
 void LayerTreeView::CompositeAndReadbackAsync(
     base::OnceCallback<void(const SkBitmap&)> callback) {
-  DCHECK(layout_and_paint_async_callback_.is_null());
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner =
       layer_tree_host_->GetTaskRunnerProvider()->MainThreadTaskRunner();
   std::unique_ptr<viz::CopyOutputRequest> request =
@@ -712,9 +690,7 @@
                                 weak_factory_.GetWeakPtr()));
 }
 
-void LayerTreeView::WillCommit() {
-  InvokeLayoutAndPaintCallback();
-}
+void LayerTreeView::WillCommit() {}
 
 void LayerTreeView::DidCommit() {
   delegate_->DidCommitCompositorFrame();
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index 7590a49..3e01903 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -145,7 +145,6 @@
                                double duration_sec) override;
   bool HasPendingPageScaleAnimation() const override;
   void HeuristicsForGpuRasterizationUpdated(bool matches_heuristics) override;
-  void LayoutAndPaintAsync(base::OnceClosure callback) override;
   void CompositeAndReadbackAsync(
       base::OnceCallback<void(const SkBitmap&)> callback) override;
   // Synchronously performs the complete set of document lifecycle phases,
@@ -242,7 +241,6 @@
  private:
   void SetLayerTreeFrameSink(
       std::unique_ptr<cc::LayerTreeFrameSink> layer_tree_frame_sink);
-  void InvokeLayoutAndPaintCallback();
   bool CompositeIsSynchronous() const;
   void SynchronouslyComposite(bool raster,
                               std::unique_ptr<cc::SwapPromise> swap_promise);
@@ -259,7 +257,6 @@
   bool layer_tree_frame_sink_request_failed_while_invisible_ = false;
 
   bool in_synchronous_compositor_update_ = false;
-  base::OnceClosure layout_and_paint_async_callback_;
 
   viz::FrameSinkId frame_sink_id_;
   base::circular_deque<
diff --git a/content/shell/renderer/web_test/blink_test_runner.cc b/content/shell/renderer/web_test/blink_test_runner.cc
index 444bbe5..f1441e3 100644
--- a/content/shell/renderer/web_test/blink_test_runner.cc
+++ b/content/shell/renderer/web_test/blink_test_runner.cc
@@ -48,7 +48,6 @@
 #include "content/shell/renderer/web_test/web_test_render_thread_observer.h"
 #include "content/shell/test_runner/app_banner_service.h"
 #include "content/shell/test_runner/gamepad_controller.h"
-#include "content/shell/test_runner/layout_and_paint_async_then.h"
 #include "content/shell/test_runner/pixel_dump.h"
 #include "content/shell/test_runner/web_test_interfaces.h"
 #include "content/shell/test_runner/web_test_runner.h"
diff --git a/content/shell/test_runner/BUILD.gn b/content/shell/test_runner/BUILD.gn
index f576a6ba..f3680117 100644
--- a/content/shell/test_runner/BUILD.gn
+++ b/content/shell/test_runner/BUILD.gn
@@ -31,8 +31,6 @@
     "gamepad_controller.h",
     "gc_controller.cc",
     "gc_controller.h",
-    "layout_and_paint_async_then.cc",
-    "layout_and_paint_async_then.h",
     "layout_dump.cc",
     "layout_dump.h",
     "mock_content_settings_client.cc",
diff --git a/content/shell/test_runner/layout_and_paint_async_then.cc b/content/shell/test_runner/layout_and_paint_async_then.cc
deleted file mode 100644
index 7e4dab4..0000000
--- a/content/shell/test_runner/layout_and_paint_async_then.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 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 "content/shell/test_runner/layout_and_paint_async_then.h"
-
-#include "base/barrier_closure.h"
-#include "base/callback.h"
-#include "base/trace_event/trace_event.h"
-#include "third_party/blink/public/web/web_page_popup.h"
-#include "third_party/blink/public/web/web_widget.h"
-
-namespace test_runner {
-
-void LayoutAndPaintAsyncThen(blink::WebPagePopup* popup,
-                             blink::WebWidget* web_widget,
-                             base::OnceClosure callback) {
-  TRACE_EVENT0("shell", "LayoutAndPaintAsyncThen");
-
-  if (popup) {
-    auto barrier = base::BarrierClosure(2, std::move(callback));
-    web_widget->LayoutAndPaintAsync(barrier);
-    popup->LayoutAndPaintAsync(barrier);
-  } else {
-    web_widget->LayoutAndPaintAsync(std::move(callback));
-  }
-}
-
-}  // namespace test_runner
diff --git a/content/shell/test_runner/layout_and_paint_async_then.h b/content/shell/test_runner/layout_and_paint_async_then.h
deleted file mode 100644
index 798d2f1..0000000
--- a/content/shell/test_runner/layout_and_paint_async_then.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 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 CONTENT_SHELL_TEST_RUNNER_LAYOUT_AND_PAINT_ASYNC_THEN_H_
-#define CONTENT_SHELL_TEST_RUNNER_LAYOUT_AND_PAINT_ASYNC_THEN_H_
-
-#include "base/callback_forward.h"
-#include "content/shell/test_runner/test_runner_export.h"
-
-namespace blink {
-class WebPagePopup;
-class WebWidget;
-}  // namespace blink
-
-namespace test_runner {
-
-// Triggers a layout and paint of WebWidget and the active popup (if any).
-// Calls |callback| after the layout and paint happens for both the
-// widget and the popup.
-TEST_RUNNER_EXPORT void LayoutAndPaintAsyncThen(blink::WebPagePopup* popup,
-                                                blink::WebWidget* web_widget,
-                                                base::OnceClosure callback);
-
-}  // namespace test_runner
-
-#endif  // CONTENT_SHELL_TEST_RUNNER_LAYOUT_AND_PAINT_ASYNC_THEN_H_
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index b9ce921..faa25a84 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -20,7 +20,6 @@
 #include "build/build_config.h"
 #include "cc/paint/paint_canvas.h"
 #include "content/shell/common/web_test/web_test_switches.h"
-#include "content/shell/test_runner/layout_and_paint_async_then.h"
 #include "content/shell/test_runner/layout_dump.h"
 #include "content/shell/test_runner/mock_content_settings_client.h"
 #include "content/shell/test_runner/mock_screen_orientation_client.h"
@@ -194,8 +193,6 @@
   void UpdateAllLifecyclePhasesAndCompositeThen(
       v8::Local<v8::Function> callback);
   void SetAnimationRequiresRaster(bool do_raster);
-  void LayoutAndPaintAsync();
-  void LayoutAndPaintAsyncThen(v8::Local<v8::Function> callback);
   void LogToStderr(const std::string& output);
   void NotImplemented(const gin::Arguments& args);
   void NotifyDone();
@@ -482,10 +479,6 @@
                  &TestRunnerBindings::UpdateAllLifecyclePhasesAndCompositeThen)
       .SetMethod("setAnimationRequiresRaster",
                  &TestRunnerBindings::SetAnimationRequiresRaster)
-      .SetMethod("layoutAndPaintAsync",
-                 &TestRunnerBindings::LayoutAndPaintAsync)
-      .SetMethod("layoutAndPaintAsyncThen",
-                 &TestRunnerBindings::LayoutAndPaintAsyncThen)
       .SetMethod("logToStderr", &TestRunnerBindings::LogToStderr)
       .SetMethod("notifyDone", &TestRunnerBindings::NotifyDone)
       .SetMethod("overridePreference", &TestRunnerBindings::OverridePreference)
@@ -1346,17 +1339,6 @@
   runner_->SetAnimationRequiresRaster(do_raster);
 }
 
-void TestRunnerBindings::LayoutAndPaintAsync() {
-  if (view_runner_)
-    view_runner_->LayoutAndPaintAsync();
-}
-
-void TestRunnerBindings::LayoutAndPaintAsyncThen(
-    v8::Local<v8::Function> callback) {
-  if (view_runner_)
-    view_runner_->LayoutAndPaintAsyncThen(callback);
-}
-
 void TestRunnerBindings::GetManifestThen(v8::Local<v8::Function> callback) {
   if (view_runner_)
     view_runner_->GetManifestThen(callback);
diff --git a/content/shell/test_runner/test_runner_for_specific_view.cc b/content/shell/test_runner/test_runner_for_specific_view.cc
index e01f88e..f753784 100644
--- a/content/shell/test_runner/test_runner_for_specific_view.cc
+++ b/content/shell/test_runner/test_runner_for_specific_view.cc
@@ -16,7 +16,6 @@
 #include "build/build_config.h"
 #include "cc/paint/paint_canvas.h"
 #include "content/renderer/compositor/layer_tree_view.h"
-#include "content/shell/test_runner/layout_and_paint_async_then.h"
 #include "content/shell/test_runner/layout_dump.h"
 #include "content/shell/test_runner/mock_content_settings_client.h"
 #include "content/shell/test_runner/mock_screen_orientation_client.h"
@@ -217,26 +216,6 @@
       v8::UniquePersistent<v8::Function>(blink::MainThreadIsolate(), callback));
 }
 
-void TestRunnerForSpecificView::LayoutAndPaintAsync() {
-  // TODO(lfg, lukasza): TestRunnerForSpecificView assumes that there's a single
-  // WebWidget for the entire view, but with out-of-process iframes there may be
-  // multiple WebWidgets, one for each local root. We should look into making
-  // this structure more generic. Also the PagePopup for an OOPIF would be
-  // attached to a WebView without a main frame, which should be handled.
-  test_runner::LayoutAndPaintAsyncThen(
-      web_view()->GetPagePopup(),
-      web_view()->MainFrame()->ToWebLocalFrame()->FrameWidget(),
-      base::DoNothing());
-}
-
-void TestRunnerForSpecificView::LayoutAndPaintAsyncThen(
-    v8::Local<v8::Function> callback) {
-  test_runner::LayoutAndPaintAsyncThen(
-      web_view()->GetPagePopup(),
-      web_view()->MainFrame()->ToWebLocalFrame()->FrameWidget(),
-      CreateClosureThatPostsV8Callback(callback));
-}
-
 void TestRunnerForSpecificView::CapturePixelsAsyncThen(
     v8::Local<v8::Function> callback) {
   v8::UniquePersistent<v8::Function> persistent_callback(
diff --git a/content/shell/test_runner/test_runner_for_specific_view.h b/content/shell/test_runner/test_runner_for_specific_view.h
index 3b3ddcf..64621649 100644
--- a/content/shell/test_runner/test_runner_for_specific_view.h
+++ b/content/shell/test_runner/test_runner_for_specific_view.h
@@ -79,12 +79,9 @@
   void UpdateAllLifecyclePhasesAndCompositeThen(
       v8::Local<v8::Function> callback);
 
-  void LayoutAndPaintAsync();
-  void LayoutAndPaintAsyncThen(v8::Local<v8::Function> callback);
-
-  // Similar to LayoutAndPaintAsyncThen(), but pass parameters of the captured
-  // snapshot (width, height, snapshot) to the callback. The snapshot is in
-  // uint8_t RGBA format.
+  // The callback will be called after the next full frame update and raster,
+  // with the captured snapshot as the parameters (width, height, snapshot).
+  // The snapshot is in uint8_t RGBA format.
   void CapturePixelsAsyncThen(v8::Local<v8::Function> callback);
   void CapturePixelsCallback(v8::UniquePersistent<v8::Function> callback,
                              const SkBitmap& snapshot);
diff --git a/third_party/blink/public/platform/web_layer_tree_view.h b/third_party/blink/public/platform/web_layer_tree_view.h
index 15dd069a..31758b7 100644
--- a/third_party/blink/public/platform/web_layer_tree_view.h
+++ b/third_party/blink/public/platform/web_layer_tree_view.h
@@ -138,9 +138,6 @@
 
   // Flow control and scheduling ---------------------------------------
 
-  // Run layout and paint of all pending document changes asynchronously.
-  virtual void LayoutAndPaintAsync(base::OnceClosure callback) {}
-
   virtual void CompositeAndReadbackAsync(
       base::OnceCallback<void(const SkBitmap&)> callback) {}
 
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index 981ffea..c5fb93d 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -139,9 +139,6 @@
   // transormations (e.g. pinch-zoom, dev tools emulation, etc.).
   virtual void PaintContent(cc::PaintCanvas*, const WebRect& view_port) {}
 
-  // Run layout and paint of all pending document changes asynchronously.
-  virtual void LayoutAndPaintAsync(base::OnceClosure callback) {}
-
   // This should only be called when isAcceleratedCompositingActive() is true.
   virtual void CompositeAndReadbackAsync(
       base::OnceCallback<void(const SkBitmap&)> callback) {}
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index d8b9b791..8b71a1fc 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -575,10 +575,6 @@
   return MainFrame().DomWindow();
 }
 
-void WebPagePopupImpl::LayoutAndPaintAsync(base::OnceClosure callback) {
-  layer_tree_view_->LayoutAndPaintAsync(std::move(callback));
-}
-
 void WebPagePopupImpl::CompositeAndReadbackAsync(
     base::OnceCallback<void(const SkBitmap&)> callback) {
   DCHECK(IsAcceleratedCompositingActive());
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.h b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
index efdb66a..f8b1ec81 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.h
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
@@ -68,7 +68,6 @@
     return other && popup_client_ == other->popup_client_;
   }
   LocalDOMWindow* Window();
-  void LayoutAndPaintAsync(base::OnceClosure callback) override;
   void CompositeAndReadbackAsync(
       base::OnceCallback<void(const SkBitmap&)> callback) override;
   WebPoint PositionRelativeToOwner() override;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 6a695832..9f45d48 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1610,11 +1610,6 @@
                                    *page_->DeprecatedLocalMainFrame());
 }
 
-void WebViewImpl::LayoutAndPaintAsync(base::OnceClosure callback) {
-  if (layer_tree_view_)
-    layer_tree_view_->LayoutAndPaintAsync(std::move(callback));
-}
-
 void WebViewImpl::CompositeAndReadbackAsync(
     base::OnceCallback<void(const SkBitmap&)> callback) {
   if (layer_tree_view_)
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index a2b2c17..b6ba2a2 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -439,7 +439,6 @@
   void RequestPresentationCallbackForTesting(
       base::OnceClosure callback) override;
   void PaintContent(cc::PaintCanvas*, const WebRect&) override;
-  void LayoutAndPaintAsync(base::OnceClosure callback) override;
   void CompositeAndReadbackAsync(
       base::OnceCallback<void(const SkBitmap&)> callback) override;
   void ThemeChanged() override;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 53fd46d..f927f69 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -337,10 +337,6 @@
       1, View()->MinimumPageScaleFactor(), View()->MaximumPageScaleFactor());
 }
 
-void WebFrameWidgetImpl::LayoutAndPaintAsync(base::OnceClosure callback) {
-  layer_tree_view_->LayoutAndPaintAsync(std::move(callback));
-}
-
 void WebFrameWidgetImpl::CompositeAndReadbackAsync(
     base::OnceCallback<void(const SkBitmap&)> callback) {
   layer_tree_view_->CompositeAndReadbackAsync(std::move(callback));
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index c30359f0..622592b 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -89,7 +89,6 @@
   void UpdateLifecycle(LifecycleUpdate requested_update,
                        LifecycleUpdateReason reason) override;
   void PaintContent(cc::PaintCanvas*, const WebRect&) override;
-  void LayoutAndPaintAsync(base::OnceClosure callback) override;
   void CompositeAndReadbackAsync(
       base::OnceCallback<void(const SkBitmap&)> callback) override;
   void ThemeChanged() override;
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index 6f2aa599..a762ca2c 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -70,10 +70,6 @@
   web_view_->PaintContent(canvas, view_port);
 }
 
-void WebViewFrameWidget::LayoutAndPaintAsync(base::OnceClosure callback) {
-  web_view_->LayoutAndPaintAsync(std::move(callback));
-}
-
 void WebViewFrameWidget::CompositeAndReadbackAsync(
     base::OnceCallback<void(const SkBitmap&)> callback) {
   web_view_->CompositeAndReadbackAsync(std::move(callback));
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.h b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
index 2d41ec8..2ec4c80 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.h
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
@@ -53,7 +53,6 @@
   void UpdateLifecycle(LifecycleUpdate requested_update,
                        LifecycleUpdateReason reason) override;
   void PaintContent(cc::PaintCanvas*, const WebRect& view_port) override;
-  void LayoutAndPaintAsync(base::OnceClosure callback) override;
   void CompositeAndReadbackAsync(
       base::OnceCallback<void(const SkBitmap&)>) override;
   void ThemeChanged() override;
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 6ac5142..f77a636 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -314,7 +314,7 @@
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl.html [ Crash ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/select-text-overflow-ellipsis.html [ Crash ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/word-granularity.html [ Crash ]
-crbug.com/916511 virtual/composite-after-paint/paint/background/scrolling-background-with-negative-z-child.html [ Failure ]
+crbug.com/916511 virtual/composite-after-paint/paint/background/scrolling-background-with-negative-z-child.html [ Failure Crash ]
 crbug.com/591099 virtual/composite-after-paint/paint/invalidation/box/margin.html [ Failure Pass ]
 crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/acquire-update-measure-remove.html [ Failure ]
 crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/measure-forced-layout.html [ Failure ]
diff --git a/third_party/blink/web_tests/PRESUBMIT.py b/third_party/blink/web_tests/PRESUBMIT.py
index c527a4dd..39cb587 100644
--- a/third_party/blink/web_tests/PRESUBMIT.py
+++ b/third_party/blink/web_tests/PRESUBMIT.py
@@ -24,7 +24,7 @@
         return []
 
     checker_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
-        '..', '..', 'blink', 'tools', 'check_testharness_expected_pass.py')
+        '..', 'tools', 'check_testharness_expected_pass.py')
 
     args = [input_api.python_executable, checker_path]
     args.extend(baseline_files)
@@ -75,7 +75,7 @@
 
 def _CheckTestExpectations(input_api, output_api):
     lint_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
-        '..', '..', 'blink', 'tools', 'lint_test_expectations.py')
+        '..', 'tools', 'lint_test_expectations.py')
     _, errs = input_api.subprocess.Popen(
         [input_api.python_executable, lint_path],
         stdout=input_api.subprocess.PIPE,
@@ -125,6 +125,22 @@
                 results.append(output_api.PresubmitError('Found an invalid preference %s in expected result %s:%s' % (error.group(1), f, line_num)))
     return results
 
+def _CheckRunAfterLayoutAndPaintJS(input_api, output_api):
+    """Checks if resources/run-after-layout-and-paint.js and
+       http/tests/resources/run-after-layout-and-paint.js are the same."""
+    js_file = input_api.os_path.join(input_api.PresubmitLocalPath(),
+        'resources', 'run-after-layout-and-paint.js')
+    http_tests_js_file = input_api.os_path.join(input_api.PresubmitLocalPath(),
+        'http', 'tests', 'resources', 'run-after-layout-and-paint.js')
+    for f in input_api.AffectedFiles():
+        path = f.AbsoluteLocalPath()
+        if path == js_file or path == http_tests_js_file:
+            if not filecmp.cmp(js_file, http_tests_js_file):
+                return [output_api.PresubmitError(
+                    '%s and %s must be kept exactly the same' %
+                    (js_file, http_tests_js_file))]
+            break
+    return []
 
 def CheckChangeOnUpload(input_api, output_api):
     results = []
@@ -133,6 +149,7 @@
     results.extend(_CheckTestExpectations(input_api, output_api))
     results.extend(_CheckForJSTest(input_api, output_api))
     results.extend(_CheckForInvalidPreferenceError(input_api, output_api))
+    results.extend(_CheckRunAfterLayoutAndPaintJS(input_api, output_api))
     return results
 
 
diff --git a/third_party/blink/web_tests/accessibility/animation-policy.html b/third_party/blink/web_tests/accessibility/animation-policy.html
index 64c6f7e..f4bbb08 100644
--- a/third_party/blink/web_tests/accessibility/animation-policy.html
+++ b/third_party/blink/web_tests/accessibility/animation-policy.html
@@ -1,3 +1,4 @@
+<script src="../resources/run-after-layout-and-paint.js"></script>
 <script>
 var updated = false;
 var prevTime;
@@ -12,11 +13,6 @@
     updated = true;
 }
 
-function finishTest() {
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-
 function imageLoaded() {
     if (!updated)
         return;
@@ -24,11 +20,8 @@
 }
 
 function onTimeout() {
-    if (window.testRunner) {
-        testRunner.layoutAndPaintAsyncThen(function () {
-            window.requestAnimationFrame(finishTest);
-        });
-    }
+    if (window.testRunner)
+        testRunner.notifyDone();
 }
 </script>
 <body onload="changeImage()">
diff --git a/third_party/blink/web_tests/css3/filters/background-image-blur-repaint.html b/third_party/blink/web_tests/css3/filters/background-image-blur-repaint.html
index 8e2f89b..696d0bb 100644
--- a/third_party/blink/web_tests/css3/filters/background-image-blur-repaint.html
+++ b/third_party/blink/web_tests/css3/filters/background-image-blur-repaint.html
@@ -3,24 +3,15 @@
 <!-- You should see a 50x50 green box over a blurred background, with no white 100x100 box bleeding into the background image.-->
 <html>
 <head>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-  if (window.testRunner)
-    testRunner.waitUntilDone();
-
   function runTest() {
     function shrinkBox() {
       var box = document.getElementsByClassName("box")[0];
       box.style.width = "50px";
       box.style.height = "50px";
-      if (window.testRunner) {
-        testRunner.notifyDone();
-      }
     }
-    if (window.testRunner) {
-      testRunner.layoutAndPaintAsyncThen(shrinkBox);
-    } else {
-      setTimeout(shrinkBox, 500);
-    }
+    runAfterLayoutAndPaint(shrinkBox, true);
   }
 </script>
 <style>
diff --git a/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background-rounded.html b/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background-rounded.html
index b278082..a83fe4d1 100644
--- a/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background-rounded.html
+++ b/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background-rounded.html
@@ -9,13 +9,14 @@
       width: 200px;
     }
   </style>
+  <script src="../../resources/run-after-layout-and-paint.js"></script>
   <script type="text/javascript" charset="utf-8">
     if (window.testRunner)
       testRunner.waitUntilDone();
     
     function pageLoaded() 
     {
-      testRunner.layoutAndPaintAsyncThen(function () {
+      runAfterLayoutAndPaint(function () {
         window.setTimeout(function() {
           if (window.testRunner)
             testRunner.notifyDone();
diff --git a/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background.html b/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background.html
index 2868a08..d6054a1 100644
--- a/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background.html
+++ b/third_party/blink/web_tests/fast/backgrounds/animated-gif-as-background.html
@@ -8,13 +8,14 @@
       width: 200px;
     }
   </style>
+  <script src="../../resources/run-after-layout-and-paint.js"></script>
   <script type="text/javascript" charset="utf-8">
     if (window.testRunner)
       testRunner.waitUntilDone();
     
     function pageLoaded() 
     {
-      testRunner.layoutAndPaintAsyncThen(function () {
+      runAfterLayoutAndPaint(function () {
         window.setTimeout(function() {
           if (window.testRunner)
             testRunner.notifyDone();
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-webgl.html b/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-webgl.html
index 7508cebc..be7acb89 100644
--- a/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-webgl.html
+++ b/third_party/blink/web_tests/fast/canvas/canvas-createImageBitmap-webgl.html
@@ -1,6 +1,7 @@
 <html>
 <head>
 <script src="../../resources/js-test.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 </head>
 <body onload="start();">
 <canvas id="webgl" width="200" height="200"></canvas>
@@ -40,11 +41,7 @@
         gl.clearColor(0.0, 1.0, 0.0, 1.0);
         gl.clear(gl.COLOR_BUFFER_BIT);
         createImageBitmapAndCheck(webgl_canvas, false).then(function() {
-            if (window.testRunner) {
-                testRunner.layoutAndPaintAsyncThen(asyncTest);
-            } else {
-                window.requestAnimationFrame(asyncTest);
-            }
+            runAfterLayoutAndPaint(asyncTest);
         });
     }
 
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-no-alpha-invalidation.html b/third_party/blink/web_tests/fast/canvas/canvas-no-alpha-invalidation.html
index 0427b4c..7c101ff 100644
--- a/third_party/blink/web_tests/fast/canvas/canvas-no-alpha-invalidation.html
+++ b/third_party/blink/web_tests/fast/canvas/canvas-no-alpha-invalidation.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <p>Expect to see a black square below this line.</p>
 <canvas id = 'c' width='100' height='100'></canvas>
 <script>
 if (window.testRunner) {
   testRunner.waitUntilDone();
-  testRunner.layoutAndPaintAsyncThen(function(){
+  runAfterLayoutAndPaint(function(){
     // Verify that creating a context with alpha:false trigger a graphics
     // update even though nothing is drawn to the canvas.
     document.getElementById('c').getContext('2d', { alpha:false });
@@ -13,4 +14,4 @@
 } else {
   console.log('ERROR: This test requires the testRunner interface.')
 }
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d-after-to-data-url-without-context.html b/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d-after-to-data-url-without-context.html
index 359649a..9bf67c2 100644
--- a/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d-after-to-data-url-without-context.html
+++ b/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d-after-to-data-url-without-context.html
@@ -13,6 +13,7 @@
 <canvas id="nonpreserve-canvas3d" width="100" height="100"></canvas>
 <canvas id="nonpreserve-canvas2d" width="100" height="100"></canvas>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="resources/draw-webgl-to-canvas-2d.js"></script>
 <script>
 function createContexts() {
diff --git a/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d.html b/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d.html
index 3b90d53..14cbfa3a 100644
--- a/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d.html
+++ b/third_party/blink/web_tests/fast/canvas/webgl/draw-webgl-to-canvas-2d.html
@@ -27,6 +27,7 @@
 <canvas id="nonpreserve-canvas3d" width="100" height="100"></canvas>
 <canvas id="nonpreserve-canvas2d" width="100" height="100"></canvas>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="resources/draw-webgl-to-canvas-2d.js"></script>
 <script>
 function createContexts() {
diff --git a/third_party/blink/web_tests/fast/canvas/webgl/resources/draw-webgl-to-canvas-2d.js b/third_party/blink/web_tests/fast/canvas/webgl/resources/draw-webgl-to-canvas-2d.js
index 28d2247..eddb57f3 100644
--- a/third_party/blink/web_tests/fast/canvas/webgl/resources/draw-webgl-to-canvas-2d.js
+++ b/third_party/blink/web_tests/fast/canvas/webgl/resources/draw-webgl-to-canvas-2d.js
@@ -68,14 +68,9 @@
     debug("2) when drawingBuffer is not preserved.")
     drawWebGLToCanvas2D(nonpreserve_ctx2D, nonpreserve_canvas3D, false);
 
-    if (window.testRunner) {
-        testRunner.waitUntilDone();
-        testRunner.layoutAndPaintAsyncThen(asyncTest);
-    } else {
-        window.requestAnimationFrame(asyncTest);
-    }
+    runAfterLayoutAndPaint(asyncTest);
 }
 
 window.onload = function () {
-    window.requestAnimationFrame(startTestAfterFirstPaint);
+    runAfterLayoutAndPaint(startTestAfterFirstPaint);
 }
diff --git a/third_party/blink/web_tests/fast/deprecated-flexbox/stretch-align-svg.html b/third_party/blink/web_tests/fast/deprecated-flexbox/stretch-align-svg.html
index 6cb1cdb4..9baa0aa4 100644
--- a/third_party/blink/web_tests/fast/deprecated-flexbox/stretch-align-svg.html
+++ b/third_party/blink/web_tests/fast/deprecated-flexbox/stretch-align-svg.html
@@ -19,9 +19,6 @@
   <div id="spanner"></div>
 </div>
 <script>
-if (window.testRunner)
-  testRunner.waitUntilDone();
-
 runAfterLayoutAndPaint(function() {
   document.body.appendChild(document.createElement('div'));
   document.body.offsetLeft;
@@ -29,8 +26,5 @@
   var c = document.querySelector('rect');
   c.setAttribute('height', '100%');
   c.getScreenCTM();
-
-  if (window.testRunner)
-    testRunner.layoutAndPaintAsyncThen(function() { testRunner.notifyDone(); });
-});
+}, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/events/middleClickAutoscroll-drag-scrollable-iframe-div.html b/third_party/blink/web_tests/fast/events/middleClickAutoscroll-drag-scrollable-iframe-div.html
index a870a1e..49c4f19 100644
--- a/third_party/blink/web_tests/fast/events/middleClickAutoscroll-drag-scrollable-iframe-div.html
+++ b/third_party/blink/web_tests/fast/events/middleClickAutoscroll-drag-scrollable-iframe-div.html
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <script src="../../resources/js-test.js"> </script>
+<script src="../../resources/run-after-layout-and-paint.js"> </script>
 <script>
 var button_was_clicked = false;
 
@@ -7,14 +8,14 @@
     if (window.testRunner) {
         testRunner.waitUntilDone();
         testRunner.dumpAsText();
-        testRunner.layoutAndPaintAsyncThen(
+        runAfterLayoutAndPaint(
             function() {
                 var iframe = document.getElementById('ScrollIFrame');
                 var iframeDocument = iframe.contentDocument;
                 var textInIFrame = iframeDocument.getElementById('textInFrame');
                 if (window.eventSender) {
                     debug("Starting Autoscroll test on iframe");
-                    testRunner.layoutAndPaintAsyncThen(
+                    runAfterLayoutAndPaint(
                         function() {
                             var x = iframe.offsetLeft + textInIFrame.offsetLeft + 7;
                             var y = iframe.offsetTop + textInIFrame.offsetTop + 7;
@@ -23,7 +24,7 @@
                             eventSender.mouseDown(1);
                             eventSender.mouseMoveTo(x + 220, y + 220);
                             eventSender.mouseUp(1);
-                            testRunner.layoutAndPaintAsyncThen(autoscrollTestPart2);                            
+                            runAfterLayoutAndPaint(autoscrollTestPart2);
                         });
                 }
             });
@@ -43,7 +44,7 @@
     // This click actually clicks the button.
     eventSender.mouseDown();
     eventSender.mouseUp();
-    testRunner.runAfterLayoutAndPaint(checkButtonClicked);
+    runAfterLayoutAndPaint(checkButtonClicked);
 }
 
 function checkButtonClicked() {
@@ -73,4 +74,4 @@
 
     <button id="testCompletedButton" type="button" onclick="testCompleted()"> Click me </button>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/fast/events/middleClickAutoscroll-modal-scrollable-iframe-div.html b/third_party/blink/web_tests/fast/events/middleClickAutoscroll-modal-scrollable-iframe-div.html
index bfcd0b1..ccb3b7a 100644
--- a/third_party/blink/web_tests/fast/events/middleClickAutoscroll-modal-scrollable-iframe-div.html
+++ b/third_party/blink/web_tests/fast/events/middleClickAutoscroll-modal-scrollable-iframe-div.html
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <script src="../../resources/js-test.js"> </script>
+<script src="../../resources/run-after-layout-and-paint.js"> </script>
 <script>
 var button_was_clicked = false;
 
@@ -7,14 +8,14 @@
     if (window.testRunner) {
         testRunner.waitUntilDone();
         testRunner.dumpAsText();
-        testRunner.layoutAndPaintAsyncThen(
+        runAfterLayoutAndPaint(
             function() {
                 var iframe = document.getElementById('ScrollIFrame');
                 var iframeDocument = iframe.contentDocument;
                 var textInIFrame = iframeDocument.getElementById('textInFrame');
                 if (window.eventSender) {
                     debug("Starting Autoscroll test on iframe");
-                    testRunner.layoutAndPaintAsyncThen(
+                    runAfterLayoutAndPaint(
                         function() {
                             var x = iframe.offsetLeft + textInIFrame.offsetLeft + 7;
                             var y = iframe.offsetTop + textInIFrame.offsetTop + 7;
@@ -23,7 +24,7 @@
                             eventSender.mouseDown(1);
                             eventSender.mouseUp(1);
                             eventSender.mouseMoveTo(x + 220, y + 220);
-                            testRunner.layoutAndPaintAsyncThen(autoscrollTestPart2);                            
+                            runAfterLayoutAndPaint(autoscrollTestPart2);                            
                         });
                 }
             });
@@ -43,7 +44,7 @@
     // This click actually clicks the button.
     eventSender.mouseDown();
     eventSender.mouseUp();
-    testRunner.runAfterLayoutAndPaint(checkButtonClicked);
+    runAfterLayoutAndPaint(checkButtonClicked);
 }
 
 function checkButtonClicked() {
@@ -73,4 +74,4 @@
 
     <button id="testCompletedButton" type="button" onclick="testCompleted()"> Click me </button>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover-background-color.html b/third_party/blink/web_tests/fast/history/visited-link-hover-background-color.html
index 1610463..1420316 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover-background-color.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover-background-color.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
     testRunner.keepWebHistory();
-    testRunner.waitUntilDone();
-}
 </script>
 <style>
 a { background-color: red; text-decoration: none; color: initial }
@@ -11,11 +10,8 @@
 </style>
 <a id="anchor" href="">Should have a green background when hovered</a>
 <script>
-if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 1, anchor.offsetTop + 1);
-        testRunner.notifyDone();
-    });
-}
+    }, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover-border-color.html b/third_party/blink/web_tests/fast/history/visited-link-hover-border-color.html
index 353aea7..a59611bd 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover-border-color.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover-border-color.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
     testRunner.keepWebHistory();
-    testRunner.waitUntilDone();
-}
 </script>
 <style>
 a { border: solid red 10px; text-decoration: none; color: initial }
@@ -11,11 +10,8 @@
 </style>
 <a id="anchor" href="">Should have a green border when hovered</a>
 <script>
-if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 10, anchor.offsetTop + 10);
-        testRunner.notifyDone();
-    });
-}
+    }, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover-emphasis-color.html b/third_party/blink/web_tests/fast/history/visited-link-hover-emphasis-color.html
index 84077a6..368a40a0 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover-emphasis-color.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover-emphasis-color.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
-    testRunner.keepWebHistory();
+if (window.testRunner)
     testRunner.waitUntilDone();
-}
 </script>
 <style>
 a { -webkit-text-emphasis-style: filled }
@@ -15,11 +14,8 @@
     <a id="anchor" href="">Emphasis should be green when hovered</a>
 </div>
 <script>
-if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 1, anchor.offsetTop + 1);
-        testRunner.notifyDone();
-    });
-}
+    }, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover-outline-color.html b/third_party/blink/web_tests/fast/history/visited-link-hover-outline-color.html
index e748c1a6..d031640a 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover-outline-color.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover-outline-color.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
     testRunner.keepWebHistory();
-    testRunner.waitUntilDone();
-}
 </script>
 <style>
 a { outline: solid red 10px; text-decoration: none; color: initial }
@@ -11,11 +10,8 @@
 </style>
 <a id="anchor" href="">Should have a green outline when hovered</a>
 <script>
-if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 1, anchor.offsetTop + 1);
-        testRunner.notifyDone();
-    });
-}
+    }, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover-text-decoration-color.html b/third_party/blink/web_tests/fast/history/visited-link-hover-text-decoration-color.html
index 27f9f9a..969cd5a 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover-text-decoration-color.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover-text-decoration-color.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
     testRunner.keepWebHistory();
-    testRunner.waitUntilDone();
-}
 </script>
 <style>
 div { text-decoration-color: red; }
@@ -14,11 +13,8 @@
     <a id="anchor" href="">Should be green (including underline) when hovered</a>
 </div>
 <script>
-if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 1, anchor.offsetTop + 1);
-        testRunner.notifyDone();
-    });
-}
+    }, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover-text-fill-color.html b/third_party/blink/web_tests/fast/history/visited-link-hover-text-fill-color.html
index 8dd6e23f..fb3680a 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover-text-fill-color.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover-text-fill-color.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 if (window.testRunner) {
     testRunner.keepWebHistory();
@@ -15,7 +16,7 @@
 </div>
 <script>
 if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 1, anchor.offsetTop + 1);
         testRunner.notifyDone();
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover-text-stroke-color.html b/third_party/blink/web_tests/fast/history/visited-link-hover-text-stroke-color.html
index ca567cc..8794d32 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover-text-stroke-color.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover-text-stroke-color.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
     testRunner.keepWebHistory();
-    testRunner.waitUntilDone();
-}
 </script>
 <style>
 div { -webkit-text-stroke-width: 1px; -webkit-text-stroke-color: red }
@@ -14,11 +13,8 @@
     <a id="anchor" href="">Should have green stroke when hovered</a>
 </div>
 <script>
-if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 1, anchor.offsetTop + 1);
-        testRunner.notifyDone();
-    });
-}
+    }, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/history/visited-link-hover.html b/third_party/blink/web_tests/fast/history/visited-link-hover.html
index 5e3053a..46af269 100644
--- a/third_party/blink/web_tests/fast/history/visited-link-hover.html
+++ b/third_party/blink/web_tests/fast/history/visited-link-hover.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
     testRunner.keepWebHistory();
-    testRunner.waitUntilDone();
-}
 </script>
 <style>
 div { color: red; }
@@ -14,11 +13,8 @@
     <a id="anchor" href="">Should be green when hovered</a>
 </div>
 <script>
-if (window.testRunner) {
-    testRunner.layoutAndPaintAsyncThen(function(){
+    runAfterLayoutAndPaint(function(){
         if (window.eventSender);
             eventSender.mouseMoveTo(anchor.offsetLeft + 1, anchor.offsetTop + 1);
-        testRunner.notifyDone();
-    });
-}
+    }, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/layers/layer-status-change-crash.html b/third_party/blink/web_tests/fast/layers/layer-status-change-crash.html
index 92719f6..0d4f312 100644
--- a/third_party/blink/web_tests/fast/layers/layer-status-change-crash.html
+++ b/third_party/blink/web_tests/fast/layers/layer-status-change-crash.html
@@ -1,16 +1,17 @@
 <!DOCTYPE HTML>
 <body>
 <div id="div">This test passes if it does not crash.</div>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 if (window.testRunner) {
   testRunner.waitUntilDone();
   testRunner.dumpAsText();
 
   var div = document.getElementById('div');
-  testRunner.layoutAndPaintAsyncThen(function() {
+  runAfterLayoutAndPaint(function() {
     div.style.position = 'relative';
     div.style.backgroundColor = 'blue';
-    testRunner.layoutAndPaintAsyncThen(function() {
+    runAfterLayoutAndPaint(function() {
       div.textContent += ' PASS';
       testRunner.notifyDone();
     });
diff --git a/third_party/blink/web_tests/fast/layers/layer-visibility-sublayer.html b/third_party/blink/web_tests/fast/layers/layer-visibility-sublayer.html
index 7209217..8de8f32 100644
--- a/third_party/blink/web_tests/fast/layers/layer-visibility-sublayer.html
+++ b/third_party/blink/web_tests/fast/layers/layer-visibility-sublayer.html
@@ -8,18 +8,13 @@
 .abstop { position: absolute; left:0; right:0; height:30px   }
 .abs { position: absolute; left:0; right:0; top:0; bottom:0 }
 </style>
-
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 var node1;
 var node2;
 
 function startTest() {
-    if (window.testRunner) {
-        testRunner.waitUntilDone();
-        testRunner.layoutAndPaintAsyncThen(doTest)
-    } else {
-        requestAnimationFrame(doTest)
-    }
+    runAfterLayoutAndPaint(doTest, true);
 }
 
 function doTest()
@@ -35,12 +30,6 @@
     //26
     document.getElementById('26c').style.setProperty('visibility','hidden','');
     document.getElementById('26a').removeChild(document.getElementById('26b'));
-
-    if (window.testRunner) {
-    	testRunner.layoutAndPaintAsyncThen(function() {
-      	    testRunner.notifyDone();
-        });
-    }
 }
 
 
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-animates.html b/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-animates.html
index c90ccd6..7ada4dd 100644
--- a/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-animates.html
+++ b/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-animates.html
@@ -15,6 +15,7 @@
 
 </style>
 <script src="../../resources/js-test.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <div id="container">
   <div id="content"></div>
 </div>
@@ -32,7 +33,7 @@
 
     element.addEventListener("scroll", onElementScroll);
 
-    testRunner.layoutAndPaintAsyncThen(function() {
+    runAfterLayoutAndPaint(function() {
         // Give the container focus.
         eventSender.mouseMoveTo(100, 100);
         eventSender.mouseDown();
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-precise-deltas-dont-animate.html b/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-precise-deltas-dont-animate.html
index a71993b..4a17147 100644
--- a/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-precise-deltas-dont-animate.html
+++ b/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-precise-deltas-dont-animate.html
@@ -15,6 +15,7 @@
 
 </style>
 <script src="../../resources/js-test.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <div id="container">
   <div id="content"></div>
 </div>
@@ -32,7 +33,7 @@
 
     element.addEventListener("scroll", onElementScroll);
 
-    testRunner.layoutAndPaintAsyncThen(function() {
+    runAfterLayoutAndPaint(function() {
         // Give the container focus.
         eventSender.mouseMoveTo(100, 100);
         eventSender.mouseScrollBy(0, -20, /* paged */ false,
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-root-frame-animates.html b/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-root-frame-animates.html
index 2105aab..39aeffaf 100644
--- a/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-root-frame-animates.html
+++ b/third_party/blink/web_tests/fast/scroll-behavior/overflow-scroll-root-frame-animates.html
@@ -7,6 +7,7 @@
 }
 </style>
 <script src="../../resources/js-test.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <div id="content"></div>
 <div id="console"></div>
 <script>
@@ -21,7 +22,7 @@
 
     window.addEventListener("scroll", onWindowScroll);
 
-    testRunner.layoutAndPaintAsyncThen(function() {
+    runAfterLayoutAndPaint(function() {
         eventSender.keyDown('End');
     });
 }
diff --git a/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-local-background.html b/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-local-background.html
index 188a4c1..14a1b0e 100644
--- a/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-local-background.html
+++ b/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-local-background.html
@@ -16,17 +16,9 @@
 <div id="container">
     <div id="bloat"></div>
 </div>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-    if (window.testRunner) {
-        testRunner.waitUntilDone();
-        testRunner.layoutAndPaintAsyncThen(function() {
-            document.getElementById('container').scrollTop = 1000;
-            testRunner.notifyDone();
-        });
-    } else {
-        // For manual test.
-        setTimeout(function() {
-            document.getElementById('container').scrollTop = 1000;
-        }, 500);
-    }
+runAfterLayoutAndPaint(function() {
+    document.getElementById('container').scrollTop = 1000;
+}, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-to-ancestor-backing.html b/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-to-ancestor-backing.html
index caed95fe..5269e8e 100644
--- a/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-to-ancestor-backing.html
+++ b/third_party/blink/web_tests/fast/scrolling/non-composited-scrolling-repaint-to-ancestor-backing.html
@@ -26,17 +26,9 @@
         <div id="clipped"></div>
     </div>
 </div>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-    if (window.testRunner) {
-        testRunner.waitUntilDone();
-        testRunner.layoutAndPaintAsyncThen(function() {
-            document.getElementById('container').scrollTop = 1000;
-            testRunner.notifyDone();
-        });
-    } else {
-        // For manual test.
-        setTimeout(function() {
-            document.getElementById('container').scrollTop = 1000;
-        }, 500);
-    }
+runAfterLayoutAndPaint(function() {
+    document.getElementById('container').scrollTop = 1000;
+}, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/webgl/canvas-to-data-url.html b/third_party/blink/web_tests/fast/webgl/canvas-to-data-url.html
index 63cdfaf5..2a66980 100644
--- a/third_party/blink/web_tests/fast/webgl/canvas-to-data-url.html
+++ b/third_party/blink/web_tests/fast/webgl/canvas-to-data-url.html
@@ -4,6 +4,7 @@
 <canvas id="preserve-canvas3d" width="10" height="10"></canvas>
 <canvas id="nonpreserve-canvas3d" width="10" height="10"></canvas>
 <script src="../../resources/js-test.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 if (window.testRunner) {
     testRunner.dumpAsText();
@@ -44,12 +45,7 @@
     debug("2) when drawingBuffer is not preserved.")
     shouldBeTrue("nonpreserve_canvas3D.toDataURL('image/png') == internals.getImageSourceURL(nonpreserve_canvas3D)");
 
-    if (window.testRunner) {
-        testRunner.waitUntilDone();
-        testRunner.layoutAndPaintAsyncThen(asyncTest);
-    } else {
-        window.requestAnimationFrame(asyncTest);
-    }
+    runAfterLayoutAndPaint(asyncTest);
 }
 
 window.onload = function () {
diff --git a/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-repaint.html b/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-repaint.html
index 54a537f..a2b8cb3 100644
--- a/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-repaint.html
+++ b/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-repaint.html
@@ -42,6 +42,7 @@
 }
 </script>
 <script src="resources/webgl-test-utils.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 
 var ctxs = []
@@ -116,17 +117,9 @@
 
 function repaintTest() {
     drawAll(50);
-    if (window.testRunner) {
-        testRunner.notifyDone();
-    }
 }
 
 function runRepaintTest() {
-    if (window.testRunner) {
-        testRunner.waitUntilDone();
-        testRunner.layoutAndPaintAsyncThen(repaintTest);
-    } else {
-        setTimeout(repaintTest, 100);
-    }
+    runAfterLayoutAndPaint(repaintTest, true);
 }
 </script>
diff --git a/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-tabswitching.html b/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-tabswitching.html
index fc1eb58d..800295a 100644
--- a/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-tabswitching.html
+++ b/third_party/blink/web_tests/fast/webgl/webgl-composite-modes-tabswitching.html
@@ -42,6 +42,7 @@
 }
 </script>
 <script src="resources/webgl-test-utils.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 
 var ctxs = []
@@ -121,32 +122,27 @@
     }
 }
 
-function setPageVisible() {
-    if (window.testRunner) {
-        testRunner.setPageVisibility("visible");
-        testRunner.layoutAndPaintAsyncThen(repaintOnVisiblePage);
-    } else {
-        setTimeout(repaintOnVisiblePage, 50);
-    }
-}
-
 function repaintOnHiddenPage() {
     if (window.testRunner) {
         testRunner.setPageVisibility("hidden");
     }
     // Although page is hidden, WebGL must draw this frame.
     drawAll(30);
-    // testRunner.layoutAndPaintAsyncThen doesn't work when page is hidden.
-    setTimeout(setPageVisible, 50);
+
+    // TODO(crbug.com/919536): Ideally this should be wrapped in
+    // runAfterLayoutAndPaint() to ensure test coverage, but for now that will
+    // crash.
+    if (window.testRunner) {
+        testRunner.setPageVisibility("visible");
+    }
+    runAfterLayoutAndPaint(repaintOnVisiblePage);
 }
 
 function runRepaintTest() {
     drawAll(0);
     if (window.testRunner) {
         testRunner.waitUntilDone();
-        testRunner.layoutAndPaintAsyncThen(repaintOnHiddenPage);
-    } else {
-        setTimeout(repaintOnHiddenPage, 50);
     }
+    runAfterLayoutAndPaint(repaintOnHiddenPage);
 }
 </script>
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index 12127c1..78b2d92 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -21,6 +21,11 @@
           "object": "InlineTextBox 'test test test'",
           "rect": [0, 0, 74, 16],
           "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [26, 0, 26, 16],
+          "reason": "selection"
         }
       ],
       "transform": 2
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
index d13cd0b0..09ad592 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
@@ -21,6 +21,11 @@
           "object": "InlineTextBox 'test test test'",
           "rect": [0, 0, 74, 16],
           "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [26, 0, 26, 16],
+          "reason": "selection"
         }
       ],
       "transform": 2
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-inserted-listitem-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-inserted-listitem-expected.txt
index 05cdce6..21b0436c 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-inserted-listitem-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-inserted-listitem-expected.txt
@@ -9,10 +9,20 @@
         {
           "object": "LayoutSVGRect rect id='move'",
           "rect": [28, 38, 10, 10],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRect rect id='move'",
+          "rect": [28, 18, 10, 10],
           "reason": "chunk appeared"
         },
         {
           "object": "LayoutSVGRect rect id='move'",
+          "rect": [28, 18, 10, 10],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRect rect id='move'",
           "rect": [18, 18, 10, 10],
           "reason": "disappeared"
         }
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
index 1ba2fa3..779ace7 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
@@ -12,11 +12,31 @@
           "reason": "appeared"
         },
         {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 85, 23],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 85, 23],
+          "reason": "disappeared"
+        },
+        {
           "object": "InlineTextBox 'A'",
           "rect": [23, 8, 85, 23],
           "reason": "appeared"
         },
         {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 85, 23],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 85, 23],
+          "reason": "disappeared"
+        },
+        {
           "object": "InlineTextBox ' B C'",
           "rect": [23, 8, 80, 23],
           "reason": "disappeared"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-expected.txt
index 047e65c..4cb073e 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/modify-transferred-listitem-expected.txt
@@ -10,6 +10,11 @@
           "object": "LayoutSVGPath polygon id='target'",
           "rect": [18, 18, 30, 20],
           "reason": "full"
+        },
+        {
+          "object": "LayoutSVGPath polygon id='target'",
+          "rect": [18, 18, 20, 20],
+          "reason": "full"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt
index d552b0c..b8d8eae 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt
@@ -93,6 +93,16 @@
         },
         {
           "object": "LayoutBlockFlow (positioned) div id='html' class='outerBox'",
+          "rect": [415, 125, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) div id='html' class='outerBox'",
+          "rect": [415, 125, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) div id='html' class='outerBox'",
           "rect": [400, 100, 150, 150],
           "reason": "geometry"
         },
@@ -183,6 +193,16 @@
         },
         {
           "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [115, 125, 150, 150],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [115, 125, 150, 150],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
           "rect": [100, 100, 150, 150],
           "reason": "paint property change"
         },
@@ -273,6 +293,16 @@
         },
         {
           "object": "LayoutBlockFlow div class='innerBox'",
+          "rect": [440, 150, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow div class='innerBox'",
+          "rect": [440, 150, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow div class='innerBox'",
           "rect": [425, 125, 100, 100],
           "reason": "geometry"
         },
@@ -363,6 +393,16 @@
         },
         {
           "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [140, 150, 100, 100],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [140, 150, 100, 100],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
           "rect": [125, 125, 100, 100],
           "reason": "paint property change"
         }
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..334b04e
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -0,0 +1,44 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl INPUT id='target'",
+          "rect": [7, 7, 63, 24],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [10, 11, 57, 16],
+          "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [10, 11, 56, 16],
+          "reason": "selection"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [10, 11, 56, 16],
+          "reason": "paint property change"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..91fc6664
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
@@ -0,0 +1,39 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl INPUT id='target'",
+          "rect": [7, 7, 63, 24],
+          "reason": "subtree"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [10, 11, 57, 16],
+          "reason": "selection"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [10, 11, 57, 16],
+          "reason": "paint property change"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/fullscreen/enter-exit-full-screen-hover.html b/third_party/blink/web_tests/fullscreen/enter-exit-full-screen-hover.html
index 4724936..141d6a2 100644
--- a/third_party/blink/web_tests/fullscreen/enter-exit-full-screen-hover.html
+++ b/third_party/blink/web_tests/fullscreen/enter-exit-full-screen-hover.html
@@ -7,6 +7,7 @@
 }
 </style>
 <script src="../resources/js-test.js"></script>
+<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="full-screen-test.js"></script>
 <script src="../fast/events/touch/resources/touch-hover-active-tests.js"></script>
 <link rel="stylesheet" href="../fast/events/touch/resources/touch-hover-active-tests.css">
@@ -32,13 +33,13 @@
     // After entering fullscreen + layout, the button should lose hover.
     waitForEventOnce(document, 'webkitfullscreenchange', function() {
         shouldBeTrue("document.webkitIsFullScreen");
-        testRunner.layoutAndPaintAsyncThen(function() {
+        runAfterLayoutAndPaint(function() {
             shouldBeDefault("getHoverActiveState(enterButton)");
 
             // After exiting fullscreen + layout, the button should lose hover.
             waitForEventOnce(document, 'webkitfullscreenchange', function() {
                 shouldBeFalse("document.webkitIsFullScreen");
-                testRunner.layoutAndPaintAsyncThen(function() {
+                runAfterLayoutAndPaint(function() {
                     shouldBeDefault("getHoverActiveState(exitButton)");
                     endTest();
                 });
diff --git a/third_party/blink/web_tests/fullscreen/full-screen-stacking-context.html b/third_party/blink/web_tests/fullscreen/full-screen-stacking-context.html
index 80f5c53..f01119c 100644
--- a/third_party/blink/web_tests/fullscreen/full-screen-stacking-context.html
+++ b/third_party/blink/web_tests/fullscreen/full-screen-stacking-context.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
 <html>
     <head>
+        <script src="../resources/run-after-layout-and-paint.js"></script>
         <script>
             var runPixelTests = true;
 
             function init() {
                 waitForEventOnce(document, 'webkitfullscreenchange', function() {
-                    testRunner.layoutAndPaintAsyncThen(endTest);
+                    runAfterLayoutAndPaint(endTest);
                 });
                 runWithKeyDown(goFullScreen);
             }
diff --git a/third_party/blink/web_tests/fullscreen/full-screen-test.js b/third_party/blink/web_tests/fullscreen/full-screen-test.js
index 800322b..729dcb2b 100644
--- a/third_party/blink/web_tests/fullscreen/full-screen-test.js
+++ b/third_party/blink/web_tests/fullscreen/full-screen-test.js
@@ -142,7 +142,7 @@
     consoleWrite("END OF TEST");
     testEnded = true;
     if (window.testRunner)
-        testRunner.layoutAndPaintAsyncThen(() => testRunner.notifyDone());
+        testRunner.notifyDone();
 }
 
 function logResult(success, text)
diff --git a/third_party/blink/web_tests/http/tests/csspaint/border-color.html b/third_party/blink/web_tests/http/tests/csspaint/border-color.html
index 2520a9d..3a1a4e7 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/border-color.html
+++ b/third_party/blink/web_tests/http/tests/csspaint/border-color.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html>
-<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="resources/test-runner-paint-worklet.js"></script>
 
 <style>
@@ -159,7 +158,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importPaintWorkletThenEndTest(document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/csspaint/geometry-background-image-zoom.html b/third_party/blink/web_tests/http/tests/csspaint/geometry-background-image-zoom.html
index f5c1351..5bd64cf8f 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/geometry-background-image-zoom.html
+++ b/third_party/blink/web_tests/http/tests/csspaint/geometry-background-image-zoom.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html>
-<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="./resources/test-runner-paint-worklet.js"></script>
 <style>
 html, body { margin: 0; padding: 0; }
@@ -28,7 +27,7 @@
 
 <script>
     document.body.style.zoom = "200%"
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importPaintWorkletThenEndTest(document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/csspaint/geometry-border-image-zoom.html b/third_party/blink/web_tests/http/tests/csspaint/geometry-border-image-zoom.html
index 96c1ff0..c2f1113 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/geometry-border-image-zoom.html
+++ b/third_party/blink/web_tests/http/tests/csspaint/geometry-border-image-zoom.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html>
-<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="./resources/test-runner-paint-worklet.js"></script>
 <style>
 html, body { margin: 0; padding: 0; }
@@ -34,7 +33,7 @@
 <script>
     document.getElementById('canvas-geometry').style.borderImageOutset = '10px';
     document.body.style.zoom = "200%";
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importPaintWorkletThenEndTest(document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/csspaint/hidpi/geometry-with-hidpi-zoom.html b/third_party/blink/web_tests/http/tests/csspaint/hidpi/geometry-with-hidpi-zoom.html
index 7e083af..81d912d0 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/hidpi/geometry-with-hidpi-zoom.html
+++ b/third_party/blink/web_tests/http/tests/csspaint/hidpi/geometry-with-hidpi-zoom.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html>
-<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/test-runner-paint-worklet.js"></script>
 <style>
 html, body { margin: 0; padding: 0; }
@@ -35,7 +34,7 @@
 
 <script>
     document.body.style.zoom = "33%";
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importPaintWorkletThenEndTest(document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/csspaint/line-dash-scale-with-page-zoom.html b/third_party/blink/web_tests/http/tests/csspaint/line-dash-scale-with-page-zoom.html
index cd8d2097..6a25362 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/line-dash-scale-with-page-zoom.html
+++ b/third_party/blink/web_tests/http/tests/csspaint/line-dash-scale-with-page-zoom.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html>
-<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="./resources/test-runner-paint-worklet.js"></script>
 <style>
 html, body { margin: 0; padding: 0; }
@@ -38,7 +37,7 @@
 
 <script>
     document.body.style.zoom = "200%"
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importPaintWorkletThenEndTest(document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/csspaint/paint2d-zoom.html b/third_party/blink/web_tests/http/tests/csspaint/paint2d-zoom.html
index d4bf63e..9015592 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/paint2d-zoom.html
+++ b/third_party/blink/web_tests/http/tests/csspaint/paint2d-zoom.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html>
-<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="./resources/test-runner-paint-worklet.js"></script>
 <style>
 html, body { margin: 0; padding: 0; }
@@ -26,7 +25,7 @@
 
 <script>
     document.body.style.zoom = "300%";
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importPaintWorkletThenEndTest(document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/csspaint/resources/test-runner-paint-worklet.js b/third_party/blink/web_tests/http/tests/csspaint/resources/test-runner-paint-worklet.js
index 8fd3d1b2..b8dc099c 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/resources/test-runner-paint-worklet.js
+++ b/third_party/blink/web_tests/http/tests/csspaint/resources/test-runner-paint-worklet.js
@@ -1,20 +1,18 @@
-// Given a piece of 'code', runs it in the worklet, then once loaded waits for
-// layout and paint, before finishing the test.
+// Given a piece of 'code', runs it in the worklet, then once loaded finish the
+// test (content_shell will ensure a full frame update before exit).
 //
 // Usage:
-//   importPaintWorkletAndTerminateTestAfterAsyncPaint('/* worklet code goes here. */');
+//   importPaintWorkletThenEndTest('/* worklet code goes here. */');
 
-function importPaintWorkletAndTerminateTestAfterAsyncPaint(code) {
+function importPaintWorkletThenEndTest(code) {
     if (window.testRunner) {
       testRunner.waitUntilDone();
     }
 
     var blob = new Blob([code], {type: 'text/javascript'});
     CSS.paintWorklet.addModule(URL.createObjectURL(blob)).then(function() {
-        runAfterLayoutAndPaint(function() {
-            if (window.testRunner) {
-                testRunner.notifyDone();
-            }
-        });
+        if (window.testRunner) {
+            testRunner.notifyDone();
+        }
     });
 }
diff --git a/third_party/blink/web_tests/http/tests/csspaint/shadow-scale-with-page-zoom.html b/third_party/blink/web_tests/http/tests/csspaint/shadow-scale-with-page-zoom.html
index 5f08bf9..d98a875 100644
--- a/third_party/blink/web_tests/http/tests/csspaint/shadow-scale-with-page-zoom.html
+++ b/third_party/blink/web_tests/http/tests/csspaint/shadow-scale-with-page-zoom.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html>
-<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="./resources/test-runner-paint-worklet.js"></script>
 <style>
 html, body { margin: 0; padding: 0; }
@@ -37,7 +36,7 @@
 
 <script>
     document.body.style.zoom = "200%"
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importPaintWorkletThenEndTest(document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/anonymous-image-object.js b/third_party/blink/web_tests/http/tests/devtools/tracing/anonymous-image-object.js
index 2c1ab8d6..70556fa8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/anonymous-image-object.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/anonymous-image-object.js
@@ -8,6 +8,7 @@
   await TestRunner.loadModule('performance_test_runner');
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
+      <script src="../../resources/run-after-layout-and-paint.js"></script>
       <style>
       div.marker::before {
           content: url(resources/test.bmp);
@@ -26,7 +27,7 @@
           img.addEventListener("load", onImageLoaded, false);
           function onImageLoaded()
           {
-              testRunner.layoutAndPaintAsyncThen(callback);
+              runAfterLayoutAndPaint(callback);
           }
           return promise;
       }
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/decode-resize.js b/third_party/blink/web_tests/http/tests/devtools/tracing/decode-resize.js
index 92a5dd31..a83bd44 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/decode-resize.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/decode-resize.js
@@ -7,6 +7,7 @@
   await TestRunner.loadModule('performance_test_runner');
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
+    <script src="../../resources/run-after-layout-and-paint.js"></script>
     <style>
     div {
         display: inline-block;
@@ -47,7 +48,7 @@
     {
         for (let image of images) {
             await addImage(image);
-            await new Promise(fulfill => testRunner.layoutAndPaintAsyncThen(fulfill));
+            await new Promise(fulfill => runAfterLayoutAndPaint(fulfill));
         }
         return generateFrames(3);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations.js b/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations.js
index 1c92ca4e..d4a5c50 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations.js
@@ -7,13 +7,14 @@
   await TestRunner.loadModule('performance_test_runner');
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
+    <script src="../../resources/run-after-layout-and-paint.js"></script>
     <div style="width: 400px; height: 2000px; background-color: grey"></div>
     <div style="position: fixed; left: 50px; top: 100px; width: 50px; height: 50px; background-color: rgba(255, 100, 100, 0.6)"></div>
   `);
   await TestRunner.evaluateInPagePromise(`
     function scrollAndDisplay() {
       scrollTo(0, 200);
-      return new Promise(fulfill => testRunner.layoutAndPaintAsyncThen(fulfill));
+      return new Promise(fulfill => runAfterLayoutAndPaint(fulfill));
     }
   `);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-gc-event.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-gc-event.js
index ad49930..ba20d38e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-gc-event.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-gc-event.js
@@ -6,11 +6,14 @@
   TestRunner.addResult(`Tests the Timeline API instrumentation of a gc event\n`);
   await TestRunner.loadModule('performance_test_runner');
   await TestRunner.showPanel('timeline');
+  await TestRunner.loadHTML(`
+    <script src="../../../resources/run-after-layout-and-paint.js"></script>
+  `);
   await TestRunner.evaluateInPagePromise(`
       function produceGarbageForGCEvents()
       {
           window.gc();
-          return new Promise(fulfill => testRunner.layoutAndPaintAsyncThen(fulfill));
+          return new Promise(fulfill => runAfterLayoutAndPaint(fulfill));
       }
   `);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-dom-gc.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-dom-gc.js
index a3ac236..24b74ecb 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-dom-gc.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-dom-gc.js
@@ -7,11 +7,15 @@
       `Tests the Timeline API instrumentation of a DOM GC event\n`);
   await TestRunner.loadModule('performance_test_runner');
   await TestRunner.showPanel('timeline');
+  await TestRunner.loadHTML(`
+    <script src="../../../resources/run-after-layout-and-paint.js"></script>
+  `);
+
   await TestRunner.evaluateInPagePromise(`
         function produceGarbageForGCEvents()
         {
             window.gc();
-            return new Promise(fulfill => testRunner.layoutAndPaintAsyncThen(fulfill));
+            return new Promise(fulfill => runAfterLayoutAndPaint(fulfill));
         }
     `);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations-expected.txt
index 6a8db3f..1aeae29 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations-expected.txt
@@ -2,7 +2,18 @@
 
 paint invalidations[
     {
-        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:21}
+        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:22}
+        changedAttribute : undefined
+        changedClass : undefined
+        changedId : undefined
+        changedPseudo : undefined
+        extraData : ""
+        nodeName : "DIV class='testElement'"
+        selectorPart : undefined
+        type : "StyleRecalcInvalidationTracking"
+    }
+    {
+        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:23}
         changedAttribute : undefined
         changedClass : undefined
         changedId : undefined
@@ -24,7 +35,7 @@
         type : "StyleRecalcInvalidationTracking"
     }
     {
-        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:21}
+        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:23}
         changedAttribute : undefined
         changedClass : undefined
         changedId : undefined
@@ -46,7 +57,7 @@
         type : "StyleRecalcInvalidationTracking"
     }
     {
-        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:21}
+        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:23}
         changedAttribute : undefined
         changedClass : undefined
         changedId : undefined
@@ -68,18 +79,7 @@
         type : "StyleRecalcInvalidationTracking"
     }
     {
-        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:21}
-        changedAttribute : undefined
-        changedClass : undefined
-        changedId : undefined
-        changedPseudo : undefined
-        extraData : ""
-        nodeName : "DIV class='testElement'"
-        selectorPart : undefined
-        type : "StyleRecalcInvalidationTracking"
-    }
-    {
-        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:22}
+        cause : {reason: Inline CSS style declaration was mutated, stackTrace: test://evaluations/0/timeline-grouped-invalidations.js:23}
         changedAttribute : undefined
         changedClass : undefined
         changedId : undefined
@@ -90,6 +90,6 @@
         type : "StyleRecalcInvalidationTracking"
     }
 ]
-PASS - record contained Inline CSS style declaration was mutated for [ DIV class='testElement' ], [ DIV class='testElement' ], and 2 others. (anonymous) @ timeline-grouped-invalidations.js:21
 PASS - record contained Inline CSS style declaration was mutated for [ DIV class='testElement' ], [ DIV class='testElement' ], and 2 others. (anonymous) @ timeline-grouped-invalidations.js:22
+PASS - record contained Inline CSS style declaration was mutated for [ DIV class='testElement' ], [ DIV class='testElement' ], and 2 others. (anonymous) @ timeline-grouped-invalidations.js:23
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations.js
index 8fa05b8..3316923d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-grouped-invalidations.js
@@ -8,6 +8,7 @@
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
     <!DOCTYPE HTML>
+    <script src="../../../resources/run-after-layout-and-paint.js"></script>
     <div class="testElement">P</div><div class="testElement">A</div>
     <div class="testElement">S</div><div class="testElement">S</div>
   `);
@@ -21,7 +22,7 @@
             testElements[i].style.color = "red";
             testElements[i].style.backgroundColor = "blue";
           }
-          testRunner.layoutAndPaintAsyncThen(resolve);
+          runAfterLayoutAndPaint(resolve);
         });
       });
     }
@@ -43,10 +44,10 @@
   var invalidations = invalidationsTree.shadowRoot.textContent;
   checkStringContains(
       invalidations,
-      `Inline CSS style declaration was mutated for [ DIV class='testElement' ], [ DIV class='testElement' ], and 2 others. (anonymous) @ timeline-grouped-invalidations.js:21`);
+      `Inline CSS style declaration was mutated for [ DIV class='testElement' ], [ DIV class='testElement' ], and 2 others. (anonymous) @ timeline-grouped-invalidations.js:22`);
   checkStringContains(
       invalidations,
-      `Inline CSS style declaration was mutated for [ DIV class='testElement' ], [ DIV class='testElement' ], and 2 others. (anonymous) @ timeline-grouped-invalidations.js:22`);
+      `Inline CSS style declaration was mutated for [ DIV class='testElement' ], [ DIV class='testElement' ], and 2 others. (anonymous) @ timeline-grouped-invalidations.js:23`);
   TestRunner.completeTest();
 
   function checkStringContains(string, contains) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/paint-profiler-update.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/paint-profiler-update.js
index a8f5818..4fac56a5 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/paint-profiler-update.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/paint-profiler-update.js
@@ -7,8 +7,9 @@
   await TestRunner.loadModule('performance_test_runner');
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
-      <div id="square" style="width: 40px; height: 40px"></div>
-    `);
+    <script src="../../../resources/run-after-layout-and-paint.js"></script>
+    <div id="square" style="width: 40px; height: 40px"></div>
+  `);
   await TestRunner.evaluateInPagePromise(`
       function performActions()
       {
@@ -20,13 +21,13 @@
           function step1()
           {
               square.style.backgroundColor = "red";
-              testRunner.layoutAndPaintAsyncThen(step2);
+              runAfterLayoutAndPaint(step2);
           }
 
           function step2()
           {
               square.style.backgroundColor = "black";
-              testRunner.layoutAndPaintAsyncThen(callback);
+              runAfterLayoutAndPaint(callback);
           }
           return promise;
       }
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/update-layer-tree.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/update-layer-tree.js
index 2abe03c..561b46c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/update-layer-tree.js
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-paint/update-layer-tree.js
@@ -7,6 +7,7 @@
   await TestRunner.loadModule('performance_test_runner');
   await TestRunner.showPanel('timeline');
   await TestRunner.loadHTML(`
+      <script src="../../../resources/run-after-layout-and-paint.js"></script>
       <style>
       .layer {
           position: absolute;
@@ -23,7 +24,7 @@
           var layer = document.createElement("div");
           layer.classList.add("layer");
           document.getElementById("parent-layer").appendChild(layer);
-          return new Promise((fulfill) => testRunner.layoutAndPaintAsyncThen(fulfill));
+          return new Promise((fulfill) => runAfterLayoutAndPaint(fulfill));
       }
   `);
 
diff --git a/third_party/blink/web_tests/http/tests/media/controls/controls-list-add-hide.html b/third_party/blink/web_tests/http/tests/media/controls/controls-list-add-hide.html
index 5866f91..f77903c 100644
--- a/third_party/blink/web_tests/http/tests/media/controls/controls-list-add-hide.html
+++ b/third_party/blink/web_tests/http/tests/media/controls/controls-list-add-hide.html
@@ -2,6 +2,7 @@
 <title>Test adding keywords to controlsList hides buttons</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../../media-resources/media-controls.js"></script>
 <video controls id="enabled-controls" width="500px"></video>
 <script>
@@ -14,12 +15,12 @@
 
     v.controlsList.add('nodownload');
 
-    testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+    runAfterLayoutAndPaint(t.step_func(() => {
       assert_true(isFullscreenButtonEnabled(v));
       assert_false(isDownloadsButtonEnabled(v));
       v.controlsList.add('nofullscreen');
 
-      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+      runAfterLayoutAndPaint(t.step_func_done(() => {
         assert_false(isFullscreenButtonEnabled(v));
         assert_false(isDownloadsButtonEnabled(v));
       }));
diff --git a/third_party/blink/web_tests/http/tests/media/controls/controls-list-remove-show.html b/third_party/blink/web_tests/http/tests/media/controls/controls-list-remove-show.html
index bb58312..5b25665 100644
--- a/third_party/blink/web_tests/http/tests/media/controls/controls-list-remove-show.html
+++ b/third_party/blink/web_tests/http/tests/media/controls/controls-list-remove-show.html
@@ -2,6 +2,7 @@
 <title>Test removing keywords from controlsList shows buttons</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../../media-resources/media-controls.js"></script>
 <video controlslist="nodownload nofullscreen" id="disabled-controls" width="500px"></video>
 <script>
@@ -14,13 +15,13 @@
 
     v.controlsList.remove('nodownload');
 
-    testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+    runAfterLayoutAndPaint(t.step_func(() => {
       assert_false(isFullscreenButtonEnabled(v));
       assert_true(isDownloadsButtonEnabled(v));
 
       v.controlsList.remove('nofullscreen');
 
-      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+      runAfterLayoutAndPaint(t.step_func_done(() => {
         assert_true(isFullscreenButtonEnabled(v));
         assert_true(isDownloadsButtonEnabled(v));
       }));
diff --git a/third_party/blink/web_tests/http/tests/resources/run-after-layout-and-paint.js b/third_party/blink/web_tests/http/tests/resources/run-after-layout-and-paint.js
index e317341..bf2ed0c 100644
--- a/third_party/blink/web_tests/http/tests/resources/run-after-layout-and-paint.js
+++ b/third_party/blink/web_tests/http/tests/resources/run-after-layout-and-paint.js
@@ -1,26 +1,34 @@
-// Run a callback after layout and paint of all pending document changes.
+// Run a callback after a frame update.
 //
-// It has two modes:
-// - traditional mode, for existing tests, and tests needing customized notifyDone timing:
-//   Usage:
+// Note that this file has two copies:
+//   resources/run-after-layout-and-paint.js
+// and
+//   http/tests/resources/run-after-layout-and-paint.js.
+// They should be kept always the same.
+//
+// The function runAfterLayoutAndPaint() has two modes:
+// - traditional mode, for existing tests, and tests needing customized
+//   notifyDone timing:
 //     if (window.testRunner)
 //       testRunner.waitUntilDone();
 //     runAfterLayoutAndPaint(function() {
 //       ... // some code which modifies style/layout
 //       if (window.testRunner)
 //         testRunner.notifyDone();
-//       // Or to ensure the next paint is executed before the test finishes:
-//       // if (window.testRunner)
-//       //   runAfterAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 //       // Or notifyDone any time later if needed.
 //     });
 //
-// - autoNotifyDone mode, for new tests which just need to change style/layout and finish:
-//   Usage:
+// - autoNotifyDone mode, for new tests which just need to change style/layout
+//   and finish:
 //     runAfterLayoutAndPaint(function() {
 //       ... // some code which modifies style/layout
 //     }, true);
-
+//
+// Note that because we always update a frame before finishing a test,
+// we don't need
+//     runAfterLayoutAndPaint(function() { testRunner.notifyDone(); })
+// to ensure the test finish after a frame update.
+//
 if (window.internals)
     internals.runtimeFlags.paintUnderInvalidationCheckingEnabled = true;
 
@@ -35,9 +43,18 @@
     if (autoNotifyDone)
         testRunner.waitUntilDone();
 
-    testRunner.layoutAndPaintAsyncThen(function() {
-        callback();
-        if (autoNotifyDone)
-            testRunner.notifyDone();
+    // We do requestAnimationFrame and setTimeout to ensure a frame has started
+    // and layout and paint have run. The requestAnimationFrame fires after the
+    // frame has started but before layout and paint. The setTimeout fires
+    // at the beginning of the next frame, meaning that the previous frame has
+    // completed layout and paint.
+    // See http://crrev.com/c/1395193/10/third_party/blink/web_tests/http/tests/resources/run-after-layout-and-paint.js
+    // for more discussions.
+    requestAnimationFrame(function() {
+        setTimeout(function() {
+            callback();
+            if (autoNotifyDone)
+                testRunner.notifyDone();
+        }, 0);
     });
 }
diff --git a/third_party/blink/web_tests/images/animated-gif-fast-crash.html b/third_party/blink/web_tests/images/animated-gif-fast-crash.html
index 2214ad9..379aa1f 100644
--- a/third_party/blink/web_tests/images/animated-gif-fast-crash.html
+++ b/third_party/blink/web_tests/images/animated-gif-fast-crash.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 Tests fast animating gif (10ms per frame). Passes if it doesn't crash.<br>
 <img src="resources/animated-10color-fast.gif">
+<script src="../resources/run-after-layout-and-paint.js"></script>
 <script>
 
 // Busy-wait 50ms to trigger the bug of invalidation during paint.
@@ -16,7 +17,7 @@
 function displayOneFrame() {
   delay();
   if (--count >= 0)
-    testRunner.layoutAndPaintAsyncThen(displayOneFrame);
+    runAfterLayoutAndPaint(displayOneFrame);
   else
     testRunner.notifyDone();
 }
diff --git a/third_party/blink/web_tests/images/animated-gif-with-offsets.html b/third_party/blink/web_tests/images/animated-gif-with-offsets.html
index 26db055..3d118de 100644
--- a/third_party/blink/web_tests/images/animated-gif-with-offsets.html
+++ b/third_party/blink/web_tests/images/animated-gif-with-offsets.html
@@ -4,6 +4,7 @@
 doesn't crash, we're good.
 <img src="resources/animated-gif-with-offsets.gif">
 </body>
+<script src="../resources/run-after-layout-and-paint.js"></script>
 <script>
 
 // Delay 50ms for each frame.
@@ -17,7 +18,7 @@
 function displayOneFrame() {
   delay();
   if (--count >= 0)
-    testRunner.layoutAndPaintAsyncThen(displayOneFrame);
+    runAfterLayoutAndPaint(displayOneFrame);
   else
     testRunner.notifyDone();
 }
diff --git a/third_party/blink/web_tests/images/resources/color-checker-munsell-chart.js b/third_party/blink/web_tests/images/resources/color-checker-munsell-chart.js
index 4b213b4..951389b 100644
--- a/third_party/blink/web_tests/images/resources/color-checker-munsell-chart.js
+++ b/third_party/blink/web_tests/images/resources/color-checker-munsell-chart.js
@@ -6,7 +6,7 @@
   var image = document.querySelector('img');
 
   image.onload = function() {
-    runAfterLayoutAndPaint(function () { setTimeout(drawImageToCanvas, 0) });
+    runAfterLayoutAndPaint(drawImageToCanvas);
   };
 
   image.src = source;
diff --git a/third_party/blink/web_tests/inspector-protocol/animation/animation-pause-infinite.js b/third_party/blink/web_tests/inspector-protocol/animation/animation-pause-infinite.js
index 837fa28..e928d32 100644
--- a/third_party/blink/web_tests/inspector-protocol/animation/animation-pause-infinite.js
+++ b/third_party/blink/web_tests/inspector-protocol/animation/animation-pause-infinite.js
@@ -1,5 +1,6 @@
 (async function(testRunner) {
   var {page, session, dp} = await testRunner.startHTML(`
+    <script src='../../resources/run-after-layout-and-paint.js'></script>
     <div id='node' style='background-color: red; height: 100px'></div>
   `, 'Tests that the animation is correctly paused.');
 
@@ -17,8 +18,7 @@
     (function rafWidth() {
         var callback;
         var promise = new Promise((fulfill) => callback = fulfill);
-        if (window.testRunner)
-            testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
+        runAfterLayoutAndPaint(() => callback(node.offsetWidth));
         return promise;
     })()
   `);
diff --git a/third_party/blink/web_tests/inspector-protocol/animation/animation-pause.js b/third_party/blink/web_tests/inspector-protocol/animation/animation-pause.js
index 37cfa58..238c974a 100644
--- a/third_party/blink/web_tests/inspector-protocol/animation/animation-pause.js
+++ b/third_party/blink/web_tests/inspector-protocol/animation/animation-pause.js
@@ -1,5 +1,6 @@
 (async function(testRunner) {
   var {page, session, dp} = await testRunner.startHTML(`
+    <script src='../../resources/run-after-layout-and-paint.js'></script>
     <div id='node' style='background-color: red; height: 100px'></div>
   `, 'Tests that the animation is correctly paused.');
 
@@ -17,8 +18,7 @@
     (function rafWidth() {
         var callback;
         var promise = new Promise((fulfill) => callback = fulfill);
-        if (window.testRunner)
-            testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
+        runAfterLayoutAndPaint(() => callback(node.offsetWidth));
         return promise;
     })()
   `);
diff --git a/third_party/blink/web_tests/inspector-protocol/animation/animation-seek-past-end.js b/third_party/blink/web_tests/inspector-protocol/animation/animation-seek-past-end.js
index 37dc66c..34206650 100644
--- a/third_party/blink/web_tests/inspector-protocol/animation/animation-seek-past-end.js
+++ b/third_party/blink/web_tests/inspector-protocol/animation/animation-seek-past-end.js
@@ -1,5 +1,6 @@
 (async function(testRunner) {
   var {page, session, dp} = await testRunner.startHTML(`
+    <script src='../../resources/run-after-layout-and-paint.js'></script>
     <div id='node' style='background-color: red; height: 100px; width: 100px'></div>
   `, 'Tests seeking animation past end time.');
 
@@ -16,8 +17,7 @@
     (function rafWidth() {
         var callback;
         var promise = new Promise((fulfill) => callback = fulfill);
-        if (window.testRunner)
-            testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
+        runAfterLayoutAndPaint(() => callback(node.offsetWidth));
         return promise;
     })()
   `);
diff --git a/third_party/blink/web_tests/media/controls-slider-appearance-crash.html b/third_party/blink/web_tests/media/controls-slider-appearance-crash.html
index e398a65..ff43cf30 100644
--- a/third_party/blink/web_tests/media/controls-slider-appearance-crash.html
+++ b/third_party/blink/web_tests/media/controls-slider-appearance-crash.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <script src="../resources/js-test.js"></script>
+<script src="../resources/run-after-layout-and-paint.js"></script>
 <script>
 description("This test ensures that applying media control slider thumb appearance to pseudo-elements does not cause a crash.");
 </script>
@@ -25,6 +26,6 @@
 <script>
 if (window.testRunner) {
     testRunner.waitUntilDone();
-    testRunner.layoutAndPaintAsyncThen(function() { testRunner.notifyDone(); });
+    runAfterLayoutAndPaint(function() { testRunner.notifyDone(); });
 }
 </script>
diff --git a/third_party/blink/web_tests/media/controls/buttons-after-reset.html b/third_party/blink/web_tests/media/controls/buttons-after-reset.html
index 9bca350..496e966 100644
--- a/third_party/blink/web_tests/media/controls/buttons-after-reset.html
+++ b/third_party/blink/web_tests/media/controls/buttons-after-reset.html
@@ -2,6 +2,7 @@
 <html>
 <title>Test that resetting the controls after load is a no-op.</title>
 <script src="../media-controls.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <video controls width=400>
   <track kind=subtitles src=fake-en-sub.vtt srclang=en label=English>
   <track kind=subtitles src=fake-fr-sub.vtt srclang=fr label=French>
@@ -15,10 +16,10 @@
   video.src = '../content/test.ogv';
   video.addEventListener('loadedmetadata', () => {
     video.controls = false;
-    testRunner.layoutAndPaintAsyncThen(() => {
+    runAfterLayoutAndPaint(() => {
       video.controls = true;
 
-      testRunner.layoutAndPaintAsyncThen(() => {
+      runAfterLayoutAndPaint(() => {
         testRunner.notifyDone();
       });
     });
diff --git a/third_party/blink/web_tests/media/controls/captions-menu-always-visible-expected.html b/third_party/blink/web_tests/media/controls/captions-menu-always-visible-expected.html
index ce92e5c..b3473a9 100644
--- a/third_party/blink/web_tests/media/controls/captions-menu-always-visible-expected.html
+++ b/third_party/blink/web_tests/media/controls/captions-menu-always-visible-expected.html
@@ -28,8 +28,7 @@
     // :hover sometimes applying.
     textTrackMenu(video).style = 'pointer-events: none;';
 
-    testRunner.layoutAndPaintAsyncThen(() => {
+    if (window.testRunner)
       testRunner.notifyDone();
-    });
   });
 </script>
diff --git a/third_party/blink/web_tests/media/controls/captions-menu-always-visible.html b/third_party/blink/web_tests/media/controls/captions-menu-always-visible.html
index c361949..7507a2c5 100644
--- a/third_party/blink/web_tests/media/controls/captions-menu-always-visible.html
+++ b/third_party/blink/web_tests/media/controls/captions-menu-always-visible.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <title>Captions menu always visible</title>
 <script src="../media-controls.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <style>
   .container {
     overflow: hidden;
@@ -31,7 +32,7 @@
     // :hover sometimes applying.
     textTrackMenu(video).style = 'pointer-events: none;';
 
-    testRunner.layoutAndPaintAsyncThen(() => {
+    runAfterLayoutAndPaint(() => {
       testRunner.notifyDone();
     });
   });
diff --git a/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html b/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html
index 143b16a..7d0b0898 100644
--- a/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html
+++ b/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html
@@ -3,6 +3,7 @@
 <title>Media Controls: Test controls layout correctly in different small sizes.</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <video controls></video>
 <script>
@@ -23,7 +24,7 @@
     function runTestCase(index) {
       let test = testCases[index];
       video.width = test;
-      testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+      runAfterLayoutAndPaint(t.step_func(() => {
         expectLayoutCorrectly();
 
         assert_not_equals(getComputedStyle(overflowBtn), 'none',
diff --git a/third_party/blink/web_tests/media/controls/download-button-displays-with-preload-none.html b/third_party/blink/web_tests/media/controls/download-button-displays-with-preload-none.html
index e1e6c46..c4fd9d4 100644
--- a/third_party/blink/web_tests/media/controls/download-button-displays-with-preload-none.html
+++ b/third_party/blink/web_tests/media/controls/download-button-displays-with-preload-none.html
@@ -2,13 +2,14 @@
 <title>media controls download button preload none</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <video controls preload="none" src="https://someexample.example/example.mp4"></video>
 <script>
 async_test(function(t) {
   var video = document.querySelector("video");
 
-  testRunner.layoutAndPaintAsyncThen(t.step_func_done(function() {
+  runAfterLayoutAndPaint(t.step_func_done(function() {
     assert_true(isDownloadsButtonEnabled(video));
   }));
 
diff --git a/third_party/blink/web_tests/media/controls/large-overflow-menu-when-pip-enabled.html b/third_party/blink/web_tests/media/controls/large-overflow-menu-when-pip-enabled.html
index ced98b1..c272e2a 100644
--- a/third_party/blink/web_tests/media/controls/large-overflow-menu-when-pip-enabled.html
+++ b/third_party/blink/web_tests/media/controls/large-overflow-menu-when-pip-enabled.html
@@ -2,6 +2,7 @@
 <title>Large overflow menu when pip enabled</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <body>
 <video controls></video>
@@ -20,11 +21,11 @@
     expectOverflowMenuAndTrackListContainsPip();
 
     video.setAttribute('disablepictureinpicture', '');
-    testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+    runAfterLayoutAndPaint(t.step_func(() => {
       expectOverflowMenuAndTrackListNotContainsPip();
 
       video.removeAttribute('disablepictureinpicture');
-      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+      runAfterLayoutAndPaint(t.step_func_done(() => {
         expectOverflowMenuAndTrackListContainsPip();
       }));
     }));
diff --git a/third_party/blink/web_tests/media/controls/modern/immersive-mode-adds-css-class.html b/third_party/blink/web_tests/media/controls/modern/immersive-mode-adds-css-class.html
index 61a49242..9dd1f748 100644
--- a/third_party/blink/web_tests/media/controls/modern/immersive-mode-adds-css-class.html
+++ b/third_party/blink/web_tests/media/controls/modern/immersive-mode-adds-css-class.html
@@ -3,6 +3,7 @@
 <title>Test that enabling immersive mode adds the immersive mode CSS class.</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../../media-controls.js"></script>
 <video controls width=400 preload=metadata src="../../content/60_sec_video.webm"></video>
 <script>
@@ -19,7 +20,7 @@
 
     // Make sure that we still respect immersive mode when enlarged.
     video.width = 1000;
-    testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+    runAfterLayoutAndPaint(t.step_func_done(() => {
       assert_true(controls.classList.contains(immersiveModeCSSClass));
       checkThatVRCSSRulesAreRespected();
     }));
diff --git a/third_party/blink/web_tests/media/controls/overflow-menu-always-visible-expected.html b/third_party/blink/web_tests/media/controls/overflow-menu-always-visible-expected.html
index 4a69e0f..327ea430 100644
--- a/third_party/blink/web_tests/media/controls/overflow-menu-always-visible-expected.html
+++ b/third_party/blink/web_tests/media/controls/overflow-menu-always-visible-expected.html
@@ -10,9 +10,6 @@
 </style>
 <video controls muted></video>
 <script>
-  if (window.testRunner)
-    testRunner.waitUntilDone();
-
   var video = document.querySelector('video');
   enableTestMode(video);
   video.src = '../content/test.ogv';
@@ -26,9 +23,5 @@
     // Disabling pointer events on the overflow menu to avoid a flakyness with
     // :hover sometimes applying.
     overflowMenu(video).style = 'pointer-events: none;';
-
-    testRunner.layoutAndPaintAsyncThen(() => {
-      testRunner.notifyDone();
-    });
   });
 </script>
diff --git a/third_party/blink/web_tests/media/controls/overflow-menu-always-visible.html b/third_party/blink/web_tests/media/controls/overflow-menu-always-visible.html
index ffcc86f..602edd8 100644
--- a/third_party/blink/web_tests/media/controls/overflow-menu-always-visible.html
+++ b/third_party/blink/web_tests/media/controls/overflow-menu-always-visible.html
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <title>Overflow menu always visible</title>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <style>
   .container {
@@ -30,7 +31,7 @@
     // :hover sometimes applying.
     overflowMenu(video).style = 'pointer-events: none;';
 
-    testRunner.layoutAndPaintAsyncThen(() => {
+    runAfterLayoutAndPaint(() => {
       testRunner.notifyDone();
     });
   });
diff --git a/third_party/blink/web_tests/media/controls/overflow-menu-animation.html b/third_party/blink/web_tests/media/controls/overflow-menu-animation.html
index 69cc4a2..3d5c070 100644
--- a/third_party/blink/web_tests/media/controls/overflow-menu-animation.html
+++ b/third_party/blink/web_tests/media/controls/overflow-menu-animation.html
@@ -2,6 +2,7 @@
 <title>Media Controls: overflow menu item list animation.</title>
 <script src='../../resources/testharness.js'></script>
 <script src='../../resources/testharnessreport.js'></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src='../media-controls.js'></script>
 <video controls width=400></video>
 <script>
@@ -18,7 +19,7 @@
 
     function runTestCase(index) {
       video.width = testCasesWidth[index];
-      testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+      runAfterLayoutAndPaint(t.step_func(() => {
 
         // Go through item list, check 'animated-##' class is presented if item is displayed
         // and check if the items' classes are in sequential order
diff --git a/third_party/blink/web_tests/media/controls/overlay-play-button-document-move.html b/third_party/blink/web_tests/media/controls/overlay-play-button-document-move.html
index cacba37..f39d8fc 100644
--- a/third_party/blink/web_tests/media/controls/overlay-play-button-document-move.html
+++ b/third_party/blink/web_tests/media/controls/overlay-play-button-document-move.html
@@ -2,6 +2,7 @@
 <title>media controls overlay play button document move</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <script src="overlay-play-button.js"></script>
 <body>
@@ -19,22 +20,22 @@
 
     // If the width goes under the minimum, the button should be hidden.
     video.width = NARROW_VIDEO_WIDTH;
-    testRunner.layoutAndPaintAsyncThen(t.step_func(function() {
+    runAfterLayoutAndPaint(t.step_func(function() {
       assertOverlayPlayButtonNotVisible(video);
 
       // Re-widening the video should display the button.
       video.width = NORMAL_VIDEO_WIDTH;
-      testRunner.layoutAndPaintAsyncThen(t.step_func(function() {
+      runAfterLayoutAndPaint(t.step_func(function() {
         assertOverlayPlayButtonVisible(video);
 
         // If the height goes under the minimum, the button should be hidden.
         video.height = NARROW_VIDEO_HEIGHT;
-        testRunner.layoutAndPaintAsyncThen(t.step_func(function() {
+        runAfterLayoutAndPaint(t.step_func(function() {
           assertOverlayPlayButtonNotVisible(video);
 
           // Re-heightening the video should display the button.
           video.height = NORMAL_VIDEO_HEIGHT;
-          testRunner.layoutAndPaintAsyncThen(t.step_func_done(function() {
+          runAfterLayoutAndPaint(t.step_func_done(function() {
             assertOverlayPlayButtonVisible(video);
           }));
         }));
diff --git a/third_party/blink/web_tests/media/controls/overlay-play-button-narrow.html b/third_party/blink/web_tests/media/controls/overlay-play-button-narrow.html
index 248a6a4..66d4b80 100644
--- a/third_party/blink/web_tests/media/controls/overlay-play-button-narrow.html
+++ b/third_party/blink/web_tests/media/controls/overlay-play-button-narrow.html
@@ -2,6 +2,7 @@
 <title>media controls overlay play button narrow</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <script src="overlay-play-button.js"></script>
 <body>
@@ -24,22 +25,22 @@
 
     // If the width goes under the minimum, the button should be hidden.
     video.width = NARROW_VIDEO_WIDTH;
-    testRunner.layoutAndPaintAsyncThen(t.step_func(function() {
+    runAfterLayoutAndPaint(t.step_func(function() {
       assertOverlayPlayButtonNotVisible(video);
 
       // Re-widening the video should display the button.
       video.width = NORMAL_VIDEO_WIDTH;
-      testRunner.layoutAndPaintAsyncThen(t.step_func(function() {
+      runAfterLayoutAndPaint(t.step_func(function() {
         assertOverlayPlayButtonVisible(video);
 
         // If the height goes under the minimum, the button should be hidden.
         video.height = NARROW_VIDEO_HEIGHT;
-        testRunner.layoutAndPaintAsyncThen(t.step_func(function() {
+        runAfterLayoutAndPaint(t.step_func(function() {
           assertOverlayPlayButtonNotVisible(video);
 
           // Re-heightening the video should display the button.
           video.height = NORMAL_VIDEO_HEIGHT;
-          testRunner.layoutAndPaintAsyncThen(t.step_func_done(function() {
+          runAfterLayoutAndPaint(t.step_func_done(function() {
             assertOverlayPlayButtonVisible(video);
           }));
         }));
diff --git a/third_party/blink/web_tests/media/controls/resizing-changes-css-class.html b/third_party/blink/web_tests/media/controls/resizing-changes-css-class.html
index 741625d..0d1592f0 100644
--- a/third_party/blink/web_tests/media/controls/resizing-changes-css-class.html
+++ b/third_party/blink/web_tests/media/controls/resizing-changes-css-class.html
@@ -3,6 +3,7 @@
 <title>Test that sizing changes are reflected in CSS classes.</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <video controls width=200 preload=none></video>
 <script>
@@ -26,7 +27,7 @@
   function runTestCase(index) {
     let test = testCases[index];
     video.width = test.width;
-    testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+    runAfterLayoutAndPaint(t.step_func(() => {
       test.expect();
       let nextIndex = index + 1;
       if (nextIndex === testCases.length) {
diff --git a/third_party/blink/web_tests/media/controls/settings-disable-controls.html b/third_party/blink/web_tests/media/controls/settings-disable-controls.html
index d753eb1..63735381 100644
--- a/third_party/blink/web_tests/media/controls/settings-disable-controls.html
+++ b/third_party/blink/web_tests/media/controls/settings-disable-controls.html
@@ -2,6 +2,7 @@
 <title>Test that 'mediaControlsEnabled' properly toggles the native controls</title>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script src="../media-controls.js"></script>
 <video controls></video>
 <script>
@@ -23,12 +24,12 @@
     assert_not_equals(mediaControlsButton(video, "panel").style.display, "none");
 
     internals.settings.setMediaControlsEnabled(false);
-    testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+    runAfterLayoutAndPaint(t.step_func(() => {
       assert_equals(mediaControlsButton(video, "panel").style.display, "none");
       assert_equals(overlayCastButton(video).style.display, "none");
 
       internals.settings.setMediaControlsEnabled(true);
-      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+      runAfterLayoutAndPaint(t.step_func_done(() => {
         assert_not_equals(mediaControlsButton(video, "panel").style.display, "none");
         assert_equals(overlayCastButton(video).style.display, "none");
       }));
diff --git a/third_party/blink/web_tests/media/resources/munsell-video-chart.js b/third_party/blink/web_tests/media/resources/munsell-video-chart.js
index 8efbe02..2f7fce82 100644
--- a/third_party/blink/web_tests/media/resources/munsell-video-chart.js
+++ b/third_party/blink/web_tests/media/resources/munsell-video-chart.js
@@ -5,7 +5,7 @@
 
   var image = document.querySelector('img');
   image.onload = function() {
-    runAfterLayoutAndPaint(function () { setTimeout(drawImageToCanvas, 0) });
+    runAfterLayoutAndPaint(drawImageToCanvas);
   };
 
   image.src = source;
diff --git a/third_party/blink/web_tests/media/video-controls-overflow-menu-fullscreen-button.html b/third_party/blink/web_tests/media/video-controls-overflow-menu-fullscreen-button.html
index 8982077..47e3be2c 100644
--- a/third_party/blink/web_tests/media/video-controls-overflow-menu-fullscreen-button.html
+++ b/third_party/blink/web_tests/media/video-controls-overflow-menu-fullscreen-button.html
@@ -2,6 +2,7 @@
 <title>Clicking on the overflow fullscreen button opens the video in fullscreen.</title>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
+<script src="../resources/run-after-layout-and-paint.js"></script>
 <script src="media-controls.js"></script>
 <script src="overflow-menu.js"></script>
 
@@ -33,7 +34,7 @@
     document.onwebkitfullscreenchange = t.step_func(() => {
       assert_equals(document.fullscreenElement, video);
       // Hiding the overflow menu is triggered by layout.
-      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+      runAfterLayoutAndPaint(t.step_func_done(() => {
         assert_equals(getComputedStyle(overflowMenu).display, "none");
       }));
     });
diff --git a/third_party/blink/web_tests/media/video-persistence-expected.html b/third_party/blink/web_tests/media/video-persistence-expected.html
index 6d52a37..bdb3365f 100644
--- a/third_party/blink/web_tests/media/video-persistence-expected.html
+++ b/third_party/blink/web_tests/media/video-persistence-expected.html
@@ -55,9 +55,7 @@
     });
 
     document.addEventListener('webkitfullscreenchange', e => {
-      testRunner.layoutAndPaintAsyncThen(() => {
-        testRunner.notifyDone();
-      });
+      testRunner.notifyDone();
     });
   }
 </script>
diff --git a/third_party/blink/web_tests/media/video-persistence.html b/third_party/blink/web_tests/media/video-persistence.html
index a9112eb..4f31080 100644
--- a/third_party/blink/web_tests/media/video-persistence.html
+++ b/third_party/blink/web_tests/media/video-persistence.html
@@ -50,10 +50,7 @@
 
     document.addEventListener('webkitfullscreenchange', e => {
       internals.setPersistent(video, true);
-
-      testRunner.layoutAndPaintAsyncThen(() => {
-        testRunner.notifyDone();
-      });
+      testRunner.notifyDone();
     });
   }
 </script>
diff --git a/third_party/blink/web_tests/paint/background/scrolling-background-with-negative-z-child.html b/third_party/blink/web_tests/paint/background/scrolling-background-with-negative-z-child.html
index 89b02050..0f4634f 100644
--- a/third_party/blink/web_tests/paint/background/scrolling-background-with-negative-z-child.html
+++ b/third_party/blink/web_tests/paint/background/scrolling-background-with-negative-z-child.html
@@ -1,4 +1,5 @@
 <!doctype html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <style>
 #container {
   overflow: scroll;
@@ -22,16 +23,7 @@
   <div id="compositedChild"></div>
 </div>
 <script>
-  if (window.testRunner) {
-    testRunner.waitUntilDone();
-    testRunner.layoutAndPaintAsyncThen(function() {
-      document.getElementById('container').scrollTop = 1000;
-      testRunner.notifyDone();
-    });
-  } else {
-    // For manual testing.
-    setTimeout(function() {
-      document.getElementById('container').scrollTop = 1000;
-    }, 500);
-  }
+  runAfterLayoutAndPaint(function() {
+    document.getElementById('container').scrollTop = 1000;
+  }, true);
 </script>
diff --git a/third_party/blink/web_tests/paint/invalidation/background/obscured-background-no-repaint.html b/third_party/blink/web_tests/paint/invalidation/background/obscured-background-no-repaint.html
index 2b7e28c..399fd65 100644
--- a/third_party/blink/web_tests/paint/invalidation/background/obscured-background-no-repaint.html
+++ b/third_party/blink/web_tests/paint/invalidation/background/obscured-background-no-repaint.html
@@ -1,5 +1,6 @@
 <html>
 <head>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <style type="text/css">
     #test1 div {
         height: 100px;
@@ -74,12 +75,12 @@
         // Ensure the deferred decoder has decoded ../resources/apple.jpg.
         testRunner.updateAllLifecyclePhasesAndCompositeThen(function() {
             internals.advanceImageAnimation(imgForAdvanceImageAnimation);
-            testRunner.layoutAndPaintAsyncThen(function() {
+            runAfterLayoutAndPaint(function() {
                 internals.startTrackingRepaints(document);
                 internals.advanceImageAnimation(imgForAdvanceImageAnimation);
-                testRunner.layoutAndPaintAsyncThen(function() {
+                runAfterLayoutAndPaint(function() {
                     internals.advanceImageAnimation(imgForAdvanceImageAnimation);
-                    testRunner.layoutAndPaintAsyncThen(finish);
+                    runAfterLayoutAndPaint(finish);
                 });
             });
         });
diff --git a/third_party/blink/web_tests/paint/invalidation/resources/text-based-repaint.js b/third_party/blink/web_tests/paint/invalidation/resources/text-based-repaint.js
index a9966490..f55bd95 100644
--- a/third_party/blink/web_tests/paint/invalidation/resources/text-based-repaint.js
+++ b/third_party/blink/web_tests/paint/invalidation/resources/text-based-repaint.js
@@ -28,11 +28,16 @@
     else
         testRunner.dumpAsText();
 
-    testRunner.layoutAndPaintAsyncThen(function() {
-        internals.startTrackingRepaints(top.document);
-        repaintTest();
-        if (!window.testIsAsync)
-            finishRepaintTest();
+    // This is equivalent to runAfterLayoutAndPaint() in
+    // ../../resources/run-after-layout-and-paint.js. Duplicate it here so that
+    // the callers don't need to include that file.
+    requestAnimationFrame(() => {
+        setTimeout(() => {
+            internals.startTrackingRepaints(top.document);
+            repaintTest();
+            if (!window.testIsAsync)
+                finishRepaintTest();
+        }, 0);
     });
 }
 
@@ -42,22 +47,11 @@
     runRepaintTest();
 }
 
-function forceStyleRecalc()
-{
-    if (document.body)
-        document.body.clientTop;
-    else if (document.documentElement)
-        document.documentElement.clientTop;
-}
-
 function finishRepaintTest()
 {
     if (!window.testRunner || !window.internals)
         return;
 
-    // Force a style recalc.
-    forceStyleRecalc();
-
     var flags = internals.LAYER_TREE_INCLUDES_PAINT_INVALIDATIONS;
 
     if (window.layerTreeAsTextAdditionalFlags)
diff --git a/third_party/blink/web_tests/paint/invalidation/resources/window-resize-repaint.js b/third_party/blink/web_tests/paint/invalidation/resources/window-resize-repaint.js
index ae0d641a..c5ef19e 100644
--- a/third_party/blink/web_tests/paint/invalidation/resources/window-resize-repaint.js
+++ b/third_party/blink/web_tests/paint/invalidation/resources/window-resize-repaint.js
@@ -21,7 +21,7 @@
     if (sizeIndex < testSizes.length) {
         internals.startTrackingRepaints(document);
         window.resizeTo(testSizes[sizeIndex].width, testSizes[sizeIndex].height);
-        testRunner.layoutAndPaintAsyncThen(doTest);
+        runAfterLayoutAndPaint(doTest);
     } else if (window.testRunner) {
         testRunner.setCustomTextOutput(repaintRects);
         testRunner.notifyDone();
@@ -33,6 +33,6 @@
     testRunner.waitUntilDone();
     onload = function() {
         window.resizeTo(testSizes[0].width, testSizes[0].height);
-        testRunner.layoutAndPaintAsyncThen(doTest);
+        runAfterLayoutAndPaint(doTest);
     };
 }
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container.html b/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container.html
index 4a0d229..c9c73d8 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container.html
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html> 
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <script src="../../../editing/editing.js"></script>
 <input id="root" style="will-change: transform" size="5"  value="test test test">
@@ -11,7 +12,7 @@
 function repaintTest() {
   root.focus();
   root.scrollLeft = 200;
-  requestAnimationFrame(function() {
+  runAfterLayoutAndPaint(function() {
     execMoveSelectionForwardByWordCommand();
     execMoveSelectionForwardByWordCommand();
     execMoveSelectionForwardByWordCommand();
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container.html b/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container.html
index dcd95ff..4210de6 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container.html
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html> 
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <script src="../../../editing/editing.js"></script>
 <input id="root" style="will-change: transform" size="5"  value="test test test">
@@ -11,7 +12,7 @@
 function repaintTest() {
   root.focus();
   root.scrollLeft = 200;
-  requestAnimationFrame(function() {
+  runAfterLayoutAndPaint(function() {
     execMoveSelectionForwardByWordCommand();
     execMoveSelectionForwardByWordCommand();
     execMoveSelectionForwardByWordCommand();
diff --git a/third_party/blink/web_tests/paint/invalidation/selection/selection-in-composited-scrolling-container.html b/third_party/blink/web_tests/paint/invalidation/selection/selection-in-composited-scrolling-container.html
index 0e886f7..35ca562 100644
--- a/third_party/blink/web_tests/paint/invalidation/selection/selection-in-composited-scrolling-container.html
+++ b/third_party/blink/web_tests/paint/invalidation/selection/selection-in-composited-scrolling-container.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <input id="target" size="5"  value="test test test">
 <script>
@@ -10,7 +11,7 @@
 function repaintTest() {
   target.focus();
   target.scrollLeft = 200;
-  requestAnimationFrame(function() {
+  runAfterLayoutAndPaint(function() {
       target.setSelectionRange(5, 10);
       finishRepaintTest();
   });
diff --git a/third_party/blink/web_tests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html b/third_party/blink/web_tests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html
index 9f4151bd..69ca757 100644
--- a/third_party/blink/web_tests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html
+++ b/third_party/blink/web_tests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <input id="target"size="5"  value="test test test">
 <script>
@@ -10,7 +11,7 @@
 function repaintTest() {
   target.focus();
   target.scrollLeft = 5;
-  requestAnimationFrame(function() {
+  runAfterLayoutAndPaint(function() {
       target.setSelectionRange(5, 10);
       finishRepaintTest();
   });
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document.svg b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document.svg
index a6a2bc8..ca2ac50 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document.svg
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document.svg
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="runRepaintAndPixelTest()">
+    <script xlink:href="../../../resources/run-after-layout-and-paint.js"></script>
     <script xlink:href="../resources/text-based-repaint.js"></script>
     <title>There should be a single green 100x100 square.</title>
     <defs>
@@ -20,7 +21,7 @@
             var greenImage = document.getElementById("feimage-green");
             document.getElementById("filter").removeChild(greenImage);
 
-            testRunner.layoutAndPaintAsyncThen(function() {
+            runAfterLayoutAndPaint(function() {
                 document.getElementById("filter").appendChild(greenImage);
                 finishRepaintTest();
             });
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/js-late-mask-and-object-creation.svg b/third_party/blink/web_tests/paint/invalidation/svg/js-late-mask-and-object-creation.svg
index bd34aaca..e5861f1e 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/js-late-mask-and-object-creation.svg
+++ b/third_party/blink/web_tests/paint/invalidation/svg/js-late-mask-and-object-creation.svg
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">  
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root" width="100%" height="100%" viewBox="0 0 800 600" onload="runRepaintAndPixelTest()">
+<script xlink:href="../../../resources/run-after-layout-and-paint.js"/>
 <script xlink:href="../resources/text-based-repaint.js"/>
 
 <g id="content"/>
@@ -10,7 +11,7 @@
     var content = document.getElementById("content");
 
     function repaintTest() {
-        testRunner.layoutAndPaintAsyncThen(createObject);
+        createObject();
     }
 
     function createObject()
@@ -23,7 +24,7 @@
         rect.setAttribute("mask", "url(#dynMask)");
 
         content.appendChild(rect);
-        testRunner.layoutAndPaintAsyncThen(createMask);
+        runAfterLayoutAndPaint(createMask);
     }
 
     function createMask()
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation-expected.txt
index 30a8bab7..257c371 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation-expected.txt
@@ -29,6 +29,26 @@
         },
         {
           "object": "LayoutSVGRect rect",
+          "rect": [200, 100, 403, 249],
+          "reason": "chunk appeared"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [200, 100, 403, 249],
+          "reason": "chunk appeared"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [200, 100, 403, 249],
+          "reason": "chunk disappeared"
+        },
+        {
+          "object": "LayoutSVGRect rect",
+          "rect": [200, 100, 403, 249],
+          "reason": "chunk disappeared"
+        },
+        {
+          "object": "LayoutSVGRect rect",
           "rect": [100, 100, 403, 249],
           "reason": "chunk appeared"
         },
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation.svg b/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation.svg
index 47b1034..df86666 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation.svg
+++ b/third_party/blink/web_tests/paint/invalidation/svg/mask-invalidation.svg
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="runRepaintAndPixelTest()">
+<script xlink:href="../../../resources/run-after-layout-and-paint.js"></script>
 <script xlink:href="../resources/text-based-repaint.js"/>
 <script>
 window.testIsAsync = true;
@@ -24,7 +25,7 @@
 
 function repaintTest() {
     draw(150, 50);
-    requestAnimationFrame(function() {
+    runAfterLayoutAndPaint(function() {
         draw(50, 50);
         finishRepaintTest();
     });
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem-expected.txt
index 2ccb818..4f41439 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem-expected.txt
@@ -20,10 +20,20 @@
         {
           "object": "LayoutSVGRect rect id='move'",
           "rect": [28, 38, 10, 10],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRect rect id='move'",
+          "rect": [28, 18, 10, 10],
           "reason": "chunk appeared"
         },
         {
           "object": "LayoutSVGRect rect id='move'",
+          "rect": [28, 18, 10, 10],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRect rect id='move'",
           "rect": [18, 18, 10, 10],
           "reason": "disappeared"
         }
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem.html b/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem.html
index b0c3d165..895e5b7 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem.html
+++ b/third_party/blink/web_tests/paint/invalidation/svg/modify-inserted-listitem.html
@@ -3,6 +3,7 @@
   <rect id="ref" x="20" y="30" width="10" height="10" fill="red"></rect>
   <rect id="move" x="10" y="10" width="10" height="10" fill="green"></rect>
 </svg>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <script>
 testIsAsync = true;
@@ -13,10 +14,9 @@
     transform.matrix.e = 10;
     document.querySelector('#move').transform.baseVal.appendItem(transform);
 
-    requestAnimationFrame(function() {
+    runAfterLayoutAndPaint(function() {
         transform.matrix.f = 20;
-        if (window.testRunner)
-            finishRepaintTest();
+        finishRepaintTest();
     });
 }
 </script>
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-different-attr.html b/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-different-attr.html
index 86737dbc..02a6c215 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-different-attr.html
+++ b/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-different-attr.html
@@ -13,6 +13,7 @@
   <text id="target" x="15" y="10 20" fill="green">A B C</text>
   <text id="source" x="50" y="50 60" fill="blue">X Y Z</text>
 </svg>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <script>
 testIsAsync = true;
@@ -22,10 +23,9 @@
     var moved = document.querySelector('#source').y.baseVal.removeItem(1);
     document.querySelector('#target').x.baseVal.appendItem(moved);
 
-    requestAnimationFrame(function() {
+    runAfterLayoutAndPaint(function() {
         moved.value = 65;
-        if (window.testRunner)
-            finishRepaintTest();
+        finishRepaintTest();
     });
 }
 </script>
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-expected.txt
index 04c85d1f..cda0c1a3 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem-expected.txt
@@ -21,6 +21,11 @@
           "object": "LayoutSVGPath polygon id='target'",
           "rect": [18, 18, 30, 20],
           "reason": "full"
+        },
+        {
+          "object": "LayoutSVGPath polygon id='target'",
+          "rect": [18, 18, 20, 20],
+          "reason": "full"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem.html b/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem.html
index 52ab1488..7b4f493 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem.html
+++ b/third_party/blink/web_tests/paint/invalidation/svg/modify-transferred-listitem.html
@@ -4,6 +4,7 @@
   <polygon id="target" points="20,10 10,30 30,30" fill="green"></polygon>
   <polygon id="source" points="20,20" fill="blue"></polygon>
 </svg>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <script>
 testIsAsync = true;
@@ -13,11 +14,10 @@
     var moved = document.querySelector('#source').points.removeItem(0);
     document.querySelector('#target').points.appendItem(moved);
 
-    requestAnimationFrame(function() {
+    runAfterLayoutAndPaint(function() {
         moved.x = 40;
         moved.y = 10;
-        if (window.testRunner)
-            finishRepaintTest();
+        finishRepaintTest();
     });
 }
 </script>
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt
index 6ba0ad1..b12e613 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div-expected.txt
@@ -104,6 +104,16 @@
         },
         {
           "object": "LayoutBlockFlow (positioned) div id='html' class='outerBox'",
+          "rect": [415, 125, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) div id='html' class='outerBox'",
+          "rect": [415, 125, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (positioned) div id='html' class='outerBox'",
           "rect": [400, 100, 150, 150],
           "reason": "geometry"
         },
@@ -194,6 +204,16 @@
         },
         {
           "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [115, 125, 150, 150],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [115, 125, 150, 150],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
           "rect": [100, 100, 150, 150],
           "reason": "paint property change"
         },
@@ -284,6 +304,16 @@
         },
         {
           "object": "LayoutBlockFlow div class='innerBox'",
+          "rect": [440, 150, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow div class='innerBox'",
+          "rect": [440, 150, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow div class='innerBox'",
           "rect": [425, 125, 100, 100],
           "reason": "geometry"
         },
@@ -374,6 +404,16 @@
         },
         {
           "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [140, 150, 100, 100],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
+          "rect": [140, 150, 100, 100],
+          "reason": "paint property change"
+        },
+        {
+          "object": "LayoutSVGRoot (positioned) svg id='svg'",
           "rect": [125, 125, 100, 100],
           "reason": "paint property change"
         }
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div.xhtml b/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div.xhtml
index 3850746..b5fac673 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div.xhtml
+++ b/third_party/blink/web_tests/paint/invalidation/svg/repaint-moving-svg-and-div.xhtml
@@ -1,5 +1,6 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/text-based-repaint.js"></script>
 <style>
 
@@ -86,7 +87,7 @@
         htmlStyle.top = htmlTop + "px";
 
         ++iterations;
-        requestAnimationFrame(repaintTest);
+        runAfterLayoutAndPaint(repaintTest);
     } else {
         finishRepaintTest();
     }
diff --git a/third_party/blink/web_tests/paint/invalidation/text-decoration-invalidation.html b/third_party/blink/web_tests/paint/invalidation/text-decoration-invalidation.html
index 9d62333..feddcfc 100644
--- a/third_party/blink/web_tests/paint/invalidation/text-decoration-invalidation.html
+++ b/third_party/blink/web_tests/paint/invalidation/text-decoration-invalidation.html
@@ -22,15 +22,12 @@
 <span>When</span> <span>not hovered, </span><span>there</span> <span>should</span>   <span>  be  </span> <span>no</span> <span>underlines.</span>
 </div>
 
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
-if (window.testRunner) {
-    testRunner.waitUntilDone();
-    if (window.eventSender);
-        eventSender.mouseMoveTo(underlineNoHover.offsetLeft + 10, underlineNoHover.offsetTop + 10);
-    testRunner.layoutAndPaintAsyncThen(function(){
-        if (window.eventSender);
-            eventSender.mouseMoveTo(underlineHover.offsetLeft + 10, underlineHover.offsetTop + 10);
-        testRunner.notifyDone();
-    });
-}
-</script>
\ No newline at end of file
+if (window.eventSender)
+    eventSender.mouseMoveTo(underlineNoHover.offsetLeft + 10, underlineNoHover.offsetTop + 10);
+runAfterLayoutAndPaint(function() {
+    if (window.eventSender)
+        eventSender.mouseMoveTo(underlineHover.offsetLeft + 10, underlineHover.offsetTop + 10);
+}, true);
+</script>
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered-composited.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered-composited.html
index 9f8e48b..b44f303 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered-composited.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered-composited.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <script>
     if (window.internals)
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered.html
index 062f9ac..6ba82ac 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-centered.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
 body {
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-generated.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-generated.html
index e0158c8..cfc9cff5 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-generated.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-generated.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
 body {
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-scrolling-contents.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-scrolling-contents.html
index 0307d0af..9def798 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-scrolling-contents.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-fixed-scrolling-contents.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
 body {
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-non-fixed.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-non-fixed.html
index 046c9c6..32b682e 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-non-fixed.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-background-image-non-fixed.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
 body {
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos.html
index 96f0ab0..12cd01f3 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-centered-inline-under-fixed-pos.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
 .container {
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-child-background-image-fixed-centered.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-child-background-image-fixed-centered.html
index 97b67ba8..a4573a1 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-child-background-image-fixed-centered.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-child-background-image-fixed-centered.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
 #target {
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-frameset.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-frameset.html
index 88066c15..192249a0 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-frameset.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-frameset.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <frameset cols="25%,*,25%">
   <frame> 
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-media-query.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-media-query.html
index 35b681fc..fb6a549 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-media-query.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-media-query.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style media="(min-height: 201px)">
 body { background-color: blue; }
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change1.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change1.html
index 0f3c882..b93d6b0d 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change1.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change1.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
   html { overflow: hidden; }
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change2.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change2.html
index ed04a5c..1be0a5da 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change2.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-no-layout-change2.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <style>
   html { overflow: hidden; }
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-html.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-html.html
index 873a28c5..f462890 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-html.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-html.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <html style="height: 50%">
 <body style="height: 100%; margin: 0">
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-width-height.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-width-height.html
index 6a1e1f0..f3d5d9c 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-width-height.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-percent-width-height.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <body style="margin: 0">
     <div style="position: absolute; width: 50%; height: 50%; background-color: blue"></div>
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-bottom.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-bottom.html
index 74872c5..5b781c9 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-bottom.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-bottom.html
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <body style="margin: 0">
     <div style="position: absolute; width: 20px; height: 20px; bottom: 20px; background-color: blue"></div>
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-percent-top.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-percent-top.html
index 6f613906..79097ac 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-percent-top.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-positioned-percent-top.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <body style="margin: 0">
     <div style="position: absolute; top: 50%; width: 20px; height: 20px; background-color: blue"></div>
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-vertical-writing-mode.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-vertical-writing-mode.html
index 2af6f0c..64e25e1ff 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-vertical-writing-mode.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-vertical-writing-mode.html
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <html style="-webkit-writing-mode: vertical-rl">
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <body style="font-size: 60px">
 AAAA BBBB CCCC DDDD EEEE FFFF GGGG HHHH IIII JJJJ KKKK LLLL MMMM NNNN
diff --git a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-viewport-percent.html b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-viewport-percent.html
index 18bf896..d216e7d 100644
--- a/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-viewport-percent.html
+++ b/third_party/blink/web_tests/paint/invalidation/window-resize/window-resize-viewport-percent.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
 <script src="../resources/window-resize-repaint.js"></script>
 <body style="margin: 0">
     <div style="position: absolute">
diff --git a/third_party/blink/web_tests/paint/selection/resources/selection.js b/third_party/blink/web_tests/paint/selection/resources/selection.js
index bfc2a44..292e26d 100644
--- a/third_party/blink/web_tests/paint/selection/resources/selection.js
+++ b/third_party/blink/web_tests/paint/selection/resources/selection.js
@@ -1,7 +1,7 @@
 function selectRangeAfterLayoutAndPaint(startElement, startIndex, endElement, endIndex) {
     runAfterLayoutAndPaint(function() {
         selectRange(startElement, startIndex, endElement, endIndex);
-      }, true);
+    }, true);
 }
 
 function selectRange(startElement, startIndex, endElement, endIndex) {
@@ -15,4 +15,4 @@
   var range = document.createRange();
   range.selectNode(element);
   window.getSelection().addRange(range);
-}
\ No newline at end of file
+}
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index f5c64f2..83570e7 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -32,6 +32,11 @@
           "object": "LayoutTextControl INPUT id='root'",
           "rect": [0, 0, 61, 22],
           "reason": "full layer"
+        },
+        {
+          "object": "Caret",
+          "rect": [58, 4, 1, 16],
+          "reason": "caret"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
index f5c64f2..83570e7 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -32,6 +32,11 @@
           "object": "LayoutTextControl INPUT id='root'",
           "rect": [0, 0, 61, 22],
           "reason": "full layer"
+        },
+        {
+          "object": "Caret",
+          "rect": [58, 4, 1, 16],
+          "reason": "caret"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index cc0c514..09a0829d 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -31,6 +31,11 @@
           "object": "LayoutBlockFlow DIV",
           "rect": [10, 11, 56, 16],
           "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [18, 11, 26, 16],
+          "reason": "selection"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
index f7a54884..e144691 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
@@ -26,6 +26,11 @@
           "object": "LayoutBlockFlow DIV",
           "rect": [10, 11, 57, 16],
           "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [31, 11, 26, 16],
+          "reason": "selection"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index ae51837..f1d0681 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -26,6 +26,11 @@
           "object": "LayoutBlockFlow DIV",
           "rect": [11, 11, 35, 13],
           "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [11, 11, 15, 13],
+          "reason": "selection"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..6b51b7d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
@@ -0,0 +1,39 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutTextControl INPUT id='target'",
+          "rect": [5, 5, 47, 25],
+          "reason": "subtree"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [11, 11, 35, 13],
+          "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [28, 11, 18, 13],
+          "reason": "selection"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index c0ef892..b5b2bfb9 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -32,6 +32,11 @@
           "object": "LayoutTextControl INPUT id='root'",
           "rect": [0, 0, 41, 19],
           "reason": "full layer"
+        },
+        {
+          "object": "Caret",
+          "rect": [39, 6, 1, 13],
+          "reason": "caret"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
index c0ef892..b5b2bfb9 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -32,6 +32,11 @@
           "object": "LayoutTextControl INPUT id='root'",
           "rect": [0, 0, 41, 19],
           "reason": "full layer"
+        },
+        {
+          "object": "Caret",
+          "rect": [39, 6, 1, 13],
+          "reason": "caret"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index 674f7f3..1b536cb 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -31,6 +31,11 @@
           "object": "LayoutBlockFlow DIV",
           "rect": [11, 11, 34, 13],
           "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [11, 11, 14, 13],
+          "reason": "selection"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
index ae51837..cecda7d 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
@@ -26,6 +26,11 @@
           "object": "LayoutBlockFlow DIV",
           "rect": [11, 11, 35, 13],
           "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [29, 11, 17, 13],
+          "reason": "selection"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
index f912a02..a9f775b 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
@@ -23,11 +23,31 @@
           "reason": "appeared"
         },
         {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 85, 24],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 85, 24],
+          "reason": "disappeared"
+        },
+        {
           "object": "InlineTextBox 'A'",
           "rect": [23, 8, 85, 24],
           "reason": "appeared"
         },
         {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 85, 24],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 85, 24],
+          "reason": "disappeared"
+        },
+        {
           "object": "InlineTextBox ' B C'",
           "rect": [23, 8, 80, 24],
           "reason": "disappeared"
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index 1a1be903..b2c5d38 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -32,6 +32,11 @@
           "object": "LayoutTextControl INPUT id='root'",
           "rect": [0, 0, 68, 22],
           "reason": "full layer"
+        },
+        {
+          "object": "Caret",
+          "rect": [65, 4, 1, 16],
+          "reason": "caret"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
index 1a1be903..b2c5d38 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -32,6 +32,11 @@
           "object": "LayoutTextControl INPUT id='root'",
           "rect": [0, 0, 68, 22],
           "reason": "full layer"
+        },
+        {
+          "object": "Caret",
+          "rect": [65, 4, 1, 16],
+          "reason": "caret"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
index 671c110..986b7789 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -31,6 +31,11 @@
           "object": "LayoutBlockFlow DIV",
           "rect": [10, 11, 63, 16],
           "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [25, 11, 26, 16],
+          "reason": "selection"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
index 3ee964e..5def703 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.txt
@@ -26,6 +26,11 @@
           "object": "LayoutBlockFlow DIV",
           "rect": [10, 11, 64, 16],
           "reason": "paint property change"
+        },
+        {
+          "object": "InlineTextBox 'test test test'",
+          "rect": [31, 11, 26, 16],
+          "reason": "selection"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
index 5dea806fb..59f8f6dc 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/svg/modify-transferred-listitem-different-attr-expected.txt
@@ -23,11 +23,31 @@
           "reason": "appeared"
         },
         {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 85, 23],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox ' B C'",
+          "rect": [23, 8, 85, 23],
+          "reason": "disappeared"
+        },
+        {
           "object": "InlineTextBox 'A'",
           "rect": [23, 8, 85, 23],
           "reason": "appeared"
         },
         {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 85, 23],
+          "reason": "appeared"
+        },
+        {
+          "object": "InlineTextBox 'A'",
+          "rect": [23, 8, 85, 23],
+          "reason": "disappeared"
+        },
+        {
           "object": "InlineTextBox ' B C'",
           "rect": [23, 8, 80, 23],
           "reason": "disappeared"
diff --git a/third_party/blink/web_tests/resources/run-after-layout-and-paint.js b/third_party/blink/web_tests/resources/run-after-layout-and-paint.js
index e317341..bf2ed0c 100644
--- a/third_party/blink/web_tests/resources/run-after-layout-and-paint.js
+++ b/third_party/blink/web_tests/resources/run-after-layout-and-paint.js
@@ -1,26 +1,34 @@
-// Run a callback after layout and paint of all pending document changes.
+// Run a callback after a frame update.
 //
-// It has two modes:
-// - traditional mode, for existing tests, and tests needing customized notifyDone timing:
-//   Usage:
+// Note that this file has two copies:
+//   resources/run-after-layout-and-paint.js
+// and
+//   http/tests/resources/run-after-layout-and-paint.js.
+// They should be kept always the same.
+//
+// The function runAfterLayoutAndPaint() has two modes:
+// - traditional mode, for existing tests, and tests needing customized
+//   notifyDone timing:
 //     if (window.testRunner)
 //       testRunner.waitUntilDone();
 //     runAfterLayoutAndPaint(function() {
 //       ... // some code which modifies style/layout
 //       if (window.testRunner)
 //         testRunner.notifyDone();
-//       // Or to ensure the next paint is executed before the test finishes:
-//       // if (window.testRunner)
-//       //   runAfterAfterLayoutAndPaint(function() { testRunner.notifyDone() });
 //       // Or notifyDone any time later if needed.
 //     });
 //
-// - autoNotifyDone mode, for new tests which just need to change style/layout and finish:
-//   Usage:
+// - autoNotifyDone mode, for new tests which just need to change style/layout
+//   and finish:
 //     runAfterLayoutAndPaint(function() {
 //       ... // some code which modifies style/layout
 //     }, true);
-
+//
+// Note that because we always update a frame before finishing a test,
+// we don't need
+//     runAfterLayoutAndPaint(function() { testRunner.notifyDone(); })
+// to ensure the test finish after a frame update.
+//
 if (window.internals)
     internals.runtimeFlags.paintUnderInvalidationCheckingEnabled = true;
 
@@ -35,9 +43,18 @@
     if (autoNotifyDone)
         testRunner.waitUntilDone();
 
-    testRunner.layoutAndPaintAsyncThen(function() {
-        callback();
-        if (autoNotifyDone)
-            testRunner.notifyDone();
+    // We do requestAnimationFrame and setTimeout to ensure a frame has started
+    // and layout and paint have run. The requestAnimationFrame fires after the
+    // frame has started but before layout and paint. The setTimeout fires
+    // at the beginning of the next frame, meaning that the previous frame has
+    // completed layout and paint.
+    // See http://crrev.com/c/1395193/10/third_party/blink/web_tests/http/tests/resources/run-after-layout-and-paint.js
+    // for more discussions.
+    requestAnimationFrame(function() {
+        setTimeout(function() {
+            callback();
+            if (autoNotifyDone)
+                testRunner.notifyDone();
+        }, 0);
     });
 }
diff --git a/third_party/blink/web_tests/svg/animations/animate-restart-never.html b/third_party/blink/web_tests/svg/animations/animate-restart-never.html
index 7f8127fc..743df85 100644
--- a/third_party/blink/web_tests/svg/animations/animate-restart-never.html
+++ b/third_party/blink/web_tests/svg/animations/animate-restart-never.html
@@ -13,11 +13,7 @@
 
 function animationEnded() {
     click(50,50);
-    if (window.testRunner) {
-      testRunner.layoutAndPaintAsyncThen(function() {
-        testRunner.notifyDone();
-      });
-    }
+    testRunner.notifyDone();
 }
 </script>
 <svg>
diff --git a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-1.html b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-1.html
index dbe549f6..14b80064 100644
--- a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-1.html
+++ b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-1.html
@@ -1,18 +1,11 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 if (window.testRunner) {
   testRunner.dumpAsText();
-  testRunner.waitUntilDone();
   window.onload = function() {
-    testRunner.layoutAndPaintAsyncThen(function() {
-      mutateTree();
-      testRunner.layoutAndPaintAsyncThen(function() {
-        testRunner.notifyDone();
-      });
-    });
+    runAfterLayoutAndPaint(mutateTree, true);
   };
-} else {
-  window.onload = function() { setTimeout(mutateTree, 100); };
 }
 function mutateTree() {
   // A reference from the 'rect' to the pattern cycle.
diff --git a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-2.html b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-2.html
index 84175b1..5060ecd6 100644
--- a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-2.html
+++ b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-2.html
@@ -1,18 +1,11 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 if (window.testRunner) {
   testRunner.dumpAsText();
-  testRunner.waitUntilDone();
   window.onload = function() {
-    testRunner.layoutAndPaintAsyncThen(function() {
-      mutateTree();
-      testRunner.layoutAndPaintAsyncThen(function() {
-        testRunner.notifyDone();
-      });
-    });
+    runAfterLayoutAndPaint(mutateTree, true);
   };
-} else {
-  window.onload = function() { setTimeout(mutateTree, 100); };
 }
 function mutateTree() {
   // Add a reference from the rect in pattern#p3 to form a cycle.
diff --git a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-3.html b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-3.html
index 58325935..ad7cb352 100644
--- a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-3.html
+++ b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-3.html
@@ -1,18 +1,11 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 if (window.testRunner) {
   testRunner.dumpAsText();
-  testRunner.waitUntilDone();
   window.onload = function() {
-    testRunner.layoutAndPaintAsyncThen(function() {
-      mutateTree();
-      testRunner.layoutAndPaintAsyncThen(function() {
-        testRunner.notifyDone();
-      });
-    });
+    runAfterLayoutAndPaint(mutateTree, true);
   };
-} else {
-  window.onload = function() { setTimeout(mutateTree, 100); };
 }
 const svgNs = 'http://www.w3.org/2000/svg';
 function buildPattern(patternId, refId) {
diff --git a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-4.html b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-4.html
index b053b48..5308106 100644
--- a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-4.html
+++ b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle-dynamic-4.html
@@ -1,18 +1,11 @@
 <!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
 <script>
 if (window.testRunner) {
   testRunner.dumpAsText();
-  testRunner.waitUntilDone();
   window.onload = function() {
-    testRunner.layoutAndPaintAsyncThen(function() {
-      mutateTree();
-      testRunner.layoutAndPaintAsyncThen(function() {
-        testRunner.notifyDone();
-      });
-    });
+    runAfterLayoutAndPaint(mutateTree, true);
   };
-} else {
-  window.onload = function() { setTimeout(mutateTree, 100); };
 }
 const svgNs = 'http://www.w3.org/2000/svg';
 function buildPattern(patternId, refId) {
diff --git a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle.html b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle.html
index 6356e35..619ce00 100644
--- a/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle.html
+++ b/third_party/blink/web_tests/svg/custom/pattern-3-step-cycle.html
@@ -1,12 +1,7 @@
 <!DOCTYPE html>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
   testRunner.dumpAsText();
-  testRunner.waitUntilDone();
-  window.onload = function() {
-    testRunner.layoutAndPaintAsyncThen(function() { testRunner.notifyDone(); });
-  };
-}
 </script>
 <p>PASS if no crash (stack overflow).</p>
 <svg width="100" height="100">
diff --git a/third_party/blink/web_tests/svg/custom/pattern-content-cycle-w-resourceless-container.html b/third_party/blink/web_tests/svg/custom/pattern-content-cycle-w-resourceless-container.html
index b15db7065..53c91cd 100644
--- a/third_party/blink/web_tests/svg/custom/pattern-content-cycle-w-resourceless-container.html
+++ b/third_party/blink/web_tests/svg/custom/pattern-content-cycle-w-resourceless-container.html
@@ -1,12 +1,7 @@
 <!DOCTYPE html>
 <script>
-if (window.testRunner) {
+if (window.testRunner)
   testRunner.dumpAsText();
-  testRunner.waitUntilDone();
-  window.onload = function() {
-    testRunner.layoutAndPaintAsyncThen(function() { testRunner.notifyDone(); });
-  };
-}
 </script>
 <p>PASS if no crash (stack overflow).</p>
 <svg width="100" height="100">
diff --git a/third_party/blink/web_tests/svg/custom/svg-image-container-size.html b/third_party/blink/web_tests/svg/custom/svg-image-container-size.html
index 772220b..d896df6 100644
--- a/third_party/blink/web_tests/svg/custom/svg-image-container-size.html
+++ b/third_party/blink/web_tests/svg/custom/svg-image-container-size.html
@@ -19,10 +19,7 @@
 }
 
 function startTest() {
-  if (window.testRunner)
-    testRunner.layoutAndPaintAsyncThen(insertSVGImage);
-  else
-    requestAnimationFrame(insertSVGImage);
+  runAfterLayoutAndPaint(insertSVGImage);
 }
 </script>
 <svg width="384" height="128" style="display: block">