diff --git a/DEPS b/DEPS
index dec3d3d..dc23976 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '6668a514ac5d507359a885480cbaabd812ef92db',
+  'skia_revision': '68300c270916b2740fccdbe6c6dce8f085e83316',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '4c27ac5c13df9499357e04985bd7de55dd61e062',
+  'v8_revision': '1e6d83a22470cb8b04ed423ceeaafc591c45132f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -117,11 +117,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '5188a2747b404b0aac332a3f866e94bdfcd8bb99',
+  'angle_revision': '203b26f27d00e2c00e92405af4d1863187db309d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '0dd5c6f980d22be96b728155249df2da355989d9',
+  'buildtools_revision': '691bfec9d73bfefae30bad32e7a6496f2beceb9c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -129,7 +129,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '1f7db295b1deeecb562d6213b3ea17b9168405eb',
+  'pdfium_revision': '91b8302dec04ca4ddc1f91545d192350665580cf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -165,7 +165,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'b76f0192a83adb19a2e97595bca2086186145eb2',
+  'catapult_revision': 'c829a63538ba53b1f31e3d2eb2e2fdd6f390619a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -539,7 +539,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7dc67f1a80f0e43c1ef8004494da76ec53e8b612',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd81364a25e1e01848eddbd45a2e5fb2b9598bbb8',
       'condition': 'checkout_linux',
   },
 
@@ -564,7 +564,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '01ce05914f99eb5d6676f415706467b828ba5ad6',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ad463c9517e348df1ea5cb5f0f9a615365f52bf6',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1034,7 +1034,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '21dbf06b5aa6c7dc8cf56314d4a3f96f57956c53',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a3df0f2d05c7b0973c31fe171507e97e588671a5',
+    Var('webrtc_git') + '/src.git' + '@' + '625efe6dfe5b5681a2d8d3d5ecaa0c5ac9edd6ee',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1068,7 +1068,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@442a61600537d67a9631c60e370cf4556a66b130',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e83d89c060cd9054044316926ed7353e99bda9d3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index 392b57c2..4a37cc9f 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -58,10 +58,8 @@
 }
 
 bool AwBrowserMainParts::ShouldContentCreateFeatureList() {
-  // If variations is enabled, the FeatureList will be created in
-  // AwFieldTrialCreator.
-  base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
-  return !cmd->HasSwitch(switches::kEnableWebViewVariations);
+  // FeatureList will be created in AwFieldTrialCreator.
+  return false;
 }
 
 int AwBrowserMainParts::PreEarlyInitialization() {
@@ -131,10 +129,7 @@
         std::make_unique<AwBrowserTerminator>(crash_dir));
   }
 
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableWebViewVariations)) {
-    aw_field_trial_creator_.SetUpFieldTrials();
-  }
+  aw_field_trial_creator_.SetUpFieldTrials();
 
   return service_manager::RESULT_CODE_NORMAL_EXIT;
 }
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index 028d687..2e7ec96 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -165,11 +165,8 @@
 }
 
 void AwMetricsServiceClient::InitializeWithClientId() {
-  // The guid must have already been initialized at this point, either
-  // synchronously or asynchronously depending on the kEnableWebViewFinch flag
-  DCHECK_EQ(g_client_id.Get().length(), kGuidSize);
+  DCHECK_EQ(g_client_id.Get().length(), kGuidSize);  // Must have client ID
   pref_service_->SetString(metrics::prefs::kMetricsClientID, g_client_id.Get());
-
   in_sample_ = IsInSample(g_client_id.Get());
 
   metrics_state_manager_ = metrics::MetricsStateManager::Create(
diff --git a/android_webview/common/aw_switches.cc b/android_webview/common/aw_switches.cc
index f1ff2358..2286d02 100644
--- a/android_webview/common/aw_switches.cc
+++ b/android_webview/common/aw_switches.cc
@@ -6,7 +6,6 @@
 
 namespace switches {
 
-const char kEnableWebViewVariations[] = "enable-webview-variations";
 const char kWebViewSandboxedRenderer[] = "webview-sandboxed-renderer";
 
 // used to enable safebrowsing functionality in webview
diff --git a/android_webview/common/aw_switches.h b/android_webview/common/aw_switches.h
index 0042064..cb2fc2c 100644
--- a/android_webview/common/aw_switches.h
+++ b/android_webview/common/aw_switches.h
@@ -7,7 +7,6 @@
 
 namespace switches {
 
-extern const char kEnableWebViewVariations[];
 extern const char kWebViewSandboxedRenderer[];
 extern const char kWebViewEnableSafeBrowsingSupport[];
 extern const char kWebViewDisableSafeBrowsingSupport[];
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
index d0c2ded..f1039a4 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
@@ -21,7 +21,7 @@
 public class SharedWebViewChromium {
     private final WebViewChromiumRunQueue mRunQueue;
     private final WebViewChromiumAwInit mAwInit;
-    // The WebView wrapper for ContentViewCore and required browser compontents.
+    // The WebView wrapper for WebContents and required browser components.
     private AwContents mAwContents;
 
     // Default WebViewClient used to avoid null checks.
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index 39102d0..a78264d 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -79,7 +79,7 @@
 /**
  * This class is the delegate to which WebViewProxy forwards all API calls.
  *
- * Most of the actual functionality is implemented by AwContents (or ContentViewCore within
+ * Most of the actual functionality is implemented by AwContents (or WebContents within
  * it). This class also contains WebView-specific APIs that require the creation of other
  * adapters (otherwise org.chromium.content would depend on the webview.chromium package)
  * and a small set of no-op deprecated APIs.
@@ -101,7 +101,7 @@
 
     // Variables for functionality provided by this adapter ---------------------------------------
     private ContentSettingsAdapter mWebSettings;
-    // The WebView wrapper for ContentViewCore and required browser compontents.
+    // The WebView wrapper for WebContents and required browser components.
     AwContents mAwContents;
 
     private final WebView.HitTestResult mHitTestResult;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java b/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java
index 50cce9d..347601a 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwAutofillProvider.java
@@ -226,21 +226,23 @@
 
     @VisibleForTesting
     public AwAutofillProvider(ViewGroup containerView, AwAutofillManager manager, Context context) {
-        assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
-        mAutofillManager = manager;
-        mContainerView = containerView;
-        mAutofillUMA = new AwAutofillUMA(context);
-        mInputUIObserver = new AwAutofillManager.InputUIObserver() {
-            @Override
-            public void onInputUIShown() {
-                // Not need to report suggestion window displayed if there is no live autofill
-                // session.
-                if (mRequest == null) return;
-                mAutofillUMA.onSuggestionDisplayed(
-                        System.currentTimeMillis() - mAutofillTriggeredTimeMillis);
-            }
-        };
-        mAutofillManager.addInputUIObserver(mInputUIObserver);
+        try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("AwAutofillProvider.constructor")) {
+            assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+            mAutofillManager = manager;
+            mContainerView = containerView;
+            mAutofillUMA = new AwAutofillUMA(context);
+            mInputUIObserver = new AwAutofillManager.InputUIObserver() {
+                @Override
+                public void onInputUIShown() {
+                    // Not need to report suggestion window displayed if there is no live autofill
+                    // session.
+                    if (mRequest == null) return;
+                    mAutofillUMA.onSuggestionDisplayed(
+                            System.currentTimeMillis() - mAutofillTriggeredTimeMillis);
+                }
+            };
+            mAutofillManager.addInputUIObserver(mInputUIObserver);
+        }
     }
 
     @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 279f3c35..cdaea7f 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -846,7 +846,7 @@
             mIoThreadClient = new IoThreadClientImpl();
             mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl();
             mDisplayObserver = new AwDisplayAndroidObserver();
-            mUpdateVisibilityRunnable = () -> updateContentViewCoreVisibility();
+            mUpdateVisibilityRunnable = () -> updateWebContentsVisibility();
 
             AwSettings.ZoomSupportChangeListener zoomListener =
                     (supportsDoubleTapZoom, supportsMultiTouchZoom) -> {
@@ -1178,7 +1178,7 @@
 
         mDisplayObserver.onDIPScaleChanged(getDeviceScaleFactor());
 
-        updateContentViewCoreVisibility();
+        updateWebContentsVisibility();
 
         // The native side object has been bound to this java instance, so now is the time to
         // bind all the native->java relationships.
@@ -2075,7 +2075,7 @@
         nativeSetIsPaused(mNativeAwContents, mIsPaused);
 
         // Geolocation is paused/resumed via the page visibility mechanism.
-        updateContentViewCoreVisibility();
+        updateWebContentsVisibility();
     }
 
     /**
@@ -2086,7 +2086,7 @@
         if (!mIsPaused || isDestroyedOrNoOperation(NO_WARN)) return;
         mIsPaused = false;
         nativeSetIsPaused(mNativeAwContents, mIsPaused);
-        updateContentViewCoreVisibility();
+        updateWebContentsVisibility();
     }
 
     /**
@@ -2621,7 +2621,7 @@
         if (!isDestroyedOrNoOperation(NO_WARN)) {
             nativeSetViewVisibility(mNativeAwContents, mIsViewVisible);
         }
-        postUpdateContentViewCoreVisibility();
+        postUpdateWebContentsVisibility();
     }
 
     private void setWindowVisibilityInternal(boolean visible) {
@@ -2631,10 +2631,10 @@
         if (!isDestroyedOrNoOperation(NO_WARN)) {
             nativeSetWindowVisibility(mNativeAwContents, mIsWindowVisible);
         }
-        postUpdateContentViewCoreVisibility();
+        postUpdateWebContentsVisibility();
     }
 
-    private void postUpdateContentViewCoreVisibility() {
+    private void postUpdateWebContentsVisibility() {
         if (mIsUpdateVisibilityTaskPending) return;
         // When WebView is attached to a visible window, WebView will be
         // attached to a window whose visibility is initially invisible, then
@@ -2650,7 +2650,7 @@
         mHandler.post(mUpdateVisibilityRunnable);
     }
 
-    private void updateContentViewCoreVisibility() {
+    private void updateWebContentsVisibility() {
         mIsUpdateVisibilityTaskPending = false;
         if (isDestroyedOrNoOperation(NO_WARN)) return;
         boolean contentVisible = nativeIsVisible(mNativeAwContents);
@@ -3470,7 +3470,7 @@
             nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(),
                     mContainerView.getHeight());
             updateHardwareAcceleratedFeaturesToggle();
-            postUpdateContentViewCoreVisibility();
+            postUpdateWebContentsVisibility();
             mCurrentFunctor.onAttachedToWindow();
 
             updateDefaultLocale();
@@ -3494,7 +3494,7 @@
 
             mViewEventSink.onDetachedFromWindow();
             updateHardwareAcceleratedFeaturesToggle();
-            postUpdateContentViewCoreVisibility();
+            postUpdateWebContentsVisibility();
             mCurrentFunctor.onDetachedFromWindow();
 
             if (mComponentCallbacks != null) {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
index 3936de9..ec44351 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java
@@ -29,8 +29,6 @@
 
 /**
  * Base-class that an AwContents embedder derives from to receive callbacks.
- * This extends ContentViewClient, as in many cases we want to pass-thru ContentViewCore
- * callbacks right to our embedder, and this setup facilities that.
  * For any other callbacks we need to make transformations of (e.g. adapt parameters
  * or perform filtering) we can provide final overrides for methods here, and then introduce
  * new abstract methods that the our own client must implement.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index 90d012e1..dce5551 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -589,7 +589,7 @@
     }
 
     /**
-     * @returns the default User-Agent used by each ContentViewCore instance, i.e. unless
+     * @returns the default User-Agent used by each WebContents instance, i.e. unless
      * overridden by {@link #setUserAgentString()}
      */
     public static String getDefaultUserAgent() {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSwitches.java b/android_webview/java/src/org/chromium/android_webview/AwSwitches.java
index ccf4283..4610a52 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSwitches.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSwitches.java
@@ -29,10 +29,6 @@
     public static final String WEBVIEW_SAFEBROWSING_BLOCK_ALL_RESOURCES =
             "webview-safebrowsing-block-all-resources";
 
-    // Enables variations AB testing experiments in webview.
-    // Native switch kEnableWebViewVariations.
-    public static final String ENABLE_WEBVIEW_VARIATIONS = "enable-webview-variations";
-
     // Do not instantiate this class.
     private AwSwitches() {}
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index 2893a8d..22b60860 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -181,7 +181,7 @@
     @Override
     public void showRepostFormWarningDialog() {
         // TODO(mkosiba) We should be using something akin to the JsResultReceiver as the
-        // callback parameter (instead of ContentViewCore) and implement a way of converting
+        // callback parameter (instead of WebContents) and implement a way of converting
         // that to a pair of messages.
         final int msgContinuePendingReload = 1;
         final int msgCancelPendingReload = 2;
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
index ed68cf1f..9fbd9e4 100644
--- a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
@@ -16,7 +16,6 @@
 
 import org.chromium.android_webview.services.IVariationsSeedServer;
 import org.chromium.android_webview.services.VariationsSeedServer;
-import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
@@ -30,7 +29,6 @@
 import java.io.IOException;
 import java.text.ParseException;
 import java.util.Date;
-import java.util.Random;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
@@ -56,8 +54,8 @@
  *    more often than MAX_REQUEST_PERIOD_MILLIS).
  *
  * VariationsSeedLoader should be used during WebView startup like so:
- * 1. Ensure ContextUtils.getApplicationContext(), AwBrowserProcess.getWebViewPackageName(),
- *    CommandLine, and PathUtils are ready to use.
+ * 1. Ensure ContextUtils.getApplicationContext(), AwBrowserProcess.getWebViewPackageName(), and
+ *    PathUtils are ready to use.
  * 2. As early as possible, call startVariationsInit() to begin the task.
  * 3. Perform any WebView startup tasks which don't require variations to be initialized.
  * 4. Call finishVariationsInit() with the value returned from startVariationsInit(). This will
@@ -79,61 +77,14 @@
     // is exceeded, proceed with variations disabled.
     private static final long SEED_LOAD_TIMEOUT_MILLIS = 20;
 
-    // Must match AndroidWebViewVariationsEnableState UMA enum. Do not reorder
-    private static final int DEFAULT_ENABLED = 0;
-    private static final int CONTROL_DISABLED = 1;
-    private static final int EXPERIMENT_ENABLED = 2;
-    private static final int ENABLE_STATE_COUNT = 3;
-    private static final int ENABLE_STATE_UNINITIALIZED = -1;
-
-    // If false, then variations will be enabled if either the CMD flag or the AGSA experiment file
-    // is present. If true, then variations will be enabled regardless of flag or experiment file.
-    private static boolean sVariationsAlwaysEnabled = true;
-
     private SeedLoadAndUpdateRunnable mRunnable;
 
-    // See Android.WebView.VariationsEnableState in histograms.xml for explanation. This setting
-    // overrides sVariationsAlwaysEnabled if the latter is true.
-    private int mEnableState = ENABLE_STATE_UNINITIALIZED;
-
-    // Generate an EnableState with the following probabilities:
-    // 99.8 % - DEFAULT_ENABLED
-    //  0.1 % - CONTROL_DISABLED
-    //  0.1 % - EXPERIMENT_ENABLED
-    // It's difficult to prevent this state from changing across runs without adding IPC or file
-    // I/O, which could perturb performance metrics, defeating the purpose of the experiment. So the
-    // state is allowed to change on each run. This doesn't break variations, since WebView doesn't
-    // support permanent consistency studies anyway.
-    private static int chooseEnableState() {
-        Random r = new Random();
-        int i = r.nextInt(1000);
-        if (i == 0) return CONTROL_DISABLED;
-        if (i == 1) return EXPERIMENT_ENABLED;
-        return DEFAULT_ENABLED;
-    }
-
-    private static int chooseAndLogEnableState() {
-        int state = chooseEnableState();
-        EnumeratedHistogramSample histogram = new EnumeratedHistogramSample(
-                "Android.WebView.VariationsEnableState", ENABLE_STATE_COUNT);
-        histogram.record(state);
-        return state;
-    }
-
     private static void recordLoadSeedResult(int result) {
         EnumeratedHistogramSample histogram = new EnumeratedHistogramSample(
                 "Variations.SeedLoadResult", LoadSeedResult.ENUM_SIZE);
         histogram.record(result);
     }
 
-    // AGSA will notify us to enable variations by touching this file.
-    // TODO(paulmiller): Remove this after completing the experiment.
-    private static boolean checkEnabledByExperiment() {
-        File filesDir = ContextUtils.getApplicationContext().getFilesDir();
-        File experimentFile = new File(new File(filesDir, "webview"), "finch-exp");
-        return experimentFile.exists();
-    }
-
     private static boolean isExpired(long seedFileTime) {
         long expirationTime = seedFileTime + SEED_EXPIRATION_MILLIS;
         long now = (new Date()).getTime();
@@ -143,28 +94,17 @@
     // Loads our local copy of the seed, if any, and then renames our local copy and/or requests a
     // new seed, if necessary.
     private class SeedLoadAndUpdateRunnable implements Runnable {
-        private boolean mEnabledByCmd;
-
         // mLoadTask will set these to indicate what additional work to do after mLoadTask finishes:
         // - mFoundNewSeed: Is a "new" seed file present? (If so, it should be renamed to an "old"
         //   seed, replacing any existing "old" seed.)
         // - mNeedNewSeed: Should we request a new seed from the service?
         // - mCurrentSeedDate: The "date" field of our local seed, converted to milliseconds since
         //   epoch, or Long.MIN_VALUE if we have no seed.
-        // - mEnabledByExperiment: Whether variations enabled by the AGSA experiment. If so, and
-        //   variations is not already enabled by CommandLine, then it should be made enabled by
-        //   CommandLine. This is volatile because it's set inside mLoadTask on a background thread,
-        //   but read in isVariationsEnabled() on the main thread. TODO(paulmiller): Remove this
-        //   after completing the experiment.
         private boolean mFoundNewSeed;
         private boolean mNeedNewSeed;
         private long mCurrentSeedDate = Long.MIN_VALUE;
-        private volatile boolean mEnabledByExperiment;
 
         private FutureTask<SeedInfo> mLoadTask = new FutureTask<>(() -> {
-            mEnabledByExperiment = checkEnabledByExperiment();
-            if (!isVariationsEnabled()) return null;
-
             AwMetricsServiceClient.preloadClientId();
 
             File newSeedFile = VariationsUtils.getNewSeedFile();
@@ -219,10 +159,6 @@
             return seed;
         });
 
-        public SeedLoadAndUpdateRunnable(boolean enabledByCmd) {
-            mEnabledByCmd = enabledByCmd;
-        }
-
         @Override
         public void run() {
             mLoadTask.run();
@@ -250,14 +186,6 @@
                 throws InterruptedException, ExecutionException, TimeoutException {
             return mLoadTask.get(timeout, unit);
         }
-
-        // mEnabledByExperiment is set in mLoadTask, so isVariationsEnabled() should only be called
-        // after run() returns.
-        public boolean isVariationsEnabled() {
-            assert mEnableState != ENABLE_STATE_UNINITIALIZED;
-            return (sVariationsAlwaysEnabled && mEnableState != CONTROL_DISABLED)
-                    || mEnabledByCmd || mEnabledByExperiment;
-        }
     }
 
     // Connects to VariationsSeedServer service. Sends a file descriptor for our local copy of the
@@ -305,25 +233,17 @@
             try {
                 return mRunnable.get(SEED_LOAD_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
             } finally {
-                if (mRunnable.isVariationsEnabled()) {
-                    long end = SystemClock.elapsedRealtime();
-                    TimesHistogramSample histogram = new TimesHistogramSample(
-                            "Variations.SeedLoadBlockingTime", TimeUnit.MILLISECONDS);
-                    histogram.record(end - start);
-                }
+                long end = SystemClock.elapsedRealtime();
+                TimesHistogramSample histogram = new TimesHistogramSample(
+                        "Variations.SeedLoadBlockingTime", TimeUnit.MILLISECONDS);
+                histogram.record(end - start);
             }
         } catch (TimeoutException e) {
-            if (mRunnable.isVariationsEnabled()) {
-                recordLoadSeedResult(LoadSeedResult.LOAD_TIMED_OUT);
-            }
+            recordLoadSeedResult(LoadSeedResult.LOAD_TIMED_OUT);
         } catch (InterruptedException e) {
-            if (mRunnable.isVariationsEnabled()) {
-                recordLoadSeedResult(LoadSeedResult.LOAD_INTERRUPTED);
-            }
+            recordLoadSeedResult(LoadSeedResult.LOAD_INTERRUPTED);
         } catch (ExecutionException e) {
-            if (mRunnable.isVariationsEnabled()) {
-                recordLoadSeedResult(LoadSeedResult.LOAD_OTHER_FAILURE);
-            }
+            recordLoadSeedResult(LoadSeedResult.LOAD_OTHER_FAILURE);
         }
         Log.e(TAG, "Failed loading variations seed. Variations disabled.");
         return null;
@@ -333,11 +253,6 @@
     protected void onBackgroundWorkFinished() {}
 
     @VisibleForTesting // and non-static for overriding by tests
-    protected boolean isEnabledByCmd() {
-        return CommandLine.getInstance().hasSwitch(AwSwitches.ENABLE_WEBVIEW_VARIATIONS);
-    }
-
-    @VisibleForTesting // and non-static for overriding by tests
     protected Intent getServerIntent() throws NameNotFoundException {
         Context c = ContextUtils.getApplicationContext()
                 .createPackageContext(AwBrowserProcess.getWebViewPackageName(), /*flags=*/0);
@@ -369,9 +284,7 @@
     // Begin asynchronously loading the variations seed. ContextUtils.getApplicationContext() and
     // AwBrowserProcess.getWebViewPackageName() must be ready to use before calling this.
     public void startVariationsInit() {
-        assert mEnableState == ENABLE_STATE_UNINITIALIZED;
-        mEnableState = chooseAndLogEnableState();
-        mRunnable = new SeedLoadAndUpdateRunnable(isEnabledByCmd());
+        mRunnable = new SeedLoadAndUpdateRunnable();
         (new Thread(mRunnable)).start();
     }
 
@@ -379,11 +292,6 @@
     // variations.
     public void finishVariationsInit() {
         SeedInfo seed = getSeedBlockingAndLog();
-        if (seed != null) {
-            if (!isEnabledByCmd()) {
-                CommandLine.getInstance().appendSwitch(AwSwitches.ENABLE_WEBVIEW_VARIATIONS);
-            }
-            AwVariationsSeedBridge.setSeed(seed);
-        }
+        if (seed != null) AwVariationsSeedBridge.setSeed(seed);
     }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
index a30bebf71..3d48021 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
@@ -62,11 +62,6 @@
             mResult = result;
         }
 
-        @Override
-        protected boolean isEnabledByCmd() {
-            return true;
-        }
-
         // Bind to the MockVariationsSeedServer built in to the instrumentation test app, rather
         // than the real server in the WebView provider.
         @Override
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 4d18a7bc..0df02b9f 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1740,6 +1740,7 @@
     "ime/ime_controller_unittest.cc",
     "ime/ime_focus_handler_unittest.cc",
     "keyboard/virtual_keyboard_controller_unittest.cc",
+    "keyboard/virtual_keyboard_unittest.cc",
     "laser/laser_pointer_controller_unittest.cc",
     "laser/laser_segment_utils_unittest.cc",
     "lock_screen_action/lock_screen_action_background_controller_impl_unittest.cc",
diff --git a/ash/accelerators/accelerator_confirmation_dialog.cc b/ash/accelerators/accelerator_confirmation_dialog.cc
index d628f1c..0c32270 100644
--- a/ash/accelerators/accelerator_confirmation_dialog.cc
+++ b/ash/accelerators/accelerator_confirmation_dialog.cc
@@ -7,6 +7,8 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/bind.h"
@@ -33,7 +35,16 @@
       views::LayoutProvider::Get()->GetDialogInsetsForContentType(
           views::TEXT, views::TEXT)));
   AddChildView(new views::Label(l10n_util::GetStringUTF16(dialog_text_id)));
-  views::Widget* widget = CreateDialogWidget(this, nullptr, nullptr);
+
+  // Parent the dialog widget to the LockSystemModalContainer to ensure that it
+  // gets displayed on lock/signin screen.
+  gfx::NativeView parent = Shell::GetContainer(
+      ash::Shell::GetPrimaryRootWindow(), kShellWindowId_SystemModalContainer);
+  if (Shell::Get()->session_controller()->IsUserSessionBlocked())
+    parent = Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
+                                 kShellWindowId_LockSystemModalContainer);
+
+  views::Widget* widget = CreateDialogWidget(this, nullptr, parent);
   widget->Show();
 }
 
diff --git a/ash/accessibility/accessibility_highlight_controller.cc b/ash/accessibility/accessibility_highlight_controller.cc
index c029880a..c4ad082 100644
--- a/ash/accessibility/accessibility_highlight_controller.cc
+++ b/ash/accessibility/accessibility_highlight_controller.cc
@@ -7,7 +7,6 @@
 #include <vector>
 
 #include "ash/accessibility/accessibility_focus_ring_controller.h"
-#include "ash/public/cpp/config.h"
 #include "ash/public/interfaces/accessibility_focus_ring_controller.mojom.h"
 #include "ash/shell.h"
 #include "ui/aura/window.h"
@@ -35,9 +34,7 @@
 
 AccessibilityHighlightController::AccessibilityHighlightController() {
   Shell::Get()->AddPreTargetHandler(this);
-  // TODO: CursorManager not created in mash. https://crbug.com/631103.
-  if (Shell::GetAshConfig() != Config::MASH_DEPRECATED)
-    Shell::Get()->cursor_manager()->AddObserver(this);
+  Shell::Get()->cursor_manager()->AddObserver(this);
   aura::Window* root_window = Shell::GetPrimaryRootWindow();
   ui::InputMethod* input_method = GetInputMethod(root_window);
   input_method->AddObserver(this);
@@ -55,9 +52,7 @@
   aura::Window* root_window = Shell::GetPrimaryRootWindow();
   ui::InputMethod* input_method = GetInputMethod(root_window);
   input_method->RemoveObserver(this);
-  // TODO: CursorManager not created in mash. https://crbug.com/631103.
-  if (Shell::GetAshConfig() != Config::MASH_DEPRECATED)
-    Shell::Get()->cursor_manager()->RemoveObserver(this);
+  Shell::Get()->cursor_manager()->RemoveObserver(this);
   Shell::Get()->RemovePreTargetHandler(this);
 }
 
@@ -129,9 +124,6 @@
 }
 
 bool AccessibilityHighlightController::IsCursorVisible() {
-  // TODO: CursorManager not created in mash. https://crbug.com/631103.
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return false;
   return Shell::Get()->cursor_manager()->IsCursorVisible();
 }
 
diff --git a/ash/accessibility/accessibility_highlight_controller.h b/ash/accessibility/accessibility_highlight_controller.h
index 210c57d..84018f7f 100644
--- a/ash/accessibility/accessibility_highlight_controller.h
+++ b/ash/accessibility/accessibility_highlight_controller.h
@@ -37,7 +37,6 @@
   void HighlightCaret(bool caret);
   void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen);
 
- protected:
   // ui::EventHandler:
   void OnMouseEvent(ui::MouseEvent* event) override;
   void OnKeyEvent(ui::KeyEvent* event) override;
@@ -53,10 +52,8 @@
   // aura::client::CursorClientObserver:
   void OnCursorVisibilityChanged(bool is_visible) override;
 
-  // virtual for testing overridden.
-  virtual bool IsCursorVisible();
-
  private:
+  bool IsCursorVisible();
   bool IsCaretVisible(const gfx::Rect& caret_bounds);
   void UpdateFocusAndCaretHighlights();
   void UpdateCursorHighlight();
diff --git a/ash/accessibility/accessibility_highlight_controller_unittest.cc b/ash/accessibility/accessibility_highlight_controller_unittest.cc
index d7ae502..21e1f85 100644
--- a/ash/accessibility/accessibility_highlight_controller_unittest.cc
+++ b/ash/accessibility/accessibility_highlight_controller_unittest.cc
@@ -42,26 +42,6 @@
   DISALLOW_COPY_AND_ASSIGN(MockTextInputClient);
 };
 
-class TestAccessibilityHighlightController
-    : public AccessibilityHighlightController {
- public:
-  TestAccessibilityHighlightController() = default;
-  ~TestAccessibilityHighlightController() override = default;
-
-  void OnCaretBoundsChanged(const ui::TextInputClient* client) override {
-    AccessibilityHighlightController::OnCaretBoundsChanged(client);
-  }
-
-  void OnMouseEvent(ui::MouseEvent* event) override {
-    AccessibilityHighlightController::OnMouseEvent(event);
-  }
-
- private:
-  bool IsCursorVisible() override { return true; }
-
-  DISALLOW_COPY_AND_ASSIGN(TestAccessibilityHighlightController);
-};
-
 }  // namespace
 
 class AccessibilityHighlightControllerTest : public AshTestBase {
@@ -167,7 +147,7 @@
 
   CaptureBeforeImage(capture_bounds);
 
-  TestAccessibilityHighlightController controller;
+  AccessibilityHighlightController controller;
   controller.HighlightCaret(true);
   MockTextInputClient text_input_client;
   text_input_client.SetCaretBounds(caret_bounds);
@@ -195,7 +175,7 @@
 
   CaptureBeforeImage(capture_bounds);
 
-  TestAccessibilityHighlightController controller;
+  AccessibilityHighlightController controller;
   controller.HighlightFocus(true);
   controller.SetFocusHighlightRect(focus_bounds);
 
@@ -218,7 +198,7 @@
   aura::Window::Windows root_windows = ash::Shell::Get()->GetAllRootWindows();
   ASSERT_EQ(2u, root_windows.size());
 
-  TestAccessibilityHighlightController highlight_controller;
+  AccessibilityHighlightController highlight_controller;
   highlight_controller.HighlightCursor(true);
   gfx::Point location(90, 90);
   ui::MouseEvent event0(ui::ET_MOUSE_MOVED, location, location,
@@ -262,7 +242,7 @@
   std::unique_ptr<views::Widget> window = CreateTestWidget();
   window->SetBounds(gfx::Rect(5, 5, 300, 300));
 
-  TestAccessibilityHighlightController highlight_controller;
+  AccessibilityHighlightController highlight_controller;
   MockTextInputClient text_input_client;
   highlight_controller.HighlightCaret(true);
   gfx::Rect caret_bounds(10, 10, 40, 40);
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index f2ead80..e26e9af5 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -466,12 +466,14 @@
     return;
   }
 
-  if (!is_home_launcher_enabled_ || !display::Display::HasInternalDisplay() ||
-      (Shell::Get()->session_controller() &&
-       Shell::Get()->session_controller()->login_status() !=
-           LoginStatus::USER)) {
+  if (!is_home_launcher_enabled_ || !display::Display::HasInternalDisplay())
     return;
-  }
+
+  SessionController const* session_controller =
+      Shell::Get()->session_controller();
+  if (session_controller && !session_controller->IsActiveUserSessionStarted())
+    return;
+
   // Show the app list if the tablet mode starts.
   Show(display::Display::InternalDisplayId(), app_list::kTabletMode,
        base::TimeTicks());
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index edf03b9..1e7a19c 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -84,14 +84,6 @@
   StartTextInteraction(query.value());
 }
 
-void AssistantInteractionController::OnCommittedQueryChanged(
-    const AssistantQuery& committed_query) {
-  // We clear the interaction when a query is committed, but need to retain
-  // the committed query as it is query that is currently being fulfilled.
-  assistant_interaction_model_.ClearInteraction(
-      /*retain_committed_query=*/true);
-}
-
 void AssistantInteractionController::OnUiVisibilityChanged(
     bool visible,
     AssistantSource source) {
@@ -164,9 +156,12 @@
     assistant_interaction_model_.SetMicState(MicState::kOpen);
   } else {
     // In the case of a non-voice interaction, we commit the pending query.
-    // This will trigger a clearing of the interaction which wipes the stage.
     assistant_interaction_model_.CommitPendingQuery();
     assistant_interaction_model_.SetMicState(MicState::kClosed);
+
+    // Clear the interaction to wipe the stage.
+    assistant_interaction_model_.ClearInteraction(
+        /*retain_committed_query=*/true);
   }
 }
 
@@ -252,6 +247,10 @@
   assistant_interaction_model_.SetPendingQuery(
       std::make_unique<AssistantVoiceQuery>(final_result));
   assistant_interaction_model_.CommitPendingQuery();
+
+  // Clear the interaction to wipe the stage.
+  assistant_interaction_model_.ClearInteraction(
+      /*retain_committed_query=*/true);
 }
 
 void AssistantInteractionController::OnSpeechLevelUpdated(float speech_level) {
diff --git a/ash/assistant/assistant_interaction_controller.h b/ash/assistant/assistant_interaction_controller.h
index e3631af..19ca7f80 100644
--- a/ash/assistant/assistant_interaction_controller.h
+++ b/ash/assistant/assistant_interaction_controller.h
@@ -64,7 +64,6 @@
   // AssistantInteractionModelObserver:
   void OnInteractionStateChanged(InteractionState interaction_state) override;
   void OnInputModalityChanged(InputModality input_modality) override;
-  void OnCommittedQueryChanged(const AssistantQuery& committed_query) override;
 
   // AssistantUiModelObserver:
   void OnUiVisibilityChanged(bool visible, AssistantSource source) override;
diff --git a/ash/assistant/model/assistant_interaction_model.cc b/ash/assistant/model/assistant_interaction_model.cc
index ae5f0d6e..f110de4 100644
--- a/ash/assistant/model/assistant_interaction_model.cc
+++ b/ash/assistant/model/assistant_interaction_model.cc
@@ -61,6 +61,9 @@
 }
 
 void AssistantInteractionModel::ClearCommittedQuery() {
+  if (committed_query_->type() == AssistantQueryType::kEmpty)
+    return;
+
   committed_query_ = std::make_unique<AssistantEmptyQuery>();
   NotifyCommittedQueryCleared();
 }
@@ -81,6 +84,9 @@
 }
 
 void AssistantInteractionModel::ClearPendingQuery() {
+  if (pending_query_->type() == AssistantQueryType::kEmpty)
+    return;
+
   pending_query_ = std::make_unique<AssistantEmptyQuery>();
   NotifyPendingQueryCleared();
 }
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.cc b/ash/assistant/ui/dialog_plate/dialog_plate.cc
index ef82ec08b..ce6ddc7 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.cc
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.cc
@@ -4,8 +4,6 @@
 
 #include "ash/assistant/ui/dialog_plate/dialog_plate.h"
 
-#include <memory>
-
 #include "ash/assistant/assistant_controller.h"
 #include "ash/assistant/assistant_interaction_controller.h"
 #include "ash/assistant/assistant_ui_controller.h"
@@ -14,8 +12,10 @@
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
@@ -51,7 +51,14 @@
 // DialogPlate -----------------------------------------------------------------
 
 DialogPlate::DialogPlate(AssistantController* assistant_controller)
-    : assistant_controller_(assistant_controller) {
+    : assistant_controller_(assistant_controller),
+      animation_observer_(std::make_unique<ui::CallbackLayerAnimationObserver>(
+          /*start_animation_callback=*/base::BindRepeating(
+              &DialogPlate::OnAnimationStarted,
+              base::Unretained(this)),
+          /*end_animation_callback=*/base::BindRepeating(
+              &DialogPlate::OnAnimationEnded,
+              base::Unretained(this)))) {
   InitLayout();
 
   // The Assistant controller indirectly owns the view hierarchy to which
@@ -237,14 +244,15 @@
 }
 
 void DialogPlate::OnInputModalityChanged(InputModality input_modality) {
+  using namespace assistant::util;
+
   switch (input_modality) {
     case InputModality::kKeyboard: {
       // Animate voice layout container opacity to 0%.
       voice_layout_container_->layer()->GetAnimator()->StartAnimation(
-          assistant::util::CreateLayerAnimationSequence(
-              assistant::util::CreateOpacityElement(
-                  0.f, kAnimationFadeOutDuration,
-                  gfx::Tween::Type::FAST_OUT_LINEAR_IN)));
+          CreateLayerAnimationSequence(
+              CreateOpacityElement(0.f, kAnimationFadeOutDuration,
+                                   gfx::Tween::Type::FAST_OUT_LINEAR_IN)));
 
       // Apply a pre-transformation on the keyboard layout container so that it
       // can be animated into place.
@@ -253,32 +261,32 @@
       keyboard_layout_container_->layer()->SetTransform(transform);
 
       // Animate keyboard layout container.
-      keyboard_layout_container_->layer()->GetAnimator()->StartTogether(
+      StartLayerAnimationSequencesTogether(
+          keyboard_layout_container_->layer()->GetAnimator(),
           {// Animate transformation.
-           assistant::util::CreateLayerAnimationSequence(
-               assistant::util::CreateTransformElement(
-                   gfx::Transform(), kAnimationTransformInDuration,
-                   gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
+           CreateLayerAnimationSequence(CreateTransformElement(
+               gfx::Transform(), kAnimationTransformInDuration,
+               gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
            // Animate opacity to 100% with delay.
-           assistant::util::CreateLayerAnimationSequence(
+           CreateLayerAnimationSequence(
                ui::LayerAnimationElement::CreatePauseElement(
                    ui::LayerAnimationElement::AnimatableProperty::OPACITY,
                    kAnimationFadeInDelay),
-               assistant::util::CreateOpacityElement(
-                   1.f, kAnimationFadeInDuration,
-                   gfx::Tween::Type::FAST_OUT_LINEAR_IN))});
+               CreateOpacityElement(1.f, kAnimationFadeInDuration,
+                                    gfx::Tween::Type::FAST_OUT_LINEAR_IN))},
+          // Observe this animation.
+          animation_observer_.get());
 
-      // When switching to keyboard input modality, we focus the textfield.
-      textfield_->RequestFocus();
+      // Activate the animation observer to receive start/end events.
+      animation_observer_->SetActive();
       break;
     }
     case InputModality::kVoice: {
       // Animate keyboard layout container opacity to 0%.
       keyboard_layout_container_->layer()->GetAnimator()->StartAnimation(
-          assistant::util::CreateLayerAnimationSequence(
-              assistant::util::CreateOpacityElement(
-                  0.f, kAnimationFadeOutDuration,
-                  gfx::Tween::Type::FAST_OUT_LINEAR_IN)));
+          CreateLayerAnimationSequence(
+              CreateOpacityElement(0.f, kAnimationFadeOutDuration,
+                                   gfx::Tween::Type::FAST_OUT_LINEAR_IN)));
 
       // Apply a pre-transformation on the voice layout container so that it can
       // be animated into place.
@@ -287,20 +295,24 @@
       voice_layout_container_->layer()->SetTransform(transform);
 
       // Animate voice layout container.
-      voice_layout_container_->layer()->GetAnimator()->StartTogether(
+      StartLayerAnimationSequencesTogether(
+          voice_layout_container_->layer()->GetAnimator(),
           {// Animate transformation.
-           assistant::util::CreateLayerAnimationSequence(
-               assistant::util::CreateTransformElement(
-                   gfx::Transform(), kAnimationTransformInDuration,
-                   gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
+           CreateLayerAnimationSequence(CreateTransformElement(
+               gfx::Transform(), kAnimationTransformInDuration,
+               gfx::Tween::Type::FAST_OUT_SLOW_IN_2)),
            // Animate opacity to 100% with delay.
-           assistant::util::CreateLayerAnimationSequence(
+           CreateLayerAnimationSequence(
                ui::LayerAnimationElement::CreatePauseElement(
                    ui::LayerAnimationElement::AnimatableProperty::OPACITY,
                    kAnimationFadeInDelay),
-               assistant::util::CreateOpacityElement(
-                   1.f, kAnimationFadeInDuration,
-                   gfx::Tween::Type::FAST_OUT_LINEAR_IN))});
+               CreateOpacityElement(1.f, kAnimationFadeInDuration,
+                                    gfx::Tween::Type::FAST_OUT_LINEAR_IN))},
+          // Observe this animation.
+          animation_observer_.get());
+
+      // Activate the animation observer to receive start/end events.
+      animation_observer_->SetActive();
       break;
     }
     case InputModality::kStylus:
@@ -309,6 +321,34 @@
   }
 }
 
+void DialogPlate::OnAnimationStarted(
+    const ui::CallbackLayerAnimationObserver& observer) {
+  keyboard_layout_container_->set_can_process_events_within_subtree(false);
+  voice_layout_container_->set_can_process_events_within_subtree(false);
+}
+
+bool DialogPlate::OnAnimationEnded(
+    const ui::CallbackLayerAnimationObserver& observer) {
+  InputModality input_modality = assistant_controller_->interaction_controller()
+                                     ->model()
+                                     ->input_modality();
+  switch (input_modality) {
+    case InputModality::kKeyboard:
+      keyboard_layout_container_->set_can_process_events_within_subtree(true);
+      textfield_->RequestFocus();
+      break;
+    case InputModality::kVoice:
+      voice_layout_container_->set_can_process_events_within_subtree(true);
+      break;
+    case InputModality::kStylus:
+      // No action necessary.
+      break;
+  }
+
+  // We return false so that the animation observer will not destroy itself.
+  return false;
+}
+
 void DialogPlate::OnUiVisibilityChanged(bool visible, AssistantSource source) {
   // When the Assistant UI is hidden we need to clear the dialog plate so that
   // text does not persist across Assistant entries.
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.h b/ash/assistant/ui/dialog_plate/dialog_plate.h
index 53db9de..7609a1ef 100644
--- a/ash/assistant/ui/dialog_plate/dialog_plate.h
+++ b/ash/assistant/ui/dialog_plate/dialog_plate.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_UI_DIALOG_PLATE_DIALOG_PLATE_H_
 #define ASH_ASSISTANT_UI_DIALOG_PLATE_DIALOG_PLATE_H_
 
+#include <memory>
 #include <string>
 
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
@@ -16,6 +17,10 @@
 #include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/view.h"
 
+namespace ui {
+class CallbackLayerAnimationObserver;
+}  // namespace ui
+
 namespace ash {
 
 class AssistantController;
@@ -94,12 +99,17 @@
 
   void OnButtonPressed(DialogPlateButtonId id);
 
+  void OnAnimationStarted(const ui::CallbackLayerAnimationObserver& observer);
+  bool OnAnimationEnded(const ui::CallbackLayerAnimationObserver& observer);
+
   AssistantController* const assistant_controller_;  // Owned by Shell.
 
   views::View* keyboard_layout_container_;           // Owned by view hierarchy.
   views::View* voice_layout_container_;              // Owned by view hierarchy.
   views::Textfield* textfield_;                      // Owned by view hierarchy.
 
+  std::unique_ptr<ui::CallbackLayerAnimationObserver> animation_observer_;
+
   base::ObserverList<DialogPlateObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(DialogPlate);
diff --git a/ash/assistant/ui/main_stage/assistant_main_stage.cc b/ash/assistant/ui/main_stage/assistant_main_stage.cc
index 72837a9..080f726c 100644
--- a/ash/assistant/ui/main_stage/assistant_main_stage.cc
+++ b/ash/assistant/ui/main_stage/assistant_main_stage.cc
@@ -7,20 +7,86 @@
 #include <memory>
 
 #include "ash/assistant/assistant_controller.h"
+#include "ash/assistant/assistant_interaction_controller.h"
 #include "ash/assistant/ui/main_stage/assistant_query_view.h"
 #include "ash/assistant/ui/main_stage/suggestion_container_view.h"
 #include "ash/assistant/ui/main_stage/ui_element_container_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/layout/layout_manager.h"
 
 namespace ash {
 
+namespace {
+
+// StackLayout -----------------------------------------------------------------
+
+// A layout manager which lays out its views atop each other. This differs from
+// FillLayout in that we respect the preferred size of views during layout. In
+// contrast, FillLayout will cause its views to match the bounds of the host.
+class StackLayout : public views::LayoutManager {
+ public:
+  StackLayout() = default;
+  ~StackLayout() override = default;
+
+  gfx::Size GetPreferredSize(const views::View* host) const override {
+    gfx::Size preferred_size;
+
+    for (int i = 0; i < host->child_count(); ++i)
+      preferred_size.SetToMax(host->child_at(i)->GetPreferredSize());
+
+    return preferred_size;
+  }
+
+  int GetPreferredHeightForWidth(const views::View* host,
+                                 int width) const override {
+    int preferred_height = 0;
+
+    for (int i = 0; i < host->child_count(); ++i) {
+      preferred_height = std::max(host->child_at(i)->GetHeightForWidth(width),
+                                  preferred_height);
+    }
+
+    return preferred_height;
+  }
+
+  void Layout(views::View* host) override {
+    const int host_width = host->GetContentsBounds().width();
+
+    for (int i = 0; i < host->child_count(); ++i) {
+      views::View* child = host->child_at(i);
+
+      int child_width = std::min(child->GetPreferredSize().width(), host_width);
+      int child_height = child->GetHeightForWidth(child_width);
+
+      // Children are horizontally centered, top aligned.
+      child->SetBounds(/*x=*/(host_width - child_width) / 2, /*y=*/0,
+                       child_width, child_height);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StackLayout);
+};
+
+}  // namespace
+
+// AssistantMainStage ----------------------------------------------------------
+
 AssistantMainStage::AssistantMainStage(
-    AssistantController* assistant_controller) {
+    AssistantController* assistant_controller)
+    : assistant_controller_(assistant_controller) {
   InitLayout(assistant_controller);
+
+  // The view hierarchy will be destructed before Shell, which owns
+  // AssistantController, so AssistantController is guaranteed to outlive the
+  // AssistantMainStage.
+  assistant_controller_->interaction_controller()->AddModelObserver(this);
 }
 
-AssistantMainStage::~AssistantMainStage() = default;
+AssistantMainStage::~AssistantMainStage() {
+  assistant_controller_->interaction_controller()->RemoveModelObserver(this);
+}
 
 void AssistantMainStage::ChildPreferredSizeChanged(views::View* child) {
   PreferredSizeChanged();
@@ -30,37 +96,39 @@
   PreferredSizeChanged();
 }
 
-void AssistantMainStage::OnViewPreferredSizeChanged(views::View* view) {
+void AssistantMainStage::OnViewBoundsChanged(views::View* view) {
   if (view == committed_query_view_) {
-    // Because it reserves layout space for |committed_query_view_|, the
-    // associated spacer needs to match its preferred size.
-    committed_query_view_spacer_->SetPreferredSize(
-        committed_query_view_->GetPreferredSize());
-  } else if (view == suggestion_container_) {
-    // Because it reserves layout space for the |suggestion_container_|, the
-    // associated spacer needs to match its preferred size.
-    suggestion_container_spacer_->SetPreferredSize(
-        suggestion_container_->GetPreferredSize());
+    UpdateCommittedQueryViewSpacer();
+  } else if (view == pending_query_view_) {
+    // The pending query should be bottom aligned in its parent until it is
+    // committed at which point it is animated to the top.
+    const int top_offset =
+        query_layout_container_->height() - pending_query_view_->height();
+
+    gfx::Transform transform;
+    transform.Translate(0, top_offset);
+    pending_query_view_->layer()->SetTransform(transform);
   }
+}
+
+void AssistantMainStage::OnViewPreferredSizeChanged(views::View* view) {
   PreferredSizeChanged();
 }
 
 void AssistantMainStage::OnViewVisibilityChanged(views::View* view) {
-  if (view == committed_query_view_) {
-    // We only reserve space for |committed_query_view_| when it is visible.
-    committed_query_view_spacer_->SetVisible(committed_query_view_->visible());
-  } else if (view == suggestion_container_) {
-    // We only reserve space for the |suggestion_container_| when it is hidden.
-    suggestion_container_spacer_->SetVisible(!suggestion_container_->visible());
-  } else if (view == pending_query_view_) {
-    // We only display |suggestion_container_| when |pending_query_view_| is
-    // hidden. When |suggestion_container_| is hidden, its space will be
-    // preserved in the layout by |suggestion_container_spacer_|.
-    suggestion_container_->SetVisible(!pending_query_view_->visible());
-  }
   PreferredSizeChanged();
 }
 
+void AssistantMainStage::OnViewIsDeleting(views::View* view) {
+  if (view == committed_query_view_) {
+    committed_query_view_ = nullptr;
+    UpdateCommittedQueryViewSpacer();
+  } else if (view == pending_query_view_) {
+    pending_query_view_ = nullptr;
+    UpdateSuggestionContainer();
+  }
+}
+
 void AssistantMainStage::InitLayout(AssistantController* assistant_controller) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
@@ -97,50 +165,97 @@
   // Suggestion container.
   suggestion_container_ = new SuggestionContainerView(assistant_controller);
   suggestion_container_->AddObserver(this);
-  content_layout_container->AddChildView(suggestion_container_);
 
-  // Suggestion container spacer.
-  // Note: This view reserves layout space for the |suggestion_container_|,
-  // dynamically mirroring its preferred size and being visible only when the
-  // |suggestion_container_| is hidden.
-  suggestion_container_spacer_ = new views::View();
-  content_layout_container->AddChildView(suggestion_container_spacer_);
+  // The suggestion container will be animated on its own layer.
+  suggestion_container_->SetPaintToLayer();
+  suggestion_container_->layer()->SetFillsBoundsOpaquely(false);
+
+  content_layout_container->AddChildView(suggestion_container_);
 
   AddChildView(content_layout_container);
 }
 
 void AssistantMainStage::InitQueryLayoutContainer(
     AssistantController* assistant_controller) {
-  // Note that we will observe children of |query_layout_container| to handle
+  // Note that we will observe children of |query_layout_container_| to handle
   // preferred size and visibility change events in AssistantMainStage. This is
-  // necessary because |query_layout_container| may not change size in response
-  // to these events, necessitating an explicit layout pass.
-  views::View* query_layout_container = new views::View();
-  query_layout_container->set_can_process_events_within_subtree(false);
+  // necessary because |query_layout_container_| may not change size in response
+  // to these events, thereby requiring an explicit layout pass.
+  query_layout_container_ = new views::View();
+  query_layout_container_->set_can_process_events_within_subtree(false);
+  query_layout_container_->SetLayoutManager(std::make_unique<StackLayout>());
 
-  views::BoxLayout* layout_manager = query_layout_container->SetLayoutManager(
-      std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kVertical));
+  AddChildView(query_layout_container_);
+}
 
-  // Committed query.
-  committed_query_view_ = new AssistantQueryView(
-      assistant_controller, AssistantQueryView::ObservedQueryState::kCommitted);
-  committed_query_view_->AddObserver(this);
-  query_layout_container->AddChildView(committed_query_view_);
+// TODO(dmblack): Animate transformation.
+void AssistantMainStage::OnCommittedQueryChanged(const AssistantQuery& query) {
+  // Clean up any previous committed query.
+  OnCommittedQueryCleared();
 
-  // Spacer.
-  views::View* spacer = new views::View();
-  query_layout_container->AddChildView(spacer);
+  committed_query_view_ = pending_query_view_;
+  pending_query_view_ = nullptr;
 
-  layout_manager->SetFlexForView(spacer, 1);
+  // Update the view and move it to the top of its parent.
+  committed_query_view_->SetQuery(query);
+  committed_query_view_->layer()->SetTransform(gfx::Transform());
 
-  // Pending query.
-  pending_query_view_ = new AssistantQueryView(
-      assistant_controller, AssistantQueryView::ObservedQueryState::kPending);
-  pending_query_view_->AddObserver(this);
-  query_layout_container->AddChildView(pending_query_view_);
+  UpdateCommittedQueryViewSpacer();
+  UpdateSuggestionContainer();
+}
 
-  AddChildView(query_layout_container);
+void AssistantMainStage::OnCommittedQueryCleared() {
+  if (!committed_query_view_)
+    return;
+
+  query_layout_container_->RemoveChildView(committed_query_view_);
+  delete committed_query_view_;
+}
+
+void AssistantMainStage::OnPendingQueryChanged(const AssistantQuery& query) {
+  if (!pending_query_view_) {
+    pending_query_view_ = new AssistantQueryView();
+    pending_query_view_->AddObserver(this);
+
+    // The query view will be animated on its own layer.
+    pending_query_view_->SetPaintToLayer();
+    pending_query_view_->layer()->SetFillsBoundsOpaquely(false);
+
+    query_layout_container_->AddChildView(pending_query_view_);
+
+    UpdateSuggestionContainer();
+  }
+
+  pending_query_view_->SetQuery(query);
+}
+
+void AssistantMainStage::OnPendingQueryCleared() {
+  if (pending_query_view_) {
+    query_layout_container_->RemoveChildView(pending_query_view_);
+    delete pending_query_view_;
+  } else {
+    // We only need to update the suggestion container when we are not deleting
+    // the pending query view. Deleting the pending query view will trigger an
+    // update on the suggestion container itself.
+    UpdateSuggestionContainer();
+  }
+}
+
+void AssistantMainStage::UpdateCommittedQueryViewSpacer() {
+  // The spacer reserves room in the layout for the committed query view, so
+  // it should match its size.
+  committed_query_view_spacer_->SetPreferredSize(
+      committed_query_view_ ? committed_query_view_->size() : gfx::Size());
+}
+
+// TODO(dmblack): Animate visibility changes.
+void AssistantMainStage::UpdateSuggestionContainer() {
+  // The suggestion container is only visible when the pending query is not.
+  // When it is not visible, it should not process events.
+  bool visible = pending_query_view_ == nullptr;
+  suggestion_container_->layer()->SetOpacity(visible ? 1.f : 0.f);
+  suggestion_container_->set_can_process_events_within_subtree(visible ? true
+                                                                       : false);
 }
 
 }  // namespace ash
diff --git a/ash/assistant/ui/main_stage/assistant_main_stage.h b/ash/assistant/ui/main_stage/assistant_main_stage.h
index 2e7785ab..9d861c5 100644
--- a/ash/assistant/ui/main_stage/assistant_main_stage.h
+++ b/ash/assistant/ui/main_stage/assistant_main_stage.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_MAIN_STAGE_H_
 #define ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_MAIN_STAGE_H_
 
+#include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "base/macros.h"
 #include "ui/views/view.h"
 #include "ui/views/view_observer.h"
@@ -19,7 +20,9 @@
 // AssistantMainStage is the child of AssistantMainView responsible for
 // displaying the Assistant interaction to the user. This includes visual
 // affordances for the query, response, as well as suggestions.
-class AssistantMainStage : public views::View, public views::ViewObserver {
+class AssistantMainStage : public views::View,
+                           public views::ViewObserver,
+                           public AssistantInteractionModelObserver {
  public:
   explicit AssistantMainStage(AssistantController* assistant_controller);
   ~AssistantMainStage() override;
@@ -29,21 +32,36 @@
   void ChildVisibilityChanged(views::View* child) override;
 
   // views::ViewObserver:
+  void OnViewBoundsChanged(views::View* view) override;
   void OnViewPreferredSizeChanged(views::View* view) override;
   void OnViewVisibilityChanged(views::View* view) override;
+  void OnViewIsDeleting(views::View* view) override;
+
+  // AssistantInteractionModelObserver:
+  void OnCommittedQueryChanged(const AssistantQuery& query) override;
+  void OnCommittedQueryCleared() override;
+  void OnPendingQueryChanged(const AssistantQuery& query) override;
+  void OnPendingQueryCleared() override;
 
  private:
   void InitLayout(AssistantController* assistant_controller);
   void InitContentLayoutContainer(AssistantController* assistant_controller);
   void InitQueryLayoutContainer(AssistantController* assistant_controller);
 
-  AssistantQueryView* committed_query_view_;       // Owned by view hierarchy.
+  void UpdateCommittedQueryViewSpacer();
+  void UpdateSuggestionContainer();
+
+  AssistantController* const assistant_controller_;  // Owned by Shell.
+
   views::View* committed_query_view_spacer_;       // Owned by view hierarchy.
-  AssistantQueryView* pending_query_view_;         // Owned by view hierarchy.
+  views::View* query_layout_container_;            // Owned by view hierarchy.
   SuggestionContainerView* suggestion_container_;  // Owned by view hierarchy.
-  views::View* suggestion_container_spacer_;       // Owned by view hierarchy.
   UiElementContainerView* ui_element_container_;   // Owned by view hierarchy.
 
+  // Owned by view hierarchy.
+  AssistantQueryView* committed_query_view_ = nullptr;
+  AssistantQueryView* pending_query_view_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(AssistantMainStage);
 };
 
diff --git a/ash/assistant/ui/main_stage/assistant_query_view.cc b/ash/assistant/ui/main_stage/assistant_query_view.cc
index ff80153..efc9f49 100644
--- a/ash/assistant/ui/main_stage/assistant_query_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_query_view.cc
@@ -6,12 +6,10 @@
 
 #include <memory>
 
-#include "ash/assistant/assistant_controller.h"
-#include "ash/assistant/assistant_interaction_controller.h"
-#include "ash/assistant/model/assistant_interaction_model.h"
 #include "ash/assistant/model/assistant_query.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace ash {
@@ -21,23 +19,24 @@
 // Appearance.
 constexpr int kMinHeightDip = 32;
 
+// Helpers ---------------------------------------------------------------------
+
+views::StyledLabel::RangeStyleInfo CreateStyleInfo(SkColor color) {
+  views::StyledLabel::RangeStyleInfo style;
+  style.custom_font = views::Label::GetDefaultFontList().DeriveWithSizeDelta(2);
+  style.override_color = color;
+  return style;
+}
+
 }  // namespace
 
-AssistantQueryView::AssistantQueryView(
-    AssistantController* assistant_controller,
-    ObservedQueryState observed_query_state)
-    : assistant_controller_(assistant_controller),
-      observed_query_state_(observed_query_state) {
+// AssistantQueryView ----------------------------------------------------------
+
+AssistantQueryView::AssistantQueryView() {
   InitLayout();
-
-  // The Assistant controller indirectly owns the view hierarchy to which
-  // AssistantQueryView belongs so is guaranteed to outlive it.
-  assistant_controller_->interaction_controller()->AddModelObserver(this);
 }
 
-AssistantQueryView::~AssistantQueryView() {
-  assistant_controller_->interaction_controller()->RemoveModelObserver(this);
-}
+AssistantQueryView::~AssistantQueryView() = default;
 
 gfx::Size AssistantQueryView::CalculatePreferredSize() const {
   return gfx::Size(INT_MAX, GetHeightForWidth(INT_MAX));
@@ -71,116 +70,55 @@
   label_->set_auto_color_readability_enabled(false);
   label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER);
   AddChildView(label_);
-
-  // Artificially trigger event to initialize state.
-  OnPendingQueryChanged(observed_query_state_ == ObservedQueryState::kCommitted
-                            ? assistant_controller_->interaction_controller()
-                                  ->model()
-                                  ->committed_query()
-                            : assistant_controller_->interaction_controller()
-                                  ->model()
-                                  ->pending_query());
 }
 
-void AssistantQueryView::OnCommittedQueryChanged(
-    const AssistantQuery& committed_query) {
-  if (observed_query_state_ != ObservedQueryState::kCommitted)
-    return;
-
-  OnQueryChanged(committed_query);
-}
-
-void AssistantQueryView::OnPendingQueryChanged(
-    const AssistantQuery& pending_query) {
-  if (observed_query_state_ != ObservedQueryState::kPending)
-    return;
-
-  OnQueryChanged(pending_query);
-}
-
-void AssistantQueryView::OnQueryChanged(const AssistantQuery& query) {
-  // Empty query.
-  if (query.Empty()) {
-    OnQueryCleared();
-  } else {
-    // Populated query.
-    switch (query.type()) {
-      case AssistantQueryType::kText: {
-        const AssistantTextQuery& text_query =
-            static_cast<const AssistantTextQuery&>(query);
-        SetText(text_query.text());
-        break;
-      }
-      case AssistantQueryType::kVoice: {
-        const AssistantVoiceQuery& voice_query =
-            static_cast<const AssistantVoiceQuery&>(query);
-        SetText(voice_query.high_confidence_speech(),
-                voice_query.low_confidence_speech());
-        break;
-      }
-      case AssistantQueryType::kEmpty:
-        // Empty queries are already handled.
-        NOTREACHED();
-        break;
+void AssistantQueryView::SetQuery(const AssistantQuery& query) {
+  switch (query.type()) {
+    case AssistantQueryType::kText: {
+      const AssistantTextQuery& text_query =
+          static_cast<const AssistantTextQuery&>(query);
+      SetText(text_query.text());
+      break;
     }
+    case AssistantQueryType::kVoice: {
+      const AssistantVoiceQuery& voice_query =
+          static_cast<const AssistantVoiceQuery&>(query);
+      SetText(voice_query.high_confidence_speech(),
+              voice_query.low_confidence_speech());
+      break;
+    }
+    case AssistantQueryType::kEmpty:
+      label_->SetText(base::string16());
+      break;
   }
 }
 
-void AssistantQueryView::OnCommittedQueryCleared() {
-  if (observed_query_state_ != ObservedQueryState::kCommitted)
-    return;
-
-  OnQueryCleared();
-}
-
-void AssistantQueryView::OnPendingQueryCleared() {
-  if (observed_query_state_ != ObservedQueryState::kPending)
-    return;
-
-  OnQueryCleared();
-}
-
-void AssistantQueryView::OnQueryCleared() {
-  SetVisible(false);
-  label_->SetText(base::string16());
-}
-
 void AssistantQueryView::SetText(const std::string& high_confidence_text,
                                  const std::string& low_confidence_text) {
-  if (observed_query_state_ == ObservedQueryState::kCommitted) {
-    // When observing a committed query, text is displayed in a single color.
-    const base::string16& text_16 =
-        base::UTF8ToUTF16(high_confidence_text + low_confidence_text);
+  // High confidence text and low confidence text are displayed in different
+  // colors for visual emphasis.
+  const base::string16& high_confidence_text_16 =
+      base::UTF8ToUTF16(high_confidence_text);
 
-    label_->SetText(text_16);
-    label_->AddStyleRange(gfx::Range(0, text_16.length()),
-                          CreateStyleInfo(kTextColorSecondary));
+  if (low_confidence_text.empty()) {
+    label_->SetText(high_confidence_text_16);
+    label_->AddStyleRange(gfx::Range(0, high_confidence_text_16.length()),
+                          CreateStyleInfo(kTextColorPrimary));
   } else {
-    // When observing a pending query, high confidence text and low confidence
-    // text are displayed in different colors for visual emphasis.
-    const base::string16& high_confidence_text_16 =
-        base::UTF8ToUTF16(high_confidence_text);
+    const base::string16& low_confidence_text_16 =
+        base::UTF8ToUTF16(low_confidence_text);
 
-    if (low_confidence_text.empty()) {
-      label_->SetText(high_confidence_text_16);
-      label_->AddStyleRange(gfx::Range(0, high_confidence_text_16.length()),
-                            CreateStyleInfo(kTextColorPrimary));
-    } else {
-      const base::string16& low_confidence_text_16 =
-          base::UTF8ToUTF16(low_confidence_text);
+    label_->SetText(high_confidence_text_16 + low_confidence_text_16);
 
-      label_->SetText(high_confidence_text_16 + low_confidence_text_16);
+    // High confidence text styling.
+    label_->AddStyleRange(gfx::Range(0, high_confidence_text_16.length()),
+                          CreateStyleInfo(kTextColorPrimary));
 
-      // High confidence text styling.
-      label_->AddStyleRange(gfx::Range(0, high_confidence_text_16.length()),
-                            CreateStyleInfo(kTextColorPrimary));
-
-      // Low confidence text styling.
-      label_->AddStyleRange(gfx::Range(high_confidence_text_16.length(),
-                                       high_confidence_text_16.length() +
-                                           low_confidence_text_16.length()),
-                            CreateStyleInfo(kTextColorHint));
-    }
+    // Low confidence text styling.
+    label_->AddStyleRange(gfx::Range(high_confidence_text_16.length(),
+                                     high_confidence_text_16.length() +
+                                         low_confidence_text_16.length()),
+                          CreateStyleInfo(kTextColorHint));
   }
 
   label_->SizeToFit(width());
@@ -188,12 +126,4 @@
   SetVisible(true);
 }
 
-views::StyledLabel::RangeStyleInfo AssistantQueryView::CreateStyleInfo(
-    SkColor color) const {
-  views::StyledLabel::RangeStyleInfo style;
-  style.custom_font = label_->GetDefaultFontList().DeriveWithSizeDelta(2);
-  style.override_color = color;
-  return style;
-}
-
 }  // namespace ash
diff --git a/ash/assistant/ui/main_stage/assistant_query_view.h b/ash/assistant/ui/main_stage/assistant_query_view.h
index 4a733f4..31d94a6 100644
--- a/ash/assistant/ui/main_stage/assistant_query_view.h
+++ b/ash/assistant/ui/main_stage/assistant_query_view.h
@@ -5,28 +5,19 @@
 #ifndef ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_QUERY_VIEW_H_
 #define ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_QUERY_VIEW_H_
 
-#include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "base/macros.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/view.h"
 
 namespace ash {
 
-class AssistantController;
+class AssistantQuery;
 
 // AssistantQueryView is the visual representation of an AssistantQuery. It is a
 // child view of UiElementContainerView.
-class AssistantQueryView : public views::View,
-                           public AssistantInteractionModelObserver {
+class AssistantQueryView : public views::View {
  public:
-  // Dictates whether AssistantQueryView observes a committed or pending query.
-  enum ObservedQueryState {
-    kCommitted,
-    kPending,
-  };
-
-  AssistantQueryView(AssistantController* assistant_controller,
-                     ObservedQueryState observed_query_state);
+  AssistantQueryView();
   ~AssistantQueryView() override;
 
   // views::View:
@@ -35,26 +26,15 @@
   void ChildPreferredSizeChanged(views::View* child) override;
   void OnBoundsChanged(const gfx::Rect& prev_bounds) override;
 
-  // AssistantInteractionModelObserver:
-  void OnCommittedQueryChanged(const AssistantQuery& committed_query) override;
-  void OnCommittedQueryCleared() override;
-  void OnPendingQueryChanged(const AssistantQuery& pending_query) override;
-  void OnPendingQueryCleared() override;
+  void SetQuery(const AssistantQuery& query);
 
  private:
   void InitLayout();
   void SetText(const std::string& high_confidence_text,
                const std::string& low_confidence_text = std::string());
-  views::StyledLabel::RangeStyleInfo CreateStyleInfo(SkColor color) const;
 
-  void OnQueryChanged(const AssistantQuery& query);
-  void OnQueryCleared();
-
-  AssistantController* const assistant_controller_;  // Owned by Shell.
   views::StyledLabel* label_;                        // Owned by view hierarchy.
 
-  const ObservedQueryState observed_query_state_;
-
   DISALLOW_COPY_AND_ASSIGN(AssistantQueryView);
 };
 
diff --git a/ash/assistant/util/animation_util.cc b/ash/assistant/util/animation_util.cc
index dbc90f7..3b3cbdc3 100644
--- a/ash/assistant/util/animation_util.cc
+++ b/ash/assistant/util/animation_util.cc
@@ -6,7 +6,9 @@
 
 #include "base/time/time.h"
 #include "ui/compositor/layer_animation_element.h"
+#include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/layer_animator.h"
 
 namespace ash {
 namespace assistant {
@@ -81,6 +83,19 @@
   return layer_animation_element;
 }
 
+void StartLayerAnimationSequencesTogether(
+    ui::LayerAnimator* layer_animator,
+    const std::vector<ui::LayerAnimationSequence*>& layer_animation_sequences,
+    ui::LayerAnimationObserver* observer) {
+  if (observer) {
+    for (ui::LayerAnimationSequence* layer_animation_sequence :
+         layer_animation_sequences) {
+      layer_animation_sequence->AddObserver(observer);
+    }
+  }
+  layer_animator->StartTogether(layer_animation_sequences);
+}
+
 }  // namespace util
 }  // namespace assistant
 }  // namespace ash
diff --git a/ash/assistant/util/animation_util.h b/ash/assistant/util/animation_util.h
index 8b0c65c..e5731ae 100644
--- a/ash/assistant/util/animation_util.h
+++ b/ash/assistant/util/animation_util.h
@@ -6,6 +6,7 @@
 #define ASH_ASSISTANT_UTIL_ANIMATION_UTIL_H_
 
 #include <memory>
+#include <vector>
 
 #include "ui/gfx/animation/tween.h"
 
@@ -15,7 +16,9 @@
 
 namespace ui {
 class LayerAnimationElement;
+class LayerAnimationObserver;
 class LayerAnimationSequence;
+class LayerAnimator;
 }  // namespace ui
 
 namespace ash {
@@ -75,6 +78,14 @@
     const base::TimeDelta& duration,
     const gfx::Tween::Type& tween = gfx::Tween::Type::LINEAR);
 
+// Starts the specified |layer_animation_sequences| together on the given
+// |layer_animator|. If an optional |observer| is supplied, it will be added
+// to each sequence in the animation set.
+void StartLayerAnimationSequencesTogether(
+    ui::LayerAnimator* layer_animator,
+    const std::vector<ui::LayerAnimationSequence*>& layer_animation_sequences,
+    ui::LayerAnimationObserver* observer = nullptr);
+
 }  // namespace util
 }  // namespace assistant
 }  // namespace ash
diff --git a/ash/keyboard/test_keyboard_ui.cc b/ash/keyboard/test_keyboard_ui.cc
index 0b457a2..a665ef29 100644
--- a/ash/keyboard/test_keyboard_ui.cc
+++ b/ash/keyboard/test_keyboard_ui.cc
@@ -19,10 +19,6 @@
   return !!keyboard_window_;
 }
 
-bool TestKeyboardUI::ShouldWindowOverscroll(aura::Window* window) const {
-  return true;
-}
-
 aura::Window* TestKeyboardUI::GetKeyboardWindow() {
   if (!keyboard_window_) {
     keyboard_window_.reset(new aura::Window(&delegate_));
diff --git a/ash/keyboard/test_keyboard_ui.h b/ash/keyboard/test_keyboard_ui.h
index 250e15e..92bb278 100644
--- a/ash/keyboard/test_keyboard_ui.h
+++ b/ash/keyboard/test_keyboard_ui.h
@@ -24,7 +24,6 @@
   ~TestKeyboardUI() override;
 
   bool HasKeyboardWindow() const override;
-  bool ShouldWindowOverscroll(aura::Window* window) const override;
   aura::Window* GetKeyboardWindow() override;
 
  private:
diff --git a/ash/keyboard/virtual_keyboard_unittest.cc b/ash/keyboard/virtual_keyboard_unittest.cc
new file mode 100644
index 0000000..83ad81ac
--- /dev/null
+++ b/ash/keyboard/virtual_keyboard_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2018 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 "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/command_line.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/keyboard/keyboard_controller.h"
+#include "ui/keyboard/keyboard_switches.h"
+
+namespace ash {
+
+class VirtualKeyboardTest : public AshTestBase {
+ public:
+  VirtualKeyboardTest() = default;
+  ~VirtualKeyboardTest() override = default;
+
+  void SetUp() override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        keyboard::switches::kEnableVirtualKeyboard);
+    AshTestBase::SetUp();
+    keyboard::SetTouchKeyboardEnabled(true);
+    Shell::Get()->EnableKeyboard();
+  }
+
+  void TearDown() override {
+    keyboard::SetTouchKeyboardEnabled(false);
+    AshTestBase::TearDown();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardTest);
+};
+
+TEST_F(VirtualKeyboardTest, EventsAreHandledBasedOnHitTestBounds) {
+  aura::Window* root_window = Shell::GetPrimaryRootWindow();
+
+  // Create a test window in the background with the same size as the screen.
+  aura::test::EventCountDelegate delegate;
+  std::unique_ptr<aura::Window> background_window(
+      CreateTestWindowInShellWithDelegate(&delegate, 0, root_window->bounds()));
+
+  auto* keyboard_controller = keyboard::KeyboardController::Get();
+  keyboard_controller->ShowKeyboard(false);
+  keyboard_controller->NotifyKeyboardWindowLoaded();
+
+  aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
+  keyboard_window->SetBounds(gfx::Rect(100, 100, 100, 100));
+
+  // Add two hit test bounds (coordinates relative to keyboard window).
+  // Both are 10x10 squares, but placed in different locations.
+  std::vector<gfx::Rect> hit_test_bounds;
+  hit_test_bounds.emplace_back(0, 0, 10, 10);
+  hit_test_bounds.emplace_back(20, 20, 10, 10);
+  keyboard_controller->SetHitTestBounds(hit_test_bounds);
+
+  // Click at various places within the keyboard window and check whether the
+  // event passes through the keyboard window to the background window.
+  ui::test::EventGenerator generator(root_window);
+  const gfx::Point origin = keyboard_window->bounds().origin();
+
+  // (0, 0) is inside the first hit rect, so the event is handled by the window
+  // and is not received by the background window.
+  generator.MoveMouseTo(origin);
+  generator.ClickLeftButton();
+  EXPECT_EQ("0 0", delegate.GetMouseButtonCountsAndReset());
+
+  // (25, 25) is inside the second hit rect, so the background window does not
+  // receive the event.
+  generator.MoveMouseTo(origin + gfx::Vector2d(25, 25));
+  generator.ClickLeftButton();
+  EXPECT_EQ("0 0", delegate.GetMouseButtonCountsAndReset());
+
+  // (5, 25) is not inside any hit rect, so the background window receives the
+  // event.
+  generator.MoveMouseTo(origin + gfx::Vector2d(5, 25));
+  generator.ClickLeftButton();
+  EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset());
+
+  // (25, 5) is not inside any hit rect, so the background window receives the
+  // event.
+  generator.MoveMouseTo(origin + gfx::Vector2d(25, 5));
+  generator.ClickLeftButton();
+  EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset());
+}
+
+}  // namespace ash
diff --git a/ash/laser/laser_pointer_controller_unittest.cc b/ash/laser/laser_pointer_controller_unittest.cc
index 8f9dc46e..d4d54f6 100644
--- a/ash/laser/laser_pointer_controller_unittest.cc
+++ b/ash/laser/laser_pointer_controller_unittest.cc
@@ -21,10 +21,13 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    controller_.reset(new LaserPointerController());
+    controller_ = std::make_unique<LaserPointerController>();
+    controller_test_api_ =
+        std::make_unique<LaserPointerControllerTestApi>(controller_.get());
   }
 
   void TearDown() override {
+    controller_test_api_.reset();
     // This needs to be called first to remove the event handler before the
     // shell instance gets torn down.
     controller_.reset();
@@ -33,6 +36,7 @@
 
  protected:
   std::unique_ptr<LaserPointerController> controller_;
+  std::unique_ptr<LaserPointerControllerTestApi> controller_test_api_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LaserPointerControllerTest);
@@ -43,115 +47,111 @@
 // Test to ensure the class responsible for drawing the laser pointer receives
 // points from stylus movements as expected.
 TEST_F(LaserPointerControllerTest, LaserPointerRenderer) {
-  LaserPointerControllerTestApi controller_test_api_(controller_.get());
-
   // The laser pointer mode only works with stylus.
   ui::test::EventGenerator* event_generator = GetEventGenerator();
   event_generator->EnterPenPointerMode();
 
   // When disabled the laser pointer should not be showing.
   event_generator->MoveTouch(gfx::Point(1, 1));
-  EXPECT_FALSE(controller_test_api_.IsShowingLaserPointer());
+  EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer());
 
   // Verify that by enabling the mode, the laser pointer should still not be
   // showing.
-  controller_test_api_.SetEnabled(true);
-  EXPECT_FALSE(controller_test_api_.IsShowingLaserPointer());
+  controller_test_api_->SetEnabled(true);
+  EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer());
 
   // Verify moving the stylus 4 times will not display the laser pointer.
   event_generator->MoveTouch(gfx::Point(2, 2));
   event_generator->MoveTouch(gfx::Point(3, 3));
   event_generator->MoveTouch(gfx::Point(4, 4));
   event_generator->MoveTouch(gfx::Point(5, 5));
-  EXPECT_FALSE(controller_test_api_.IsShowingLaserPointer());
+  EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer());
 
   // Verify pressing the stylus will show the laser pointer and add a point but
   // will not activate fading out.
   event_generator->PressTouch();
-  EXPECT_TRUE(controller_test_api_.IsShowingLaserPointer());
-  EXPECT_FALSE(controller_test_api_.IsFadingAway());
-  EXPECT_EQ(1, controller_test_api_.laser_points().GetNumberOfPoints());
+  EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer());
+  EXPECT_FALSE(controller_test_api_->IsFadingAway());
+  EXPECT_EQ(1, controller_test_api_->laser_points().GetNumberOfPoints());
 
   // Verify dragging the stylus 2 times will add 2 more points.
   event_generator->MoveTouch(gfx::Point(6, 6));
   event_generator->MoveTouch(gfx::Point(7, 7));
-  EXPECT_EQ(3, controller_test_api_.laser_points().GetNumberOfPoints());
+  EXPECT_EQ(3, controller_test_api_->laser_points().GetNumberOfPoints());
 
   // Verify releasing the stylus still shows the laser pointer, which is fading
   // away.
   event_generator->ReleaseTouch();
-  EXPECT_TRUE(controller_test_api_.IsShowingLaserPointer());
-  EXPECT_TRUE(controller_test_api_.IsFadingAway());
+  EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer());
+  EXPECT_TRUE(controller_test_api_->IsFadingAway());
 
   // Verify that disabling the mode does not display the laser pointer.
-  controller_test_api_.SetEnabled(false);
-  EXPECT_FALSE(controller_test_api_.IsShowingLaserPointer());
-  EXPECT_FALSE(controller_test_api_.IsFadingAway());
+  controller_test_api_->SetEnabled(false);
+  EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer());
+  EXPECT_FALSE(controller_test_api_->IsFadingAway());
 
   // Verify that disabling the mode while laser pointer is displayed does not
   // display the laser pointer.
-  controller_test_api_.SetEnabled(true);
+  controller_test_api_->SetEnabled(true);
   event_generator->PressTouch();
   event_generator->MoveTouch(gfx::Point(6, 6));
-  EXPECT_TRUE(controller_test_api_.IsShowingLaserPointer());
-  controller_test_api_.SetEnabled(false);
-  EXPECT_FALSE(controller_test_api_.IsShowingLaserPointer());
+  EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer());
+  controller_test_api_->SetEnabled(false);
+  EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer());
 
   // Verify that the laser pointer does not add points while disabled.
   event_generator->PressTouch();
   event_generator->MoveTouch(gfx::Point(8, 8));
   event_generator->ReleaseTouch();
   event_generator->MoveTouch(gfx::Point(9, 9));
-  EXPECT_FALSE(controller_test_api_.IsShowingLaserPointer());
+  EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer());
 
   // Verify that the laser pointer does not get shown if points are not coming
   // from the stylus, even when enabled.
   event_generator->ExitPenPointerMode();
-  controller_test_api_.SetEnabled(true);
+  controller_test_api_->SetEnabled(true);
   event_generator->PressTouch();
   event_generator->MoveTouch(gfx::Point(10, 10));
   event_generator->MoveTouch(gfx::Point(11, 11));
-  EXPECT_FALSE(controller_test_api_.IsShowingLaserPointer());
+  EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer());
   event_generator->ReleaseTouch();
 }
 
 // Test to ensure the class responsible for drawing the laser pointer handles
 // prediction as expected when it receives points from stylus movements.
 TEST_F(LaserPointerControllerTest, LaserPointerPrediction) {
-  LaserPointerControllerTestApi controller_test_api_(controller_.get());
-
-  controller_test_api_.SetEnabled(true);
+  controller_test_api_->SetEnabled(true);
   // The laser pointer mode only works with stylus.
   ui::test::EventGenerator* event_generator = GetEventGenerator();
   event_generator->EnterPenPointerMode();
   event_generator->PressTouch();
-  EXPECT_TRUE(controller_test_api_.IsShowingLaserPointer());
+  EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer());
 
-  EXPECT_EQ(1, controller_test_api_.laser_points().GetNumberOfPoints());
+  EXPECT_EQ(1, controller_test_api_->laser_points().GetNumberOfPoints());
   // Initial press event shouldn't generate any predicted points as there's no
   // history to use for prediction.
   EXPECT_EQ(0,
-            controller_test_api_.predicted_laser_points().GetNumberOfPoints());
+            controller_test_api_->predicted_laser_points().GetNumberOfPoints());
 
   // Verify dragging the stylus 3 times will add some predicted points.
   event_generator->MoveTouch(gfx::Point(10, 10));
   event_generator->MoveTouch(gfx::Point(20, 20));
   event_generator->MoveTouch(gfx::Point(30, 30));
   EXPECT_NE(0,
-            controller_test_api_.predicted_laser_points().GetNumberOfPoints());
+            controller_test_api_->predicted_laser_points().GetNumberOfPoints());
   // Verify predicted points are in the right direction.
   for (const auto& point :
-       controller_test_api_.predicted_laser_points().points()) {
+       controller_test_api_->predicted_laser_points().points()) {
     EXPECT_LT(30, point.location.x());
     EXPECT_LT(30, point.location.y());
   }
 
   // Verify releasing the stylus removes predicted points.
   event_generator->ReleaseTouch();
-  EXPECT_TRUE(controller_test_api_.IsShowingLaserPointer());
-  EXPECT_TRUE(controller_test_api_.IsFadingAway());
+  EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer());
+  EXPECT_TRUE(controller_test_api_->IsFadingAway());
   EXPECT_EQ(0,
-            controller_test_api_.predicted_laser_points().GetNumberOfPoints());
+            controller_test_api_->predicted_laser_points().GetNumberOfPoints());
 }
 
 }  // namespace ash
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index e2e30f0..67558be 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "base/test/scoped_feature_list.h"
@@ -107,6 +108,20 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
+class TestWidgetDelegate : public views::WidgetDelegateView {
+ public:
+  TestWidgetDelegate() = default;
+  ~TestWidgetDelegate() override = default;
+
+  // views::WidgetDelegateView:
+  bool CanResize() const override { return true; }
+  bool CanMaximize() const override { return true; }
+  bool CanActivate() const override { return true; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
+};
+
 class ImmersiveFullscreenControllerTest : public AshTestBase {
  public:
   enum Modality {
@@ -147,6 +162,7 @@
 
     widget_ = new views::Widget();
     views::Widget::InitParams params;
+    params.delegate = new TestWidgetDelegate();
     widget_->Init(params);
     widget_->Show();
 
@@ -671,10 +687,9 @@
   EXPECT_FALSE(controller()->IsRevealed());
 }
 
-// Tests the top-of-window views for maximized/full-screened window in tablet
-// mode.
-TEST_F(ImmersiveFullscreenControllerTest,
-       MaximizedOrFullscreenedWindowInTabletMode) {
+// Tests the top-of-window views for maximized/full-screened/snapped windows in
+// tablet mode.
+TEST_F(ImmersiveFullscreenControllerTest, WindowsInTabletMode) {
   SetWindowShowState(ui::SHOW_STATE_MAXIMIZED);
   EnableTabletMode(true);
   SetEnabled(true);
@@ -700,6 +715,16 @@
   SetWindowShowState(ui::SHOW_STATE_FULLSCREEN);
   AttemptReveal(MODALITY_GESTURE_SCROLL);
   EXPECT_FALSE(controller()->IsRevealed());
+
+  // Top-of-window views will not be revealed for snapped window in splitview
+  // mode either.
+  EnableTabletMode(true);
+  Shell::Get()->split_view_controller()->SnapWindow(window(),
+                                                    SplitViewController::LEFT);
+  EXPECT_TRUE(wm::GetWindowState(window())->IsSnapped());
+  EXPECT_TRUE(Shell::Get()->split_view_controller()->IsSplitViewModeActive());
+  AttemptReveal(MODALITY_GESTURE_SCROLL);
+  EXPECT_FALSE(controller()->IsRevealed());
 }
 
 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
diff --git a/ash/wm/immersive_gesture_handler_classic.cc b/ash/wm/immersive_gesture_handler_classic.cc
index 6409751..c9e1bae1 100644
--- a/ash/wm/immersive_gesture_handler_classic.cc
+++ b/ash/wm/immersive_gesture_handler_classic.cc
@@ -10,6 +10,7 @@
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/window_state.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/window.h"
@@ -71,10 +72,12 @@
   if (window != immersive_fullscreen_controller_->widget()->GetNativeWindow())
     return false;
 
-  // Only maximized or fullscreened none browser window in tablet mode allowed
-  // to be dragged.
-  const views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
-  if (!widget || (!widget->IsMaximized() && !widget->IsFullscreen()) ||
+  // Maximized, fullscreened and snapped none BROWSER windows in tablet mode are
+  // allowed to be dragged.
+  wm::WindowState* window_state = wm::GetWindowState(window);
+  if (!window_state ||
+      (!window_state->IsMaximized() && !window_state->IsFullscreen() &&
+       !window_state->IsSnapped()) ||
       !Shell::Get()
            ->tablet_mode_controller()
            ->IsTabletModeWindowManagerEnabled() ||
diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc
index 2381996..9480e9fd 100644
--- a/ash/wm/overview/window_grid.cc
+++ b/ash/wm/overview/window_grid.cc
@@ -849,6 +849,16 @@
     return;
   }
 
+  // If any of the items are being animated to close, do not nudge any windows
+  // otherwise we have to deal with potential items getting removed from
+  // |window_list_| midway through a nudge.
+  for (const auto& window_item : window_list_) {
+    if (window_item->animating_to_close()) {
+      nudge_data_.clear();
+      return;
+    }
+  }
+
   DCHECK(item);
 
   // Get the bounds of the windows currently, and the bounds if |item| were to
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 39234b325..a3891bd 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -141,7 +141,8 @@
   // Ends the split view mode.
   void EndSplitView();
 
-  // Called when a window's tab(s) start/end being dragging around.
+  // Called when a window's tab(s) start/end being dragged around or other
+  // non-browser windows start/end being dragged from the top of the display.
   void OnWindowDragStarted(aura::Window* dragged_window);
   void OnWindowDragEnded(aura::Window* dragged_window,
                          SnapPosition desired_snap_position,
diff --git a/ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.cc b/ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.cc
index e9375ac..e1dd72fd 100644
--- a/ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.cc
@@ -131,9 +131,12 @@
 
   float scale = CalculateWindowScale(location_in_screen.y());
   gfx::Transform transform;
+  const gfx::Rect window_bounds = drag_delegate_->dragged_window()->bounds();
   transform.Translate(
-      location_in_screen.x() - initial_location_in_screen_.x() * scale,
-      location_in_screen.y() - initial_location_in_screen_.y() * scale);
+      (location_in_screen.x() - window_bounds.x()) -
+          (initial_location_in_screen_.x() - window_bounds.x()) * scale,
+      (location_in_screen.y() - window_bounds.y()) -
+          (initial_location_in_screen_.y() - window_bounds.y()) * scale);
   transform.Scale(scale, scale);
   drag_delegate_->dragged_window()->SetTransform(transform);
 }
diff --git a/base/time/time.cc b/base/time/time.cc
index 8529bb0f..c1d06a8b 100644
--- a/base/time/time.cc
+++ b/base/time/time.cc
@@ -292,15 +292,15 @@
   return time;
 }
 
-Time Time::LocalMidnight() const {
+Time Time::Midnight(bool is_local) const {
   Exploded exploded;
-  LocalExplode(&exploded);
+  Explode(is_local, &exploded);
   exploded.hour = 0;
   exploded.minute = 0;
   exploded.second = 0;
   exploded.millisecond = 0;
   Time out_time;
-  if (FromLocalExploded(exploded, &out_time))
+  if (FromExploded(is_local, exploded, &out_time))
     return out_time;
   // This function must not fail.
   NOTREACHED();
diff --git a/base/time/time.h b/base/time/time.h
index b3c77e7..f4c2f93f 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -663,9 +663,10 @@
     return Explode(true, exploded);
   }
 
-  // Rounds this time down to the nearest day in local time. It will represent
-  // midnight on that day.
-  Time LocalMidnight() const;
+  // The following two functions round down the time to the nearest day in
+  // either UTC or local time. It will represent midnight on that day.
+  Time UTCMidnight() const { return Midnight(false); }
+  Time LocalMidnight() const { return Midnight(true); }
 
   // Converts an integer value representing Time to a class. This may be used
   // when deserializing a |Time| structure, using a value known to be
@@ -693,6 +694,10 @@
                            const Exploded& exploded,
                            Time* time) WARN_UNUSED_RESULT;
 
+  // Rounds down the time to the nearest day in either local time
+  // |is_local = true| or UTC |is_local = false|.
+  Time Midnight(bool is_local) const;
+
   // Converts a string representation of time to a Time object.
   // An example of a time string which is converted is as below:-
   // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index 2c106e5..5dc8888 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -293,6 +293,15 @@
   EXPECT_TRUE((a - b) < TimeDelta::FromSeconds(1));
 }
 
+TEST_F(TimeTest, UTCMidnight) {
+  Time::Exploded exploded;
+  Time::Now().UTCMidnight().UTCExplode(&exploded);
+  EXPECT_EQ(0, exploded.hour);
+  EXPECT_EQ(0, exploded.minute);
+  EXPECT_EQ(0, exploded.second);
+  EXPECT_EQ(0, exploded.millisecond);
+}
+
 TEST_F(TimeTest, LocalMidnight) {
   Time::Exploded exploded;
   Time::Now().LocalMidnight().LocalExplode(&exploded);
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index 997cdc8..d06814e 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -277,7 +277,7 @@
 }
 
 # Only //build/config/BUILDCONFIG.gn should reference this.
-group("executable_and_loadable_module_and_shared_library_deps") {
+group("common_deps") {
   public_deps = []
 
   if (using_sanitizer) {
@@ -301,6 +301,33 @@
   }
 }
 
+group("executable_deps") {
+  public_deps = [
+    ":common_deps",
+  ]
+  if (use_custom_libcxx && !is_component_build) {
+    public_deps += [ "//buildtools/third_party/libc++abi" ]
+  }
+}
+
+group("loadable_module_deps") {
+  public_deps = [
+    ":common_deps",
+  ]
+  if (use_custom_libcxx && !is_component_build && !(is_asan || is_ubsan_vptr)) {
+    public_deps += [ "//buildtools/third_party/libc++abi" ]
+  }
+}
+
+group("shared_library_deps") {
+  public_deps = [
+    ":common_deps",
+  ]
+  if (use_custom_libcxx && !is_component_build && !(is_asan || is_ubsan_vptr)) {
+    public_deps += [ "//buildtools/third_party/libc++abi" ]
+  }
+}
+
 # Executable configs -----------------------------------------------------------
 
 # Windows linker setup for EXEs and DLLs.
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 0c1b861f..362a7861 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -650,20 +650,20 @@
 #
 # Variables
 #   no_default_deps: If true, no standard dependencies will be added.
-foreach(target_type,
+foreach(_target_type,
         [
           "executable",
           "loadable_module",
           "shared_library",
         ]) {
-  template(target_type) {
-    target(target_type, target_name) {
+  template(_target_type) {
+    target(_target_type, target_name) {
       forward_variables_from(invoker, "*", [ "no_default_deps" ])
       if (!defined(deps)) {
         deps = []
       }
       if (!defined(invoker.no_default_deps) || !invoker.no_default_deps) {
-        deps += [ "//build/config:executable_and_loadable_module_and_shared_library_deps" ]
+        deps += [ "//build/config:${_target_type}_deps" ]
       }
     }
   }
diff --git a/build/config/c++/c++.gni b/build/config/c++/c++.gni
index 85ffde0..52edf0a 100644
--- a/build/config/c++/c++.gni
+++ b/build/config/c++/c++.gni
@@ -22,16 +22,6 @@
   # expected usage is to set use_custom_libcxx=false and
   # use_custom_libcxx_for_host=true in the passed in buildargs.
   use_custom_libcxx_for_host = false
-
-  # ASan, MSan and TSan builds need to override operator new, operator delete,
-  # and some exception handling symbols, so libc++ must be a shared library to
-  # prevent duplicate symbol errors when linking.
-  # Additionally, -fsanitize=vptr requires libc++ to be a shared library
-  # because the ubsan runtime library that implements -fsanitize=vptr calls
-  # dynamic_cast with the ABI type info classes, which won't return the right
-  # answer if each DSO has its own copy of the ABI classes.
-  libcpp_is_static = !is_component_build && !is_asan && !is_msan && !is_tsan &&
-                     !is_ubsan && !is_ubsan_security && !is_ubsan_vptr
 }
 
 use_custom_libcxx =
diff --git a/build/config/gcc/BUILD.gn b/build/config/gcc/BUILD.gn
index b6f4f5f..2fe0f4e 100644
--- a/build/config/gcc/BUILD.gn
+++ b/build/config/gcc/BUILD.gn
@@ -110,8 +110,8 @@
     ]
   } else {
     # See the rpath_for... config above for why this is necessary for component
-    # builds. Sanitizers use a custom libc++ where this is also necessary.
-    if (is_component_build || using_sanitizer || !libcpp_is_static) {
+    # builds.
+    if (is_component_build) {
       configs = [ ":rpath_for_built_shared_libraries" ]
     }
     if (current_cpu == "mipsel" || current_cpu == "mips64el") {
diff --git a/build/config/posix/BUILD.gn b/build/config/posix/BUILD.gn
index 91405fd..cf712a1d 100644
--- a/build/config/posix/BUILD.gn
+++ b/build/config/posix/BUILD.gn
@@ -35,12 +35,12 @@
   libs = []
 
   if (use_custom_libcxx) {
-    if (libcpp_is_static) {
+    if (!is_component_build) {
       # Don't leak any symbols on a static build.
-      defines += [
-        "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
-        "_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
-      ]
+      defines += [ "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" ]
+      if (!is_asan && !is_ubsan_vptr) {
+        defines += [ "_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS" ]
+      }
     }
     if (!is_clang) {
       # Gcc has a built-in abs() definition with default visibility.
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 02895ad3..5dcc9ef5 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -222,7 +222,7 @@
   if (use_custom_libcxx) {
     cflags_cc +=
         [ "-I" + rebase_path("$libcxx_prefix/include", root_build_dir) ]
-    if (libcpp_is_static) {
+    if (!is_component_build) {
       defines += [ "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" ]
     }
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 87617df..f6c0fed5 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=70
 MINOR=0
-BUILD=3503
+BUILD=3504
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 7d88c730..e883456 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -520,7 +520,7 @@
         rootView.setFitsSystemWindows(false);
 
         // Add a custom view right after the root view that stores the insets to access later.
-        // ContentViewCore needs the insets to determine the portion of the screen obscured by
+        // WebContents needs the insets to determine the portion of the screen obscured by
         // non-content displaying things such as the OSK.
         mInsetObserverView = InsetObserverView.create(this);
         rootView.addView(mInsetObserverView, 0);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 1efac69..7be4afda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -1019,7 +1019,7 @@
         if (mView == newView) return;
 
         // TODO(dtrainor): Look into changing this only if the views differ, but still parse the
-        // ContentViewCore list even if they're the same.
+        // WebContents list even if they're the same.
         updateContentOverlayVisibility(false);
 
         if (mTabVisible != tab) {
@@ -1061,8 +1061,8 @@
     private void setSizeOfUnattachedView(View view, WebContents webContents, int controlsHeight) {
         // Need to call layout() for the following View if it is not attached to the view hierarchy.
         // Calling {@code view.onSizeChanged()} is dangerous because if the View has a different
-        // size than the ContentViewCore it might think a future size update is a NOOP and not call
-        // onSizeChanged() on the ContentViewCore.
+        // size than the WebContents, it might think a future size update is a NOOP and not call
+        // onSizeChanged() on the WebContents.
         if (isAttachedToWindow(view)) return;
         Point viewportSize = getViewportSize();
         int width = viewportSize.x;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayContentDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayContentDelegate.java
index d026031..5401c19 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayContentDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayContentDelegate.java
@@ -12,9 +12,8 @@
  * TODO(mdjones): Rename to OverlayPanelContentDelegate.
  */
 public class OverlayContentDelegate {
-
     /**
-     * Called when the panel's ContentViewCore navigates in the main frame.
+     * Called when the panel's WebContents navigates in the main frame.
      * @param url The URL being navigated to.
      * @param isExternalUrl Whether the URL is different from the initially loaded URL.
      */
@@ -51,7 +50,7 @@
     }
 
     // ============================================================================================
-    // ContentViewCore related events.
+    // WebContents related events.
     // ============================================================================================
 
     /**
@@ -61,17 +60,17 @@
     public void onVisibilityChanged(boolean isVisible) {}
 
     /**
-     * Called once the ContentViewCore has been seen.
+     * Called once the WebContents has been seen.
      */
     public void onContentViewSeen() {}
 
     /**
-     * Called once the ContentViewCore has been created and set up completely.
+     * Called once the WebContents has been created and set up completely.
      */
     public void onContentViewCreated() {}
 
     /**
-     * Called once the ContentViewCore has been destroyed.
+     * Called once the WebContents has been destroyed.
      */
     public void onContentViewDestroyed() {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
index 91c7323..9acd2eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -244,7 +244,7 @@
     }
 
     /**
-     * @return True if a URL has been loaded in the panel's current ContentViewCore.
+     * @return True if a URL has been loaded in the panel's current WebContents.
      */
     public boolean isProcessingPendingNavigation() {
         return mContent != null && mContent.isProcessingPendingNavigation();
@@ -307,7 +307,6 @@
     /**
      * Set the visibility of the base page text selection controls. This will also attempt to
      * remove focus from the base page to clear any open controls.
-     * TODO(mdjones): This should be replaced with focusing the panel's ContentViewCore.
      * @param visible If the text controls are visible.
      */
     protected void setBasePageTextControlsVisibility(boolean visible) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index bd0eb36..7c1b95a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -31,7 +31,7 @@
 
 /**
  * Content container for an OverlayPanel. This class is responsible for the management of the
- * ContentViewCore displayed inside of a panel and exposes a simple API relevant to actions a
+ * WebContents displayed inside of a panel and exposes a simple API relevant to actions a
  * panel has.
  */
 public class OverlayPanelContent {
@@ -57,7 +57,7 @@
     /** The URL that was directly loaded using the {@link #loadUrl(String)} method. */
     private String mLoadedUrl;
 
-    /** Whether the ContentViewCore has started loading a URL. */
+    /** Whether the content has started loading a URL. */
     private boolean mDidStartLoadingUrl;
 
     /**
@@ -67,7 +67,7 @@
     private boolean mShouldReuseWebContents;
 
     /**
-     * Whether the ContentViewCore is processing a pending navigation.
+     * Whether the WebContents is processing a pending navigation.
      * NOTE(pedrosimonetti): This is being used to prevent redirections on the SERP to be
      * interpreted as a regular navigation, which should cause the Contextual Search Panel
      * to be promoted as a Tab. This was added to work around a server bug that has been fixed.
@@ -127,7 +127,7 @@
     // ============================================================================================
 
     // Used to intercept intent navigations.
-    // TODO(jeremycho): Consider creating a Tab with the Panel's ContentViewCore,
+    // TODO(jeremycho): Consider creating a Tab with the Panel's WebContents.
     // which would also handle functionality like long-press-to-paste.
     private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate {
         final ExternalNavigationHandler mExternalNavHandler;
@@ -223,11 +223,11 @@
     }
 
     // ============================================================================================
-    // ContentViewCore related
+    // WebContents related
     // ============================================================================================
 
     /**
-     * Load a URL; this will trigger creation of a new ContentViewCore if being loaded immediately,
+     * Load a URL; this will trigger creation of a new WebContents if being loaded immediately,
      * otherwise one is created when the panel's content becomes visible.
      * @param url The URL that should be loaded.
      * @param shouldLoadImmediately If a URL should be loaded immediately or wait until visibility
@@ -288,7 +288,7 @@
      */
     private void createNewWebContents() {
         if (mWebContents != null) {
-            // If the ContentViewCore has already been created, but never used,
+            // If the WebContents has already been created, but never used,
             // then there's no need to create a new one.
             if (!mDidStartLoadingUrl || mShouldReuseWebContents) return;
 
@@ -389,7 +389,7 @@
     // ============================================================================================
 
     /**
-     * Calls updateBrowserControlsState on the ContentViewCore.
+     * Calls updateBrowserControlsState on the WebContents.
      * @param areControlsHidden Whether the browser controls are hidden for the web contents. If
      *                          false, the web contents viewport always accounts for the controls.
      *                          Otherwise the web contents never accounts for them.
@@ -406,7 +406,7 @@
     }
 
     /**
-     * Reset the ContentViewCore's scroll position to (0, 0).
+     * Reset the content's scroll position to (0, 0).
      */
     public void resetContentViewScroll() {
         if (mWebContents != null) {
@@ -440,7 +440,7 @@
             // one in order to display an empty panel.
             if (mWebContents == null) createNewWebContents();
 
-            // NOTE(pedrosimonetti): Calling onShow() on the ContentViewCore will cause the page
+            // NOTE(pedrosimonetti): Calling onShow() on the WebContents will cause the page
             // to be rendered. This has a side effect of causing the page to be included in
             // your Web History (if enabled). For this reason, onShow() should only be called
             // when we know for sure the page will be seen by the user.
@@ -462,14 +462,14 @@
     }
 
     /**
-     * @return true if the ContentViewCore is visible on the page.
+     * @return true if the content is visible on the page.
      */
     public boolean isContentShowing() {
         return mIsContentViewShowing;
     }
 
     // ============================================================================================
-    // Methods for managing this panel's ContentViewCore.
+    // Methods for managing this panel's WebContents.
     // ============================================================================================
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index 57009f8..c4453e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -276,7 +276,7 @@
     @Override
     protected void onClosed(@StateChangeReason int reason) {
         // Must be called before destroying Content because unseen visits should be removed from
-        // history, and if the Content gets destroyed there won't be a ContentViewCore to do that.
+        // history, and if the Content gets destroyed there won't be a Webcontents to do that.
         mManagementDelegate.onCloseContextualSearch(reason);
 
         setProgressBarCompletion(0);
@@ -449,7 +449,7 @@
     // ============================================================================================
 
     /**
-     * Notify the panel that the ContentViewCore was seen.
+     * Notify the panel that the content was seen.
      */
     public void setWasSearchContentViewSeen() {
         mPanelMetrics.setWasSearchContentViewSeen();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
index a6debcb8..0143e0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
@@ -25,7 +25,7 @@
 /**
  * The {@link GestureEventFilter} used when an overlay panel is being shown. It filters
  * events that happen in the Content View area and propagates them to the appropriate
- * ContentViewCore.
+ * WebContents.
  */
 public class OverlayPanelEventFilter extends GestureEventFilter {
     /**
@@ -289,7 +289,7 @@
             MotionEvent syntheticActionDownEvent = copyEvent(e, MotionEvent.ACTION_DOWN);
 
             // Store the synthetic ACTION_DOWN coordinates to prevent unwanted taps from
-            // happening. See {@link OverlayPanelEventFilter#propagateEventToContentViewCore}.
+            // happening. See {@link OverlayPanelEventFilter#propagateEventToContent}.
             mWasActionDownEventSynthetic = true;
             mSyntheticActionDownX = syntheticActionDownEvent.getX();
             mSyntheticActionDownY =
@@ -347,15 +347,15 @@
             }
             super.onTouchEventInternal(e);
         } else if (target == EventTarget.CONTENT_VIEW) {
-            propagateEventToContentViewCore(e);
+            propagateEventToContent(e);
         }
     }
 
     /**
-     * Propagates the given {@link MotionEvent} to the {@link ContentViewCore}.
+     * Propagates the given {@link MotionEvent} to the {@link WebContents}.
      * @param e The {@link MotionEvent} to be propagated.
      */
-    protected void propagateEventToContentViewCore(MotionEvent e) {
+    protected void propagateEventToContent(MotionEvent e) {
         MotionEvent event = e;
         int action = event.getActionMasked();
         boolean isSyntheticEvent = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java
index 6b4d3575..8069902 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuItemDelegate.java
@@ -31,8 +31,7 @@
     void onDestroy();
 
     /**
-     * @return Whether or not this context menu is being shown for an incognito
-     *     {@link ContentViewCore}.
+     * @return Whether or not this context menu is being shown for an incognito content.
      */
     boolean isIncognito();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 389ef57..23b546e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -188,7 +188,8 @@
      */
     public interface ContextualSearchTabPromotionDelegate {
         /**
-         * Called when {@code searchContentViewCore} should be promoted to a {@link Tab}.
+         * Called when {@link WebContents} for contextual search should be promoted to a {@link
+         * Tab}.
          * @param searchUrl The Search URL to be promoted.
          */
         void createContextualSearchTab(String searchUrl);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
index 500e6855..a5c6e66e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -436,8 +436,7 @@
     }
 
     /**
-     * @return The Base Page's {@link WebContents}, or {@code null} if there is no current tab or
-     *         the current tab has no {@link ContentViewCore}.
+     * @return The Base Page's {@link WebContents}, or {@code null} if there is no current tab.
      */
     @Nullable
     WebContents getBaseWebContents() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index c3b4445..1b6df395 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -187,7 +187,7 @@
 
     /**
      * Should be called whenever the Tab's WebContents may have changed. Removes hooks from the
-     * existing WebContents, if necessary, and then adds hooks for the new ContentViewCore.
+     * existing WebContents, if necessary, and then adds hooks for the new WebContents.
      * @param tab The current tab.
      */
     private void updateHooksForTab(Tab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
index 72ca9eb..43c50cf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
@@ -259,7 +259,6 @@
         };
         contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
 
-        // getWebContents() will return null if contentViewCore has been destroyed
         if (webContents != null && !webContents.isDestroyed()) webContents.exitFullscreen();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
index e327b631..602c15d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
@@ -136,7 +136,7 @@
     void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad);
 
     /**
-     * Called when a context menu is shown for a {@link ContentViewCore} owned by a {@link Tab}.
+     * Called when a context menu is shown for a {@link WebContents} owned by a {@link Tab}.
      * @param tab  The notifying {@link Tab}.
      * @param menu The {@link ContextMenu} that is being shown.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java
index d4f3766..f1806d47 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java
@@ -77,9 +77,9 @@
         }
 
         @Override
-        protected void propagateEventToContentViewCore(MotionEvent e) {
+        protected void propagateEventToContent(MotionEvent e) {
             mEventPropagatedToContent = MotionEvent.obtain(e);
-            super.propagateEventToContentViewCore(e);
+            super.propagateEventToContent(e);
             mEventPropagatedToContent.recycle();
         }
 
@@ -103,7 +103,7 @@
     // --------------------------------------------------------------------------------------------
 
     /**
-     * Mocks an OverlayPanel, so it doesn't create ContentViewCore or animations.
+     * Mocks an OverlayPanel, so it doesn't create WebContents or animations.
      */
     private final class MockOverlayPanel extends OverlayPanel {
         private boolean mWasTapDetectedOnPanel = false;
@@ -119,7 +119,7 @@
         }
 
         /**
-         * Override creation and destruction of the ContentViewCore as they rely on native methods.
+         * Override creation and destruction of the WebContents as they rely on native methods.
          */
         private class MockOverlayPanelContent extends OverlayPanelContent {
             public MockOverlayPanelContent() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManagerTest.java
index a9e3922..ca69176 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelManagerTest.java
@@ -37,7 +37,7 @@
     // --------------------------------------------------------------------------------------------
 
     /**
-     * Mocks the ContextualSearchPanel, so it doesn't create ContentViewCore.
+     * Mocks the ContextualSearchPanel, so it doesn't create WebContents.
      */
     private static class MockOverlayPanel extends OverlayPanel {
         private @PanelPriority int mPriority;
@@ -101,7 +101,7 @@
         }
 
         /**
-         * Override creation and destruction of the ContentViewCore as they rely on native methods.
+         * Override creation and destruction of the WebContents as they rely on native methods.
          */
         private static class MockOverlayPanelContent extends OverlayPanelContent {
             public MockOverlayPanelContent() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
index b098c819..ce5e5f5b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
@@ -112,7 +112,7 @@
     // --------------------------------------------------------------------------------------------
 
     /**
-     * Selection controller that mocks out anything to do with a ContentViewCore.
+     * Selection controller that mocks out anything to do with a WebContents.
      */
     private static class MockCSSelectionController extends ContextualSearchSelectionController {
         private StubbedSelectionPopupController mPopupController;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
index bfbc8ef..9df4f3df 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
@@ -32,7 +32,7 @@
 import org.chromium.ui.base.WindowAndroid;
 
 /**
- * Test the select popup and how it interacts with another ContentViewCore.
+ * Test the select popup and how it interacts with another WebContents.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@@ -106,7 +106,7 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         // The popup should still be shown.
-        Assert.assertTrue("The select popup got hidden by destroying of unrelated ContentViewCore.",
+        Assert.assertTrue("The select popup got hidden by destroying of unrelated WebContents.",
                 mActivityTestRule.getActivity()
                         .getActivityTab()
                         .getWebContents()
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
index f203d72..c97df66 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
@@ -141,7 +141,7 @@
 for VR Browser testing. Examples include:
 
 * Retrieving HTML test files
-* Accessing the WebContents and ContentViewCore of whatever tab the test started
+* Accessing the WebContents of whatever tab the test started
   on, which is where the vast majority of testing takes place
 * Waiting on JavaScript test steps and running arbitrary JavaScript code
 
diff --git a/chrome/app/md_extensions_strings.grdp b/chrome/app/md_extensions_strings.grdp
index f091fb4..1987ee3d4 100644
--- a/chrome/app/md_extensions_strings.grdp
+++ b/chrome/app/md_extensions_strings.grdp
@@ -87,6 +87,10 @@
       =1 {&lt;1 line not shown&gt;}
       other {&lt;$1 lines not shown&gt;}}
   </message>
+  <!-- TODO(devlin): Unify this with IDS_SETTINGS_EDIT. -->
+  <message name="IDS_MD_EXTENSIONS_HOST_PERMISSIONS_EDIT" desc="The label of the button used to edit an extension's access to a given website.">
+    Edit
+  </message>
   <message name="IDS_MD_EXTENSIONS_ITEM_ERRORS" desc="The label of the button to bring the user to the page showing an extension's errors.">
     Errors
   </message>
@@ -177,9 +181,6 @@
   <message name="IDS_MD_EXTENSIONS_ITEM_PERMISSIONS_EMPTY" desc="The text to indicate that an extension does not have any special permissions.">
     This extension requires no special permissions
   </message>
-  <message name="IDS_MD_EXTENSIONS_ITEM_REMOVE" desc="The label of the button to remove an extension.">
-    Remove
-  </message>
   <message name="IDS_MD_EXTENSIONS_ITEM_REMOVE_EXTENSION" desc="The label on the button to remove an extension.">
     Remove extension
   </message>
@@ -264,6 +265,9 @@
   <message name="IDS_MD_EXTENSIONS_APPS_TITLE" desc="The text displayed in the sidebar to go to the apps section of the page.">
     Chrome Apps
   </message>
+  <message name="IDS_MD_EXTENSIONS_REMOVE" desc="The generic label for a button to remove an entry.">
+    Remove
+  </message>
   <message name="IDS_MD_EXTENSIONS_RUNTIME_HOSTS_DIALOG_TITLE" desc="The title of the dialog used to allow a user to add a new site that an extension is allowed to run on.">
     Add a site
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 00d178b..7048fc8e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1197,6 +1197,10 @@
     "previews/previews_infobar_delegate.h",
     "previews/previews_infobar_tab_helper.cc",
     "previews/previews_infobar_tab_helper.h",
+    "previews/previews_lite_page_decider.cc",
+    "previews/previews_lite_page_decider.h",
+    "previews/previews_lite_page_navigation_throttle.cc",
+    "previews/previews_lite_page_navigation_throttle.h",
     "previews/previews_service.cc",
     "previews/previews_service.h",
     "previews/previews_service_factory.cc",
@@ -1263,6 +1267,10 @@
     "profiles/profile_shortcut_manager_win.h",
     "profiles/profiles_state.cc",
     "profiles/profiles_state.h",
+    "profiles/renderer_updater.cc",
+    "profiles/renderer_updater.h",
+    "profiles/renderer_updater_factory.cc",
+    "profiles/renderer_updater_factory.h",
     "profiles/sql_init_error_message_ids.cc",
     "profiles/sql_init_error_message_ids.h",
     "profiles/storage_partition_descriptor.h",
@@ -1398,10 +1406,6 @@
     "signin/signin_status_metrics_provider_chromeos.h",
     "signin/signin_tracker_factory.cc",
     "signin/signin_tracker_factory.h",
-    "signin/signin_updater.cc",
-    "signin/signin_updater.h",
-    "signin/signin_updater_factory.cc",
-    "signin/signin_updater_factory.h",
     "signin/signin_util.cc",
     "signin/signin_util.h",
     "signin/unified_consent_helper.cc",
diff --git a/chrome/browser/android/bottombar/overlay_panel_content.cc b/chrome/browser/android/bottombar/overlay_panel_content.cc
index 9bcd7736..78867da 100644
--- a/chrome/browser/android/bottombar/overlay_panel_content.cc
+++ b/chrome/browser/android/bottombar/overlay_panel_content.cc
@@ -109,9 +109,8 @@
 
   DCHECK(web_contents);
 
-  // NOTE(pedrosimonetti): Takes ownership of the WebContents associated
-  // with the ContentViewCore. This is to make sure that the WebContens
-  // and the Compositor are in the same process.
+  // NOTE(pedrosimonetti): Takes ownership of the WebContents. This is to make
+  // sure that the WebContens and the Compositor are in the same process.
   // TODO(pedrosimonetti): Confirm with dtrainor@ if the comment above
   // is accurate.
   web_contents_.reset(web_contents);
diff --git a/chrome/browser/android/bottombar/overlay_panel_content.h b/chrome/browser/android/bottombar/overlay_panel_content.h
index 7afaff6..4b851d6 100644
--- a/chrome/browser/android/bottombar/overlay_panel_content.h
+++ b/chrome/browser/android/bottombar/overlay_panel_content.h
@@ -47,8 +47,7 @@
       const base::android::JavaParamRef<jstring>& search_url,
       jlong search_start_time_ms);
 
-  // Takes ownership of the WebContents associated with the given
-  // |ContentViewCore| which holds the panel content.
+  // Takes ownership of the WebContents which holds the panel content.
   void SetWebContents(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/download/download_controller.h b/chrome/browser/android/download/download_controller.h
index 7662077..24159ce 100644
--- a/chrome/browser/android/download/download_controller.h
+++ b/chrome/browser/android/download/download_controller.h
@@ -101,9 +101,6 @@
   // The download item contains dangerous file types.
   void OnDangerousDownload(download::DownloadItem* item);
 
-  base::android::ScopedJavaLocalRef<jobject> GetContentViewCoreFromWebContents(
-      content::WebContents* web_contents);
-
   // Helper methods to start android download on UI thread.
   void StartAndroidDownload(
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index ac4e1be3..f9429e57 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -82,9 +82,12 @@
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/prerender/prerender_message_filter.h"
 #include "chrome/browser/prerender/prerender_util.h"
+#include "chrome/browser/previews/previews_lite_page_decider.h"
 #include "chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_io_data.h"
+#include "chrome/browser/profiles/renderer_updater.h"
+#include "chrome/browser/profiles/renderer_updater_factory.h"
 #include "chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.h"
 #include "chrome/browser/profiling_host/profiling_process_host.h"
 #include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
@@ -102,8 +105,6 @@
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/signin/signin_updater.h"
-#include "chrome/browser/signin/signin_updater_factory.h"
 #include "chrome/browser/speech/chrome_speech_recognition_manager_delegate.h"
 #include "chrome/browser/speech/tts_controller.h"
 #include "chrome/browser/speech/tts_message_filter.h"
@@ -829,32 +830,6 @@
 #endif  // defined(OS_ANDROID)
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-// By default, JavaScript, images and autoplay are enabled in guest content.
-void GetGuestViewDefaultContentSettingRules(
-    bool incognito,
-    RendererContentSettingRules* rules) {
-  rules->image_rules.push_back(ContentSettingPatternSource(
-      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
-      base::Value::FromUniquePtrValue(
-          content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
-      std::string(), incognito));
-
-  rules->script_rules.push_back(ContentSettingPatternSource(
-      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
-      base::Value::FromUniquePtrValue(
-          content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
-      std::string(), incognito));
-  rules->autoplay_rules.push_back(ContentSettingPatternSource(
-      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
-      base::Value::FromUniquePtrValue(
-          content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
-      std::string(), incognito));
-  rules->client_hints_rules.push_back(ContentSettingPatternSource(
-      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
-      base::Value::FromUniquePtrValue(
-          content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
-      std::string(), incognito));
-}
 
 AppLoadedInTabSource ClassifyAppLoadedInTabSource(
     const GURL& opener_url,
@@ -1297,43 +1272,19 @@
                                                   profile->GetPath()));
 #endif
 
-  bool is_incognito_process = profile->IsOffTheRecord();
-
 #if defined(OS_ANDROID)
   // Data cannot be persisted if the profile is off the record.
   host->AddFilter(
-      new cdm::CdmMessageFilterAndroid(!is_incognito_process, false));
+      new cdm::CdmMessageFilterAndroid(!profile->IsOffTheRecord(), false));
 #endif
 
   Profile* original_profile = profile->GetOriginalProfile();
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfile(original_profile);
-  bool is_signed_in = signin_manager->IsAuthenticated();
-  // Create the sign-in updater service that is responsible to update the
-  // sign-in state for all renderers.
-  SigninUpdaterFactory::GetForProfile(original_profile);
-
-  chrome::mojom::RendererConfigurationAssociatedPtr rc_interface;
-  host->GetChannel()->GetRemoteAssociatedInterface(&rc_interface);
-  rc_interface->SetInitialConfiguration(is_incognito_process);
-  rc_interface->SetIsSignedIn(is_signed_in);
+  RendererUpdaterFactory::GetForProfile(original_profile)
+      ->InitializeRenderer(host);
 
   for (size_t i = 0; i < extra_parts_.size(); ++i)
     extra_parts_[i]->RenderProcessWillLaunch(host);
 
-  RendererContentSettingRules rules;
-  if (host->IsForGuestsOnly()) {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-    GetGuestViewDefaultContentSettingRules(is_incognito_process, &rules);
-#else
-    NOTREACHED();
-#endif
-  } else {
-    GetRendererContentSettingRules(
-        HostContentSettingsMapFactory::GetForProfile(profile), &rules);
-  }
-  rc_interface->SetContentSettingRules(rules);
-
   service_manager::mojom::ServicePtr service;
   *service_request = mojo::MakeRequest(&service);
   service_manager::mojom::PIDReceiverPtr pid_receiver;
@@ -3991,6 +3942,11 @@
     throttles.push_back(std::move(new_tab_page_throttle));
 #endif
 
+  std::unique_ptr<content::NavigationThrottle> previews_lite_page_throttle =
+      PreviewsLitePageDecider::MaybeCreateThrottleFor(handle);
+  if (previews_lite_page_throttle)
+    throttles.push_back(std::move(previews_lite_page_throttle));
+
   return throttles;
 }
 
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
index bad48493..fdc1881 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -33,6 +33,30 @@
 constexpr char kArcIMEProxyExtensionName[] =
     "org.chromium.arc.inputmethod.proxy";
 
+void SwitchImeToCallback(const std::string& ime_id,
+                         const std::string& component_id,
+                         bool success) {
+  if (success)
+    return;
+
+  // TODO(yhanana): We should prevent InputMethodManager from changing current
+  // input method until this callback is called with true and once it's done the
+  // IME switching code below can be removed.
+  LOG(ERROR) << "Switch the active IME to \"" << ime_id << "\"(component_id=\""
+             << component_id << "\") failed";
+  auto* imm = chromeos::input_method::InputMethodManager::Get();
+  if (imm && imm->GetActiveIMEState()) {
+    for (const auto& id : imm->GetActiveIMEState()->GetActiveInputMethodIds()) {
+      if (!chromeos::extension_ime_util::IsArcIME(id)) {
+        imm->GetActiveIMEState()->ChangeInputMethod(id,
+                                                    false /* show_message */);
+        return;
+      }
+    }
+  }
+  NOTREACHED() << "There is no enabled non-ARC IME.";
+}
+
 // Singleton factory for ArcInputMethodManagerService
 class ArcInputMethodManagerServiceFactory
     : public internal::ArcBrowserContextKeyedServiceFactoryBase<
@@ -54,6 +78,42 @@
 
 }  // namespace
 
+class ArcInputMethodManagerService::ArcProxyInputMethodObserver
+    : public input_method::InputMethodEngineBase::Observer {
+ public:
+  ArcProxyInputMethodObserver() = default;
+  ~ArcProxyInputMethodObserver() override = default;
+
+  // input_method::InputMethodEngineBase::Observer overrides:
+  // TODO(yhanada): Implement below methods to forward those events to ARC.
+  void OnActivate(const std::string& engine_id) override {}
+  void OnFocus(
+      const ui::IMEEngineHandlerInterface::InputContext& context) override {}
+  void OnBlur(int context_id) override {}
+  void OnKeyEvent(
+      const std::string& engine_id,
+      const input_method::InputMethodEngineBase::KeyboardEvent& event,
+      ui::IMEEngineHandlerInterface::KeyEventDoneCallback key_data) override {}
+  void OnReset(const std::string& engine_id) override {}
+  void OnDeactivated(const std::string& engine_id) override {}
+  void OnCompositionBoundsChanged(
+      const std::vector<gfx::Rect>& bounds) override {}
+  bool IsInterestedInKeyEvent() const override { return false; }
+  void OnSurroundingTextChanged(const std::string& engine_id,
+                                const std::string& text,
+                                int cursor_pos,
+                                int anchor_pos,
+                                int offset_pos) override {}
+  void OnInputContextUpdate(
+      const ui::IMEEngineHandlerInterface::InputContext& context) override {}
+  void OnCandidateClicked(
+      const std::string& component_id,
+      int candidate_id,
+      input_method::InputMethodEngineBase::MouseButtonEvent button) override {}
+  void OnMenuItemActivated(const std::string& component_id,
+                           const std::string& menu_id) override {}
+};
+
 // static
 ArcInputMethodManagerService*
 ArcInputMethodManagerService::GetForBrowserContext(
@@ -82,6 +142,9 @@
   auto* imm = chromeos::input_method::InputMethodManager::Get();
   imm->AddObserver(this);
   imm->AddImeMenuObserver(this);
+
+  proxy_ime_engine_->Initialize(std::make_unique<ArcProxyInputMethodObserver>(),
+                                proxy_ime_extension_id_.c_str(), profile_);
 }
 
 ArcInputMethodManagerService::~ArcInputMethodManagerService() {
@@ -221,16 +284,7 @@
   if (!ceiu::IsArcIME(ime_id))
     component_id = kChromeOSIMEIdInArcContainer;
   imm_bridge_->SendSwitchImeTo(
-      component_id, base::BindOnce(
-                        [](const std::string& ime_id,
-                           const std::string& component_id, bool success) {
-                          if (!success) {
-                            LOG(ERROR) << "Switch the active IME to \""
-                                       << ime_id << "\"(component_id=\""
-                                       << component_id << "\") failed";
-                          }
-                        },
-                        ime_id, component_id));
+      component_id, base::BindOnce(&SwitchImeToCallback, ime_id, component_id));
 }
 
 chromeos::input_method::InputMethodDescriptor
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
index b7174a37..2aecf3f 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
@@ -65,6 +65,8 @@
                           bool show_message) override;
 
  private:
+  class ArcProxyInputMethodObserver;
+
   void EnableIme(const std::string& ime_id, bool enable);
   void SwitchImeTo(const std::string& ime_id);
   chromeos::input_method::InputMethodDescriptor BuildInputMethodDescriptor(
diff --git a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc
index c2b4b77d0..4462e8b 100644
--- a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc
+++ b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.cc
@@ -2,7 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "usage_time_limit_processor.h"
+#include "chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
 
 #include "base/strings/string_number_conversions.h"
 
@@ -42,22 +46,6 @@
   return b.is_null() || a < b;
 }
 
-// The UTC midnight for a timestamp.
-base::Time UTCMidnight(base::Time time) {
-  base::Time::Exploded exploded;
-  time.UTCExplode(&exploded);
-  exploded.hour = 0;
-  exploded.minute = 0;
-  exploded.second = 0;
-  exploded.millisecond = 0;
-  base::Time utc_midnight;
-  if (base::Time::FromUTCExploded(exploded, &utc_midnight))
-    return utc_midnight;
-
-  LOG(ERROR) << "Malformed exploded time.";
-  return time;
-}
-
 // Shifts the current weekday, if the value is positive shifts forward and if
 // negative backwards.
 Weekday WeekdayShift(Weekday current_day, int shift) {
@@ -754,12 +742,12 @@
 
 base::Time UsageTimeLimitProcessor::LocalMidnight(base::Time time) {
   base::TimeDelta time_zone_offset = GetTimeZoneOffset(time);
-  return UTCMidnight(time + time_zone_offset) - time_zone_offset;
+  return (time + time_zone_offset).UTCMidnight() - time_zone_offset;
 }
 
 Weekday UsageTimeLimitProcessor::GetCurrentWeekday() {
   base::TimeDelta time_zone_offset = GetTimeZoneOffset(current_time);
-  base::TimeDelta midnight_delta = current_time - UTCMidnight(current_time);
+  base::TimeDelta midnight_delta = current_time - current_time.UTCMidnight();
   // Shift in days due to the timezone.
   int time_zone_shift = 0;
   if (midnight_delta + time_zone_offset < base::TimeDelta::FromHours(0)) {
diff --git a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h
index 9a12f1f..ae04f5a4 100644
--- a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h
+++ b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h
@@ -9,6 +9,9 @@
 #ifndef CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_USAGE_TIME_LIMIT_PROCESSOR_H_
 #define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_USAGE_TIME_LIMIT_PROCESSOR_H_
 
+#include <memory>
+#include <unordered_map>
+
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -55,7 +58,7 @@
 
 class TimeWindowLimit {
  public:
-  TimeWindowLimit(const base::Value& window_limit_dict);
+  explicit TimeWindowLimit(const base::Value& window_limit_dict);
   ~TimeWindowLimit();
   TimeWindowLimit(TimeWindowLimit&&);
   TimeWindowLimit& operator=(TimeWindowLimit&&);
@@ -75,7 +78,7 @@
 
 class TimeUsageLimit {
  public:
-  TimeUsageLimit(const base::Value& usage_limit_dict);
+  explicit TimeUsageLimit(const base::Value& usage_limit_dict);
   ~TimeUsageLimit();
   TimeUsageLimit(TimeUsageLimit&&);
   TimeUsageLimit& operator=(TimeUsageLimit&&);
@@ -91,7 +94,7 @@
  public:
   enum class Action { kLock, kUnlock };
 
-  Override(const base::Value& override_dict);
+  explicit Override(const base::Value& override_dict);
   ~Override();
   Override(Override&&);
   Override& operator=(Override&&);
diff --git a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc
index 738dc02..b1b37445 100644
--- a/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/usage_time_limit_processor_unittest.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "usage_time_limit_processor.h"
+#include "chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h"
 
 #include <memory>
+#include <string>
+#include <utility>
 
 #include "base/values.h"
 #include "chromeos/settings/timezone_settings.h"
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 8bd859a9..8672870 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -215,6 +215,15 @@
                       TestCase("imageOpenGalleryOpenDrive").EnableDriveFs()));
 
 WRAPPED_INSTANTIATE_TEST_CASE_P(
+    ZipFiles, /* zip_files.js */
+    FilesAppBrowserTest,
+    ::testing::Values(ZipCase("zipFileOpenDownloads").InGuestMode(),
+                      ZipCase("zipFileOpenDownloads"),
+                      ZipCase("zipFileOpenDrive").EnableDriveFs(),
+                      ZipCase("zipFileOpenDrive"),
+                      ZipCase("zipFileOpenUsb")));
+
+WRAPPED_INSTANTIATE_TEST_CASE_P(
     CreateNewFolder, /* create_new_folder.js */
     FilesAppBrowserTest,
     ::testing::Values(TestCase("selectCreateFolderDownloads").InGuestMode(),
diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc
index 4a6e7589..4ee61db 100644
--- a/chrome/browser/chromeos/login/login_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_browsertest.cc
@@ -230,8 +230,7 @@
 }
 
 // Verifies the cursor is hidden at startup on login screen.
-// Flaky, see https://crbug.com/865520.
-IN_PROC_BROWSER_TEST_F(LoginCursorTest, DISABLED_CursorHidden) {
+IN_PROC_BROWSER_TEST_F(LoginCursorTest, CursorHidden) {
   // Login screen needs to be shown explicitly when running test.
   ShowLoginWizard(OobeScreen::SCREEN_SPECIAL_LOGIN);
 
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen.h b/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
index f5680fa..8da543f 100644
--- a/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
+++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
@@ -53,6 +53,17 @@
 
   // If the response is not a valid JSON, return false.
   // If the response contains no app, return false;
+  // Value output, in true, is a list containing:
+  // 1. name: the title of the app.
+  // 2. package_name
+  // 3. Possibly an Icon URL.
+  // Parses an input string that looks somewhat like this:
+  // [{"title_" : "title of app",
+  //   "packageName_" : "com.package.name",
+  //  "icon_": {"url_": {"privateDoNotAccessOrElseSafeUrlWrappedValue_": "http://icon_url.com/url"}}},
+  //  {"title_" : "title of second app",
+  //   "packageName_": "second package name.",
+  //  }]
   bool ParseResponse(const std::string& response, base::Value* output);
 
   RecommendAppsScreenView* view_;
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index 4a49016c..1f842c4 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -19,6 +19,7 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/aura/window.h"
 #include "ui/base/accelerators/accelerator.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/views/controls/webview/web_dialog_view.h"
@@ -41,9 +42,18 @@
     : controller_(controller),
       size_(gfx::Size(kGaiaDialogWidth, kGaiaDialogHeight)),
       display_observer_(this),
-      tablet_mode_observer_(this) {
+      tablet_mode_observer_(this),
+      keyboard_observer_(this) {
   display_observer_.Add(display::Screen::GetScreen());
   tablet_mode_observer_.Add(TabletModeClient::Get());
+  // TODO(mash): Support virtual keyboard under MASH. There is no
+  // KeyboardController in the browser process under MASH.
+  if (features::IsAshInBrowserProcess()) {
+    keyboard_observer_.Add(keyboard::KeyboardController::Get());
+  } else {
+    NOTIMPLEMENTED();
+  }
+
   accel_map_[ui::Accelerator(
       ui::VKEY_S, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] = kAppLaunchBailout;
   accel_map_[ui::Accelerator(ui::VKEY_ESCAPE, 0)] = kCancel;
@@ -268,4 +278,11 @@
   return true;
 }
 
+void OobeUIDialogDelegate::OnKeyboardVisibilityStateChanged(bool is_visible) {
+  if (!dialog_widget_)
+    return;
+
+  UpdateSizeAndPosition(size_.width(), size_.height());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
index f45bad5..e76af93 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h"
 #include "components/web_modal/web_contents_modal_dialog_host.h"
 #include "ui/display/display_observer.h"
+#include "ui/keyboard/keyboard_controller_observer.h"
 #include "ui/web_dialogs/web_dialog_delegate.h"
 
 class TabletModeClient;
@@ -53,7 +54,8 @@
                              public TabletModeClientObserver,
                              public ui::WebDialogDelegate,
                              public ChromeWebModalDialogManagerDelegate,
-                             public web_modal::WebContentsModalDialogHost {
+                             public web_modal::WebContentsModalDialogHost,
+                             public keyboard::KeyboardControllerObserver {
  public:
   explicit OobeUIDialogDelegate(base::WeakPtr<LoginDisplayHostMojo> controller);
   ~OobeUIDialogDelegate() override;
@@ -112,6 +114,9 @@
   std::vector<ui::Accelerator> GetAccelerators() override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
+  // keyboard::KeyboardControllerObserver:
+  void OnKeyboardVisibilityStateChanged(bool is_visible) override;
+
   base::WeakPtr<LoginDisplayHostMojo> controller_;
 
   // This is owned by the underlying native widget.
@@ -124,6 +129,8 @@
   ScopedObserver<display::Screen, display::DisplayObserver> display_observer_;
   ScopedObserver<TabletModeClient, TabletModeClientObserver>
       tablet_mode_observer_;
+  ScopedObserver<keyboard::KeyboardController, KeyboardControllerObserver>
+      keyboard_observer_;
 
   std::map<ui::Accelerator, std::string> accel_map_;
 
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 2d68735..c967998 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -187,6 +187,17 @@
   return true;
 }
 
+bool ChromeVirtualKeyboardDelegate::SetHitTestBounds(
+    const std::vector<gfx::Rect>& bounds) {
+  keyboard::KeyboardController* controller =
+      keyboard::KeyboardController::Get();
+  if (!controller->enabled())
+    return false;
+
+  controller->SetHitTestBounds(bounds);
+  return true;
+}
+
 keyboard::ContainerType
 ChromeVirtualKeyboardDelegate::ConvertKeyboardModeToContainerType(
     int mode) const {
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h
index 7a79ca4..bdb28de4 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h
@@ -52,6 +52,7 @@
       const api::virtual_keyboard_private::Bounds& rect) override;
   bool SetRequestedKeyboardState(int state_enum) override;
   bool SetOccludedBounds(const std::vector<gfx::Rect>& bounds) override;
+  bool SetHitTestBounds(const std::vector<gfx::Rect>& bounds) override;
 
   api::virtual_keyboard::FeatureRestrictions RestrictFeatures(
       const api::virtual_keyboard::RestrictFeatures::Params& params) override;
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index 28fcf0e..3e0ce50 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -31,6 +31,7 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/browsertest_util.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/notification_types.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_features.h"
@@ -800,6 +801,81 @@
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
+// Tests the cross-origin access of content scripts.
+IN_PROC_BROWSER_TEST_F(ContentScriptApiTest, CrossOriginXhr) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(
+      R"({
+           "name": "Cross Origin XHR",
+           "description": "Content script cross origin XHR",
+           "version": "0.1",
+           "manifest_version": 2,
+           "content_scripts": [{
+             "matches": ["*://example.com:*/*"],
+             "js": ["script.js"]
+           }],
+           "permissions": ["http://chromium.org:*/*"]
+         })");
+  constexpr char kScript[] =
+      R"(document.getElementById('go-button').addEventListener(
+             'click',
+             function() {
+               fetch('%s').then((response) => {
+                 domAutomationController.send('Fetched');
+               }).catch((e) => {
+                 domAutomationController.send('Not Fetched');
+               });
+             });)";
+
+  const GURL cross_origin_url =
+      embedded_test_server()->GetURL("chromium.org", "/extensions/body1.html");
+  test_dir.WriteFile(
+      FILE_PATH_LITERAL("script.js"),
+      base::StringPrintf(kScript, cross_origin_url.spec().c_str()));
+
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  const GURL example_com_url = embedded_test_server()->GetURL(
+      "example.com", "/extensions/page_with_button.html");
+  ui_test_utils::NavigateToURL(browser(), example_com_url);
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  constexpr char kClickButtonScript[] =
+      "document.getElementById('go-button').click();";
+
+  {
+    // Since the extension has permission to access chromium.org, it should be
+    // able to make a cross-origin fetch.
+    content::DOMMessageQueue message_queue;
+    content::ExecuteScriptAsync(web_contents, kClickButtonScript);
+    std::string message;
+    EXPECT_TRUE(message_queue.WaitForMessage(&message));
+    EXPECT_EQ(R"("Fetched")", message);
+  }
+
+  extension_service()->DisableExtension(extension->id(),
+                                        disable_reason::DISABLE_USER_ACTION);
+  EXPECT_FALSE(ExtensionRegistry::Get(profile())->enabled_extensions().GetByID(
+      extension->id()));
+
+  {
+    // When the extension is unloaded, the content script remains injected
+    // (since we don't currently have any means of "uninjecting" JS). However,
+    // it should no longer have the extra cross-origin permissions, so a
+    // cross-origin fetch should fail.
+    // https://crbug.com/843381.
+    content::DOMMessageQueue message_queue;
+    content::ExecuteScriptAsync(web_contents, kClickButtonScript);
+    std::string message;
+    EXPECT_TRUE(message_queue.WaitForMessage(&message));
+    EXPECT_EQ(R"("Not Fetched")", message);
+  }
+}
+
 // Test fixture which sets a custom NTP Page.
 // TODO(karandeepb): Similar logic to set up a custom NTP is used elsewhere as
 // well. Abstract this away into a reusable test fixture class.
diff --git a/chrome/browser/history/history_utils.cc b/chrome/browser/history/history_utils.cc
index 8bf6a91..4cfbd05e 100644
--- a/chrome/browser/history/history_utils.cc
+++ b/chrome/browser/history/history_utils.cc
@@ -18,6 +18,7 @@
   // Right now, URLs like about:version are not registered in the history.
   if (url.SchemeIs(url::kJavaScriptScheme) ||
       url.SchemeIs(url::kAboutScheme) ||
+      url.SchemeIs(url::kContentScheme) ||
       url.SchemeIs(content::kChromeDevToolsScheme) ||
       url.SchemeIs(content::kChromeUIScheme) ||
       url.SchemeIs(content::kViewSourceScheme) ||
diff --git a/chrome/browser/notifications/arc_application_notifier_controller.cc b/chrome/browser/notifications/arc_application_notifier_controller.cc
index 1fe52a09..1ed467e 100644
--- a/chrome/browser/notifications/arc_application_notifier_controller.cc
+++ b/chrome/browser/notifications/arc_application_notifier_controller.cc
@@ -82,12 +82,12 @@
       continue;
 
     // Load icons for notifier.
-    std::unique_ptr<ArcAppIcon> icon(
-        new ArcAppIcon(profile, app_id,
+    std::unique_ptr<ArcAppIcon> icon =
+        std::make_unique<ArcAppIcon>(profile, app_id,
                        // ARC icon is available only for 48x48 dips.
                        kArcAppIconSizeInDp,
                        // The life time of icon must shorter than |this|.
-                       this));
+                       this);
     // Apply icon now to set the default image.
     OnIconUpdated(icon.get());
 
diff --git a/chrome/browser/previews/previews_browsertest.cc b/chrome/browser/previews/previews_browsertest.cc
index 319367c..bb35cc9 100644
--- a/chrome/browser/previews/previews_browsertest.cc
+++ b/chrome/browser/previews/previews_browsertest.cc
@@ -327,3 +327,37 @@
   EXPECT_TRUE(noscript_js_requested());
   EXPECT_FALSE(noscript_css_requested());
 }
+
+// This test class enables LitePageServerPreviews.
+class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
+ public:
+  PreviewsLitePageServerBrowserTest() {}
+
+  ~PreviewsLitePageServerBrowserTest() override {}
+
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatures(
+        {previews::features::kPreviews,
+         previews::features::kLitePageServerPreviews},
+        {});
+    PreviewsBrowserTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Previews InfoBar (which these tests triggers) does not work on Mac.
+// See crbug.com/782322 for detail.
+// Also occasional flakes on win7 (crbug.com/789542).
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+#define MAYBE_LitePagePreviewsEnabled LitePagePreviewsEnabled
+#else
+#define MAYBE_LitePagePreviewsEnabled DISABLED_LitePagePreviewsEnabled
+#endif
+
+// Makes sure that nothing crashes.
+IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
+                       MAYBE_LitePagePreviewsEnabled) {
+  ui_test_utils::NavigateToURL(browser(), https_url());
+}
diff --git a/chrome/browser/previews/previews_lite_page_decider.cc b/chrome/browser/previews/previews_lite_page_decider.cc
new file mode 100644
index 0000000..cc9370ec
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_decider.cc
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/previews/previews_lite_page_decider.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
+#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
+#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
+#include "chrome/browser/previews/previews_service.h"
+#include "chrome/browser/previews/previews_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/previews/core/previews_experiments.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/web_contents.h"
+
+PreviewsLitePageDecider::PreviewsLitePageDecider() = default;
+
+PreviewsLitePageDecider::~PreviewsLitePageDecider() = default;
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+PreviewsLitePageDecider::MaybeCreateThrottleFor(
+    content::NavigationHandle* handle) {
+  DCHECK(handle);
+  DCHECK(handle->GetWebContents());
+  DCHECK(handle->GetWebContents()->GetBrowserContext());
+
+  content::BrowserContext* browser_context =
+      handle->GetWebContents()->GetBrowserContext();
+
+  PreviewsService* previews_service = PreviewsServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(browser_context));
+  if (!previews_service)
+    return nullptr;
+  DCHECK(!browser_context->IsOffTheRecord());
+
+  PreviewsLitePageDecider* decider =
+      previews_service->previews_lite_page_decider();
+  DCHECK(decider);
+
+  // TODO(crbug/842233): Replace this logic with PreviewsState.
+  bool drp_enabled = decider->IsDataSaverEnabled(handle);
+  bool preview_enabled = previews::params::ArePreviewsAllowed() &&
+                         previews::params::IsLitePageServerPreviewsEnabled();
+
+  if (drp_enabled && preview_enabled) {
+    return PreviewsLitePageNavigationThrottle::MaybeCreateThrottleFor(handle);
+  }
+  return nullptr;
+}
+
+void PreviewsLitePageDecider::SetRetryAt(base::TimeTicks retry_at) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (retry_at > retry_at_)
+    retry_at_ = retry_at;
+}
+
+bool PreviewsLitePageDecider::IsDataSaverEnabled(
+    content::NavigationHandle* handle) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(handle);
+  DCHECK(handle->GetWebContents());
+  DCHECK(handle->GetWebContents()->GetBrowserContext());
+
+  DataReductionProxyChromeSettings* drp_settings =
+      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+          handle->GetWebContents()->GetBrowserContext());
+  DCHECK(drp_settings);
+  return drp_settings->IsDataReductionProxyEnabled();
+}
+
+bool PreviewsLitePageDecider::IsServerUnavailable(base::TimeTicks now) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!retry_at_.has_value())
+    return false;
+  bool server_loadshedding = retry_at_ > now;
+  if (!server_loadshedding)
+    retry_at_.reset();
+  return server_loadshedding;
+}
diff --git a/chrome/browser/previews/previews_lite_page_decider.h b/chrome/browser/previews/previews_lite_page_decider.h
new file mode 100644
index 0000000..ec92037
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_decider.h
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
+#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
+
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+
+namespace content {
+class NavigationHandle;
+class NavigationThrottle;
+}  // namespace content
+
+// This class manages the state for triggering the Lite Page Server Preview.
+// This class ensures that the feature is enabled and the
+// current Profile is not incognito before handing off the real legwork of the
+// triggering decision to |PreviewsLitePageNavigationThrottle|.
+class PreviewsLitePageDecider {
+ public:
+  PreviewsLitePageDecider();
+  virtual ~PreviewsLitePageDecider();
+
+  // Checks if the feature is enabled and if so, returns a
+  // |PreviewsLitePageNavigationThrottle| that handles the rest of the decision
+  // making.
+  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
+      content::NavigationHandle* handle);
+
+  // Used to notify that the Previews Server should not be sent anymore requests
+  // until after the given time.
+  void SetRetryAt(base::TimeTicks retry_at);
+
+ protected:
+  // Virtual for testing.
+  virtual bool IsDataSaverEnabled(content::NavigationHandle* handle) const;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestServerUnavailable);
+
+  // Returns true if a Preview should not be triggered because the server is
+  // unavailable.
+  bool IsServerUnavailable(base::TimeTicks now);
+
+  // The time after which it is ok to send the server more preview requests.
+  base::Optional<base::TimeTicks> retry_at_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageDecider);
+};
+
+#endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
diff --git a/chrome/browser/previews/previews_lite_page_decider_unittest.cc b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
new file mode 100644
index 0000000..ae6b142
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/previews/previews_lite_page_decider.h"
+
+#include <memory>
+
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class PreviewsLitePageDeciderTest : public testing::Test {
+ protected:
+  PreviewsLitePageDeciderTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) {
+  const base::TimeTicks kNow = base::TimeTicks::Now();
+  struct TestCase {
+    base::TimeTicks retry_at;
+    base::TimeTicks now;
+    bool want_is_unavailable;
+  };
+  const TestCase kTestCases[]{
+      {
+          kNow - base::TimeDelta::FromMinutes(1), kNow, false,
+      },
+      {
+          kNow + base::TimeDelta::FromMinutes(1), kNow, true,
+      },
+  };
+
+  for (const TestCase& test_case : kTestCases) {
+    std::unique_ptr<PreviewsLitePageDecider> decider =
+        std::make_unique<PreviewsLitePageDecider>();
+    decider->SetRetryAt(test_case.retry_at);
+    EXPECT_EQ(decider->IsServerUnavailable(test_case.now),
+              test_case.want_is_unavailable);
+  }
+}
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
new file mode 100644
index 0000000..36368f42
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
+
+PreviewsLitePageNavigationThrottle::PreviewsLitePageNavigationThrottle(
+    content::NavigationHandle* handle)
+    : content::NavigationThrottle(handle) {
+  DCHECK(handle);
+}
+
+PreviewsLitePageNavigationThrottle::~PreviewsLitePageNavigationThrottle() =
+    default;
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+PreviewsLitePageNavigationThrottle::MaybeCreateThrottleFor(
+    content::NavigationHandle* handle) {
+  DCHECK(handle);
+  return nullptr;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+PreviewsLitePageNavigationThrottle::WillStartRequest() {
+  return content::NavigationThrottle::PROCEED;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+PreviewsLitePageNavigationThrottle::WillRedirectRequest() {
+  return content::NavigationThrottle::PROCEED;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+PreviewsLitePageNavigationThrottle::WillFailRequest() {
+  return content::NavigationThrottle::PROCEED;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+PreviewsLitePageNavigationThrottle::WillProcessResponse() {
+  return content::NavigationThrottle::PROCEED;
+}
+
+const char* PreviewsLitePageNavigationThrottle::GetNameForLogging() {
+  return "PreviewsLitePageNavigationThrottle";
+}
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.h b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
new file mode 100644
index 0000000..48af4f8
--- /dev/null
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
@@ -0,0 +1,40 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
+#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
+
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+
+// This class does the actual decision making about when to serve a Lite Page
+// Server Preview, and the legwork to trigger the Preview navigation. When a
+// Preview is triggered, it will cancel the incoming navigation and PostTask a
+// new one to the Previews Server.
+class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle {
+ public:
+  explicit PreviewsLitePageNavigationThrottle(
+      content::NavigationHandle* handle);
+
+  ~PreviewsLitePageNavigationThrottle() override;
+
+  // If a Preview is triggered, this method returns an instance of |this|.
+  // Otherwise it will return a nullptr.
+  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
+      content::NavigationHandle* handle);
+
+ private:
+  // content::NavigationThrottle implementation:
+  content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
+  content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
+      override;
+  content::NavigationThrottle::ThrottleCheckResult WillFailRequest() override;
+  content::NavigationThrottle::ThrottleCheckResult WillProcessResponse()
+      override;
+  const char* GetNameForLogging() override;
+
+  DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageNavigationThrottle);
+};
+
+#endif  // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/previews/previews_service.cc b/chrome/browser/previews/previews_service.cc
index eeabc215..6876b12 100644
--- a/chrome/browser/previews/previews_service.cc
+++ b/chrome/browser/previews/previews_service.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
+#include "chrome/browser/previews/previews_lite_page_decider.h"
 #include "chrome/common/chrome_constants.h"
 #include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
 #include "components/blacklist/opt_out_blacklist/opt_out_store.h"
@@ -94,7 +95,8 @@
 
 }  // namespace
 
-PreviewsService::PreviewsService() {
+PreviewsService::PreviewsService()
+    : previews_lite_page_decider_(std::make_unique<PreviewsLitePageDecider>()) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
diff --git a/chrome/browser/previews/previews_service.h b/chrome/browser/previews/previews_service.h
index 08e6dd1..1c33f0b8 100644
--- a/chrome/browser/previews/previews_service.h
+++ b/chrome/browser/previews/previews_service.h
@@ -25,6 +25,8 @@
 class PreviewsUIService;
 }
 
+class PreviewsLitePageDecider;
+
 // Keyed service that owns a previews::PreviewsUIService. PreviewsService lives
 // on the UI thread.
 class PreviewsService : public KeyedService {
@@ -43,14 +45,23 @@
       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
       const base::FilePath& profile_path);
 
+  // The previews UI thread service.
   previews::PreviewsUIService* previews_ui_service() {
     return previews_ui_service_.get();
   }
 
+  // The server lite page preview decider.
+  PreviewsLitePageDecider* previews_lite_page_decider() {
+    return previews_lite_page_decider_.get();
+  }
+
  private:
   // The previews UI thread service.
   std::unique_ptr<previews::PreviewsUIService> previews_ui_service_;
 
+  // The server lite page preview decider.
+  std::unique_ptr<PreviewsLitePageDecider> previews_lite_page_decider_;
+
   DISALLOW_COPY_AND_ASSIGN(PreviewsService);
 };
 
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index f2e36b0..13803085 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -57,6 +57,7 @@
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/prerender/prerender_message_filter.h"
 #include "chrome/browser/profiles/gaia_info_update_service_factory.h"
+#include "chrome/browser/profiles/renderer_updater_factory.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
 #include "chrome/browser/search/suggestions/suggestions_service_factory.h"
 #include "chrome/browser/search_engines/template_url_fetcher_factory.h"
@@ -280,6 +281,7 @@
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
   cloud_print::PrivetNotificationServiceFactory::GetInstance();
 #endif
+  RendererUpdaterFactory::GetInstance();
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   SupervisedUserServiceFactory::GetInstance();
 #endif
diff --git a/chrome/browser/profiles/renderer_updater.cc b/chrome/browser/profiles/renderer_updater.cc
new file mode 100644
index 0000000..e851926
--- /dev/null
+++ b/chrome/browser/profiles/renderer_updater.cc
@@ -0,0 +1,133 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/profiles/renderer_updater.h"
+
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/renderer_configuration.mojom.h"
+#include "components/content_settings/core/browser/content_settings_utils.h"
+#include "components/content_settings/core/common/content_settings_utils.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "extensions/buildflags/buildflags.h"
+
+namespace {
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+
+// By default, JavaScript, images and autoplay are enabled in guest content.
+void GetGuestViewDefaultContentSettingRules(
+    bool incognito,
+    RendererContentSettingRules* rules) {
+  rules->image_rules.push_back(ContentSettingPatternSource(
+      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
+      base::Value::FromUniquePtrValue(
+          content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
+      std::string(), incognito));
+
+  rules->script_rules.push_back(ContentSettingPatternSource(
+      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
+      base::Value::FromUniquePtrValue(
+          content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
+      std::string(), incognito));
+  rules->autoplay_rules.push_back(ContentSettingPatternSource(
+      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
+      base::Value::FromUniquePtrValue(
+          content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
+      std::string(), incognito));
+  rules->client_hints_rules.push_back(ContentSettingPatternSource(
+      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
+      base::Value::FromUniquePtrValue(
+          content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
+      std::string(), incognito));
+}
+
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+}  // namespace
+
+RendererUpdater::RendererUpdater(Profile* profile) : profile_(profile) {
+  signin_manager_ = SigninManagerFactory::GetForProfile(profile_);
+  signin_manager_->AddObserver(this);
+}
+
+RendererUpdater::~RendererUpdater() {
+  DCHECK(!signin_manager_);
+}
+
+void RendererUpdater::Shutdown() {
+  signin_manager_->RemoveObserver(this);
+  signin_manager_ = nullptr;
+}
+
+void RendererUpdater::InitializeRenderer(
+    content::RenderProcessHost* render_process_host) {
+  auto renderer_configuration = GetRendererConfiguration(render_process_host);
+
+  Profile* profile =
+      Profile::FromBrowserContext(render_process_host->GetBrowserContext());
+  bool is_incognito_process = profile->IsOffTheRecord();
+
+  renderer_configuration->SetInitialConfiguration(is_incognito_process);
+  renderer_configuration->SetIsSignedIn(signin_manager_->IsAuthenticated());
+
+  RendererContentSettingRules rules;
+  if (render_process_host->IsForGuestsOnly()) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+    GetGuestViewDefaultContentSettingRules(is_incognito_process, &rules);
+#else
+    NOTREACHED();
+#endif
+  } else {
+    content_settings::GetRendererContentSettingRules(
+        HostContentSettingsMapFactory::GetForProfile(profile), &rules);
+  }
+  renderer_configuration->SetContentSettingRules(rules);
+}
+
+void RendererUpdater::UpdateRenderersSignin() {
+  bool is_signed_in = signin_manager_->IsAuthenticated();
+  auto renderer_configurations = GetRendererConfigurations();
+  for (auto& renderer_configuration : renderer_configurations)
+    renderer_configuration->SetIsSignedIn(is_signed_in);
+}
+
+std::vector<chrome::mojom::RendererConfigurationAssociatedPtr>
+RendererUpdater::GetRendererConfigurations() {
+  std::vector<chrome::mojom::RendererConfigurationAssociatedPtr> rv;
+  for (content::RenderProcessHost::iterator it(
+           content::RenderProcessHost::AllHostsIterator());
+       !it.IsAtEnd(); it.Advance()) {
+    if (it.GetCurrentValue()->GetBrowserContext() == profile_ ||
+        it.GetCurrentValue()->GetBrowserContext() ==
+            profile_->GetOffTheRecordProfile()) {
+      auto renderer_configuration =
+          GetRendererConfiguration(it.GetCurrentValue());
+      if (renderer_configuration)
+        rv.push_back(std::move(renderer_configuration));
+    }
+  }
+  return rv;
+}
+
+chrome::mojom::RendererConfigurationAssociatedPtr
+RendererUpdater::GetRendererConfiguration(
+    content::RenderProcessHost* render_process_host) {
+  IPC::ChannelProxy* channel = render_process_host->GetChannel();
+  if (!channel)
+    return nullptr;
+
+  chrome::mojom::RendererConfigurationAssociatedPtr renderer_configuration;
+  channel->GetRemoteAssociatedInterface(&renderer_configuration);
+  return renderer_configuration;
+}
+
+void RendererUpdater::GoogleSigninSucceeded(const AccountInfo& account_info) {
+  UpdateRenderersSignin();
+}
+
+void RendererUpdater::GoogleSignedOut(const AccountInfo& account_info) {
+  UpdateRenderersSignin();
+}
diff --git a/chrome/browser/profiles/renderer_updater.h b/chrome/browser/profiles/renderer_updater.h
new file mode 100644
index 0000000..3c2f11b7
--- /dev/null
+++ b/chrome/browser/profiles/renderer_updater.h
@@ -0,0 +1,52 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PROFILES_RENDERER_UPDATER_H_
+#define CHROME_BROWSER_PROFILES_RENDERER_UPDATER_H_
+
+#include "base/macros.h"
+#include "chrome/common/renderer_configuration.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+
+class Profile;
+
+namespace content {
+class RenderProcessHost;
+}
+
+// The RendererUpdater is responsible for updating renderers about state change.
+class RendererUpdater : public KeyedService,
+                        public SigninManagerBase::Observer {
+ public:
+  explicit RendererUpdater(Profile* profile);
+  ~RendererUpdater() override;
+
+  // KeyedService:
+  void Shutdown() override;
+
+  // Initialize a newly-started renderer process.
+  void InitializeRenderer(content::RenderProcessHost* render_process_host);
+
+ private:
+  // Update the signin state for all renderers due to a signin notification.
+  void UpdateRenderersSignin();
+
+  std::vector<chrome::mojom::RendererConfigurationAssociatedPtr>
+  GetRendererConfigurations();
+
+  chrome::mojom::RendererConfigurationAssociatedPtr GetRendererConfiguration(
+      content::RenderProcessHost* render_process_host);
+
+  // SigninManagerBase::Observer:
+  void GoogleSigninSucceeded(const AccountInfo& account_info) override;
+  void GoogleSignedOut(const AccountInfo& account_info) override;
+
+  Profile* profile_;
+  SigninManagerBase* signin_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(RendererUpdater);
+};
+
+#endif  // CHROME_BROWSER_PROFILES_RENDERER_UPDATER_H_
diff --git a/chrome/browser/profiles/renderer_updater_factory.cc b/chrome/browser/profiles/renderer_updater_factory.cc
new file mode 100644
index 0000000..3de9b05
--- /dev/null
+++ b/chrome/browser/profiles/renderer_updater_factory.cc
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/profiles/renderer_updater_factory.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/renderer_updater.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+RendererUpdaterFactory::RendererUpdaterFactory()
+    : BrowserContextKeyedServiceFactory(
+          "RendererUpdater",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(SigninManagerFactory::GetInstance());
+}
+
+RendererUpdaterFactory::~RendererUpdaterFactory() {}
+
+// static
+RendererUpdaterFactory* RendererUpdaterFactory::GetInstance() {
+  return base::Singleton<RendererUpdaterFactory>::get();
+}
+
+// static
+RendererUpdater* RendererUpdaterFactory::GetForProfile(Profile* profile) {
+  return static_cast<RendererUpdater*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+KeyedService* RendererUpdaterFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new RendererUpdater(static_cast<Profile*>(context));
+}
+
+bool RendererUpdaterFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
diff --git a/chrome/browser/profiles/renderer_updater_factory.h b/chrome/browser/profiles/renderer_updater_factory.h
new file mode 100644
index 0000000..345f937a
--- /dev/null
+++ b/chrome/browser/profiles/renderer_updater_factory.h
@@ -0,0 +1,40 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PROFILES_RENDERER_UPDATER_FACTORY_H_
+#define CHROME_BROWSER_PROFILES_RENDERER_UPDATER_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+class RendererUpdater;
+
+// Singleton that creates/deletes RendererUpdater as new Profiles are
+// created/shutdown.
+class RendererUpdaterFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  // Returns an instance of the RendererUpdaterFactory singleton.
+  static RendererUpdaterFactory* GetInstance();
+
+  // Returns the instance of RendererUpdater for the passed |profile|.
+  static RendererUpdater* GetForProfile(Profile* profile);
+
+ protected:
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* profile) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+
+ private:
+  friend struct base::DefaultSingletonTraits<RendererUpdaterFactory>;
+
+  RendererUpdaterFactory();
+  ~RendererUpdaterFactory() override;
+
+  DISALLOW_COPY_AND_ASSIGN(RendererUpdaterFactory);
+};
+
+#endif  // CHROME_BROWSER_PROFILES_RENDERER_UPDATER_FACTORY_H_
diff --git a/chrome/browser/resources/accessibility/accessibility.css b/chrome/browser/resources/accessibility/accessibility.css
index 440ebefd8..63359c2 100644
--- a/chrome/browser/resources/accessibility/accessibility.css
+++ b/chrome/browser/resources/accessibility/accessibility.css
@@ -58,7 +58,9 @@
 }
 
 a {
-  margin: 10px 20px;
+  margin-top: 10px;
+  margin-bottom: 10px;
+  margin-left: 20px;
 }
 
 label {
diff --git a/chrome/browser/resources/accessibility/accessibility.html b/chrome/browser/resources/accessibility/accessibility.html
index 551ac063..1b7543d 100644
--- a/chrome/browser/resources/accessibility/accessibility.html
+++ b/chrome/browser/resources/accessibility/accessibility.html
@@ -142,7 +142,7 @@
   <div id="native_ui">
     <a is="action-link" tabindex="0" role="button" id="showNativeUI">
       show accessibility tree
-    </a>
+    </a> after <input type="number" value="0" id="native_ui_delay"> ms
   </div>
 
   <h2>Pages:</h2>
diff --git a/chrome/browser/resources/accessibility/accessibility.js b/chrome/browser/resources/accessibility/accessibility.js
index 0df9bc0d..102788ad 100644
--- a/chrome/browser/resources/accessibility/accessibility.js
+++ b/chrome/browser/resources/accessibility/accessibility.js
@@ -69,7 +69,10 @@
 
     var showNativeUI = $('showNativeUI');
     showNativeUI.addEventListener('click', function() {
-      chrome.send('requestNativeUITree');
+      var delay = $('native_ui_delay').value;
+      setTimeout(function() {
+        chrome.send('requestNativeUITree');
+      }, delay);
     });
   }
 
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js
index 0ea9704..11cd393 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js
@@ -89,7 +89,7 @@
       if (loadTimeData.getBoolean('useNewWallpaperPicker')) {
         // On the new picker, do not show the image until |cropImageToFitGrid_|
         // is done.
-        imageEl.hidden = true;
+        imageEl.style.visibility = 'hidden';
         imageEl.setAttribute('aria-hidden', 'true');
         this.setAttribute('aria-label', this.dataItem.ariaLabel);
         this.tabIndex = 0;
@@ -487,25 +487,35 @@
         newHeight = GRID_SIZE_CSS;
         newWidth = GRID_SIZE_CSS;
       } else {
+        var ROUND_CORNER_RADIUS_PX = 4;
+        var getClipPathString = (top, left, round) => {
+          return 'inset(' + top + 'px ' + left + 'px round ' + round + 'px)';
+        };
         var aspectRatio = image.offsetWidth / image.offsetHeight;
         if (aspectRatio > 1) {
           newHeight = GRID_SIZE_CSS;
           newWidth = GRID_SIZE_CSS * aspectRatio;
           // The center portion is visible, and the overflow area on the left
           // and right will be hidden.
-          image.style.left = (GRID_SIZE_CSS - newWidth) / 2 + 'px';
+          var leftDistance = (newWidth - GRID_SIZE_CSS) / 2;
+          image.style.left = -leftDistance + 'px';
+          image.style.clipPath =
+              getClipPathString(0, leftDistance, ROUND_CORNER_RADIUS_PX);
         } else {
           newWidth = GRID_SIZE_CSS;
           newHeight = GRID_SIZE_CSS / aspectRatio;
           // The center portion is visible, and the overflow area on the top and
           // buttom will be hidden.
-          image.style.top = (GRID_SIZE_CSS - newHeight) / 2 + 'px';
+          var topDistance = (newHeight - GRID_SIZE_CSS) / 2;
+          image.style.top = -topDistance + 'px';
+          image.style.clipPath =
+              getClipPathString(topDistance, 0, ROUND_CORNER_RADIUS_PX);
         }
       }
 
       image.style.height = newHeight + 'px';
       image.style.width = newWidth + 'px';
-      image.hidden = false;
+      image.style.visibility = 'visible';
     },
 
     /**
diff --git a/chrome/browser/resources/local_ntp/custom_backgrounds.css b/chrome/browser/resources/local_ntp/custom_backgrounds.css
index 3bb7435..b330887 100644
--- a/chrome/browser/resources/local_ntp/custom_backgrounds.css
+++ b/chrome/browser/resources/local_ntp/custom_backgrounds.css
@@ -24,12 +24,8 @@
   background-color: rgba(255, 255, 255, .1);
 }
 
-.bg-option:focus {
-  outline: 0;
-}
-
-.using-keyboard-nav .bg-option:focus {
-  outline: auto 5px -webkit-focus-ring-color;
+.using-mouse-nav .bg-option:focus {
+  outline: none;
 }
 
 #edit-bg-gear {
@@ -144,6 +140,7 @@
   background-color: rgba(255, 255, 255, .75);
 }
 
+/* The width here should match that used in customBackgrounds.getNextTile. */
 #bg-sel-menu {
   background-color: #fff;
   border: none;
@@ -162,6 +159,7 @@
   z-index: 10000;
 }
 
+/* The width here should match that used in customBackgrounds.getNextTile. */
 @media (max-width: 520px) {
    #bg-sel-menu {
      width: 352px;
@@ -185,6 +183,15 @@
   display: none;
 }
 
+.using-mouse-nav :focus {
+  outline: none;
+}
+
+html[dir=rtl] #bg-sel-tiles {
+  padding-left: auto;
+  padding-right: 8px;
+}
+
 html[dir=rtl] #bg-sel-tiles {
   padding-left: 0;
   padding-right: 8px;
diff --git a/chrome/browser/resources/local_ntp/custom_backgrounds.js b/chrome/browser/resources/local_ntp/custom_backgrounds.js
index 747b276..970bcc1 100644
--- a/chrome/browser/resources/local_ntp/custom_backgrounds.js
+++ b/chrome/browser/resources/local_ntp/custom_backgrounds.js
@@ -14,9 +14,14 @@
  */
 customBackgrounds.KEYCODES = {
   BACKSPACE: 8,
+  DOWN: 40,
   ENTER: 13,
   ESC: 27,
+  LEFT: 37,
+  RIGHT: 39,
+  SPACE: 32,
   TAB: 9,
+  UP: 38,
 };
 
 /**
@@ -35,6 +40,7 @@
   DONE: 'bg-sel-footer-done',
   EDIT_BG: 'edit-bg',
   EDIT_BG_DIALOG: 'edit-bg-dialog',
+  EDIT_BG_GEAR: 'edit-bg-gear',
   MSG_BOX: 'message-box',
   MSG_BOX_MSG: 'message-box-message',
   MSG_BOX_LINK: 'message-box-link',
@@ -69,8 +75,8 @@
   FLOAT_UP: 'float-up',
   HAS_LINK: 'has-link',
   IMAGE_DIALOG: 'is-img-sel',
-  KEYBOARD_NAV: 'using-keyboard-nav',
   PLUS_ICON: 'plus-icon',
+  MOUSE_NAV: 'using-mouse-nav',
   SELECTED_BORDER: 'selected-border',
   SELECTED_CHECK: 'selected-check',
   SELECTED_CIRCLE: 'selected-circle',
@@ -89,6 +95,18 @@
   IMAGE_UPLOAD: 2,
 };
 
+/**
+ * Enum for background option menu entries, in the order they appear in the UI.
+ * @enum {int}
+ * @const
+ */
+customBackgrounds.MENU_ENTRIES = {
+  GOOGLE_PHOTOS: 0,
+  CHROME_BACKGROUNDS: 1,
+  UPLOAD_IMAGE: 2,
+  RESTORE_DEFAULT: 3,
+};
+
 customBackgrounds.CUSTOM_BACKGROUND_OVERLAY =
     'linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2))';
 
@@ -169,6 +187,7 @@
  */
 customBackgrounds.resetSelectionDialog = function() {
   $(customBackgrounds.IDS.TILES).scrollTop = 0;
+  $(customBackgrounds.IDS.DONE).tabIndex = -1;
   var tileContainer = $(customBackgrounds.IDS.TILES);
   while (tileContainer.firstChild) {
     tileContainer.removeChild(tileContainer.firstChild);
@@ -234,6 +253,38 @@
   return tile;
 };
 
+/* Get the next tile when the arrow keys are used to navigate the grid.
+ * Returns null if the tile doesn't exist.
+ * @param {int} deltaX Change in the x direction.
+ * @param {int} deltaY Change in the y direction.
+ * @param {string} current Number of the current tile.
+ */
+customBackgrounds.getNextTile = function(deltaX, deltaY, current) {
+  var tilesWide = 3;
+
+  // Browser window can only fit two columns. Should match #bg-sel-menu width.
+  if ($(customBackgrounds.IDS.MENU).offsetWidth < 516) {
+    tilesWide = 2;
+  }
+
+  // Browser window can only fit one column. Should match @media (max-width:
+  // 520px) #bg-sel-menu width.
+  if ($(customBackgrounds.IDS.MENU).offsetWidth < 352) {
+    tilesWide = 1;
+  }
+
+  var targetNum = parseInt(current) + deltaX + (deltaY * tilesWide);
+
+  if ($(customBackgrounds.IDS.MENU)
+          .classList.contains(customBackgrounds.CLASSES.IMAGE_DIALOG)) {
+    return $('img_tile_' + targetNum);
+  }
+  if ($(customBackgrounds.IDS.MENU)
+          .classList.contains(customBackgrounds.CLASSES.COLLECTION_DIALOG)) {
+    return $('coll_tile_' + targetNum);
+  }
+};
+
 /**
  * Show dialog for selecting either a Chrome background collection or Google
  * Photo album. Draw data from either coll or albums.
@@ -284,7 +335,8 @@
     }
     tile.classList.add(customBackgrounds.CLASSES.COLLECTION_TILE);
     tile.id = 'coll_tile_' + i;
-    tile.tabIndex = 0;
+    tile.dataset.tile_num = i;
+    tile.tabIndex = -1;
 
     var title = document.createElement('div');
     title.classList.add(customBackgrounds.CLASSES.COLLECTION_TITLE);
@@ -351,17 +403,33 @@
 
     tile.onclick = tileInteraction;
     tile.onkeyup = function(event) {
+      event.preventDefault();
+      event.stopPropagation();
+
       if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
         tileInteraction(event);
       }
+
+      var target = null;
+      if (event.keyCode == customBackgrounds.KEYCODES.LEFT) {
+        target = customBackgrounds.getNextTile(-1, 0, this.dataset.tile_num);
+      } else if (event.keyCode == customBackgrounds.KEYCODES.UP) {
+        target = customBackgrounds.getNextTile(0, -1, this.dataset.tile_num);
+      } else if (event.keyCode == customBackgrounds.KEYCODES.RIGHT) {
+        target = customBackgrounds.getNextTile(1, 0, this.dataset.tile_num);
+      } else if (event.keyCode == customBackgrounds.KEYCODES.DOWN) {
+        target = customBackgrounds.getNextTile(0, 1, this.dataset.tile_num);
+      }
+      if (target) {
+        target.focus();
+      }
     };
 
     tile.appendChild(title);
     tileContainer.appendChild(tile);
   }
 
-  $(customBackgrounds.IDS.DONE).tabIndex = -1;
-  $('coll_tile_0').focus();
+  $(customBackgrounds.IDS.TILES).focus();
 };
 
 /**
@@ -444,6 +512,7 @@
                imageData[i].attributions[1] :
                '');
       tile.dataset.attributionActionUrl = imageData[i].attributionActionUrl;
+      tile.setAttribute('aria-label', imageData[i].attributions[0]);
     } else {
       tile.style.backgroundImage =
           'url(' + imageData[i].thumbnailPhotoUrl + ')';
@@ -451,10 +520,12 @@
       tile.dataset.attributionLine1 = '';
       tile.dataset.attributionLine2 = '';
       tile.dataset.attributionActionUrl = '';
+      tile.setAttribute('aria-label', configData.translatedStrings.photoLabel);
     }
 
     tile.id = 'img_tile_' + i;
-    tile.tabIndex = 0;
+    tile.dataset.tile_num = i;
+    tile.tabIndex = -1;
 
     var tileInteraction = function(event) {
       var tile = event.target;
@@ -465,6 +536,8 @@
 
       customBackgrounds.applySelectedState(tile);
 
+      $(customBackgrounds.IDS.DONE).tabIndex = 0;
+
       // Turn toggle off when an image is selected.
       $(customBackgrounds.IDS.REFRESH_TOGGLE).children[0].checked = false;
       $(customBackgrounds.IDS.DONE)
@@ -482,17 +555,31 @@
       }
     };
     tile.onkeyup = function(event) {
+      event.preventDefault();
+      event.stopPropagation();
+
       if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
         tileInteraction(event);
-        doneButton.focus();
+      }
+
+      var target = null;
+      if (event.keyCode == customBackgrounds.KEYCODES.LEFT) {
+        target = customBackgrounds.getNextTile(-1, 0, this.dataset.tile_num);
+      } else if (event.keyCode == customBackgrounds.KEYCODES.UP) {
+        target = customBackgrounds.getNextTile(0, -1, this.dataset.tile_num);
+      } else if (event.keyCode == customBackgrounds.KEYCODES.RIGHT) {
+        target = customBackgrounds.getNextTile(1, 0, this.dataset.tile_num);
+      } else if (event.keyCode == customBackgrounds.KEYCODES.DOWN) {
+        target = customBackgrounds.getNextTile(0, 1, this.dataset.tile_num);
+      }
+      if (target) {
+        target.focus();
       }
     };
 
     tileContainer.appendChild(tile);
   }
-
-  $(customBackgrounds.IDS.DONE).tabIndex = 0;
-  $('img_tile_0').focus();
+  $(customBackgrounds.IDS.TILES).focus();
 };
 
 /**
@@ -532,7 +619,32 @@
 /* Close dialog when an image is selected via the file picker. */
 customBackgrounds.closeCustomizationDialog = function() {
   $(customBackgrounds.IDS.EDIT_BG_DIALOG).close();
-  $(customBackgrounds.IDS.EDIT_BG).focus();
+};
+
+/*
+ * Get the next visible option. There are times when various combinations of
+ * options are hidden.
+ * @param {int} current_index Index of the option the key press occurred on.
+ * @param {int} deltaY Direction to search in, -1 for up, 1 for down.
+ */
+customBackgrounds.getNextOption = function(current_index, deltaY) {
+  // Create array corresponding to the menu. Important that this is in the same
+  // order as the MENU_ENTRIES enum, so we can index into it.
+  var entries = [];
+  entries.push($(customBackgrounds.IDS.CONNECT_GOOGLE_PHOTOS));
+  entries.push($(customBackgrounds.IDS.DEFAULT_WALLPAPERS));
+  entries.push($(customBackgrounds.IDS.UPLOAD_IMAGE));
+  entries.push($(customBackgrounds.IDS.RESTORE_DEFAULT));
+
+  var idx = current_index;
+  do {
+    idx = idx + deltaY;
+    if (idx === -1)
+      idx = 3;
+    if (idx === 4)
+      idx = 0;
+  } while (entries[idx].hidden);
+  return entries[idx];
 };
 
 /* Hide custom background options based on the network state
@@ -580,6 +692,16 @@
     customBackgrounds.networkStateChanged(false);
   }
 
+  $(customBackgrounds.IDS.BACK)
+      .setAttribute('aria-label', configData.translatedStrings.backLabel);
+  $(customBackgrounds.IDS.CANCEL)
+      .setAttribute('aria-label', configData.translatedStrings.selectionCancel);
+  $(customBackgrounds.IDS.DONE)
+      .setAttribute('aria-label', configData.translatedStrings.selectionDone);
+  $(customBackgrounds.IDS.EDIT_BG_GEAR)
+      .setAttribute(
+          'aria-label', configData.translatedStrings.customizeThisPage);
+
   // Interactions with the "Upload an image" option.
   var uploadImageInteraction = function(event) {
     window.chrome.embeddedSearch.newTabPage.selectLocalBackgroundImage();
@@ -590,6 +712,18 @@
     if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
       uploadImageInteraction(event);
     }
+
+    // Handle arrow key navigation.
+    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.UPLOAD_IMAGE, -1)
+          .focus();
+    }
+    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.UPLOAD_IMAGE, 1)
+          .focus();
+    }
   };
 
   // Edit gear icon interaction events.
@@ -597,16 +731,24 @@
     editDialog.showModal();
   };
   $(customBackgrounds.IDS.EDIT_BG).onclick = function(event) {
-    editDialog.classList.remove(customBackgrounds.CLASSES.KEYBOARD_NAV);
+    editDialog.classList.add(customBackgrounds.CLASSES.MOUSE_NAV);
     editBackgroundInteraction(event);
   };
   $(customBackgrounds.IDS.EDIT_BG).onkeyup = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
-      editDialog.classList.add(customBackgrounds.CLASSES.KEYBOARD_NAV);
+    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
+        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
+      editDialog.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
       editBackgroundInteraction(event);
+      $(customBackgrounds.IDS.CONNECT_GOOGLE_PHOTOS).focus();
     }
   };
 
+  // Handle focus state for the gear icon.
+  $(customBackgrounds.IDS.EDIT_BG).onmousedown = function() {
+    $(customBackgrounds.IDS.EDIT_BG)
+        .classList.add(customBackgrounds.CLASSES.MOUSE_NAV);
+  };
+
   // Interactions to close the customization option dialog.
   var editDialogInteraction = function(event) {
     editDialog.close();
@@ -616,10 +758,18 @@
       editDialogInteraction(event);
   };
   editDialog.onkeyup = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ESC ||
-        event.keyCode === customBackgrounds.KEYCODES.BACKSPACE) {
+    if (event.keyCode === customBackgrounds.KEYCODES.ESC) {
       editDialogInteraction(event);
     }
+
+    // If keyboard navigation is attempted, remove mouse-only mode.
+    if (event.keyCode === customBackgrounds.KEYCODES.TAB ||
+        event.keyCode === customBackgrounds.KEYCODES.LEFT ||
+        event.keyCode === customBackgrounds.KEYCODES.UP ||
+        event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
+        event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+      editDialog.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
+    }
   };
 
   // Interactions with the "Restore default background" option.
@@ -632,6 +782,18 @@
     if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
       restoreDefaultInteraction(event);
     }
+
+    // Handle arrow key navigation.
+    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.RESTORE_DEFAULT, -1)
+          .focus();
+    }
+    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.RESTORE_DEFAULT, 1)
+          .focus();
+    }
   };
 
   // Interactions with the "Chrome backgrounds" option.
@@ -647,12 +809,29 @@
       }
     };
   };
-  $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).onclick =
-      defaultWallpapersInteraction;
+  $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).onclick = function() {
+    $(customBackgrounds.IDS.MENU)
+        .classList.add(customBackgrounds.CLASSES.MOUSE_NAV);
+    defaultWallpapersInteraction(event);
+  };
   $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).onkeyup = function(event) {
     if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
+      $(customBackgrounds.IDS.MENU)
+          .classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
       defaultWallpapersInteraction(event);
     }
+
+    // Handle arrow key navigation.
+    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.CHROME_BACKGROUNDS, -1)
+          .focus();
+    }
+    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.CHROME_BACKGROUNDS, 1)
+          .focus();
+    }
   };
 
   // Interactions with the Google Photos option.
@@ -682,6 +861,18 @@
     if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
       googlePhotosInteraction(event);
     }
+
+    // Handle arrow key navigation.
+    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.GOOGLE_PHOTOS, -1)
+          .focus();
+    }
+    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+      customBackgrounds
+          .getNextOption(customBackgrounds.MENU_ENTRIES.GOOGLE_PHOTOS, 1)
+          .focus();
+    }
   };
 
   // Escape and Backspace handling for the background picker dialog.
@@ -700,6 +891,15 @@
             customBackgrounds.dialogCollectionsSource);
       }
     }
+
+    // If keyboard navigation is attempted, remove mouse-only mode.
+    if (event.keyCode === customBackgrounds.KEYCODES.TAB ||
+        event.keyCode === customBackgrounds.KEYCODES.LEFT ||
+        event.keyCode === customBackgrounds.KEYCODES.UP ||
+        event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
+        event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+      menu.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
+    }
   };
 
   // Clicks that happen outside of the dialog.
@@ -761,6 +961,22 @@
     $(customBackgrounds.IDS.DONE)
         .classList.add(customBackgrounds.CLASSES.DONE_AVAILABLE);
   };
+
+  // On any arrow key event in the tiles area, focus the first tile.
+  $(customBackgrounds.IDS.TILES).onkeyup = function(event) {
+    if (event.keyCode === customBackgrounds.KEYCODES.LEFT ||
+        event.keyCode === customBackgrounds.KEYCODES.UP ||
+        event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
+        event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+      if ($(customBackgrounds.IDS.MENU)
+              .classList.contains(
+                  customBackgrounds.CLASSES.COLLECTION_DIALOG)) {
+        $('coll_tile_0').focus();
+      } else {
+        $('img_tile_0').focus();
+      }
+    }
+  };
 };
 
 customBackgrounds.hideMessageBox = function() {
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index 049d584..2bb4148 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -112,11 +112,11 @@
   <dialog id="bg-sel-menu">
     <div id="bg-sel-title-bar">
     <div id="bg-sel-back-circle">
-      <div id="bg-sel-back"></div>
+      <div id="bg-sel-back" tabindex="0"></div>
     </div>
     <div id="bg-sel-title"></div>
     </div>
-    <div id="bg-sel-tiles"></div>
+    <div id="bg-sel-tiles" tabindex="0"></div>
     <div id="bg-sel-footer">
       <label id="bg-daily-refresh" class="switch">
         <input type="checkbox">
@@ -124,7 +124,7 @@
       </label>
       <div id="bg-sel-refresh-text"></div>
       <div id="bg-sel-footer-cancel" class="bg-sel-footer-button ripple" tabindex="0"></div>
-      <div id="bg-sel-footer-done" class="bg-sel-footer-button ripple"></div>
+      <div id="bg-sel-footer-done" class="bg-sel-footer-button ripple" tabindex="-1"></div>
     </div>
   </dialog>
 
diff --git a/chrome/browser/resources/md_extensions/BUILD.gn b/chrome/browser/resources/md_extensions/BUILD.gn
index 19a4c5d..6b0ba11 100644
--- a/chrome/browser/resources/md_extensions/BUILD.gn
+++ b/chrome/browser/resources/md_extensions/BUILD.gn
@@ -95,6 +95,7 @@
     ":item_util",
     ":navigation_helper",
     "//ui/webui/resources/cr_elements:cr_container_shadow_behavior",
+    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
     "//ui/webui/resources/js:assert",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:load_time_data",
@@ -293,6 +294,7 @@
   deps = [
     "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
     "//ui/webui/resources/cr_elements/cr_input:cr_input",
+    "//ui/webui/resources/js:load_time_data",
   ]
 }
 
diff --git a/chrome/browser/resources/md_extensions/item.html b/chrome/browser/resources/md_extensions/item.html
index aa93924..ce73ea1c 100644
--- a/chrome/browser/resources/md_extensions/item.html
+++ b/chrome/browser/resources/md_extensions/item.html
@@ -319,7 +319,7 @@
           <paper-button id="remove-button" on-click="onRemoveTap_"
               aria-describedby="a11yAssociation"
               hidden="[[isControlled_(data.controlledInfo)]]">
-            $i18n{itemRemove}
+            $i18n{remove}
           </paper-button>
           <template is="dom-if" if="[[shouldShowErrorsButton_(data.*)]]">
             <paper-button id="errors-button" on-click="onErrorsTap_"
diff --git a/chrome/browser/resources/md_extensions/item.js b/chrome/browser/resources/md_extensions/item.js
index 6d8bc99..a951f9dc 100644
--- a/chrome/browser/resources/md_extensions/item.js
+++ b/chrome/browser/resources/md_extensions/item.js
@@ -73,9 +73,16 @@
     /**
      * @param {string} id
      * @param {string} host
-     * @return {!Promise<string>}
+     * @return {!Promise<void>}
      */
     addRuntimeHostPermission(id, host) {}
+
+    /**
+     * @param {string} id
+     * @param {string} host
+     * @return {!Promise<void>}
+     */
+    removeRuntimeHostPermission(id, host) {}
   }
 
   const Item = Polymer({
diff --git a/chrome/browser/resources/md_extensions/runtime_host_permissions.html b/chrome/browser/resources/md_extensions/runtime_host_permissions.html
index 8c2ca89..6fbca8aa 100644
--- a/chrome/browser/resources/md_extensions/runtime_host_permissions.html
+++ b/chrome/browser/resources/md_extensions/runtime_host_permissions.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
@@ -20,6 +21,12 @@
         margin-top: 10px;
       }
 
+      #hosts li {
+        align-items: center;
+        display: flex;
+        justify-content: space-between;
+      }
+
       .mid-section-header {
         color: var(--cr-primary-text-color);
         margin-top: 12px;
@@ -61,14 +68,34 @@
       <ul id="hosts">
         <template is="dom-repeat"
             items="[[permissions.runtimeHostPermissions]]">
-          <li>[[item]]</li>
+          <li>
+            <div>[[item]]</div>
+            <paper-icon-button-light class="icon-more-vert">
+              <button class="edit-host"
+                  on-click="onEditHostClick_"
+                  title="$i18n{hostPermissionsEdit}">
+              </button>
+            </paper-icon-button-light>
+          </li>
         </template>
       </ul>
     </template>
-    <template is="dom-if" if="[[showHostsDialog_]]" restamp>
+    <cr-action-menu id="hostActionMenu"
+        on-close="onActionMenuClose_">
+      <button slot="item" class="dropdown-item" id="action-menu-edit"
+          on-click="onActionMenuEditClick_">
+        $i18n{hostPermissionsEdit}
+      </button>
+      <button slot="item" class="dropdown-item" id="action-menu-remove"
+          on-click="onActionMenuRemoveClick_">
+        $i18n{remove}
+      </button>
+    </cr-action-menu>
+    <template is="dom-if" if="[[showHostDialog_]]" restamp>
       <extensions-runtime-hosts-dialog
           delegate="[[delegate]]" item-id="[[itemId]]"
-          on-close="onHostsDialogClosed_">
+          current-site="[[hostDialogModel_]]"
+          on-close="onHostDialogClose_">
       </extensions-runtime-hosts-dialog>
     </template>
   </template>
diff --git a/chrome/browser/resources/md_extensions/runtime_host_permissions.js b/chrome/browser/resources/md_extensions/runtime_host_permissions.js
index c78a8b76..c5b1d0ec 100644
--- a/chrome/browser/resources/md_extensions/runtime_host_permissions.js
+++ b/chrome/browser/resources/md_extensions/runtime_host_permissions.js
@@ -25,7 +25,50 @@
        * Whether the dialog to add a new host permission is shown.
        * @private
        */
-      showHostsDialog_: Boolean,
+      showHostDialog_: Boolean,
+
+      /**
+       * The current site of the entry that the host dialog is editing, if the
+       * dialog is open for editing.
+       * @type {?string}
+       * @private
+       */
+      hostDialogModel_: {
+        type: String,
+        value: null,
+      },
+
+      /**
+       * The element to return focus to once the host dialog closes.
+       * @type {?HTMLElement}
+       * @private
+       */
+      hostDialogAnchorElement_: {
+        type: Object,
+        value: null,
+      },
+
+      /**
+       * If the action menu is open, the site of the entry it is open for.
+       * Otherwise null.
+       * @type {?string}
+       * @private
+       */
+      actionMenuModel_: {
+        type: String,
+        value: null,
+      },
+
+      /**
+       * The element that triggered the action menu, so that the page will
+       * return focus once the action menu (or dialog) closes.
+       * @type {?HTMLElement}
+       * @private
+       */
+      actionMenuAnchorElement_: {
+        type: Object,
+        value: null,
+      },
 
       /**
        * Proxying the enum to be used easily by the html template.
@@ -63,15 +106,86 @@
           chrome.developerPrivate.HostAccess.ON_SPECIFIC_SITES;
     },
 
-    /** @private */
-    onAddHostClick_: function() {
-      this.showHostsDialog_ = true;
+    /**
+     * @param {Event} e
+     * @private
+     */
+    onAddHostClick_: function(e) {
+      const target = /** @type {!HTMLElement} */ (e.target);
+      this.doShowHostDialog_(target, null);
+    },
+
+    /**
+     * @param {!HTMLElement} anchorElement The element to return focus to once
+     *     the dialog closes.
+     * @param {?string} currentSite The site entry currently being
+     *     edited, or null if this is to add a new entry.
+     * @private
+     */
+    doShowHostDialog_: function(anchorElement, currentSite) {
+      this.hostDialogAnchorElement_ = anchorElement;
+      this.hostDialogModel_ = currentSite;
+      this.showHostDialog_ = true;
     },
 
     /** @private */
-    onHostsDialogClosed_: function() {
-      this.showHostsDialog_ = false;
-      cr.ui.focusWithoutInk(assert(this.$$('#add-host')));
+    onHostDialogClose_: function() {
+      this.hostDialogModel_ = null;
+      this.showHostDialog_ = false;
+      cr.ui.focusWithoutInk(
+          assert(this.hostDialogAnchorElement_, 'Host Anchor'));
+      this.hostDialogAnchorElement_ = null;
+    },
+
+    /**
+     * @param {!{
+     *   model: !{item: string},
+     *   target: !HTMLElement,
+     * }} e
+     * @private
+     */
+    onEditHostClick_: function(e) {
+      this.actionMenuModel_ = e.model.item;
+      this.actionMenuAnchorElement_ = e.target;
+      const actionMenu =
+          /** @type {CrActionMenuElement} */ (this.$.hostActionMenu);
+      actionMenu.showAt(e.target);
+    },
+
+    /** @private */
+    onActionMenuEditClick_: function() {
+      // Cache the site before closing the action menu, since it's cleared.
+      const site = this.actionMenuModel_;
+
+      // Cache and reset actionMenuAnchorElement_ so focus is not returned
+      // to the action menu's trigger (since the dialog will be shown next).
+      // Instead, curry the element to the dialog, so once it closes, focus
+      // will be returned.
+      const anchorElement =
+          assert(this.actionMenuAnchorElement_, 'Menu Anchor');
+      this.actionMenuAnchorElement_ = null;
+      this.closeActionMenu_();
+      this.doShowHostDialog_(anchorElement, site);
+    },
+
+    /** @private */
+    onActionMenuRemoveClick_: function() {
+      this.delegate.removeRuntimeHostPermission(
+          this.itemId, assert(this.actionMenuModel_, 'Action Menu Model'));
+      this.closeActionMenu_();
+    },
+
+    /** @private */
+    closeActionMenu_: function() {
+      const menu = this.$.hostActionMenu;
+      assert(menu.open);
+      menu.close();
+    },
+
+    /** @private */
+    onActionMenuClose_: function() {
+      this.actionMenuModel_ = null;
+      this.actionMenuAnchorElement_ = null;
     },
   });
 
diff --git a/chrome/browser/resources/md_extensions/runtime_hosts_dialog.html b/chrome/browser/resources/md_extensions/runtime_hosts_dialog.html
index 082370ad..b639ed57 100644
--- a/chrome/browser/resources/md_extensions/runtime_hosts_dialog.html
+++ b/chrome/browser/resources/md_extensions/runtime_hosts_dialog.html
@@ -5,12 +5,13 @@
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="strings.html">
 
 <dom-module id="extensions-runtime-hosts-dialog">
   <template>
     <style include="cr-shared-style paper-button-style"></style>
     <cr-dialog id="dialog" close-text="$i18n{close}">
-      <div slot="title">$i18n{runtimeHostsDialogTitle}</div>
+      <div slot="title">[[computeDialogTitle_(currentSite)]]</div>
       <div slot="body">
         <cr-input id="input" label="$i18n{runtimeHostsDialogInputLabel}"
             placeholder="http://example.com"
@@ -25,9 +26,9 @@
         <paper-button class="cancel-button" on-click="onCancelTap_">
           $i18n{cancel}
         </paper-button>
-        <paper-button class="action-button" id="add" on-click="onAddTap_"
-            disabled="[[computeAddButtonDisabled_(inputInvalid_, site_)]]">
-          $i18n{add}
+        <paper-button class="action-button" id="submit" on-click="onSubmitTap_"
+            disabled="[[computeSubmitButtonDisabled_(inputInvalid_, site_)]]">
+          [[computeSubmitButtonLabel_(currentSite)]]
         </paper-button>
       </div>
     </cr-dialog>
diff --git a/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js b/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js
index def01c5..2b1cbac2 100644
--- a/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js
+++ b/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js
@@ -16,6 +16,16 @@
       itemId: String,
 
       /**
+       * The site that this entry is currently managing. Only non-empty if this
+       * is for editing an existing entry.
+       * @type {?string}
+       */
+      currentSite: {
+        type: String,
+        value: null,
+      },
+
+      /**
        * The site to add an exception for.
        * @private
        */
@@ -33,6 +43,10 @@
 
     /** @override */
     attached: function() {
+      if (this.currentSite !== null) {
+        this.site_ = this.currentSite;
+        this.validate_();
+      }
       this.$.dialog.showModal();
     },
 
@@ -63,24 +77,67 @@
     },
 
     /**
+     * @return {string}
+     * @private
+     */
+    computeDialogTitle_: function() {
+      const stringId = this.currentSite === null ? 'runtimeHostsDialogTitle' :
+                                                   'hostPermissionsEdit';
+      return loadTimeData.getString(stringId);
+    },
+
+    /**
      * @return {boolean}
      * @private
      */
-    computeAddButtonDisabled_: function() {
+    computeSubmitButtonDisabled_: function() {
       return this.inputInvalid_ || this.site_.trim().length == 0;
     },
 
+    /**
+     * @return {string}
+     * @private
+     */
+    computeSubmitButtonLabel_: function() {
+      const stringId = this.currentSite === null ? 'add' : 'save';
+      return loadTimeData.getString(stringId);
+    },
+
     /** @private */
     onCancelTap_: function() {
       this.$.dialog.cancel();
     },
 
     /**
-     * The tap handler for the Add [Site] button (adds the pattern and closes
+     * The tap handler for the submit button (adds the pattern and closes
      * the dialog).
      * @private
      */
-    onAddTap_: function() {
+    onSubmitTap_: function() {
+      if (this.currentSite !== null) {
+        // No change in values, so no need to update the delegate.
+        if (this.currentSite == this.site_) {
+          this.$.dialog.close();
+          return;
+        }
+
+        // Changing the entry is done through a remove followed by an add.
+        this.delegate.removeRuntimeHostPermission(this.itemId, this.currentSite)
+            .then(() => {
+              this.addPermission_();
+            });
+        return;
+      }
+
+      this.addPermission_();
+    },
+
+    /**
+     * Adds the runtime host permission through the delegate. If successful,
+     * closes the dialog; otherwise displays the invalid input message.
+     * @private
+     */
+    addPermission_: function() {
       this.delegate.addRuntimeHostPermission(this.itemId, this.site_)
           .then(
               () => {
diff --git a/chrome/browser/resources/md_extensions/service.js b/chrome/browser/resources/md_extensions/service.js
index 10e3c79..0e1646c3 100644
--- a/chrome/browser/resources/md_extensions/service.js
+++ b/chrome/browser/resources/md_extensions/service.js
@@ -80,6 +80,19 @@
       });
     }
 
+    /** @override */
+    removeRuntimeHostPermission(id, host) {
+      return new Promise((resolve, reject) => {
+        chrome.developerPrivate.removeHostPermission(id, host, () => {
+          if (chrome.runtime.lastError) {
+            reject(chrome.runtime.lastError.message);
+            return;
+          }
+          resolve();
+        });
+      });
+    }
+
     /**
      * Opens a file browser dialog for the user to select a file (or directory).
      * @param {chrome.developerPrivate.SelectType} selectType
diff --git a/chrome/browser/resources/print_preview/new/destination_list.html b/chrome/browser/resources/print_preview/new/destination_list.html
index e2567bc..60ef4d7 100644
--- a/chrome/browser/resources/print_preview/new/destination_list.html
+++ b/chrome/browser/resources/print_preview/new/destination_list.html
@@ -100,7 +100,8 @@
       <template>
         <print-preview-destination-list-item class="list-item"
             search-query="[[searchQuery]]" destination="[[item]]"
-            on-click="onDestinationSelected_" tabindex$="[[tabIndex]]">
+            on-click="onDestinationSelected_" on-keydown="onKeydown_"
+            tabindex$="[[tabIndex]]">
         </print-preview-destination-list-item>
       </template>
     </iron-list>
diff --git a/chrome/browser/resources/print_preview/new/destination_list.js b/chrome/browser/resources/print_preview/new/destination_list.js
index e7b8127..120067a 100644
--- a/chrome/browser/resources/print_preview/new/destination_list.js
+++ b/chrome/browser/resources/print_preview/new/destination_list.js
@@ -100,6 +100,17 @@
   },
 
   /**
+   * @param {!KeyboardEvent} e Event containing the destination and key.
+   * @private
+   */
+  onKeydown_: function(e) {
+    if (e.key === 'Enter') {
+      this.onDestinationSelected_(e);
+      e.stopPropagation();
+    }
+  },
+
+  /**
    * @param {!Event} e Event containing the destination that was selected.
    * @private
    */
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html b/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html
index bdf9b96..5ceba36 100644
--- a/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html
+++ b/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html
@@ -77,12 +77,13 @@
               <template is="dom-if" if="[[item.isTextArea]]">
                 <settings-textarea label="[[item.component.fieldName]]"
                     value="{{item.value}}" on-value-changed="updateCanSave_"
-                    class$="address-column [[long_(item)]]" autofocus>
+                    class$="address-column [[long_(item)]]" autofocus
+                    spellcheck="false">
                 </settings-textarea>
               </template>
               <template is="dom-if" if="[[!item.isTextArea]]">
                 <cr-input type="text" label="[[item.component.fieldName]]"
-                    autofocus value="{{item.value}}"
+                    autofocus value="{{item.value}}" spellcheck="false"
                     on-value-changed="updateCanSave_"
                     class$="address-column [[long_(item)]]">
                 </cr-input>
@@ -106,11 +107,11 @@
         <div class="address-row">
           <cr-input id="phoneInput" type="text" label="$i18n{addressPhone}"
               class="address-column last-row" on-value-changed="updateCanSave_"
-              value="{{phoneNumber_}}">
+              value="{{phoneNumber_}}" spellcheck="false">
           </cr-input>
           <cr-input id="emailInput" type="text" label="$i18n{addressEmail}"
               on-value-changed="updateCanSave_" value="{{email_}}"
-              class="address-column long last-row">
+              class="address-column long last-row" spellcheck="false">
           </cr-input>
         </div>
       </div>
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html b/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html
index 4426fdf1..7138118 100644
--- a/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html
+++ b/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html
@@ -54,7 +54,7 @@
       <div slot="title">[[title_]]</div>
       <div slot="body">
         <cr-input id="nameInput" label="$i18n{creditCardName}"
-            value="{{creditCard.name}}" autofocus
+            value="{{creditCard.name}}" autofocus spellcheck="false"
             on-input="onCreditCardNameOrNumberChanged_">
         </cr-input>
         <cr-input id="numberInput" label="$i18n{creditCardNumber}"
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 987641d..02569ce 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -180,6 +180,12 @@
     AddString(translated_strings.get(), "moreInfo", IDS_NTP_ERROR_MORE_INFO);
     AddString(translated_strings.get(), "backgroundsUnavailable",
               IDS_NTP_CUSTOM_BG_BACKGROUNDS_UNAVAILABLE);
+    AddString(translated_strings.get(), "customizeThisPage",
+              IDS_NTP_CUSTOM_BG_CUSTOMIZE_NTP_LABEL);
+    AddString(translated_strings.get(), "backLabel",
+              IDS_NTP_CUSTOM_BG_BACK_LABEL);
+    AddString(translated_strings.get(), "photoLabel",
+              IDS_NTP_CUSTOM_BG_GOOGLE_PHOTO_LABEL);
 
     // Voice Search
     AddString(translated_strings.get(), "audioError",
diff --git a/chrome/browser/signin/signin_updater.cc b/chrome/browser/signin/signin_updater.cc
deleted file mode 100644
index 4a03eab..0000000
--- a/chrome/browser/signin/signin_updater.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/signin_updater.h"
-
-#include "chrome/common/renderer_configuration.mojom.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_process_host.h"
-
-SigninUpdater::SigninUpdater(SigninManagerBase* signin_manager)
-    : signin_manager_(signin_manager) {
-  DCHECK(signin_manager_);
-  signin_manager_->AddObserver(this);
-}
-
-SigninUpdater::~SigninUpdater() {
-  DCHECK(!signin_manager_);
-}
-
-void SigninUpdater::Shutdown() {
-  signin_manager_->RemoveObserver(this);
-  signin_manager_ = nullptr;
-}
-
-void SigninUpdater::GoogleSigninSucceeded(const AccountInfo& account_info) {
-  UpdateRenderer(true /* is_signed_in */);
-}
-
-void SigninUpdater::GoogleSignedOut(const AccountInfo& account_info) {
-  UpdateRenderer(false /* is_signed_in */);
-}
-
-void SigninUpdater::UpdateRenderer(bool is_signed_in) {
-  for (content::RenderProcessHost::iterator it(
-           content::RenderProcessHost::AllHostsIterator());
-       !it.IsAtEnd(); it.Advance()) {
-    IPC::ChannelProxy* channel = it.GetCurrentValue()->GetChannel();
-    if (channel) {
-      chrome::mojom::RendererConfigurationAssociatedPtr renderer_configuration;
-      channel->GetRemoteAssociatedInterface(&renderer_configuration);
-      renderer_configuration->SetIsSignedIn(is_signed_in);
-    }
-  }
-}
diff --git a/chrome/browser/signin/signin_updater.h b/chrome/browser/signin/signin_updater.h
deleted file mode 100644
index 0ba0d43..0000000
--- a/chrome/browser/signin/signin_updater.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SIGNIN_SIGNIN_UPDATER_H_
-#define CHROME_BROWSER_SIGNIN_SIGNIN_UPDATER_H_
-
-#include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/signin_manager.h"
-
-// The SigninUpdater is responsible for updating renderers about the signin
-// change.
-class SigninUpdater : public KeyedService, public SigninManagerBase::Observer {
- public:
-  explicit SigninUpdater(SigninManagerBase* signin_manager);
-  ~SigninUpdater() override;
-
-  // KeyedService:
-  void Shutdown() override;
-
-  // SigninManagerBase::Observer:
-  void GoogleSigninSucceeded(const AccountInfo& account_info) override;
-  void GoogleSignedOut(const AccountInfo& account_info) override;
-
- private:
-  void UpdateRenderer(bool is_signed_in);
-
-  SigninManagerBase* signin_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(SigninUpdater);
-};
-
-#endif  // CHROME_BROWSER_SIGNIN_SIGNIN_UPDATER_H_
diff --git a/chrome/browser/signin/signin_updater_factory.cc b/chrome/browser/signin/signin_updater_factory.cc
deleted file mode 100644
index d4bcc3b..0000000
--- a/chrome/browser/signin/signin_updater_factory.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/signin_updater_factory.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/signin/signin_updater.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-
-SigninUpdaterFactory::SigninUpdaterFactory()
-    : BrowserContextKeyedServiceFactory(
-          "SigninUpdater",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(SigninManagerFactory::GetInstance());
-}
-
-SigninUpdaterFactory::~SigninUpdaterFactory() {}
-
-// static
-SigninUpdaterFactory* SigninUpdaterFactory::GetInstance() {
-  return base::Singleton<SigninUpdaterFactory>::get();
-}
-
-// static
-SigninUpdater* SigninUpdaterFactory::GetForProfile(Profile* profile) {
-  return static_cast<SigninUpdater*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-KeyedService* SigninUpdaterFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  Profile* profile = static_cast<Profile*>(context);
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfile(profile);
-  return new SigninUpdater(signin_manager);
-}
-
-bool SigninUpdaterFactory::ServiceIsCreatedWithBrowserContext() const {
-  return true;
-}
diff --git a/chrome/browser/signin/signin_updater_factory.h b/chrome/browser/signin/signin_updater_factory.h
deleted file mode 100644
index ef04a85..0000000
--- a/chrome/browser/signin/signin_updater_factory.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SIGNIN_SIGNIN_UPDATER_FACTORY_H_
-#define CHROME_BROWSER_SIGNIN_SIGNIN_UPDATER_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class Profile;
-class SigninUpdater;
-
-// Singleton that owns all SigninUpdater and creates/deletes them as
-// new Profiles are created/shutdown.
-class SigninUpdaterFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns an instance of the SigninUpdaterFactory singleton.
-  static SigninUpdaterFactory* GetInstance();
-
-  // Returns the instance of SigninUpdater for the passed |profile|.
-  static SigninUpdater* GetForProfile(Profile* profile);
-
- protected:
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* profile) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
-
- private:
-  friend struct base::DefaultSingletonTraits<SigninUpdaterFactory>;
-
-  SigninUpdaterFactory();
-  ~SigninUpdaterFactory() override;
-
-  DISALLOW_COPY_AND_ASSIGN(SigninUpdaterFactory);
-};
-
-#endif  // CHROME_BROWSER_SIGNIN_SIGNIN_UPDATER_FACTORY_H_
diff --git a/chrome/browser/ui/app_list/app_context_menu_unittest.cc b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
index a7e0052..4cab9032 100644
--- a/chrome/browser/ui/app_list/app_context_menu_unittest.cc
+++ b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
@@ -242,7 +242,7 @@
                         bool can_show_app_info,
                         AppListControllerDelegate::Pinnable pinnable,
                         extensions::LaunchType launch_type) {
-    controller_.reset(new FakeAppListControllerDelegate());
+    controller_ = std::make_unique<FakeAppListControllerDelegate>();
     controller_->SetAppPinnable(app_id, pinnable);
     controller_->SetCanShowAppInfo(can_show_app_info);
     controller_->SetExtensionLaunchType(profile(), app_id, launch_type);
@@ -312,7 +312,7 @@
   }
 
   void TestChromeApp(bool can_show_app_info) {
-    controller_.reset(new FakeAppListControllerDelegate());
+    controller_ = std::make_unique<FakeAppListControllerDelegate>();
     controller_->SetCanShowAppInfo(can_show_app_info);
     app_list::ExtensionAppContextMenu menu(menu_delegate(),
                                            profile(),
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index ce68314..5ec5ce7 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -298,8 +298,8 @@
 }
 
 void AppListClientImpl::SetUpSearchUI() {
-  search_resource_manager_.reset(
-      new app_list::SearchResourceManager(profile_, model_updater_));
+  search_resource_manager_ = std::make_unique<app_list::SearchResourceManager>(
+      profile_, model_updater_);
 
   search_controller_ =
       app_list::CreateSearchController(profile_, model_updater_, this);
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 71dd3eb..fc75e07 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -403,12 +403,15 @@
   CHECK(IsExtensionServiceReady());
   AppListClientImpl* client = AppListClientImpl::GetInstance();
   AppListControllerDelegate* controller = client;
-  apps_builder_.reset(new ExtensionAppModelBuilder(controller));
+  apps_builder_ = std::make_unique<ExtensionAppModelBuilder>(controller);
   if (arc::IsArcAllowedForProfile(profile_))
-    arc_apps_builder_.reset(new ArcAppModelBuilder(controller));
-  if (IsCrostiniUIAllowedForProfile(profile_))
-    crostini_apps_builder_.reset(new CrostiniAppModelBuilder(controller));
-  internal_apps_builder_.reset(new InternalAppModelBuilder(controller));
+    arc_apps_builder_ = std::make_unique<ArcAppModelBuilder>(controller);
+  if (IsCrostiniUIAllowedForProfile(profile_)) {
+    crostini_apps_builder_ =
+        std::make_unique<CrostiniAppModelBuilder>(controller);
+  }
+  internal_apps_builder_ =
+      std::make_unique<InternalAppModelBuilder>(controller);
 
   DCHECK(profile_);
   SyncStarted();
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
index e33d661..5989080 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
@@ -35,9 +35,9 @@
 
   // Note, ARC icon is available only for 48x48 dips. In case |icon_size_|
   // differs from this size, re-scale is required.
-  std::unique_ptr<ArcAppIcon> icon(new ArcAppIcon(
+  std::unique_ptr<ArcAppIcon> icon = std::make_unique<ArcAppIcon>(
       profile(), app_id,
-      app_list::AppListConfig::instance().arc_icon_dimension(), this));
+      app_list::AppListConfig::instance().arc_icon_dimension(), this);
   icon->image_skia().EnsureRepsForSupportedScales();
   icon_map_[app_id] = std::move(icon);
   UpdateImage(app_id);
diff --git a/chrome/browser/ui/app_list/arc/arc_app_test.cc b/chrome/browser/ui/app_list/arc/arc_app_test.cc
index b9be859..d255811 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_test.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_test.cc
@@ -108,7 +108,7 @@
   if (!arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile_))
     EXPECT_TRUE(arc_session_manager_->enable_requested());
 
-  app_instance_.reset(new arc::FakeAppInstance(arc_app_list_pref_));
+  app_instance_ = std::make_unique<arc::FakeAppInstance>(arc_app_list_pref_);
   arc_service_manager_->arc_bridge_service()->app()->SetInstance(
       app_instance_.get());
   // TODO(khmel): Resolve this gracefully. Set of default app tests does not
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
index ff721771..30bfa70 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
@@ -33,8 +33,8 @@
 
 std::unique_ptr<ArcDefaultAppList::AppInfoMap>
 ReadAppsFromFileThread() {
-  std::unique_ptr<ArcDefaultAppList::AppInfoMap> apps(
-      new ArcDefaultAppList::AppInfoMap);
+  std::unique_ptr<ArcDefaultAppList::AppInfoMap> apps =
+      std::make_unique<ArcDefaultAppList::AppInfoMap>();
 
   base::FilePath base_path;
   if (!use_test_apps_directory) {
@@ -95,12 +95,12 @@
 
       const std::string app_id = ArcAppListPrefs::GetAppId(
           package_name, activity);
-      std::unique_ptr<ArcDefaultAppList::AppInfo> app(
-          new ArcDefaultAppList::AppInfo(name,
+      std::unique_ptr<ArcDefaultAppList::AppInfo> app =
+          std::make_unique<ArcDefaultAppList::AppInfo>(name,
                                          package_name,
                                          activity,
                                          oem,
-                                         base_path.Append(app_path)));
+                                         base_path.Append(app_path));
       apps.get()->insert(
           std::pair<std::string,
                     std::unique_ptr<ArcDefaultAppList::AppInfo>>(
@@ -149,12 +149,12 @@
   const extensions::Extension* arc_host =
       service ? service->GetInstalledExtension(arc::kPlayStoreAppId) : nullptr;
   if (arc_host && arc::IsPlayStoreAvailable()) {
-    std::unique_ptr<ArcDefaultAppList::AppInfo> play_store_app(
-        new ArcDefaultAppList::AppInfo(arc_host->name(),
+    std::unique_ptr<ArcDefaultAppList::AppInfo> play_store_app =
+        std::make_unique<ArcDefaultAppList::AppInfo>(arc_host->name(),
                                        arc::kPlayStorePackage,
                                        arc::kPlayStoreActivity,
                                        false /* oem */,
-                                       base::FilePath() /* app_path */));
+                                       base::FilePath() /* app_path */);
     apps_.insert(
         std::pair<std::string,
                   std::unique_ptr<ArcDefaultAppList::AppInfo>>(
diff --git a/chrome/browser/ui/app_list/arc/arc_usb_host_permission_browsertest.cc b/chrome/browser/ui/app_list/arc/arc_usb_host_permission_browsertest.cc
index 80c1d6e..1483f34 100644
--- a/chrome/browser/ui/app_list/arc/arc_usb_host_permission_browsertest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_usb_host_permission_browsertest.cc
@@ -62,7 +62,7 @@
     arc_app_list_pref_->SetDefaltAppsReadyCallback(run_loop.QuitClosure());
     run_loop.Run();
 
-    app_instance_.reset(new arc::FakeAppInstance(arc_app_list_pref_));
+    app_instance_ = std::make_unique<arc::FakeAppInstance>(arc_app_list_pref_);
     arc_app_list_pref_->app_connection_holder()->SetInstance(
         app_instance_.get());
     WaitForInstanceReady(arc_app_list_pref_->app_connection_holder());
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_item.cc b/chrome/browser/ui/app_list/crostini/crostini_app_item.cc
index 5216275b1..14e0c95 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_item.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_item.cc
@@ -25,9 +25,9 @@
     const std::string& name)
     : ChromeAppListItem(profile, id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  crostini_app_icon_.reset(new CrostiniAppIcon(
+  crostini_app_icon_ = std::make_unique<CrostiniAppIcon>(
       profile, id, app_list::AppListConfig::instance().grid_icon_dimension(),
-      this));
+      this);
 
   SetName(name);
   UpdateIcon();
diff --git a/chrome/browser/ui/app_list/extension_app_context_menu.cc b/chrome/browser/ui/app_list/extension_app_context_menu.cc
index 79db078..a0bc6ac7 100644
--- a/chrome/browser/ui/app_list/extension_app_context_menu.cc
+++ b/chrome/browser/ui/app_list/extension_app_context_menu.cc
@@ -87,9 +87,9 @@
                            IDS_APP_CONTEXT_MENU_SHOW_INFO);
     }
   } else {
-    extension_menu_items_.reset(new extensions::ContextMenuMatcher(
+    extension_menu_items_ = std::make_unique<extensions::ContextMenuMatcher>(
         profile(), this, menu_model,
-        base::Bind(MenuItemHasLauncherContext)));
+        base::Bind(MenuItemHasLauncherContext));
 
     // First, add the primary actions.
     if (!is_platform_app_) {
diff --git a/chrome/browser/ui/app_list/extension_app_item.cc b/chrome/browser/ui/app_list/extension_app_item.cc
index 2d3b3274..48e5567 100644
--- a/chrome/browser/ui/app_list/extension_app_item.cc
+++ b/chrome/browser/ui/app_list/extension_app_item.cc
@@ -100,8 +100,8 @@
     return false;
 
   if (!extension_enable_flow_) {
-    extension_enable_flow_.reset(new ExtensionEnableFlow(
-        profile(), extension_id(), this));
+    extension_enable_flow_ = std::make_unique<ExtensionEnableFlow>(
+        profile(), extension_id(), this);
     extension_enable_flow_->StartForNativeWindow(nullptr);
   }
   return true;
diff --git a/chrome/browser/ui/app_list/extension_app_model_builder.cc b/chrome/browser/ui/app_list/extension_app_model_builder.cc
index 973f534..5c446302 100644
--- a/chrome/browser/ui/app_list/extension_app_model_builder.cc
+++ b/chrome/browser/ui/app_list/extension_app_model_builder.cc
@@ -196,7 +196,7 @@
   if (tracker_)
     tracker_->AddObserver(this);
 
-  app_updater_.reset(new LauncherExtensionAppUpdater(this, profile()));
+  app_updater_ = std::make_unique<LauncherExtensionAppUpdater>(this, profile());
 }
 
 void ExtensionAppModelBuilder::PopulateApps() {
diff --git a/chrome/browser/ui/app_list/search/arc_app_result.cc b/chrome/browser/ui/app_list/search/arc_app_result.cc
index bb9d004b..4a41992 100644
--- a/chrome/browser/ui/app_list/search/arc_app_result.cc
+++ b/chrome/browser/ui/app_list/search/arc_app_result.cc
@@ -27,10 +27,10 @@
   std::string id = kArcAppPrefix;
   id += app_id;
   set_id(id);
-  icon_loader_.reset(new ArcAppIconLoader(
+  icon_loader_ = std::make_unique<ArcAppIconLoader>(
       profile,
       AppListConfig::instance().GetPreferredIconDimension(display_type()),
-      this));
+      this);
   icon_loader_->FetchImage(app_id);
 }
 
diff --git a/chrome/browser/ui/app_list/search/common/webservice_cache.cc b/chrome/browser/ui/app_list/search/common/webservice_cache.cc
index 7c2dce5d..db1d0376 100644
--- a/chrome/browser/ui/app_list/search/common/webservice_cache.cc
+++ b/chrome/browser/ui/app_list/search/common/webservice_cache.cc
@@ -59,8 +59,8 @@
                           std::unique_ptr<base::DictionaryValue> result) {
   if (result) {
     std::string typed_query = PrependType(type, query);
-    std::unique_ptr<Payload> scoped_payload(
-        new Payload(base::Time::Now(), std::move(result)));
+    std::unique_ptr<Payload> scoped_payload =
+        std::make_unique<Payload>(base::Time::Now(), std::move(result));
     Payload* payload = scoped_payload.get();
 
     cache_.Put(typed_query, std::move(scoped_payload));
@@ -86,7 +86,7 @@
       !it.IsAtEnd();
       it.Advance()) {
     const base::DictionaryValue* payload_dict;
-    std::unique_ptr<Payload> payload(new Payload);
+    std::unique_ptr<Payload> payload = std::make_unique<Payload>();
     if (!it.value().GetAsDictionary(&payload_dict) ||
         !payload_dict ||
         !PayloadFromDict(payload_dict, payload.get())) {
diff --git a/chrome/browser/ui/app_list/search/crostini_app_result.cc b/chrome/browser/ui/app_list/search/crostini_app_result.cc
index b9f0c11..0ce1bf33 100644
--- a/chrome/browser/ui/app_list/search/crostini_app_result.cc
+++ b/chrome/browser/ui/app_list/search/crostini_app_result.cc
@@ -23,10 +23,10 @@
     : AppResult(profile, app_id, controller, is_recommendation) {
   set_id(app_id);
 
-  icon_loader_.reset(new CrostiniAppIconLoader(
+  icon_loader_ = std::make_unique<CrostiniAppIconLoader>(
       profile,
       AppListConfig::instance().GetPreferredIconDimension(display_type()),
-      this));
+      this);
   icon_loader_->FetchImage(app_id);
 }
 
diff --git a/chrome/browser/ui/app_list/search/extension_app_result.cc b/chrome/browser/ui/app_list/search/extension_app_result.cc
index 6e3c1a2..19fe004e 100644
--- a/chrome/browser/ui/app_list/search/extension_app_result.cc
+++ b/chrome/browser/ui/app_list/search/extension_app_result.cc
@@ -107,8 +107,8 @@
     return false;
 
   if (!extension_enable_flow_) {
-    extension_enable_flow_.reset(
-        new ExtensionEnableFlow(profile(), app_id(), this));
+    extension_enable_flow_ =
+        std::make_unique<ExtensionEnableFlow>(profile(), app_id(), this);
     extension_enable_flow_->StartForNativeWindow(nullptr);
   }
   return true;
diff --git a/chrome/browser/ui/app_list/search/omnibox_provider.cc b/chrome/browser/ui/app_list/search/omnibox_provider.cc
index 6e0aae5e..93a7674 100644
--- a/chrome/browser/ui/app_list/search/omnibox_provider.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_provider.cc
@@ -23,8 +23,8 @@
                                  AppListControllerDelegate* list_controller)
     : profile_(profile),
       list_controller_(list_controller),
-      controller_(new AutocompleteController(
-          base::WrapUnique(new ChromeAutocompleteProviderClient(profile)),
+      controller_(std::make_unique<AutocompleteController>(
+          std::make_unique<ChromeAutocompleteProviderClient>(profile),
           this,
           AutocompleteClassifier::DefaultOmniboxProviders() &
               ~AutocompleteProvider::TYPE_ZERO_SUGGEST)) {}
diff --git a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
index b8856670..8d71c20 100644
--- a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
@@ -67,13 +67,13 @@
     AppListTestBase::SetUp();
 
     model_updater_ = std::make_unique<FakeAppListModelUpdater>();
-    controller_.reset(new ::test::TestAppListControllerDelegate);
+    controller_ = std::make_unique<::test::TestAppListControllerDelegate>();
   }
 
   void CreateSearch() {
     clock_.SetNow(kTestCurrentTime);
-    app_search_.reset(new AppSearchProvider(profile_.get(), nullptr, &clock_,
-                                            model_updater_.get()));
+    app_search_ = std::make_unique<AppSearchProvider>(
+        profile_.get(), nullptr, &clock_, model_updater_.get());
   }
 
   std::string RunQuery(const std::string& query) {
diff --git a/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc b/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc
index 51fa6ed9..9eec775 100644
--- a/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/omnibox_result_unittest.cc
@@ -40,8 +40,8 @@
   void SetUp() override {
     AppListTestBase::SetUp();
 
-    app_list_controller_delegate_.reset(
-        new ::test::TestAppListControllerDelegate);
+    app_list_controller_delegate_ =
+        std::make_unique<::test::TestAppListControllerDelegate>();
   }
 
   std::unique_ptr<OmniboxResult> CreateOmniboxResult(
@@ -53,8 +53,8 @@
       AutocompleteMatchType::Type type,
       const std::string& keyword) {
     AutocompleteMatch match;
-    match.search_terms_args.reset(
-        new TemplateURLRef::SearchTermsArgs(base::UTF8ToUTF16(original_query)));
+    match.search_terms_args = std::make_unique<TemplateURLRef::SearchTermsArgs>(
+        base::UTF8ToUTF16(original_query));
     match.search_terms_args->original_query = base::UTF8ToUTF16(original_query);
     match.relevance = relevance;
     match.destination_url = GURL(destination_url);
@@ -63,8 +63,8 @@
     match.type = type;
     match.keyword = base::UTF8ToUTF16(keyword);
 
-    return std::unique_ptr<OmniboxResult>(new OmniboxResult(
-        profile_.get(), app_list_controller_delegate_.get(), nullptr, match));
+    return std::make_unique<OmniboxResult>(
+        profile_.get(), app_list_controller_delegate_.get(), nullptr, match);
   }
 
   const GURL& GetLastOpenedUrl() const {
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc b/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
index e7b617b..77374422 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
@@ -86,10 +86,10 @@
   }
 
   if (!webstore_search_) {
-    webstore_search_.reset(new JSONResponseFetcher(
+    webstore_search_ = std::make_unique<JSONResponseFetcher>(
         base::Bind(&WebstoreProvider::OnWebstoreSearchFetched,
                    base::Unretained(this)),
-        profile_));
+        profile_);
   }
 
   query_pending_ = true;
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc b/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc
index 95ed2dd..c5ae563 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc
@@ -175,9 +175,9 @@
     const GURL gallery_url(embedded_test_server()->base_url().spec() + "path");
     extension_test_util::SetGalleryURL(gallery_url);
 
-    mock_controller_.reset(new AppListControllerDelegateForTest);
-    webstore_provider_.reset(new WebstoreProvider(
-        ProfileManager::GetActiveUserProfile(), mock_controller_.get()));
+    mock_controller_ = std::make_unique<AppListControllerDelegateForTest>();
+    webstore_provider_ = std::make_unique<WebstoreProvider>(
+        ProfileManager::GetActiveUserProfile(), mock_controller_.get());
     webstore_provider_->set_webstore_search_fetched_callback(
         base::Bind(&WebstoreProviderTest::OnSearchResultsFetched,
                    base::Unretained(this)));
@@ -192,7 +192,7 @@
 
     if (webstore_provider_->query_pending_ && !mock_server_response.empty()) {
       DCHECK(!run_loop_);
-      run_loop_.reset(new base::RunLoop);
+      run_loop_ = std::make_unique<base::RunLoop>();
       run_loop_->Run();
       run_loop_.reset();
 
@@ -264,7 +264,8 @@
 
  private:
   std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
-    std::unique_ptr<BasicHttpResponse> response(new BasicHttpResponse);
+    std::unique_ptr<BasicHttpResponse> response =
+        std::make_unique<BasicHttpResponse>();
 
     if (request.relative_url.find("/jsonsearch?") != std::string::npos) {
       if (mock_server_response_ == "ERROR_NOT_FOUND") {
diff --git a/chrome/browser/ui/ash/chrome_keyboard_ui.cc b/chrome/browser/ui/ash/chrome_keyboard_ui.cc
index 50f6e64..f2916a8 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_ui.cc
+++ b/chrome/browser/ui/ash/chrome_keyboard_ui.cc
@@ -291,11 +291,11 @@
   while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
     content::RenderWidgetHostView* view = widget->GetView();
     if (view && window->Contains(view->GetNativeView())) {
-      gfx::Rect window_bounds = view->GetNativeView()->GetBoundsInScreen();
+      gfx::Rect view_bounds = view->GetViewBounds();
       gfx::Rect intersect = gfx::IntersectRects(
-          window_bounds, GetKeyboardWindow()->GetBoundsInScreen());
+          view_bounds, GetKeyboardWindow()->GetBoundsInScreen());
       int overlap = ShouldEnableInsets(window) ? intersect.height() : 0;
-      if (overlap > 0 && overlap < window_bounds.height())
+      if (overlap > 0 && overlap < view_bounds.height())
         view->SetInsets(gfx::Insets(0, 0, overlap, 0));
       else
         view->SetInsets(gfx::Insets());
@@ -394,10 +394,10 @@
         continue;
 
       if (ShouldWindowOverscroll(window)) {
-        gfx::Rect window_bounds = window->GetBoundsInScreen();
-        gfx::Rect intersect = gfx::IntersectRects(window_bounds, new_bounds);
+        gfx::Rect view_bounds = view->GetViewBounds();
+        gfx::Rect intersect = gfx::IntersectRects(view_bounds, new_bounds);
         int overlap = intersect.height();
-        if (overlap > 0 && overlap < window_bounds.height())
+        if (overlap > 0 && overlap < view_bounds.height())
           view->SetInsets(gfx::Insets(0, 0, overlap, 0));
         else
           view->SetInsets(gfx::Insets());
diff --git a/chrome/browser/ui/ash/chrome_keyboard_ui.h b/chrome/browser/ui/ash/chrome_keyboard_ui.h
index 3ff7e37..18f10f8 100644
--- a/chrome/browser/ui/ash/chrome_keyboard_ui.h
+++ b/chrome/browser/ui/ash/chrome_keyboard_ui.h
@@ -62,7 +62,6 @@
   // Overridden from KeyboardUI:
   aura::Window* GetKeyboardWindow() override;
   bool HasKeyboardWindow() const override;
-  bool ShouldWindowOverscroll(aura::Window* window) const override;
   void ReloadKeyboardIfNeeded() override;
   void InitInsets(const gfx::Rect& new_bounds) override;
   void ResetInsets() override;
@@ -95,6 +94,11 @@
   // Determines whether a particular window should have insets for overscroll.
   bool ShouldEnableInsets(aura::Window* window);
 
+  // Whether this window should do an overscroll to avoid occlusion by the
+  // virtual keyboard. IME windows and virtual keyboard windows should always
+  // avoid overscroll.
+  bool ShouldWindowOverscroll(aura::Window* window) const;
+
   // Adds an observer for tracking changes to a window size or
   // position while the keyboard is displayed. Any window repositioning
   // invalidates insets for overscrolling.
diff --git a/chrome/browser/ui/views/arc_app_dialog_view.cc b/chrome/browser/ui/views/arc_app_dialog_view.cc
index 673f750..399f66e 100644
--- a/chrome/browser/ui/views/arc_app_dialog_view.cc
+++ b/chrome/browser/ui/views/arc_app_dialog_view.cc
@@ -137,7 +137,8 @@
   if (!subheading_text.empty())
     AddMultiLineLabel(text_container, subheading_text);
 
-  icon_loader_.reset(new ArcAppIconLoader(profile_, kIconSourceSize, this));
+  icon_loader_ = std::make_unique<ArcAppIconLoader>(
+      profile_, kIconSourceSize, this);
   // The dialog will show once the icon is loaded.
   icon_loader_->FetchImage(app_id_);
   chrome::RecordDialogCreation(chrome::DialogIdentifier::ARC_APP);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index b3e89a0..cc3b1d9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -220,11 +220,11 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// OmniboxImageView:
+// RoundedCornerImageView:
 
-class OmniboxImageView : public views::ImageView {
+class RoundedCornerImageView : public views::ImageView {
  public:
-  OmniboxImageView() = default;
+  RoundedCornerImageView() = default;
 
   bool CanProcessEventsWithinSubtree() const override { return false; }
 
@@ -232,10 +232,10 @@
   // views::ImageView:
   void OnPaint(gfx::Canvas* canvas) override;
 
-  DISALLOW_COPY_AND_ASSIGN(OmniboxImageView);
+  DISALLOW_COPY_AND_ASSIGN(RoundedCornerImageView);
 };
 
-void OmniboxImageView::OnPaint(gfx::Canvas* canvas) {
+void RoundedCornerImageView::OnPaint(gfx::Canvas* canvas) {
   gfx::Path mask;
   mask.addRoundRect(gfx::RectToSkRect(GetImageBounds()),
                     kEntityImageCornerRadius, kEntityImageCornerRadius);
@@ -252,8 +252,8 @@
     : is_old_style_answer_(false),
       is_rich_suggestion_(false),
       is_search_type_(false) {
-  AddChildView(icon_view_ = new OmniboxImageView());
-  AddChildView(image_view_ = new OmniboxImageView());
+  AddChildView(icon_view_ = new views::ImageView());
+  AddChildView(answer_image_view_ = new RoundedCornerImageView());
   AddChildView(content_view_ = new OmniboxTextView(result_view));
   AddChildView(description_view_ = new OmniboxTextView(result_view));
   AddChildView(separator_view_ = new OmniboxTextView(result_view));
@@ -262,8 +262,8 @@
     icon_view_->SetHorizontalAlignment(views::ImageView::CENTER);
     icon_view_->SetVerticalAlignment(views::ImageView::CENTER);
   }
-  image_view_->SetHorizontalAlignment(views::ImageView::CENTER);
-  image_view_->SetVerticalAlignment(views::ImageView::CENTER);
+  answer_image_view_->SetHorizontalAlignment(views::ImageView::CENTER);
+  answer_image_view_->SetVerticalAlignment(views::ImageView::CENTER);
 
   const base::string16& separator =
       l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
@@ -280,9 +280,9 @@
   } else if (is_old_style_answer_) {
     int icon_width = icon_view_->width();
     int answer_image_size =
-        image_view_->GetImage().isNull()
+        answer_image_view_->GetImage().isNull()
             ? 0
-            : image_view_->height() + kAnswerIconToTextPadding;
+            : answer_image_view_->height() + kAnswerIconToTextPadding;
     int deduction = icon_width + (HorizontalPadding() * 3) + answer_image_size;
     int description_width = std::max(width() - deduction, 0);
     height = content_view_->GetLineHeight() +
@@ -335,16 +335,16 @@
 
   if (OmniboxFieldTrial::IsNewAnswerLayoutEnabled() &&
       match.type == AutocompleteMatchType::CALCULATOR) {
-    image_view_->SetImage(
+    answer_image_view_->SetImage(
         ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
             IDR_OMNIBOX_CALCULATOR_ROUND));
-    image_view_->SetImageSize(
+    answer_image_view_->SetImageSize(
         gfx::Size(kNewAnswerImageSize, kNewAnswerImageSize));
   } else if (!is_rich_suggestion_) {
-    // An entry with |is_old_style_answer_| may use the image_view_. But it's
-    // set when the image arrives (later).
-    image_view_->SetImage(gfx::ImageSkia());
-    image_view_->SetSize(gfx::Size());
+    // An entry with |is_old_style_answer_| may use the answer_image_view_. But
+    // it's set when the image arrives (later).
+    answer_image_view_->SetImage(gfx::ImageSkia());
+    answer_image_view_->SetSize(gfx::Size());
   } else {
     // Determine if we have a local icon (or else it will be downloaded).
     const gfx::VectorIcon* vector_icon = nullptr;
@@ -378,17 +378,17 @@
       }
       if (vector_icon) {
         const auto& icon = gfx::CreateVectorIcon(*vector_icon, SK_ColorWHITE);
-        image_view_->SetImage(
+        answer_image_view_->SetImage(
             gfx::CanvasImageSource::MakeImageSkia<EncircledImageSource>(
                 kNewAnswerImageSize / 2, gfx::kGoogleBlue600, icon));
       } else if (idr_image) {
-        image_view_->SetImage(
+        answer_image_view_->SetImage(
             ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
                 idr_image));
       }
       // Always set the image size so that downloaded images get the correct
       // size (such as Weather answers).
-      image_view_->SetImageSize(
+      answer_image_view_->SetImageSize(
           gfx::Size(kNewAnswerImageSize, kNewAnswerImageSize));
     } else {
       SkColor color = result_view->GetColor(OmniboxPart::RESULTS_BACKGROUND);
@@ -396,8 +396,8 @@
                                                   &color);
       color = SkColorSetA(color, 0x40);  // 25% transparency (arbitrary).
       const gfx::Size size = gfx::Size(kEntityImageSize, kEntityImageSize);
-      image_view_->SetImageSize(size);
-      image_view_->SetImage(
+      answer_image_view_->SetImageSize(size);
+      answer_image_view_->SetImage(
           gfx::CanvasImageSource::MakeImageSkia<PlaceholderImageSource>(size,
                                                                         color));
     }
@@ -446,13 +446,14 @@
   x += text_indent;
   content_view_->SetBounds(x, y, width() - x, text_height);
   y += text_height;
-  if (!image_view_->GetImage().isNull()) {
+  if (!answer_image_view_->GetImage().isNull()) {
     constexpr int kImageEdgeLength = 24;
     constexpr int kImagePadding = 2;
-    image_view_->SetBounds(x, y + kImagePadding, kImageEdgeLength,
-                           kImageEdgeLength);
-    image_view_->SetImageSize(gfx::Size(kImageEdgeLength, kImageEdgeLength));
-    x += image_view_->width() + kAnswerIconToTextPadding;
+    answer_image_view_->SetBounds(x, y + kImagePadding, kImageEdgeLength,
+                                  kImageEdgeLength);
+    answer_image_view_->SetImageSize(
+        gfx::Size(kImageEdgeLength, kImageEdgeLength));
+    x += answer_image_view_->width() + kAnswerIconToTextPadding;
   }
   int description_width = width() - x;
   description_view_->SetBounds(
@@ -466,7 +467,7 @@
   int y = child_area.y();
   views::ImageView* image_view;
   if (is_rich_suggestion_) {
-    image_view = image_view_;
+    image_view = answer_image_view_;
   } else {
     image_view = icon_view_;
   }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
index a651c88..0b3da1b5 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
@@ -21,7 +21,7 @@
   ~OmniboxMatchCellView() override;
 
   views::ImageView* icon() { return icon_view_; }
-  views::ImageView* image() { return image_view_; }
+  views::ImageView* answer_image() { return answer_image_view_; }
   OmniboxTextView* content() { return content_view_; }
   OmniboxTextView* description() { return description_view_; }
   OmniboxTextView* separator() { return separator_view_; }
@@ -55,8 +55,10 @@
   bool should_show_tab_match_ = false;
 
   // Weak pointers for easy reference.
-  views::ImageView* icon_view_;   // An icon representing the type or content.
-  views::ImageView* image_view_;  // For rich suggestions.
+  // An icon representing the type or content.
+  views::ImageView* icon_view_;
+  // The image for answers in suggest and rich entity suggestions.
+  views::ImageView* answer_image_view_;
   OmniboxTextView* content_view_;
   OmniboxTextView* description_view_;
   OmniboxTextView* separator_view_;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 60b6e2f..d34aa209 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -230,7 +230,7 @@
 }
 
 void OmniboxResultView::SetRichSuggestionImage(const gfx::ImageSkia& image) {
-  suggestion_view_->image()->SetImage(image);
+  suggestion_view_->answer_image()->SetImage(image);
   Layout();
   SchedulePaint();
 }
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 7231b367..c733a9e 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -128,8 +128,12 @@
     {"learnMore", IDS_LEARN_MORE},
     {"noSearchResults", IDS_SEARCH_NO_RESULTS},
     {"ok", IDS_OK},
+    {"save", IDS_SAVE},
     {"searchResults", IDS_SEARCH_RESULTS},
 
+    // Multi-use strings defined in md_extensions_strings.grdp.
+    {"remove", IDS_MD_EXTENSIONS_REMOVE},
+
     // Add extension-specific strings.
     {"title", IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE},
     {"toolbarTitle", IDS_MD_EXTENSIONS_TOOLBAR_TITLE},
@@ -158,6 +162,7 @@
     {"openChromeWebStore", IDS_MD_EXTENSIONS_SIDEBAR_OPEN_CHROME_WEB_STORE},
     {"keyboardShortcuts", IDS_MD_EXTENSIONS_SIDEBAR_KEYBOARD_SHORTCUTS},
     {"incognitoInfoWarning", IDS_EXTENSIONS_INCOGNITO_WARNING},
+    {"hostPermissionsEdit", IDS_MD_EXTENSIONS_HOST_PERMISSIONS_EDIT},
     {"hostPermissionsHeading", IDS_MD_EXTENSIONS_ITEM_HOST_PERMISSIONS_HEADING},
     {"hostAccessOnClick", IDS_MD_EXTENSIONS_HOST_ACCESS_ON_CLICK},
     {"hostAccessOnSpecificSites",
@@ -194,7 +199,6 @@
     {"itemOptions", IDS_MD_EXTENSIONS_ITEM_OPTIONS},
     {"itemPermissions", IDS_MD_EXTENSIONS_ITEM_PERMISSIONS},
     {"itemPermissionsEmpty", IDS_MD_EXTENSIONS_ITEM_PERMISSIONS_EMPTY},
-    {"itemRemove", IDS_MD_EXTENSIONS_ITEM_REMOVE},
     {"itemRemoveExtension", IDS_MD_EXTENSIONS_ITEM_REMOVE_EXTENSION},
     {"itemSource", IDS_MD_EXTENSIONS_ITEM_SOURCE},
     {"itemSourcePolicy", IDS_MD_EXTENSIONS_ITEM_SOURCE_POLICY},
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn
index ff304e3..b2ec4be7 100644
--- a/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -23,6 +23,7 @@
   deps = [
     "//base",
     "//chrome/browser/web_applications/components",
+    "//chrome/common",
     "//content/public/browser",
     "//skia",
   ]
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index de0836f..ca093fd 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -53,10 +53,6 @@
       [ "$root_out_dir/$widevine_cdm_path/libwidevinecdm.so" ]
 }
 
-if (!libcpp_is_static && use_custom_libcxx) {
-  packaging_files_shlibs += [ "$root_out_dir/libc++.so" ]
-}
-
 packaging_files_binaries = packaging_files_executables + packaging_files_shlibs
 
 # TODO(mmoss) Any convenient way to get all the relevant build files?
@@ -373,10 +369,6 @@
   if (!is_chromeos) {
     public_deps += [ ":rpm_packaging_files" ]
   }
-
-  if (!libcpp_is_static && use_custom_libcxx) {
-    public_deps += [ "//buildtools/third_party/libc++:libc++" ]
-  }
 }
 
 # Creates .deb and .rpm (RPM for non-ChromeOS only) installer packages.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bb474de..7f83242 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2569,6 +2569,7 @@
     "../browser/prerender/prerender_util_unittest.cc",
     "../browser/previews/previews_infobar_delegate_unittest.cc",
     "../browser/previews/previews_infobar_tab_helper_unittest.cc",
+    "../browser/previews/previews_lite_page_decider_unittest.cc",
     "../browser/previews/previews_service_unittest.cc",
     "../browser/process_singleton_win_unittest.cc",
     "../browser/profiles/gaia_info_update_service_unittest.cc",
diff --git a/chrome/test/data/extensions/page_with_button.html b/chrome/test/data/extensions/page_with_button.html
new file mode 100644
index 0000000..4edc103
--- /dev/null
+++ b/chrome/test/data/extensions/page_with_button.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+  <button id="go-button">Go Go Go!</button>
+</body>
+</html>
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index d852252f..1687cfa8 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -94,10 +94,10 @@
 
     <dialog id="bg-sel-menu">
       <div id="bg-sel-title-bar">
-      <div id="bg-sel-back"></div>
+      <div id="bg-sel-back" tabindex="0"></div>
       <div id="bg-sel-title"></div>
       </div>
-      <div id="bg-sel-tiles"></div>
+      <div id="bg-sel-tiles" tabindex="0"></div>
       <div id="bg-sel-footer">
         <label id="bg-daily-refresh" class="switch">
           <input type="checkbox">
@@ -105,7 +105,7 @@
         </label>
         <div id="bg-sel-refresh-text"></div>
         <div id="bg-sel-footer-cancel" class="bg-sel-footer-button ripple" tabindex="0"></div>
-        <div id="bg-sel-footer-done" class="bg-sel-footer-button ripple"></div>
+        <div id="bg-sel-footer-done" class="bg-sel-footer-button ripple" tabindex="-1"></div>
       </div>
     </dialog>
 
diff --git a/chrome/test/data/webui/extensions/runtime_host_permissions_test.js b/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
index 54474dc..201ab2c 100644
--- a/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
+++ b/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
@@ -118,5 +118,57 @@
     const dialog = element.$$('extensions-runtime-hosts-dialog');
     assertTrue(!!dialog);
     expectTrue(dialog.$.dialog.open);
+    expectEquals(null, dialog.currentSite);
+  });
+
+  test('removing runtime host permissions', function() {
+    const permissions = {
+      simplePermissions: [],
+      hostAccess: HostAccess.ON_SPECIFIC_SITES,
+      runtimeHostPermissions: ['https://example.com', 'https://chromium.org'],
+    };
+    element.set('permissions', permissions);
+    Polymer.dom.flush();
+
+    const editHost = element.$$('.edit-host');
+    assertTrue(!!editHost);
+    editHost.click();
+    const actionMenu = element.$$('cr-action-menu');
+    assertTrue(!!actionMenu);
+    expectTrue(actionMenu.open);
+
+    const remove = actionMenu.querySelector('#action-menu-remove');
+    assertTrue(!!remove);
+
+    remove.click();
+    return delegate.whenCalled('removeRuntimeHostPermission').then((args) => {
+      expectEquals(ITEM_ID, args[0] /* id */);
+      expectEquals('https://example.com', args[1] /* site */);
+      expectFalse(actionMenu.open);
+    });
+  });
+
+  test('clicking edit host triggers dialog', function() {
+    const permissions = {
+      simplePermissions: [],
+      hostAccess: HostAccess.ON_SPECIFIC_SITES,
+      runtimeHostPermissions: ['https://example.com', 'https://chromium.org'],
+    };
+    element.set('permissions', permissions);
+    Polymer.dom.flush();
+
+    const editHost = element.$$('.edit-host');
+    editHost.click();
+    const actionMenu = element.$$('cr-action-menu');
+
+    const actionMenuEdit = actionMenu.querySelector('#action-menu-edit');
+    assertTrue(!!actionMenuEdit);
+
+    actionMenuEdit.click();
+    Polymer.dom.flush();
+    const dialog = element.$$('extensions-runtime-hosts-dialog');
+    assertTrue(!!dialog);
+    expectTrue(dialog.$.dialog.open);
+    expectEquals('https://example.com', dialog.currentSite);
   });
 });
diff --git a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
index 0215a6d..0e18d23 100644
--- a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
+++ b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
@@ -29,9 +29,9 @@
     input.fire('input');
     assertFalse(input.invalid);
 
-    const add = dialog.$.add;
-    assertFalse(add.disabled);
-    MockInteractions.tap(add);
+    const submit = dialog.$.submit;
+    assertFalse(submit.disabled);
+    submit.click();
     return delegate.whenCalled('addRuntimeHostPermission').then((args) => {
       let id = args[0];
       let input = args[1];
@@ -45,21 +45,21 @@
     // should not be shown for an empty input.
     const input = dialog.$$('cr-input');
     assertFalse(input.invalid);
-    const add = dialog.$.add;
-    assertTrue(add.disabled);
+    const submit = dialog.$.submit;
+    assertTrue(submit.disabled);
 
     // Simulate user input of invalid text.
     const invalidSite = 'foobar';
     input.value = invalidSite;
     input.fire('input');
     assertTrue(input.invalid);
-    assertTrue(add.disabled);
+    assertTrue(submit.disabled);
 
-    // Entering valid text should clear the error and enable the add button.
+    // Entering valid text should clear the error and enable the submit button.
     input.value = 'http://www.example.com';
     input.fire('input');
     assertFalse(input.invalid);
-    assertFalse(add.disabled);
+    assertFalse(submit.disabled);
   });
 
   test('delegate indicates invalid input', function() {
@@ -71,12 +71,35 @@
     input.fire('input');
     assertFalse(input.invalid);
 
-    const add = dialog.$.add;
-    assertFalse(add.disabled);
-    MockInteractions.tap(add);
+    const submit = dialog.$.submit;
+    assertFalse(submit.disabled);
+    submit.click();
     return delegate.whenCalled('addRuntimeHostPermission').then(() => {
       assertTrue(input.invalid);
-      assertTrue(add.disabled);
+      assertTrue(submit.disabled);
     });
   });
+
+  test('editing current entry', function() {
+    const oldSite = 'http://example.com';
+    const newSite = 'http://chromium.org';
+
+    dialog.currentSite = oldSite;
+    const input = dialog.$$('cr-input');
+    input.value = newSite;
+    input.fire('input');
+    const submit = dialog.$.submit;
+
+    submit.click();
+    return delegate.whenCalled('removeRuntimeHostPermission')
+        .then((args) => {
+          expectEquals(ITEM_ID, args[0] /* id */);
+          expectEquals(oldSite, args[1] /* site */);
+          return delegate.whenCalled('addRuntimeHostPermission');
+        })
+        .then((args) => {
+          expectEquals(ITEM_ID, args[0] /* id */);
+          expectEquals(newSite, args[1] /* site */);
+        });
+  });
 });
diff --git a/chrome/test/data/webui/extensions/test_service.js b/chrome/test/data/webui/extensions/test_service.js
index 0074229..609f675 100644
--- a/chrome/test/data/webui/extensions/test_service.js
+++ b/chrome/test/data/webui/extensions/test_service.js
@@ -14,6 +14,7 @@
         'loadUnpacked',
         'retryLoadUnpacked',
         'reloadItem',
+        'removeRuntimeHostPermission',
         'setItemHostAccess',
         'setProfileInDevMode',
         'setShortcutHandlingSuspended',
@@ -86,6 +87,12 @@
     }
 
     /** @override */
+    removeRuntimeHostPermission(id, site) {
+      this.methodCalled('removeRuntimeHostPermission', [id, site]);
+      return Promise.resolve();
+    }
+
+    /** @override */
     setItemHostAccess(id, access) {
       this.methodCalled('setItemHostAccess', [id, access]);
     }
diff --git a/chrome/test/data/webui/print_preview/destination_list_test.js b/chrome/test/data/webui/print_preview/destination_list_test.js
index fbaf787..57fdae6a 100644
--- a/chrome/test/data/webui/print_preview/destination_list_test.js
+++ b/chrome/test/data/webui/print_preview/destination_list_test.js
@@ -6,6 +6,7 @@
   /** @enum {string} */
   const TestNames = {
     FilterDestinations: 'FilterDestinations',
+    FireDestinationSelected: 'FireDestinationSelected',
   };
 
   const suiteName = 'DestinationListTest';
@@ -133,6 +134,26 @@
       assertFalse(total.hidden);
       assertTrue(total.textContent.includes('5'));
     });
+
+    // Tests that the list correctly fires the destination selected event when
+    // the destination is clicked or the enter key is pressed.
+    test(assert(TestNames.FireDestinationSelected), function() {
+      const items = list.shadowRoot.querySelectorAll(
+          'print-preview-destination-list-item');
+      let whenDestinationSelected = test_util.eventToPromise(
+          'destination-selected', list);
+      items[0].click();
+      return whenDestinationSelected.then(event => {
+        assertEquals(items[0], event.detail);
+        whenDestinationSelected = test_util.eventToPromise(
+            'destination-selected', list);
+        MockInteractions.keyEventOn(
+            items[1], 'keydown', 13, undefined, 'Enter');
+        return whenDestinationSelected;
+      }).then(event => {
+        assertEquals(items[1], event.detail);
+      });
+    });
   });
 
   return {
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index 6b2e28b..5569cfb 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -50,7 +50,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      '../settings/test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'plugin_stub.js',
@@ -187,7 +187,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      '../settings/test_util.js',
       'print_preview_test_utils.js',
       'pages_settings_test.js',
     ]);
@@ -400,7 +400,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      '../settings/test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'plugin_stub.js',
@@ -439,8 +439,8 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
       ROOT_PATH + 'ui/webui/resources/js/cr/event_target.js',
+      '../settings/test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -553,9 +553,9 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
       ROOT_PATH + 'ui/webui/resources/js/webui_listener_tracker.js',
       ROOT_PATH + 'ui/webui/resources/js/cr/event_target.js',
+      '../settings/test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -583,7 +583,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      '../settings/test_util.js',
       'print_preview_test_utils.js',
       'advanced_dialog_test.js',
     ]);
@@ -626,7 +626,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      '../settings/test_util.js',
       'print_preview_test_utils.js',
       'custom_margins_test.js',
     ]);
@@ -663,8 +663,8 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
       ROOT_PATH + 'ui/webui/resources/js/webui_listener_tracker.js',
+      '../settings/test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'print_preview_test_utils.js',
@@ -840,6 +840,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
+      '../settings/test_util.js',
       'destination_list_test.js',
     ]);
   }
@@ -853,3 +854,8 @@
 TEST_F('PrintPreviewDestinationListTest', 'FilterDestinations', function() {
   this.runMochaTest(destination_list_test.TestNames.FilterDestinations);
 });
+
+TEST_F('PrintPreviewDestinationListTest', 'FireDestinationSelected',
+    function() {
+  this.runMochaTest(destination_list_test.TestNames.FireDestinationSelected);
+});
diff --git a/chrome/test/vr/perf/BUILD.gn b/chrome/test/vr/perf/BUILD.gn
index 6785d69..a58e971 100644
--- a/chrome/test/vr/perf/BUILD.gn
+++ b/chrome/test/vr/perf/BUILD.gn
@@ -19,8 +19,11 @@
            "./latency/latency_test_config.py",
            "./latency/motopho_thread.py",
            "./latency/robot_arm.py",
+           "./latency/robot_arm_arduino.py",
+           "./latency/robot_arm_maestro.py",
            "./latency/run_latency_test.py",
            "./latency/webvr_latency_test.py",
+           "./latency/third_party/",
          ]
   data_deps = [
     "//chrome/android:chrome_public_apk",
diff --git a/chrome/test/vr/perf/latency/robot_arm.py b/chrome/test/vr/perf/latency/robot_arm.py
index 18d574b..3727e92 100644
--- a/chrome/test/vr/perf/latency/robot_arm.py
+++ b/chrome/test/vr/perf/latency/robot_arm.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
+# Copyright 2018 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.
 
@@ -7,44 +7,15 @@
 
 
 class RobotArm():
-  """Handles the serial communication with the servos/arm used for movement."""
-  def __init__(self, device_name, num_tries=5, baud=115200, timeout=3.0):
-    self._connection = None
-    connected = False
-    for _ in xrange(num_tries):
-      try:
-        self._connection = serial.Serial('/dev/' + device_name,
-                                         baud,
-                                         timeout=timeout)
-      except serial.SerialException as e:
-        pass
-      if self._connection and 'Enter parameters' in self._connection.read(1024):
-        connected = True
-        break
-    if not connected:
-      raise serial.SerialException('Failed to connect to the robot arm.')
-
+  """Abstract class for controlling the robot arm that moves the test device."""
   def ResetPosition(self):
-    if not self._connection:
-      return
-    # If the servo stopped very close to the desired position, it can just
-    # vibrate instead of moving, so move away before going to the reset
-    # position.
-    self._connection.write('5 300 0 5\n')
-    time.sleep(0.5)
-    self._connection.write('5 250 0 5\n')
-    time.sleep(0.5)
+    raise NotImplementedError(
+        'ResetPosition must be implemented in a subclass.')
 
   def StartMotophoMovement(self):
-    if not self._connection:
-      return
-    self._connection.write('9\n')
+    raise NotImplementedError(
+        'StartMotophoMovement must be implemented in a subclass.')
 
   def StopAllMovement(self):
-    if not self._connection:
-      return
-    self._connection.write('0\n')
-    # The manual usage instructions are printed over the serial connection
-    # every time we send a command - long test runs can result in the buffer
-    # filling up and causing the arm to hang, so periodically clear the buffer.
-    self._connection.flushInput()
+    raise NotImplementedError(
+        'StopAllMovement must be implemented in a subclass.')
diff --git a/chrome/test/vr/perf/latency/robot_arm_arduino.py b/chrome/test/vr/perf/latency/robot_arm_arduino.py
new file mode 100644
index 0000000..875dabe
--- /dev/null
+++ b/chrome/test/vr/perf/latency/robot_arm_arduino.py
@@ -0,0 +1,55 @@
+# Copyright 2018 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.
+
+import robot_arm as ra
+
+import serial
+import time
+
+
+class RobotArmArduino(ra.RobotArm):
+  """Implementation of RobotArm for the older Aruduino-based robot arm.
+
+  This older version relies on a serial connection over USB to communicate
+  with an Arduino that has been flashed with the correct firmware."""
+  def __init__(self, device_name, num_tries=5, baud=115200, timeout=3.0):
+    self._connection = None
+    connected = False
+    for _ in xrange(num_tries):
+      try:
+        self._connection = serial.Serial('/dev/' + device_name,
+                                         baud,
+                                         timeout=timeout)
+      except serial.SerialException as e:
+        pass
+      if self._connection and 'Enter parameters' in self._connection.read(1024):
+        connected = True
+        break
+    if not connected:
+      raise serial.SerialException('Failed to connect to the robot arm.')
+
+  def ResetPosition(self):
+    if not self._connection:
+      return
+    # If the servo stopped very close to the desired position, it can just
+    # vibrate instead of moving, so move away before going to the reset
+    # position.
+    self._connection.write('5 300 0 5\n')
+    time.sleep(0.5)
+    self._connection.write('5 250 0 5\n')
+    time.sleep(0.5)
+
+  def StartMotophoMovement(self):
+    if not self._connection:
+      return
+    self._connection.write('9\n')
+
+  def StopAllMovement(self):
+    if not self._connection:
+      return
+    self._connection.write('0\n')
+    # The manual usage instructions are printed over the serial connection
+    # every time we send a command - long test runs can result in the buffer
+    # filling up and causing the arm to hang, so periodically clear the buffer.
+    self._connection.flushInput()
diff --git a/chrome/test/vr/perf/latency/robot_arm_maestro.py b/chrome/test/vr/perf/latency/robot_arm_maestro.py
new file mode 100644
index 0000000..31806a11
--- /dev/null
+++ b/chrome/test/vr/perf/latency/robot_arm_maestro.py
@@ -0,0 +1,139 @@
+# Copyright 2018 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.
+
+import robot_arm as ra
+import third_party.maestro.maestro as maestro
+import serial
+import threading
+import time
+
+SERVO_PORT = 0
+SERVO_ACCELERATION = 12
+SERVO_SPEED = 128
+# Approximately how long it takes for the servo to move from one endpoint to
+# the other given the current acceleration and speed values. A more exact
+# number could be calculated, but an approximate number works fine without any
+# detrimental effect on test results, and is easier.
+MOTOPHO_PERIOD = 0.75
+MOTOPHO_ENDPOINTS = {
+  'start': 4500,
+  'end': 7500,
+}
+
+
+class Command(object):
+  RESET = 1
+  MOTOPHO = 2
+
+class RobotArmMaestro(ra.RobotArm, threading.Thread):
+  """Implementation of RobotArm using the Pololu Maestro series controllers.
+
+  The Pololu Maestro is set up to communicate over a serial connection, similar
+  to the Arduino version of RobotArm. However, the servo control code is in
+  this file instead of being flashed onto the controller like in the case of
+  the Arduino.
+  """
+  def __init__(self, device_name, num_tries=5):
+    if not device_name.startswith('/dev/'):
+      device_name = '/dev/' + device_name
+    self._controller = None
+    connected = False
+    for _ in xrange(num_tries):
+      try:
+        self._controller = maestro.Controller(ttyStr=device_name)
+      except serial.SerialException as e:
+        continue
+      connected = True
+      break
+    if not connected:
+      raise serial.SerialException('Failed to connect to the robot arm.')
+
+    threading.Thread.__init__(self)
+    self._current_cycle = 0
+    self._command = None
+    self._thread_can_die = False
+    self._command_setup_required = False
+
+    # Lock for waiting on a new command to be issued
+    self._new_command_lock = threading.Event()
+    # Lock for waiting on synchronous commands to finish
+    self._command_finish_lock = threading.Event()
+    self._FinishCommand()
+    self.start()
+
+  def ResetPosition(self):
+    self._IssueCommand(Command.RESET)
+    self._WaitForSynchronousCommandFinish()
+
+  def StartMotophoMovement(self):
+    self._IssueCommand(Command.MOTOPHO)
+
+  def StopAllMovement(self):
+    self._FinishCommand()
+
+  def Terminate(self):
+    self._thread_can_die = True
+    self._new_command_lock.set()
+
+  def run(self):
+    while True:
+      self._WaitForNewCommand()
+      if self._thread_can_die:
+        # Stop sending signals so the servo isn't locked in place
+        self._controller.setTarget(SERVO_PORT, 0)
+        self._controller.close()
+        break
+      # Perform any necessary one-time setup for asynchronous commands
+      if self._command_setup_required:
+        self._command_setup_required = False
+        if self._command is Command.MOTOPHO:
+          self._SetupMotopho()
+      # Synchronous
+      if self._command is Command.RESET:
+        self._CommandReset()
+      # Asynchronous
+      elif self._command is Command.MOTOPHO:
+        self._CommandMotopho()
+
+  def _SetupMotopho(self):
+    self._current_cycle = 0
+    # The Maestro controllers support automatic acceleration when starting and
+    # stopping, so tune the acceleration and max speed values to get smooth
+    # movement instead of trying to write custom control logic to do that
+    # ourselves.
+    self._controller.setAccel(SERVO_PORT, SERVO_ACCELERATION)
+    self._controller.setSpeed(SERVO_PORT, SERVO_SPEED)
+
+  def _CommandReset(self):
+    # The Maestro controllers don't seem to have the same issue as Arduinos
+    # where stopping close to the desired position can cause the servo to
+    # vibrate in place instead of actually moving to the desired position. So,
+    # only send one move command instead of moving away and then to the desired
+    # position.
+    self._controller.setTarget(SERVO_PORT, MOTOPHO_ENDPOINTS['start'])
+    time.sleep(MOTOPHO_PERIOD)
+    self._FinishCommand()
+
+  def _CommandMotopho(self):
+    target_position = (MOTOPHO_ENDPOINTS['start'] if self._current_cycle % 2
+                       else MOTOPHO_ENDPOINTS['end'])
+    self._controller.setTarget(SERVO_PORT, target_position)
+    self._current_cycle += 1
+    time.sleep(MOTOPHO_PERIOD)
+
+  def _WaitForNewCommand(self):
+    self._new_command_lock.wait()
+
+  def _WaitForSynchronousCommandFinish(self):
+    self._command_finish_lock.wait()
+
+  def _FinishCommand(self):
+    self._new_command_lock.clear()
+    self._command_finish_lock.set()
+
+  def _IssueCommand(self, command):
+    self._command = command
+    self._command_setup_required = True
+    self._command_finish_lock.clear()
+    self._new_command_lock.set()
diff --git a/chrome/test/vr/perf/latency/third_party/__init__.py b/chrome/test/vr/perf/latency/third_party/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/vr/perf/latency/third_party/__init__.py
diff --git a/chrome/test/vr/perf/latency/third_party/maestro/LICENSE b/chrome/test/vr/perf/latency/third_party/maestro/LICENSE
new file mode 100644
index 0000000..21e4de7
--- /dev/null
+++ b/chrome/test/vr/perf/latency/third_party/maestro/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Steven L. Jacobs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/chrome/test/vr/perf/latency/third_party/maestro/README.chromium b/chrome/test/vr/perf/latency/third_party/maestro/README.chromium
new file mode 100644
index 0000000..c2b1bfb
--- /dev/null
+++ b/chrome/test/vr/perf/latency/third_party/maestro/README.chromium
@@ -0,0 +1,22 @@
+Name: Maestro
+URL: https://github.com/FRC4564/Maestro
+Version: 0
+Date: June 11, 2017
+Revision: d619b45a4847e47f5ec5758f91ee7b813ef8dd6a
+Security Critical: no
+License: MIT
+License File: NOT_SHIPPED
+
+Description:
+
+A set of Python functions for interacting with the Pololu Maestro series of
+servo controllers, which are used for automated VR motion-to-photon latency
+testing. This is simply a Python implementation of the commands described in the
+official documentation at https://www.pololu.com/docs/0J40/5.e, using PySerial
+for communicating with the controller.
+
+This code is not considered security critical since it is only to be used by
+test binaries! This should never be used by Chrome or any production code.
+
+Local Modifications: Removal of trailing/unnecessary whitespace. Rename of
+"LICENSE.md" to "LICENSE".
\ No newline at end of file
diff --git a/chrome/test/vr/perf/latency/third_party/maestro/README.md b/chrome/test/vr/perf/latency/third_party/maestro/README.md
new file mode 100644
index 0000000..b48bf9f9
--- /dev/null
+++ b/chrome/test/vr/perf/latency/third_party/maestro/README.md
@@ -0,0 +1,40 @@
+maestro.py
+==========
+
+This Python class supports Pololu's Maestro servo controller over USB serial. Great for use with the Raspberry Pi.
+
+The class includes methods to control servos (position, speed, acceleration), read servo position, and start/stop Maestro scripts.  See Pololu's on-line documentation to learn about the full capabilities of this nifty micro-controller.
+
+Pololu's Maestro Windows installer sets up the Maestro Control Center, used to configure, test and program the controller.  Be sure the Maestro is configured for "USB Dual Port" serial mode, which is [not the default](https://www.pololu.com/docs/0J40/3.c).
+
+You'll need to have the 'pyserial' Python module installed to use maestro.py.
+
+For Linux, download pyserial-2.7.tar.gz from http://sourceforge.net/projects/pyserial/files/pyserial/
+
+    wget http://sourceforge.net/projects/pyserial/files/pyserial/2.7/pyserial-2.7.tar.gz
+
+ and then install
+
+    tar –zxf pyserial-2.7.tar.gz
+    cd pyserial-2.7
+    sudo python setup.py install
+
+Alternatively, if you have pip available, pyserial can be installed as follows:
+
+    python -m pip install pyserial
+
+Check out http://pyserial.readthedocs.io/en/latest/pyserial.html#installation for other install options.
+
+Example usage of maestro.py:
+
+    import maestro.py
+    servo = maestro.Controller()
+    servo.setAccel(0,4)      #set servo 0 acceleration to 4
+    servo.setTarget(0,6000)  #set servo to move to center position
+    servo.close
+
+For use on Windows, you'll need to provide the COM port assigned to the Maestro Command Port.  You can indentify the port by starting Device Manager and looking under Ports (COM & LPT).  Here's how to instantiate the controller for Windows.
+
+    import maestro.py
+    m = maestro.Controller('COM3')
+    
diff --git a/chrome/test/vr/perf/latency/third_party/maestro/__init__.py b/chrome/test/vr/perf/latency/third_party/maestro/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/vr/perf/latency/third_party/maestro/__init__.py
diff --git a/chrome/test/vr/perf/latency/third_party/maestro/maestro.py b/chrome/test/vr/perf/latency/third_party/maestro/maestro.py
new file mode 100644
index 0000000..2f901699
--- /dev/null
+++ b/chrome/test/vr/perf/latency/third_party/maestro/maestro.py
@@ -0,0 +1,169 @@
+import serial
+from sys import version_info
+
+PY2 = version_info[0] == 2   #Running Python 2.x?
+
+#
+#---------------------------
+# Maestro Servo Controller
+#---------------------------
+#
+# Support for the Pololu Maestro line of servo controllers
+#
+# Steven Jacobs -- Aug 2013
+# https://github.com/FRC4564/Maestro/
+#
+# These functions provide access to many of the Maestro's capabilities using the
+# Pololu serial protocol
+#
+class Controller:
+    # When connected via USB, the Maestro creates two virtual serial ports
+    # /dev/ttyACM0 for commands and /dev/ttyACM1 for communications.
+    # Be sure the Maestro is configured for "USB Dual Port" serial mode.
+    # "USB Chained Mode" may work as well, but hasn't been tested.
+    #
+    # Pololu protocol allows for multiple Maestros to be connected to a single
+    # serial port. Each connected device is then indexed by number.
+    # This device number defaults to 0x0C (or 12 in decimal), which this module
+    # assumes.  If two or more controllers are connected to different serial
+    # ports, or you are using a Windows OS, you can provide the tty port.  For
+    # example, '/dev/ttyACM2' or for Windows, something like 'COM3'.
+    def __init__(self,ttyStr='/dev/ttyACM0',device=0x0c):
+        # Open the command port
+        self.usb = serial.Serial(ttyStr)
+        # Command lead-in and device number are sent for each Pololu serial command.
+        self.PololuCmd = chr(0xaa) + chr(device)
+        # Track target position for each servo. The function isMoving() will
+        # use the Target vs Current servo position to determine if movement is
+        # occuring.  Upto 24 servos on a Maestro, (0-23). Targets start at 0.
+        self.Targets = [0] * 24
+        # Servo minimum and maximum targets can be restricted to protect components.
+        self.Mins = [0] * 24
+        self.Maxs = [0] * 24
+
+    # Cleanup by closing USB serial port
+    def close(self):
+        self.usb.close()
+
+    # Send a Pololu command out the serial port
+    def sendCmd(self, cmd):
+        cmdStr = self.PololuCmd + cmd
+        if PY2:
+            self.usb.write(cmdStr)
+        else:
+            self.usb.write(bytes(cmdStr,'latin-1'))
+
+    # Set channels min and max value range.  Use this as a safety to protect
+    # from accidentally moving outside known safe parameters. A setting of 0
+    # allows unrestricted movement.
+    #
+    # ***Note that the Maestro itself is configured to limit the range of servo travel
+    # which has precedence over these values.  Use the Maestro Control Center to configure
+    # ranges that are saved to the controller.  Use setRange for software controllable ranges.
+    def setRange(self, chan, min, max):
+        self.Mins[chan] = min
+        self.Maxs[chan] = max
+
+    # Return Minimum channel range value
+    def getMin(self, chan):
+        return self.Mins[chan]
+
+    # Return Maximum channel range value
+    def getMax(self, chan):
+        return self.Maxs[chan]
+
+    # Set channel to a specified target value.  Servo will begin moving based
+    # on Speed and Acceleration parameters previously set.
+    # Target values will be constrained within Min and Max range, if set.
+    # For servos, target represents the pulse width in of quarter-microseconds
+    # Servo center is at 1500 microseconds, or 6000 quarter-microseconds
+    # Typcially valid servo range is 3000 to 9000 quarter-microseconds
+    # If channel is configured for digital output, values < 6000 = Low ouput
+    def setTarget(self, chan, target):
+        # if Min is defined and Target is below, force to Min
+        if self.Mins[chan] > 0 and target < self.Mins[chan]:
+            target = self.Mins[chan]
+        # if Max is defined and Target is above, force to Max
+        if self.Maxs[chan] > 0 and target > self.Maxs[chan]:
+            target = self.Maxs[chan]
+
+        lsb = target & 0x7f #7 bits for least significant byte
+        msb = (target >> 7) & 0x7f #shift 7 and take next 7 bits for msb
+        cmd = chr(0x04) + chr(chan) + chr(lsb) + chr(msb)
+        self.sendCmd(cmd)
+        # Record Target value
+        self.Targets[chan] = target
+
+    # Set speed of channel
+    # Speed is measured as 0.25microseconds/10milliseconds
+    # For the standard 1ms pulse width change to move a servo between extremes, a speed
+    # of 1 will take 1 minute, and a speed of 60 would take 1 second.
+    # Speed of 0 is unrestricted.
+    def setSpeed(self, chan, speed):
+        lsb = speed & 0x7f #7 bits for least significant byte
+        msb = (speed >> 7) & 0x7f #shift 7 and take next 7 bits for msb
+        cmd = chr(0x07) + chr(chan) + chr(lsb) + chr(msb)
+        self.sendCmd(cmd)
+
+    # Set acceleration of channel
+    # This provide soft starts and finishes when servo moves to target position.
+    # Valid values are from 0 to 255. 0=unrestricted, 1 is slowest start.
+    # A value of 1 will take the servo about 3s to move between 1ms to 2ms range.
+    def setAccel(self, chan, accel):
+        lsb = accel & 0x7f #7 bits for least significant byte
+        msb = (accel >> 7) & 0x7f #shift 7 and take next 7 bits for msb
+        cmd = chr(0x09) + chr(chan) + chr(lsb) + chr(msb)
+        self.sendCmd(cmd)
+
+    # Get the current position of the device on the specified channel
+    # The result is returned in a measure of quarter-microseconds, which mirrors
+    # the Target parameter of setTarget.
+    # This is not reading the true servo position, but the last target position sent
+    # to the servo. If the Speed is set to below the top speed of the servo, then
+    # the position result will align well with the acutal servo position, assuming
+    # it is not stalled or slowed.
+    def getPosition(self, chan):
+        cmd = chr(0x10) + chr(chan)
+        self.sendCmd(cmd)
+        lsb = ord(self.usb.read())
+        msb = ord(self.usb.read())
+        return (msb << 8) + lsb
+
+    # Test to see if a servo has reached the set target position.  This only provides
+    # useful results if the Speed parameter is set slower than the maximum speed of
+    # the servo.  Servo range must be defined first using setRange. See setRange comment.
+    #
+    # ***Note if target position goes outside of Maestro's allowable range for the
+    # channel, then the target can never be reached, so it will appear to always be
+    # moving to the target.
+    def isMoving(self, chan):
+        if self.Targets[chan] > 0:
+            if self.getPosition(chan) != self.Targets[chan]:
+                return True
+        return False
+
+    # Have all servo outputs reached their targets? This is useful only if Speed and/or
+    # Acceleration have been set on one or more of the channels. Returns True or False.
+    # Not available with Micro Maestro.
+    def getMovingState(self):
+        cmd = chr(0x13)
+        self.sendCmd(cmd)
+        if self.usb.read() == chr(0):
+            return False
+        else:
+            return True
+
+    # Run a Maestro Script subroutine in the currently active script. Scripts can
+    # have multiple subroutines, which get numbered sequentially from 0 on up. Code your
+    # Maestro subroutine to either infinitely loop, or just end (return is not valid).
+    def runScriptSub(self, subNumber):
+        cmd = chr(0x27) + chr(subNumber)
+        # can pass a param with command 0x28
+        # cmd = chr(0x28) + chr(subNumber) + chr(lsb) + chr(msb)
+        self.sendCmd(cmd)
+
+    # Stop the current Maestro Script
+    def stopScript(self):
+        cmd = chr(0x24)
+        self.sendCmd(cmd)
+
diff --git a/chrome/test/vr/perf/latency/third_party/maestro/test.py b/chrome/test/vr/perf/latency/third_party/maestro/test.py
new file mode 100644
index 0000000..10bf53f4
--- /dev/null
+++ b/chrome/test/vr/perf/latency/third_party/maestro/test.py
@@ -0,0 +1,43 @@
+import glob
+import re
+
+def GetTtyDevices(tty_pattern, vendor_ids):
+  """Finds all devices connected to tty that match a pattern and device id.
+
+  If a serial device is connected to the computer via USB, this function
+  will check all tty devices that match tty_pattern, and return the ones
+  that have vendor identification number in the list vendor_ids.
+
+  Args:
+    tty_pattern: The search pattern, such as r'ttyACM\d+'.
+    vendor_ids: The list of 16-bit USB vendor ids, such as [0x2a03].
+
+  Returns:
+    A list of strings of tty devices, for example ['ttyACM0'].
+  """
+  product_string = 'PRODUCT='
+  sys_class_dir = '/sys/class/tty/'
+
+  tty_devices = glob.glob(sys_class_dir + '*')
+
+  matcher = re.compile('.*' + tty_pattern)
+  tty_matches = [x for x in tty_devices if matcher.search(x)]
+  tty_matches = [x[len(sys_class_dir):] for x in tty_matches]
+
+  found_devices = []
+  for match in tty_matches:
+    class_filename = sys_class_dir + match + '/device/uevent'
+    with open(class_filename, 'r') as uevent_file:
+      # Look for the desired product id in the uevent text.
+      for line in uevent_file:
+        if product_string in line:
+          ids = line[len(product_string):].split('/')
+          ids = [int(x, 16) for x in ids]
+
+          for desired_id in vendor_ids:
+            if desired_id in ids:
+              found_devices.append(match)
+
+  return found_devices
+
+print GetTtyDevices(r'ttyACM\d+', [0x1ffb])
diff --git a/chrome/test/vr/perf/latency/webvr_latency_test.py b/chrome/test/vr/perf/latency/webvr_latency_test.py
index 5616e9e..948ff8af 100644
--- a/chrome/test/vr/perf/latency/webvr_latency_test.py
+++ b/chrome/test/vr/perf/latency/webvr_latency_test.py
@@ -3,7 +3,8 @@
 # found in the LICENSE file.
 
 import motopho_thread as mt
-import robot_arm as ra
+import robot_arm_arduino as arduino_arm
+import robot_arm_maestro as maestro_arm
 
 import json
 import glob
@@ -17,7 +18,7 @@
 
 
 MOTOPHO_THREAD_TIMEOUT = 15
-MOTOPHO_THREAD_TERMINATION_TIMEOUT = 2
+THREAD_TERMINATION_TIMEOUT = 2
 MOTOPHO_THREAD_RETRIES = 4
 DEFAULT_URLS = [
     # URLs that render 3D scenes in addition to the Motopho patch.
@@ -37,6 +38,10 @@
     'latencyPatch=1\&canvasClickPresents=1\&'
     'renderScale=1\&heavyGpu=1\&cubeScale=0.3\&workTime=10',
 ]
+VENDOR_IDS = {
+  'arduino': [0x2a03, 0x2341],
+  'maestro': [0x1ffb],
+}
 
 
 def GetTtyDevices(tty_pattern, vendor_ids):
@@ -94,10 +99,34 @@
     self._test_results = {}
     self._test_name = 'vr_perf.motopho_latency'
 
-    # Connect to the Arduino that drives the servos.
-    devices = GetTtyDevices(r'ttyACM\d+', [0x2a03, 0x2341])
-    assert (len(devices) == 1),'Found %d devices, expected 1' % len(devices)
-    self.robot_arm = ra.RobotArm(devices[0])
+    self.robot_arm = None
+    # Look for any robot arms attached to the host.
+    # It is reasonable to assume that only one is attached at any given time.
+    for device_type, vendor_ids in VENDOR_IDS.iteritems():
+      devices = GetTtyDevices(r'ttyACM\d+', vendor_ids)
+      if devices:
+        if device_type is 'arduino':
+          if len(devices) != 1:
+            raise RuntimeError(
+                'Found %d arduino devices, expected 1' % len(devices))
+          self.robot_arm = arduino_arm.RobotArmArduino(devices[0])
+          break
+        elif device_type is 'maestro':
+          # The Maestro controllers open up two serial ports. We only use one,
+          # but if we don't detect both, that's an indication that something is
+          # wrong.
+          if len(devices) != 2:
+            raise RuntimeError(
+                'Found %d maestro devices, expected 2' % len(devices))
+          # The first port is used for sending the commands, while the second is
+          # used for communication. We only want the first one, which should
+          # always be the lower number.
+          devices.sort()
+          self.robot_arm = maestro_arm.RobotArmMaestro(devices[0])
+          break
+
+    if not self.robot_arm:
+      raise RuntimeError('Could not find any robot arms attached, aborting.')
 
   def _Run(self, url):
     """Run the latency test.
@@ -154,7 +183,7 @@
     # Leaving old threads around shouldn't cause issues, but clean up just in
     # case
     motopho_thread.Terminate()
-    motopho_thread.join(MOTOPHO_THREAD_TERMINATION_TIMEOUT)
+    motopho_thread.join(THREAD_TERMINATION_TIMEOUT)
     if motopho_thread.isAlive():
       logging.warning('Motopho thread failed to terminate.')
 
@@ -262,3 +291,12 @@
 
     with file(outpath, 'w') as outfile:
       json.dump(results, outfile)
+
+
+  def _OneTimeTeardown(self):
+    super(WebVrLatencyTest, self)._OneTimeTeardown()
+    if isinstance(self.robot_arm, maestro_arm.RobotArmMaestro):
+      self.robot_arm.Terminate()
+      self.robot_arm.join(THREAD_TERMINATION_TIMEOUT)
+      if (self.robot_arm.isAlive()):
+        logging.warning('Robot arm thread failed to terminate')
diff --git a/chromeos/services/machine_learning/OWNERS b/chromeos/services/machine_learning/OWNERS
new file mode 100644
index 0000000..f560ada4
--- /dev/null
+++ b/chromeos/services/machine_learning/OWNERS
@@ -0,0 +1,2 @@
+amoylan@chromium.org
+martis@chromium.org
diff --git a/components/arc/ime/arc_ime_service.cc b/components/arc/ime/arc_ime_service.cc
index 61372bb9..00752ab8 100644
--- a/components/arc/ime/arc_ime_service.cc
+++ b/components/arc/ime/arc_ime_service.cc
@@ -13,8 +13,6 @@
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/ime/arc_ime_bridge_impl.h"
-#include "components/exo/shell_surface.h"
-#include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
@@ -31,9 +29,6 @@
 
 namespace {
 
-constexpr char kArcNotificationAppId[] =
-    "org.chromium.arc.ArcNotificationService";
-
 base::Optional<double> g_override_default_device_scale_factor;
 
 double GetDefaultDeviceScaleFactor() {
@@ -51,35 +46,18 @@
 
   ~ArcWindowDelegateImpl() override = default;
 
-  bool IsExoWindow(const aura::Window* window) const override {
-    return exo::Surface::AsSurface(window) ||
-           exo::ShellSurface::GetMainSurface(window);
-  }
-
-  bool IsArcWindow(const aura::Window* window) const override {
-    if (!IsExoWindow(window))
-      return false;
-
-    aura::Window* active = exo::WMHelper::GetInstance()->GetActiveWindow();
-    if (!active || !active->Contains(window))
-      return false;
-    // If the ARC app is focused, the active window should be ARC app window.
-    if (IsArcAppWindow(active))
-      return true;
-
-    // If the ARC notification is focused, the active window is not ARC app
-    // window. Find an app id set to the window and check if it is the ARC
-    // notification app id.
-    for (; window && window != active; window = window->parent()) {
-      const std::string* app_id = exo::ShellSurface::GetApplicationId(window);
-      if (app_id)
-        return *app_id == kArcNotificationAppId;
+  bool IsInArcAppWindow(const aura::Window* window) const override {
+    for (; window; window = window->parent()) {
+      if (IsArcAppWindow(window))
+        return true;
     }
     return false;
   }
 
   void RegisterFocusObserver() override {
-    DCHECK(exo::WMHelper::HasInstance());
+    // WMHelper is not created in tests.
+    if (!exo::WMHelper::HasInstance())
+      return;
     exo::WMHelper::GetInstance()->AddFocusObserver(ime_service_);
   }
 
@@ -140,11 +118,11 @@
       arc_window_delegate_(new ArcWindowDelegateImpl(this)),
       ime_type_(ui::TEXT_INPUT_TYPE_NONE),
       is_personalized_learning_allowed_(false),
-      has_composition_text_(false),
-      is_focus_observer_installed_(false) {
+      has_composition_text_(false) {
   aura::Env* env = aura::Env::GetInstanceDontCreate();
   if (env)
     env->AddObserver(this);
+  arc_window_delegate_->RegisterFocusObserver();
 }
 
 ArcImeService::~ArcImeService() {
@@ -154,8 +132,7 @@
 
   if (focused_arc_window_)
     focused_arc_window_->RemoveObserver(this);
-  if (is_focus_observer_installed_)
-    arc_window_delegate_->UnregisterFocusObserver();
+  arc_window_delegate_->UnregisterFocusObserver();
   aura::Env* env = aura::Env::GetInstanceDontCreate();
   if (env)
     env->RemoveObserver(this);
@@ -202,15 +179,6 @@
 // Overridden from aura::EnvObserver:
 
 void ArcImeService::OnWindowInitialized(aura::Window* new_window) {
-  // Register the focus observer when every exo window is created because an
-  // application id might not be set here yet.
-  if (arc_window_delegate_->IsExoWindow(new_window)) {
-    if (!is_focus_observer_installed_) {
-      arc_window_delegate_->RegisterFocusObserver();
-      is_focus_observer_installed_ = true;
-    }
-  }
-
   // TODO(mash): Support virtual keyboard under MASH. There is no
   // KeyboardController in the browser process under MASH.
   if (features::IsAshInBrowserProcess() &&
@@ -250,8 +218,7 @@
     return;
 
   const bool detach = (lost_focus && focused_arc_window_ == lost_focus);
-  const bool attach =
-      (gained_focus && arc_window_delegate_->IsArcWindow(gained_focus));
+  const bool attach = arc_window_delegate_->IsInArcAppWindow(gained_focus);
 
   if (detach) {
     focused_arc_window_->RemoveObserver(this);
diff --git a/components/arc/ime/arc_ime_service.h b/components/arc/ime/arc_ime_service.h
index 23ced4f..f5c50432 100644
--- a/components/arc/ime/arc_ime_service.h
+++ b/components/arc/ime/arc_ime_service.h
@@ -55,9 +55,11 @@
 
   class ArcWindowDelegate {
    public:
-    virtual ~ArcWindowDelegate() {}
-    virtual bool IsExoWindow(const aura::Window* window) const = 0;
-    virtual bool IsArcWindow(const aura::Window* window) const = 0;
+    virtual ~ArcWindowDelegate() = default;
+    // Checks the |window| is a transient child of an ARC window.
+    // This method assumes passed |window| is already attached to window
+    // hierarchy.
+    virtual bool IsInArcAppWindow(const aura::Window* window) const = 0;
     virtual void RegisterFocusObserver() = 0;
     virtual void UnregisterFocusObserver() = 0;
     virtual ui::InputMethod* GetInputMethodForWindow(
@@ -172,8 +174,6 @@
 
   aura::Window* focused_arc_window_ = nullptr;
 
-  bool is_focus_observer_installed_;
-
   DISALLOW_COPY_AND_ASSIGN(ArcImeService);
 };
 
diff --git a/components/arc/ime/arc_ime_service_unittest.cc b/components/arc/ime/arc_ime_service_unittest.cc
index 2555ba5..bcffbd6 100644
--- a/components/arc/ime/arc_ime_service_unittest.cc
+++ b/components/arc/ime/arc_ime_service_unittest.cc
@@ -124,11 +124,9 @@
   explicit FakeArcWindowDelegate(ui::InputMethod* input_method)
       : next_id_(0), test_input_method_(input_method) {}
 
-  bool IsExoWindow(const aura::Window* window) const override {
-    return IsArcWindow(window);
-  }
-
-  bool IsArcWindow(const aura::Window* window) const override {
+  bool IsInArcAppWindow(const aura::Window* window) const override {
+    if (!window)
+      return false;
     return arc_window_id_.count(window->id());
   }
 
diff --git a/components/crash/content/browser/child_exit_observer_android.cc b/components/crash/content/browser/child_exit_observer_android.cc
index aa028dfe..c3f2ff9 100644
--- a/components/crash/content/browser/child_exit_observer_android.cc
+++ b/components/crash/content/browser/child_exit_observer_android.cc
@@ -65,7 +65,15 @@
   return g_instance.Pointer();
 }
 
-ChildExitObserver::ChildExitObserver() {
+ChildExitObserver::ChildExitObserver()
+    : notification_registrar_(),
+      registered_clients_lock_(),
+      registered_clients_(),
+      process_host_id_to_pid_(),
+      browser_child_process_info_(),
+      crash_signals_lock_(),
+      child_pid_to_crash_signal_(),
+      scoped_observer_(this) {
   notification_registrar_.Add(this,
                               content::NOTIFICATION_RENDERER_PROCESS_CREATED,
                               content::NotificationService::AllSources());
@@ -76,6 +84,7 @@
                               content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
                               content::NotificationService::AllSources());
   BrowserChildProcessObserver::Add(this);
+  scoped_observer_.Add(crashpad::CrashHandlerHost::Get());
 }
 
 ChildExitObserver::~ChildExitObserver() {
@@ -88,8 +97,26 @@
   registered_clients_.push_back(std::move(client));
 }
 
-void ChildExitObserver::OnChildExit(const TerminationInfo& info) {
+void ChildExitObserver::ChildReceivedCrashSignal(base::ProcessId pid,
+                                                 int signo) {
+  base::AutoLock lock(crash_signals_lock_);
+  bool result =
+      child_pid_to_crash_signal_.insert(std::make_pair(pid, signo)).second;
+  DCHECK(result);
+}
+
+void ChildExitObserver::OnChildExit(TerminationInfo* info) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  {
+    base::AutoLock lock(crash_signals_lock_);
+    auto pid_and_signal = child_pid_to_crash_signal_.find(info->pid);
+    if (pid_and_signal != child_pid_to_crash_signal_.end()) {
+      info->crash_signo = pid_and_signal->second;
+      child_pid_to_crash_signal_.erase(pid_and_signal);
+    }
+  }
+
   std::vector<Client*> registered_clients_copy;
   {
     base::AutoLock auto_lock(registered_clients_lock_);
@@ -97,7 +124,7 @@
       registered_clients_copy.push_back(client.get());
   }
   for (auto* client : registered_clients_copy) {
-    client->OnChildExit(info);
+    client->OnChildExit(*info);
   }
 }
 
@@ -130,7 +157,7 @@
     info.app_state = base::android::ApplicationStatusListener::GetState();
     info.normal_termination = true;
   }
-  OnChildExit(info);
+  OnChildExit(&info);
 }
 
 void ChildExitObserver::BrowserChildProcessKilled(
@@ -196,7 +223,7 @@
     }
     process_host_id_to_pid_.erase(iter);
   }
-  OnChildExit(info);
+  OnChildExit(&info);
 }
 
 }  // namespace crash_reporter
diff --git a/components/crash/content/browser/child_exit_observer_android.h b/components/crash/content/browser/child_exit_observer_android.h
index 5e9f239..0164e744 100644
--- a/components/crash/content/browser/child_exit_observer_android.h
+++ b/components/crash/content/browser/child_exit_observer_android.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
 #define COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
 
+#include <map>
 #include <memory>
 #include <vector>
 
@@ -13,6 +14,9 @@
 #include "base/lazy_instance.h"
 #include "base/memory/ref_counted.h"
 #include "base/process/process.h"
+#include "base/scoped_observer.h"
+#include "base/synchronization/lock.h"
+#include "components/crash/content/browser/crash_handler_host_linux.h"
 #include "content/public/browser/browser_child_process_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -30,19 +34,30 @@
 // purpose of reacting to child process crashes.
 // The ChildExitObserver instance exists on the browser main thread.
 class ChildExitObserver : public content::BrowserChildProcessObserver,
-                          public content::NotificationObserver {
+                          public content::NotificationObserver,
+                          public crashpad::CrashHandlerHost::Observer {
  public:
   struct TerminationInfo {
+    // Used to indicate the child did not receive a crash signal.
+    static constexpr int kInvalidSigno = -1;
+
     TerminationInfo();
     TerminationInfo(const TerminationInfo& other);
     TerminationInfo& operator=(const TerminationInfo& other);
 
+    // TODO(jperaza): Not valid until Crashpad is enabled.
+    bool is_crashed() const { return crash_signo != kInvalidSigno; }
+
     int process_host_id = content::ChildProcessHost::kInvalidUniqueID;
     base::ProcessHandle pid = base::kNullProcessHandle;
     content::ProcessType process_type = content::PROCESS_TYPE_UNKNOWN;
     base::android::ApplicationState app_state =
         base::android::APPLICATION_STATE_UNKNOWN;
 
+    // TODO(jperaza): Not valid until Crashpad is enabled.
+    // The crash signal the child process received before it exited.
+    int crash_signo = kInvalidSigno;
+
     // True if this is intentional shutdown of the child process, e.g. when a
     // tab is closed. Some fields below may not be populated if this is true.
     bool normal_termination = false;
@@ -110,6 +125,9 @@
 
   void RegisterClient(std::unique_ptr<Client> client);
 
+  // crashpad::CrashHandlerHost::Observer
+  void ChildReceivedCrashSignal(base::ProcessId pid, int signo) override;
+
   // BrowserChildProcessStarted must be called from
   // ContentBrowserClient::GetAdditionalMappedFilesForChildProcess
   // overrides, to notify the ChildExitObserver of child process
@@ -137,19 +155,26 @@
                const content::NotificationDetails& details) override;
 
   // Called on child process exit (including crash).
-  void OnChildExit(const TerminationInfo& info);
+  void OnChildExit(TerminationInfo* info);
 
   content::NotificationRegistrar notification_registrar_;
 
   base::Lock registered_clients_lock_;
   std::vector<std::unique_ptr<Client>> registered_clients_;
 
-  // process_host_id to process id.
+  // process_host_id to process id. Only accessed on the UI thread.
   std::map<int, base::ProcessHandle> process_host_id_to_pid_;
 
-  // Key is process_host_id. Only used for BrowserChildProcessHost.
+  // Key is process_host_id. Only used for BrowserChildProcessHost. Only
+  // accessed on the UI thread.
   std::map<int, TerminationInfo> browser_child_process_info_;
 
+  base::Lock crash_signals_lock_;
+  std::map<base::ProcessId, int> child_pid_to_crash_signal_;
+  ScopedObserver<crashpad::CrashHandlerHost,
+                 crashpad::CrashHandlerHost::Observer>
+      scoped_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(ChildExitObserver);
 };
 
diff --git a/components/crash/content/browser/crash_handler_host_linux.cc b/components/crash/content/browser/crash_handler_host_linux.cc
index e672add..485a1fb 100644
--- a/components/crash/content/browser/crash_handler_host_linux.cc
+++ b/components/crash/content/browser/crash_handler_host_linux.cc
@@ -36,9 +36,12 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
+
+#if !defined(OS_ANDROID)
 #include "third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.h"
 #include "third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.h"
 #include "third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.h"
+#endif  // ! defined(OS_ANDROID)
 
 #if defined(OS_ANDROID) && !defined(__LP64__)
 #include <sys/syscall.h>
@@ -52,6 +55,9 @@
 #endif
 
 using content::BrowserThread;
+
+#if !defined(OS_ANDROID)
+
 using google_breakpad::ExceptionHandler;
 
 namespace breakpad {
@@ -494,6 +500,8 @@
 
 }  // namespace breakpad
 
+#endif  // !defined(OS_ANDROID)
+
 #if !defined(OS_CHROMEOS)
 
 namespace crashpad {
diff --git a/components/crash/content/browser/crash_handler_host_linux.h b/components/crash/content/browser/crash_handler_host_linux.h
index 958f4fa82..9160052 100644
--- a/components/crash/content/browser/crash_handler_host_linux.h
+++ b/components/crash/content/browser/crash_handler_host_linux.h
@@ -19,13 +19,18 @@
 #include "base/process/process_handle.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
+
+#if !defined(OS_ANDROID)
 #include "components/crash/content/app/breakpad_linux_impl.h"
+#endif
 
 namespace base {
 class SequencedTaskRunner;
 class Thread;
 }
 
+#if !defined(OS_ANDROID)
+
 namespace breakpad {
 
 struct BreakpadInfo;
@@ -112,6 +117,8 @@
 
 }  // namespace breakpad
 
+#endif  // !defined(OS_ANDROID)
+
 #if !defined(OS_CHROMEOS)
 
 namespace crashpad {
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 120352c..b9d4b48 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -13,6 +13,19 @@
 import("//third_party/protobuf/proto_library.gni")
 import("//url/features.gni")
 
+declare_args() {
+  # In integrated mode, CronetEngine will use the shared network task runner by
+  # other Chromium-based clients like webview without self-initialization.
+  # Besides, the native library would be compiled and loaded together with the
+  # native library of host. This mode is only for Android.
+  integrated_mode = false
+}
+
+buildflag_header("buildflags") {
+  header = "buildflags.h"
+  flags = [ "INTEGRATED_MODE=$integrated_mode" ]
+}
+
 generate_jni("cronet_jni_headers") {
   sources = [
     "java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
@@ -82,6 +95,16 @@
   package_path = "org/chromium/net/impl"
 }
 
+java_cpp_template("integrated_mode_state") {
+  sources = [
+    "java/src/org/chromium/net/impl/IntegratedModeState.template",
+  ]
+  package_path = "org/chromium/net/impl"
+  if (integrated_mode) {
+    defines = [ "INTEGRATED_MODE" ]
+  }
+}
+
 _generated_api_version_java_dir =
     "$target_gen_dir/templates/cronet_api_version_java"
 _generated_api_version_java =
@@ -146,6 +169,7 @@
 
 source_set("cronet_static") {
   deps = [
+    ":buildflags",
     ":cronet_jni_headers",
     ":cronet_jni_registration",
     "//base",
@@ -164,7 +188,6 @@
     "//components/cronet/android/cronet_bidirectional_stream_adapter.cc",
     "//components/cronet/android/cronet_bidirectional_stream_adapter.h",
     "//components/cronet/android/cronet_library_loader.cc",
-    "//components/cronet/android/cronet_library_loader.h",
     "//components/cronet/android/cronet_upload_data_stream_adapter.cc",
     "//components/cronet/android/cronet_upload_data_stream_adapter.h",
     "//components/cronet/android/cronet_url_request_adapter.cc",
@@ -179,6 +202,15 @@
     "//components/cronet/android/url_request_error.h",
   ]
 
+  if (integrated_mode) {
+    sources += [
+      "//components/cronet/android/cronet_integrated_mode_state.cc",
+      "//components/cronet/android/cronet_integrated_mode_state.h",
+    ]
+  } else {
+    sources += [ "//components/cronet/android/cronet_library_loader.h" ]
+  }
+
   include_dirs = [ _cronet_version_header_include_dir ]
 
   cflags = [
@@ -255,6 +287,7 @@
   ":cronet_impl_version_srcjar",
   ":effective_connection_type_java",
   ":http_cache_type_java",
+  ":integrated_mode_state",
   ":load_states_list",
   ":rtt_throughput_values_java",
 ]
diff --git a/components/cronet/android/cronet_integrated_mode_state.cc b/components/cronet/android/cronet_integrated_mode_state.cc
new file mode 100644
index 0000000..6f5e1c2
--- /dev/null
+++ b/components/cronet/android/cronet_integrated_mode_state.cc
@@ -0,0 +1,31 @@
+// Copyright 2018 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 "components/cronet/android/cronet_integrated_mode_state.h"
+
+#include "base/atomicops.h"
+
+namespace cronet {
+namespace {
+
+base::subtle::AtomicWord g_integrated_mode_network_task_runner = 0;
+
+}  // namespace
+
+void SetIntegratedModeNetworkTaskRunner(
+    base::SingleThreadTaskRunner* network_task_runner) {
+  CHECK_EQ(base::subtle::Acquire_CompareAndSwap(
+               &g_integrated_mode_network_task_runner, 0,
+               reinterpret_cast<base::subtle::AtomicWord>(network_task_runner)),
+           0);
+}
+
+base::SingleThreadTaskRunner* GetIntegratedModeNetworkTaskRunner() {
+  base::subtle::AtomicWord task_runner =
+      base::subtle::Release_Load(&g_integrated_mode_network_task_runner);
+  CHECK(task_runner);
+  return reinterpret_cast<base::SingleThreadTaskRunner*>(task_runner);
+}
+
+}  // namespace cronet
diff --git a/components/cronet/android/cronet_integrated_mode_state.h b/components/cronet/android/cronet_integrated_mode_state.h
new file mode 100644
index 0000000..b53d3a2
--- /dev/null
+++ b/components/cronet/android/cronet_integrated_mode_state.h
@@ -0,0 +1,29 @@
+// Copyright 2018 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 COMPONENTS_CRONET_ANDROID_CRONET_INTEGRATED_MODE_STATE_H_
+#define COMPONENTS_CRONET_ANDROID_CRONET_INTEGRATED_MODE_STATE_H_
+
+#include "base/task_scheduler/task_scheduler.h"
+
+namespace cronet {
+
+/**
+ * Set a shared network task runner into Cronet in integrated mode. All the
+ * Cronet network tasks would be running in this task runner. This method should
+ * be invoked in native side before creating Cronet instance.
+ */
+void SetIntegratedModeNetworkTaskRunner(
+    base::SingleThreadTaskRunner* network_task_runner);
+
+/**
+ * Get the task runner for Cronet integrated mode. It would be invoked in the
+ * initialization of CronetURLRequestContext. This method must be invoked after
+ * SetIntegratedModeNetworkTaskRunner.
+ */
+base::SingleThreadTaskRunner* GetIntegratedModeNetworkTaskRunner();
+
+}  // namespace cronet
+
+#endif  // COMPONENTS_CRONET_ANDROID_CRONET_INTEGRATED_MODE_STATE_H_
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index 9359844..6e2cba3 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/cronet/android/cronet_library_loader.h"
-
 #include <jni.h>
 #include <memory>
 #include <string>
@@ -24,7 +22,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task_scheduler/task_scheduler.h"
 #include "build/build_config.h"
-#include "components/cronet/android/cronet_jni_registration.h"
+#include "components/cronet/android/buildflags.h"
 #include "components/cronet/cronet_global_state.h"
 #include "components/cronet/version.h"
 #include "jni/CronetLibraryLoader_jni.h"
@@ -40,6 +38,11 @@
 #include "base/i18n/icu_util.h"  // nogncheck
 #endif
 
+#if !BUILDFLAG(INTEGRATED_MODE)
+#include "components/cronet/android/cronet_jni_registration.h"
+#include "components/cronet/android/cronet_library_loader.h"
+#endif
+
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
 
@@ -50,7 +53,9 @@
 // notifications generally live.
 base::MessageLoop* g_init_message_loop = nullptr;
 
+#if !BUILDFLAG(INTEGRATED_MODE)
 net::NetworkChangeNotifier* g_network_change_notifier = nullptr;
+#endif
 
 base::WaitableEvent g_init_thread_init_done(
     base::WaitableEvent::ResetPolicy::MANUAL,
@@ -69,6 +74,9 @@
   return g_init_message_loop == base::MessageLoop::current();
 }
 
+// In integrated mode, Cronet native library is built and loaded together with
+// the native library of the host app.
+#if !BUILDFLAG(INTEGRATED_MODE)
 // Checks the available version of JNI. Also, caches Java reflection artifacts.
 jint CronetOnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
@@ -88,26 +96,37 @@
 
   base::android::LibraryLoaderExitHook();
 }
+#endif
 
 void JNI_CronetLibraryLoader_CronetInitOnInitThread(
     JNIEnv* env,
     const JavaParamRef<jclass>& jcaller) {
+// In integrated mode, ICU and FeatureList has been initialized by the host.
+#if !BUILDFLAG(INTEGRATED_MODE)
 #if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
   base::i18n::InitializeICU();
 #endif
-
   base::FeatureList::InitializeInstance(std::string(), std::string());
+#endif
+
+  // Initialize message loop for init thread.
   DCHECK(!base::MessageLoop::current());
   DCHECK(!g_init_message_loop);
   g_init_message_loop =
       new base::MessageLoop(base::MessageLoop::Type::TYPE_JAVA);
-  DCHECK(!g_network_change_notifier);
 
+// In integrated mode, NetworkChangeNotifier has been initialized by the host.
+#if BUILDFLAG(INTEGRATED_MODE)
+  CHECK(net::NetworkChangeNotifier::HasNetworkChangeNotifier());
+#else
+  DCHECK(!g_network_change_notifier);
   if (!net::NetworkChangeNotifier::GetFactory()) {
     net::NetworkChangeNotifier::SetFactory(
         new net::NetworkChangeNotifierFactoryAndroid());
   }
   g_network_change_notifier = net::NetworkChangeNotifier::Create();
+#endif
+
   g_init_thread_init_done.Signal();
 }
 
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index bcdbbba05..02e2fa1 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -33,6 +33,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "components/cronet/android/buildflags.h"
 #include "components/cronet/android/cronet_library_loader.h"
 #include "components/cronet/cronet_prefs_manager.h"
 #include "components/cronet/histogram_manager.h"
@@ -59,6 +60,10 @@
 #include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_interceptor.h"
 
+#if BUILDFLAG(INTEGRATED_MODE)
+#include "components/cronet/android/cronet_integrated_mode_state.h"
+#endif
+
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
 
@@ -82,8 +87,15 @@
     std::unique_ptr<URLRequestContextConfig> context_config) {
   // Create context and pass ownership of |this| (self) to the context.
   std::unique_ptr<CronetURLRequestContextAdapter> self(this);
+#if BUILDFLAG(INTEGRATED_MODE)
+  // Create CronetURLRequestContext running in integrated network task runner.
+  context_ =
+      new CronetURLRequestContext(std::move(context_config), std::move(self),
+                                  GetIntegratedModeNetworkTaskRunner());
+#else
   context_ =
       new CronetURLRequestContext(std::move(context_config), std::move(self));
+#endif
 }
 
 CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() = default;
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
index 536d8ba..c874d007 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
@@ -32,7 +32,7 @@
     // the global singleton NetworkChangeNotifier live on it and are never killed.
     private static final HandlerThread sInitThread = new HandlerThread("CronetInit");
     // Has library loading commenced?  Setting guarded by sLoadLock.
-    private static volatile boolean sLibraryLoaded = false;
+    private static volatile boolean sLibraryLoaded = IntegratedModeState.INTEGRATED_MODE_ENABLED;
     // Has ensureInitThreadInitialized() completed?
     private static volatile boolean sInitThreadInitDone = false;
     // Block calling native methods until this ConditionVariable opens to indicate loadLibrary()
@@ -47,7 +47,10 @@
             Context applicationContext, final CronetEngineBuilderImpl builder) {
         synchronized (sLoadLock) {
             if (!sInitThreadInitDone) {
-                ContextUtils.initApplicationContext(applicationContext);
+                if (!IntegratedModeState.INTEGRATED_MODE_ENABLED) {
+                    // In integrated mode, application context should be initialized by the host.
+                    ContextUtils.initApplicationContext(applicationContext);
+                }
                 if (!sInitThread.isAlive()) {
                     sInitThread.start();
                 }
@@ -95,15 +98,19 @@
         if (sInitThreadInitDone) {
             return;
         }
-        NetworkChangeNotifier.init();
-        // Registers to always receive network notifications. Note
-        // that this call is fine for Cronet because Cronet
-        // embedders do not have API access to create network change
-        // observers. Existing observers in the net stack do not
-        // perform expensive work.
-        NetworkChangeNotifier.registerToReceiveNotificationsAlways();
-        // Wait for loadLibrary() to complete so JNI is registered.
-        sWaitForLibLoad.block();
+        if (IntegratedModeState.INTEGRATED_MODE_ENABLED) {
+            assert NetworkChangeNotifier.isInitialized();
+        } else {
+            NetworkChangeNotifier.init();
+            // Registers to always receive network notifications. Note
+            // that this call is fine for Cronet because Cronet
+            // embedders do not have API access to create network change
+            // observers. Existing observers in the net stack do not
+            // perform expensive work.
+            NetworkChangeNotifier.registerToReceiveNotificationsAlways();
+            // Wait for loadLibrary() to complete so JNI is registered.
+            sWaitForLibLoad.block();
+        }
         assert sLibraryLoaded;
         // registerToReceiveNotificationsAlways() is called before the native
         // NetworkChangeNotifierAndroid is created, so as to avoid receiving
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
index 58fa82a..0f26a7bf 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
@@ -152,7 +152,9 @@
         mNetworkQualityEstimatorEnabled = builder.networkQualityEstimatorEnabled();
         mNetworkThreadPriority = builder.threadPriority(Process.THREAD_PRIORITY_BACKGROUND);
         CronetLibraryLoader.ensureInitialized(builder.getContext(), builder);
-        nativeSetMinLogLevel(getLoggingLevel());
+        if (!IntegratedModeState.INTEGRATED_MODE_ENABLED) {
+            nativeSetMinLogLevel(getLoggingLevel());
+        }
         if (builder.httpCacheMode() == HttpCacheType.DISK) {
             mInUseStoragePath = builder.storagePath();
             synchronized (sInUseStoragePaths) {
@@ -580,8 +582,12 @@
     private void initNetworkThread() {
         mNetworkThread = Thread.currentThread();
         mInitCompleted.open();
-        Thread.currentThread().setName("ChromiumNet");
-        Process.setThreadPriority(mNetworkThreadPriority);
+        if (!IntegratedModeState.INTEGRATED_MODE_ENABLED) {
+            // In integrated mode, network thread is shared from the host.
+            // Cronet shouldn't change the property of the thread.
+            Thread.currentThread().setName("ChromiumNet");
+            Process.setThreadPriority(mNetworkThreadPriority);
+        }
     }
 
     @SuppressWarnings("unused")
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/IntegratedModeState.template b/components/cronet/android/java/src/org/chromium/net/impl/IntegratedModeState.template
new file mode 100644
index 0000000..28fb6c3
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/impl/IntegratedModeState.template
@@ -0,0 +1,21 @@
+// Copyright 2018 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.
+
+package org.chromium.net.impl;
+
+/**
+ * This template file provides the flags of Cronet integrated mode for Java side.
+ */
+public class IntegratedModeState {
+
+  // A boolean flag indicating whether integrated mode is enabled. In integrated mode, CronetEngine
+  // would use the shared network task runner by other Chromium-based clients like webview, Chrome
+  // Android, Cronet without self-initialization.
+  public static final boolean INTEGRATED_MODE_ENABLED =
+#if defined(INTEGRATED_MODE)
+    true;
+#else
+    false;
+#endif
+}
diff --git a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
index 947f8faa..1f3c914 100644
--- a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
+++ b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
@@ -128,7 +128,7 @@
         return (provider != null) ? provider : super.getAccessibilityNodeProvider();
     }
 
-    // Needed by ContentViewCore.InternalAccessDelegate
+    // Needed by ViewEventSink.InternalAccessDelegate
     @Override
     public void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index e9b00583..c437ed9 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -177,8 +177,23 @@
   if (!state)
     return;
 
-  SetFocusState(state->focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH);
-  focus_source_ = state->focus_source;
+  // The tab-management system saves the last-focused control for each tab and
+  // restores it. That operation also updates this edit model's focus_state_
+  // if necessary. This occurs before we reach this point in the code.
+  //
+  // The only reason we need to separately save and restore our focus state is
+  // to preserve our special "invisible focus" state used for the fakebox.
+  //
+  // However, in some circumstances (if the last-focused control was destroyed),
+  // the Omnibox will be focused by default, and the edit model's saved state
+  // may be invalid. We make a check to guard against that.
+  bool saved_focus_state_invalid = focus_state_ == OMNIBOX_FOCUS_VISIBLE &&
+                                   state->focus_state == OMNIBOX_FOCUS_NONE;
+  if (!saved_focus_state_invalid) {
+    SetFocusState(state->focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH);
+    focus_source_ = state->focus_source;
+  }
+
   // Restore any user editing.
   if (state->user_input_in_progress) {
     // NOTE: Be sure to set keyword-related state AFTER invoking
diff --git a/components/omnibox/browser/omnibox_edit_model_unittest.cc b/components/omnibox/browser/omnibox_edit_model_unittest.cc
index cfc8ddba..831c13f 100644
--- a/components/omnibox/browser/omnibox_edit_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_edit_model_unittest.cc
@@ -244,3 +244,20 @@
       std::string(OmniboxEditModel::kMaxPasteAndGoTextLength + 1, '.'));
   EXPECT_FALSE(model()->OmniboxEditModel::CanPasteAndGo(long_text));
 }
+
+// The tab-switching system sometimes focuses the Omnibox even if it was not
+// previously focused. In those cases, ignore the saved focus state.
+TEST_F(OmniboxEditModelTest, IgnoreInvalidSavedFocusStates) {
+  // The Omnibox starts out unfocused. Save that state.
+  ASSERT_FALSE(model()->has_focus());
+  OmniboxEditModel::State state = model()->GetStateForTabSwitch();
+  ASSERT_EQ(OMNIBOX_FOCUS_NONE, state.focus_state);
+
+  // Simulate the tab-switching system focusing the Omnibox.
+  model()->OnSetFocus(false);
+
+  // Restoring the old saved state should not clobber the model's focus state.
+  model()->RestoreState(&state);
+  EXPECT_TRUE(model()->has_focus());
+  EXPECT_TRUE(model()->is_caret_visible());
+}
diff --git a/components/password_manager/ios/BUILD.gn b/components/password_manager/ios/BUILD.gn
new file mode 100644
index 0000000..a1374cf
--- /dev/null
+++ b/components/password_manager/ios/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2018 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.
+
+component("ios") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//base",
+    "//ios/web/public",
+  ]
+
+  sources = [
+    "js_password_manager.h",
+    "js_password_manager.mm",
+  ]
+}
diff --git a/components/password_manager/ios/DEPS b/components/password_manager/ios/DEPS
new file mode 100644
index 0000000..0fc0ddd
--- /dev/null
+++ b/components/password_manager/ios/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ios/web/public",
+]
diff --git a/ios/chrome/browser/passwords/js_password_manager.h b/components/password_manager/ios/js_password_manager.h
similarity index 90%
rename from ios/chrome/browser/passwords/js_password_manager.h
rename to components/password_manager/ios/js_password_manager.h
index a2dc1af..f4480e85 100644
--- a/ios/chrome/browser/passwords/js_password_manager.h
+++ b/components/password_manager/ios/js_password_manager.h
@@ -1,9 +1,9 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
-#define IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
+#ifndef COMPONENTS_PASSWORD_MANAGER_IOS_JS_PASSWORD_MANAGER_H_
+#define COMPONENTS_PASSWORD_MANAGER_IOS_JS_PASSWORD_MANAGER_H_
 
 #include "base/ios/block_types.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
@@ -54,4 +54,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
+#endif  // COMPONENTS_PASSWORD_MANAGER_IOS_JS_PASSWORD_MANAGER_H_
diff --git a/ios/chrome/browser/passwords/js_password_manager.mm b/components/password_manager/ios/js_password_manager.mm
similarity index 94%
rename from ios/chrome/browser/passwords/js_password_manager.mm
rename to components/password_manager/ios/js_password_manager.mm
index 31f7d88..8dbb824 100644
--- a/ios/chrome/browser/passwords/js_password_manager.mm
+++ b/components/password_manager/ios/js_password_manager.mm
@@ -1,8 +1,8 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2018 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.
 
-#import "ios/chrome/browser/passwords/js_password_manager.h"
+#import "components/password_manager/ios/js_password_manager.h"
 
 #include "base/json/string_escape.h"
 #include "base/logging.h"
diff --git a/components/policy/core/common/schema_registry.cc b/components/policy/core/common/schema_registry.cc
index e44f3d7..a197877 100644
--- a/components/policy/core/common/schema_registry.cc
+++ b/components/policy/core/common/schema_registry.cc
@@ -59,7 +59,10 @@
     schema_map_ = new SchemaMap(map);
     Notify(false);
   } else {
-    NOTREACHED();
+    // Extension might be uninstalled before install so the associated policies
+    // are unregistered before registered. For example, a policy forced
+    // extension is removed from forced list during launch due to policy update.
+    DCHECK(ns.domain != POLICY_DOMAIN_CHROME);
   }
 }
 
diff --git a/components/policy/core/common/schema_registry_unittest.cc b/components/policy/core/common/schema_registry_unittest.cc
index aea54d1..a125976 100644
--- a/components/policy/core/common/schema_registry_unittest.cc
+++ b/components/policy/core/common/schema_registry_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/test/gtest_util.h"
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/schema.h"
 #include "extensions/buildflags/buildflags.h"
@@ -326,4 +327,17 @@
   EXPECT_TRUE(forwarding_2.IsReady());
 }
 
+// Extension policy unregister before register shouldn't cause DCHECK failure.
+// However, Chrome policy should always register first.
+TEST(SchemaRegistryTest, UnregisterBeforeRegister) {
+  SchemaRegistry registry;
+  ASSERT_NO_FATAL_FAILURE(registry.UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "")));
+  ASSERT_NO_FATAL_FAILURE(registry.UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_SIGNIN_EXTENSIONS, "")));
+
+  ASSERT_DCHECK_DEATH(
+      registry.UnregisterComponent(PolicyNamespace(POLICY_DOMAIN_CHROME, "")));
+}
+
 }  // namespace policy
diff --git a/components/previews/core/previews_experiments.cc b/components/previews/core/previews_experiments.cc
index 07f2dc4b..ce9c28a6 100644
--- a/components/previews/core/previews_experiments.cc
+++ b/components/previews/core/previews_experiments.cc
@@ -178,6 +178,10 @@
   return base::FeatureList::IsEnabled(features::kResourceLoadingHints);
 }
 
+bool IsLitePageServerPreviewsEnabled() {
+  return base::FeatureList::IsEnabled(features::kLitePageServerPreviews);
+}
+
 int OfflinePreviewsVersion() {
   return GetParamValueAsInt(kClientSidePreviewsFieldTrial, kVersion, 0);
 }
diff --git a/components/previews/core/previews_experiments.h b/components/previews/core/previews_experiments.h
index b63c6c62..a1a6766 100644
--- a/components/previews/core/previews_experiments.h
+++ b/components/previews/core/previews_experiments.h
@@ -98,6 +98,7 @@
 bool IsClientLoFiEnabled();
 bool IsNoScriptPreviewsEnabled();
 bool IsResourceLoadingHintsEnabled();
+bool IsLitePageServerPreviewsEnabled();
 
 // The blacklist version for each preview type.
 int OfflinePreviewsVersion();
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc
index 937ee624..499b5141 100644
--- a/components/previews/core/previews_features.cc
+++ b/components/previews/core/previews_features.cc
@@ -65,5 +65,9 @@
 const base::Feature kResourceLoadingHints{"ResourceLoadingHints",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables client redirects to a server-rendered lite page preview.
+const base::Feature kLitePageServerPreviews{"LitePageServerPreviews",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace previews
diff --git a/components/previews/core/previews_features.h b/components/previews/core/previews_features.h
index fd0a1a8..6c20004 100644
--- a/components/previews/core/previews_features.h
+++ b/components/previews/core/previews_features.h
@@ -19,6 +19,7 @@
 extern const base::Feature kOptimizationHintsExperiments;
 constexpr char kOptimizationHintsExperimentNameParam[] = "experiment_name";
 extern const base::Feature kResourceLoadingHints;
+extern const base::Feature kLitePageServerPreviews;
 
 }  // namespace features
 }  // namespace previews
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 591b739..7093641 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -127,7 +127,7 @@
   current_surface_id_ = SurfaceId(frame_sink_id_, id);
   device_scale_factor_ = device_scale_factor;
 
-  UpdateRootSurfaceResourcesLocked();
+  UpdateRootFrameMissing();
   if (scheduler_)
     scheduler_->SetNewRootSurface(current_surface_id_);
 }
@@ -252,11 +252,11 @@
   aggregator_->SetOutputColorSpace(blending_color_space_, device_color_space_);
 }
 
-void Display::UpdateRootSurfaceResourcesLocked() {
+void Display::UpdateRootFrameMissing() {
   Surface* surface = surface_manager_->GetSurfaceForId(current_surface_id_);
-  bool root_surface_resources_locked = !surface || !surface->HasActiveFrame();
+  bool root_frame_missing = !surface || !surface->HasActiveFrame();
   if (scheduler_)
-    scheduler_->SetRootSurfaceResourcesLocked(root_surface_resources_locked);
+    scheduler_->SetRootFrameMissing(root_frame_missing);
 }
 
 void Display::OnContextLost() {
@@ -495,7 +495,7 @@
   }
   if (surface_id == current_surface_id_) {
     display_damaged = true;
-    UpdateRootSurfaceResourcesLocked();
+    UpdateRootFrameMissing();
   }
   if (display_damaged)
     surfaces_to_ack_on_next_draw_.push_back(surface_id);
diff --git a/components/viz/service/display/display.h b/components/viz/service/display/display.h
index c88a5f7..fc4993f 100644
--- a/components/viz/service/display/display.h
+++ b/components/viz/service/display/display.h
@@ -135,7 +135,7 @@
 
  private:
   void InitializeRenderer();
-  void UpdateRootSurfaceResourcesLocked();
+  void UpdateRootFrameMissing();
 
   // ContextLostObserver implementation.
   void OnContextLost() override;
diff --git a/components/viz/service/display/display_scheduler.cc b/components/viz/service/display/display_scheduler.cc
index db8c8cc..f97496e 100644
--- a/components/viz/service/display/display_scheduler.cc
+++ b/components/viz/service/display/display_scheduler.cc
@@ -24,7 +24,7 @@
       inside_surface_damaged_(false),
       visible_(false),
       output_surface_lost_(false),
-      root_surface_resources_locked_(true),
+      root_frame_missing_(true),
       inside_begin_frame_deadline_interval_(false),
       needs_draw_(false),
       expecting_root_surface_damage_because_of_resize_(false),
@@ -65,15 +65,13 @@
   ScheduleBeginFrameDeadline();
 }
 
-// If we try to draw when the root surface resources are locked, the
-// draw will fail.
-void DisplayScheduler::SetRootSurfaceResourcesLocked(bool locked) {
-  TRACE_EVENT1("viz", "DisplayScheduler::SetRootSurfaceResourcesLocked",
-               "locked", locked);
-  if (root_surface_resources_locked_ == locked)
+void DisplayScheduler::SetRootFrameMissing(bool missing) {
+  TRACE_EVENT1("viz", "DisplayScheduler::SetRootFrameMissing", "missing",
+               missing);
+  if (root_frame_missing_ == missing)
     return;
 
-  root_surface_resources_locked_ = locked;
+  root_frame_missing_ = missing;
   MaybeStartObservingBeginFrames();
   ScheduleBeginFrameDeadline();
 }
@@ -297,7 +295,7 @@
   // Note: When any of these cases becomes true, MaybeStartObservingBeginFrames
   // must be called to ensure the draw will happen.
   return needs_draw_ && !output_surface_lost_ && visible_ &&
-         !root_surface_resources_locked_;
+         !root_frame_missing_;
 }
 
 void DisplayScheduler::OnBeginFrameSourcePausedChanged(bool paused) {
@@ -396,9 +394,8 @@
     return BeginFrameDeadlineMode::kLate;
   }
 
-  if (root_surface_resources_locked_) {
-    TRACE_EVENT_INSTANT0("viz", "Root surface resources locked",
-                         TRACE_EVENT_SCOPE_THREAD);
+  if (root_frame_missing_) {
+    TRACE_EVENT_INSTANT0("viz", "Root frame missing", TRACE_EVENT_SCOPE_THREAD);
     return BeginFrameDeadlineMode::kLate;
   }
 
diff --git a/components/viz/service/display/display_scheduler.h b/components/viz/service/display/display_scheduler.h
index d3cfbb04..11fa355 100644
--- a/components/viz/service/display/display_scheduler.h
+++ b/components/viz/service/display/display_scheduler.h
@@ -47,7 +47,11 @@
   void SetClient(DisplaySchedulerClient* client);
 
   void SetVisible(bool visible);
-  void SetRootSurfaceResourcesLocked(bool locked);
+
+  // Notifies that the root surface doesn't exist or doesn't have an active
+  // frame and therefore draw is not possible.
+  void SetRootFrameMissing(bool missing);
+
   void ForceImmediateSwapIfPossible();
   void SetNeedsOneBeginFrame();
   base::TimeTicks current_frame_time() const {
@@ -115,7 +119,7 @@
 
   bool visible_;
   bool output_surface_lost_;
-  bool root_surface_resources_locked_;
+  bool root_frame_missing_;
 
   bool inside_begin_frame_deadline_interval_;
   bool needs_draw_;
diff --git a/components/viz/service/display/display_scheduler_unittest.cc b/components/viz/service/display/display_scheduler_unittest.cc
index b6c7a427..2430f76 100644
--- a/components/viz/service/display/display_scheduler_unittest.cc
+++ b/components/viz/service/display/display_scheduler_unittest.cc
@@ -138,7 +138,7 @@
     surface_manager_.RemoveObserver(&scheduler_);
   }
 
-  void SetUp() override { scheduler_.SetRootSurfaceResourcesLocked(false); }
+  void SetUp() override { scheduler_.SetRootFrameMissing(false); }
 
   void AdvanceTimeAndBeginFrameForTest(
       const std::vector<SurfaceId>& observing_surfaces) {
@@ -589,7 +589,7 @@
   EXPECT_EQ(2, client_.draw_and_swap_count());
 }
 
-TEST_F(DisplaySchedulerTest, RootSurfaceResourcesLocked) {
+TEST_F(DisplaySchedulerTest, RootFrameMissing) {
   SurfaceId root_surface_id(
       kArbitraryFrameSinkId,
       LocalSurfaceId(1, base::UnguessableToken::Create()));
@@ -609,29 +609,29 @@
   scheduler_.BeginFrameDeadlineForTest();
   EXPECT_EQ(1, client_.draw_and_swap_count());
 
-  // Deadline triggers late while root resources are locked.
+  // Deadline triggers late while root frame is missing.
   AdvanceTimeAndBeginFrameForTest({sid1});
   late_deadline = now_src().NowTicks() + BeginFrameArgs::DefaultInterval();
   SurfaceDamaged(sid1);
   EXPECT_GT(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
-  scheduler_.SetRootSurfaceResourcesLocked(true);
+  scheduler_.SetRootFrameMissing(true);
   EXPECT_EQ(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
 
-  // Deadline does not DrawAndSwap while root resources are locked.
+  // Deadline does not DrawAndSwap while root frame is missing.
   EXPECT_EQ(1, client_.draw_and_swap_count());
   SurfaceDamaged(sid1);
   scheduler_.BeginFrameDeadlineForTest();
   EXPECT_EQ(1, client_.draw_and_swap_count());
 
-  //  Deadline triggers normally when root resources are unlocked.
+  //  Deadline triggers normally when root frame is not missing.
   AdvanceTimeAndBeginFrameForTest({sid1, root_surface_id});
   EXPECT_FALSE(scheduler_.inside_begin_frame_deadline_interval());
   SurfaceDamaged(sid1);
 
   // The deadline is not updated because the display scheduler does not receive
-  // a BeginFrame while root resources are locked.
+  // a BeginFrame while the root frame is missing.
   EXPECT_EQ(late_deadline, scheduler_.DesiredBeginFrameDeadlineTimeForTest());
-  scheduler_.SetRootSurfaceResourcesLocked(false);
+  scheduler_.SetRootFrameMissing(false);
   EXPECT_TRUE(scheduler_.inside_begin_frame_deadline_interval());
   SurfaceDamaged(root_surface_id);
   EXPECT_EQ(base::TimeTicks(),
@@ -752,7 +752,7 @@
   SurfaceDamaged(sid1);
   EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
 
-  scheduler_.SetRootSurfaceResourcesLocked(true);
+  scheduler_.SetRootFrameMissing(true);
   EXPECT_EQ(++count, scheduler_.scheduler_begin_frame_deadline_count());
 
   scheduler_.OutputSurfaceLost();
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index d61a444..6d90f4f 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1906,6 +1906,8 @@
       "media/capture/desktop_capture_device.h",
       "media/capture/frame_sink_video_capture_device.cc",
       "media/capture/frame_sink_video_capture_device.h",
+      "media/capture/mouse_cursor_overlay_controller.cc",
+      "media/capture/mouse_cursor_overlay_controller.h",
       "media/capture/web_contents_video_capture_device.cc",
       "media/capture/web_contents_video_capture_device.h",
     ]
@@ -1917,6 +1919,7 @@
         "media/capture/aura_window_video_capture_device.h",
         "media/capture/cursor_renderer_aura.cc",
         "media/capture/cursor_renderer_aura.h",
+        "media/capture/mouse_cursor_overlay_controller_aura.cc",
       ]
     }
     if (is_chromeos) {
@@ -1929,6 +1932,7 @@
       sources += [
         "media/capture/cursor_renderer_mac.h",
         "media/capture/cursor_renderer_mac.mm",
+        "media/capture/mouse_cursor_overlay_controller_mac.mm",
       ]
       deps += [
         "//sandbox/mac:seatbelt",
diff --git a/content/browser/android/dialog_overlay_impl.h b/content/browser/android/dialog_overlay_impl.h
index 51e4067..4cb4cbe 100644
--- a/content/browser/android/dialog_overlay_impl.h
+++ b/content/browser/android/dialog_overlay_impl.h
@@ -16,7 +16,7 @@
 namespace content {
 
 // Native counterpart to DialogOverlayImpl java class.  This is created by the
-// java side.  When the ContentViewCore for the provided token is attached or
+// java side.  When the WebContents for the provided token is attached or
 // detached from a WindowAndroid, we get the Android window token and notify the
 // java side.
 class DialogOverlayImpl : public ui::ViewAndroidObserver,
diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.h b/content/browser/android/java/gin_java_bridge_dispatcher_host.h
index 7404fcf..8e55c64 100644
--- a/content/browser/android/java/gin_java_bridge_dispatcher_host.h
+++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.h
@@ -25,7 +25,7 @@
 
 namespace content {
 
-// This class handles injecting Java objects into a single ContentViewCore /
+// This class handles injecting Java objects into a single WebContents /
 // WebView. The Java object itself lives in the browser process on a background
 // thread, while multiple JavaScript wrapper objects (one per frame) are created
 // on the renderer side.  The injected Java objects are identified by ObjectID,
diff --git a/content/browser/android/java/gin_java_bridge_message_filter.cc b/content/browser/android/java/gin_java_bridge_message_filter.cc
index 33051e1..03c151d 100644
--- a/content/browser/android/java/gin_java_bridge_message_filter.cc
+++ b/content/browser/android/java/gin_java_bridge_message_filter.cc
@@ -110,7 +110,7 @@
   // Not being able to find a host is OK -- we can receive messages from
   // RenderFrames for which the corresponding host part has already been
   // destroyed. That means, any references to Java objects that the host was
-  // holding were already released (with the death of ContentViewCore), so we
+  // holding were already released (with the death of WebContents), so we
   // can just ignore such messages.
   // RenderProcessHostImpl does the same -- if it can't find a listener
   // for the message's routing id, it just drops the message silently.
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 60452b0ae..fcbffe4a 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -403,24 +403,21 @@
   return Response::OK();
 }
 
-Response PageHandler::Reload(Maybe<bool> bypassCache,
-                             Maybe<std::string> script_to_evaluate_on_load) {
+void PageHandler::Reload(Maybe<bool> bypassCache,
+                         Maybe<std::string> script_to_evaluate_on_load,
+                         std::unique_ptr<ReloadCallback> callback) {
   WebContentsImpl* web_contents = GetWebContents();
-  if (!web_contents)
-    return Response::InternalError();
-  if (web_contents->IsCrashed() ||
-      web_contents->GetURL().scheme() == url::kDataScheme ||
-      (web_contents->GetController().GetVisibleEntry() &&
-       web_contents->GetController().GetVisibleEntry()->IsViewSourceMode())) {
-    web_contents->GetController().Reload(bypassCache.fromMaybe(false)
-                                             ? ReloadType::BYPASSING_CACHE
-                                             : ReloadType::NORMAL,
-                                         false);
-    return Response::OK();
-  } else {
-    // Handle reload in renderer except for crashed and view source mode.
-    return Response::FallThrough();
+  if (!web_contents) {
+    callback->sendFailure(Response::InternalError());
+    return;
   }
+  // It is important to fallback before triggering reload, so that
+  // renderer could prepare beforehand.
+  callback->fallThrough();
+  web_contents->GetController().Reload(bypassCache.fromMaybe(false)
+                                           ? ReloadType::BYPASSING_CACHE
+                                           : ReloadType::NORMAL,
+                                       false);
 }
 
 void PageHandler::Navigate(const std::string& url,
diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h
index eb6a09a..4cf049c 100644
--- a/content/browser/devtools/protocol/page_handler.h
+++ b/content/browser/devtools/protocol/page_handler.h
@@ -93,8 +93,9 @@
 
   Response Crash() override;
   Response Close() override;
-  Response Reload(Maybe<bool> bypassCache,
-                  Maybe<std::string> script_to_evaluate_on_load) override;
+  void Reload(Maybe<bool> bypassCache,
+              Maybe<std::string> script_to_evaluate_on_load,
+              std::unique_ptr<ReloadCallback> callback) override;
   void Navigate(const std::string& url,
                 Maybe<std::string> referrer,
                 Maybe<std::string> transition_type,
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index 0919b220..3174c153 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -51,7 +51,7 @@
                     "startScreencast", "stopScreencast", "screencastFrameAck", "handleJavaScriptDialog", "setColorPickerEnabled", "requestAppBanner",
                     "printToPDF", "bringToFront", "setDownloadBehavior", "getAppManifest", "crash", "close", "setWebLifecycleState"],
                 "include_events": ["colorPicked", "interstitialShown", "interstitialHidden", "javascriptDialogOpening", "javascriptDialogClosed", "screencastVisibilityChanged", "screencastFrame"],
-                "async": ["captureScreenshot", "printToPDF", "navigate", "getAppManifest"]
+                "async": ["captureScreenshot", "printToPDF", "navigate", "getAppManifest", "reload"]
             },
             {
                 "domain": "Runtime",
diff --git a/content/browser/media/android/browser_media_player_manager.h b/content/browser/media/android/browser_media_player_manager.h
index aaaf4cc..f942adc 100644
--- a/content/browser/media/android/browser_media_player_manager.h
+++ b/content/browser/media/android/browser_media_player_manager.h
@@ -24,9 +24,6 @@
 struct MediaPlayerHostMsg_Initialize_Params;
 
 namespace content {
-#if !defined(USE_AURA)
-class ContentViewCore;
-#endif
 class RenderFrameHost;
 class WebContents;
 
@@ -50,10 +47,6 @@
   // Returns nullptr if no factory was registered.
   static BrowserMediaPlayerManager* Create(RenderFrameHost* rfh);
 
-#if !defined(USE_AURA)
-  ContentViewCore* GetContentViewCore() const;
-#endif
-
   ~BrowserMediaPlayerManager() override;
 
   // Called when browser player wants the renderer media element to seek.
@@ -164,12 +157,6 @@
   // will release its resources later.
   ActivePlayerMap active_players_;
 
-  // Player ID of the fullscreen media player.
-  int fullscreen_player_id_;
-
-  // Whether the fullscreen player has been Release()-d.
-  bool fullscreen_player_is_released_;
-
   WebContents* const web_contents_;
 
   // Object for retrieving resources media players.
diff --git a/content/browser/media/capture/mouse_cursor_overlay_controller.cc b/content/browser/media/capture/mouse_cursor_overlay_controller.cc
new file mode 100644
index 0000000..0bc6700
--- /dev/null
+++ b/content/browser/media/capture/mouse_cursor_overlay_controller.cc
@@ -0,0 +1,166 @@
+// Copyright 2018 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/browser/media/capture/mouse_cursor_overlay_controller.h"
+
+#include <cmath>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+
+namespace content {
+
+// static
+constexpr base::TimeDelta MouseCursorOverlayController::kIdleTimeout;
+
+void MouseCursorOverlayController::Start(
+    std::unique_ptr<Overlay> overlay,
+    scoped_refptr<base::SequencedTaskRunner> task_runner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+  DCHECK(overlay);
+  DCHECK(task_runner);
+
+  Stop();
+  overlay_ = std::move(overlay);
+  overlay_task_runner_ = std::move(task_runner);
+}
+
+void MouseCursorOverlayController::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  if (overlay_) {
+    overlay_task_runner_->DeleteSoon(FROM_HERE, overlay_.release());
+    overlay_task_runner_ = nullptr;
+  }
+}
+
+bool MouseCursorOverlayController::IsUserInteractingWithView() const {
+  return mouse_move_behavior() == kRecentlyMovedOrClicked;
+}
+
+base::WeakPtr<MouseCursorOverlayController>
+MouseCursorOverlayController::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+void MouseCursorOverlayController::OnMouseMoved(const gfx::PointF& location) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  switch (mouse_move_behavior()) {
+    case kNotMoving:
+      set_mouse_move_behavior(kStartingToMove);
+      mouse_move_start_location_ = location;
+      mouse_activity_ended_timer_.Start(
+          FROM_HERE, kIdleTimeout,
+          base::BindRepeating(&MouseCursorOverlayController::OnMouseHasGoneIdle,
+                              base::Unretained(this)));
+      break;
+    case kStartingToMove:
+      if (std::abs(location.x() - mouse_move_start_location_.x()) >
+              kMinMovementPixels ||
+          std::abs(location.y() - mouse_move_start_location_.y()) >
+              kMinMovementPixels) {
+        set_mouse_move_behavior(kRecentlyMovedOrClicked);
+        mouse_activity_ended_timer_.Reset();
+      }
+      break;
+    case kRecentlyMovedOrClicked:
+      mouse_activity_ended_timer_.Reset();
+      break;
+  }
+
+  if (mouse_move_behavior() == kRecentlyMovedOrClicked) {
+    UpdateOverlay(location);
+  }
+}
+
+void MouseCursorOverlayController::OnMouseClicked(const gfx::PointF& location) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  if (mouse_activity_ended_timer_.IsRunning()) {
+    mouse_activity_ended_timer_.Reset();
+  } else {
+    mouse_activity_ended_timer_.Start(
+        FROM_HERE, kIdleTimeout,
+        base::BindRepeating(&MouseCursorOverlayController::OnMouseHasGoneIdle,
+                            base::Unretained(this)));
+  }
+  set_mouse_move_behavior(kRecentlyMovedOrClicked);
+
+  UpdateOverlay(location);
+}
+
+void MouseCursorOverlayController::OnMouseHasGoneIdle() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  // Note that the following is not redundant since callers other than the timer
+  // may have invoked this method.
+  mouse_activity_ended_timer_.Stop();
+
+  set_mouse_move_behavior(kNotMoving);
+
+  UpdateOverlay(gfx::PointF());
+}
+
+void MouseCursorOverlayController::UpdateOverlay(const gfx::PointF& location) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  if (!overlay_) {
+    return;
+  }
+
+  // Breaking out of the following do-block indicates one or more prerequisites
+  // are not met and the cursor should be(come) hidden.
+  do {
+    // If the mouse has not recently moved, hide the overlay.
+    if (mouse_move_behavior() != kRecentlyMovedOrClicked) {
+      break;
+    }
+
+    const gfx::NativeCursor cursor = GetCurrentCursorOrDefault();
+    const gfx::RectF relative_bounds =
+        ComputeRelativeBoundsForOverlay(cursor, location);
+
+    // If the cursor (and, by implication, the cursor image) has not changed,
+    // just move the overlay to its new position, if any.
+    if (cursor == last_cursor_) {
+      if (bounds_ != relative_bounds) {
+        bounds_ = relative_bounds;
+        overlay_task_runner_->PostTask(
+            FROM_HERE,
+            base::BindOnce(&Overlay::SetBounds,
+                           base::Unretained(overlay_.get()), bounds_));
+      }
+      return;
+    }
+
+    // The cursor image has changed. Edge-case: If the platform does not provide
+    // a cursor image (e.g., this can occur at browser shutdown), just hide the
+    // overlay.
+    const SkBitmap cursor_image = GetCursorImage(cursor);
+    if (cursor_image.drawsNothing()) {
+      last_cursor_ = gfx::NativeCursor();
+      break;
+    }
+    last_cursor_ = cursor;
+    bounds_ = relative_bounds;
+    overlay_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&Overlay::SetImageAndBounds,
+                                  base::Unretained(overlay_.get()),
+                                  cursor_image, bounds_));
+    return;
+  } while (false);
+
+  // If this point has been reached, then the overlay should be hidden.
+  if (!bounds_.IsEmpty()) {
+    bounds_ = gfx::RectF();
+    overlay_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&Overlay::SetBounds,
+                                  base::Unretained(overlay_.get()), bounds_));
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/media/capture/mouse_cursor_overlay_controller.h b/content/browser/media/capture/mouse_cursor_overlay_controller.h
new file mode 100644
index 0000000..78d09d2
--- /dev/null
+++ b/content/browser/media/capture/mouse_cursor_overlay_controller.h
@@ -0,0 +1,171 @@
+// Copyright 2018 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_BROWSER_MEDIA_CAPTURE_MOUSE_CURSOR_OVERLAY_CONTROLLER_H_
+#define CONTENT_BROWSER_MEDIA_CAPTURE_MOUSE_CURSOR_OVERLAY_CONTROLLER_H_
+
+#include <atomic>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/timer/timer.h"
+#include "content/common/content_export.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace content {
+
+// MouseCursorOverlayController is used by FrameSinkVideoCaptureDevice to manage
+// the mouse cursor overlay in the viz::FrameSinkVideoCapturer session based on
+// the behavior of the mouse cursor reported by the windowing system.
+//
+// All parts of this class are meant to run on the UI BrowserThread, except for
+// IsUserInteractingWithView(), which may be called from any thread. It is up to
+// the client code to ensure the controller's lifetime while in use across
+// multiple threads.
+class CONTENT_EXPORT MouseCursorOverlayController {
+ public:
+  // TODO(crbug.com/810133): Replace this stub interface with
+  // viz::mojom::FrameSinkVideoCaptureOverlay in a soon-upcoming change.
+  class Overlay {
+   public:
+    virtual ~Overlay() = default;
+    virtual void SetImageAndBounds(const SkBitmap& image,
+                                   const gfx::RectF& bounds) = 0;
+    virtual void SetBounds(const gfx::RectF& bounds) = 0;
+  };
+
+  MouseCursorOverlayController();
+  ~MouseCursorOverlayController();
+
+  // Sets a new target view to monitor for mouse cursor updates.
+  void SetTargetView(gfx::NativeView view);
+
+  // Takes ownership of and starts controlling the given |overlay|, invoking its
+  // methods (and destruction) via the given |task_runner|.
+  void Start(std::unique_ptr<Overlay> overlay,
+             scoped_refptr<base::SequencedTaskRunner> task_runner);
+
+  // Stops controlling the Overlay (passed to Start()) and schedules its
+  // destruction.
+  void Stop();
+
+  // Returns true if the user has recently interacted with the view.
+  bool IsUserInteractingWithView() const;
+
+  // Returns a weak pointer.
+  base::WeakPtr<MouseCursorOverlayController> GetWeakPtr();
+
+ private:
+  friend class MouseCursorOverlayControllerBrowserTest;
+
+  // Observes mouse events from the windowing system and reports them via
+  // OnMouseMoved(), OnMouseClicked(), and OnMouseHasGoneIdle().
+  class Observer;
+
+  enum MouseMoveBehavior {
+    kNotMoving,               // Mouse has not moved recently.
+    kStartingToMove,          // Mouse has moved, but not significantly.
+    kRecentlyMovedOrClicked,  // Sufficient mouse activity present.
+  };
+
+  // Called from platform-specific code to report on mouse events within the
+  // captured view.
+  void OnMouseMoved(const gfx::PointF& location);
+  void OnMouseClicked(const gfx::PointF& location);
+
+  // Called by the |mouse_activity_ended_timer_| once no mouse events have
+  // occurred for kIdleTimeout. Also, called by platform-specific code when
+  // changing the target view.
+  void OnMouseHasGoneIdle();
+
+  // Accessors for |mouse_move_behavior_atomic_|. See comments below.
+  MouseMoveBehavior mouse_move_behavior() const {
+    return mouse_move_behavior_atomic_.load(std::memory_order_relaxed);
+  }
+  void set_mouse_move_behavior(MouseMoveBehavior behavior) {
+    mouse_move_behavior_atomic_.store(behavior, std::memory_order_relaxed);
+  }
+
+  // Examines the current mouse movement behavior, view properties, and cursor
+  // changes to determine whether to show or hide the overlay. |location| is the
+  // current mouse cursor location.
+  void UpdateOverlay(const gfx::PointF& location);
+
+  // Returns the current mouse cursor. The default "arrow pointer" cursor will
+  // be returned in lieu of a null cursor.
+  gfx::NativeCursor GetCurrentCursorOrDefault() const;
+
+  // Computes where the overlay should be shown, in terms of relative
+  // coordinates. This takes the view size, coordinate systems of the view and
+  // cursor bitmap, and cursor hotspot offset; all into account.
+  gfx::RectF ComputeRelativeBoundsForOverlay(const gfx::NativeCursor& cursor,
+                                             const gfx::PointF& location) const;
+
+  // Called after SetTargetView() to ignore mouse events from the
+  // platform/toolkit and set a default mouse cursor. This is used by the
+  // browser tests to prevent actual mouse movement from interfering with the
+  // testing of the control logic.
+  void DisconnectFromToolkitForTesting();
+
+  // Returns the image of the mouse cursor.
+  static SkBitmap GetCursorImage(const gfx::NativeCursor&);
+
+  // Platform-specific mouse event observer. Updated by SetTargetView().
+  std::unique_ptr<Observer> observer_;
+
+  // Updated in the mouse event handlers and used to decide whether the user is
+  // interacting with the view and whether to update the overlay.
+  gfx::PointF mouse_move_start_location_;
+  base::OneShotTimer mouse_activity_ended_timer_;
+
+  // Updated in the mouse event handlers and read by IsUserInteractingWithView()
+  // (on any thread). This is not protected by a mutex since strict memory
+  // ordering semantics are not necessary, just atomicity between threads. All
+  // code should use the accessors to read or set this value.
+  std::atomic<MouseMoveBehavior> mouse_move_behavior_atomic_;
+
+  // The overlay being controlled, and the task runner to use to invoke its
+  // methods and destruction.
+  std::unique_ptr<Overlay> overlay_;
+  scoped_refptr<base::SequencedTaskRunner> overlay_task_runner_;
+
+  // The last-shown mouse cursor. UpdateOverlay() uses this to determine whether
+  // to update the cursor image, or just the overlay position.
+  gfx::NativeCursor last_cursor_ = gfx::NativeCursor();
+
+  // This is empty if the overlay should be hidden. Otherwise, it represents a
+  // shown overlay with a relative position within the view in terms of the
+  // range [0.0,1.0). It can sometimes be a little bit outside of that range,
+  // depending on the cursor's hotspot.
+  gfx::RectF bounds_;
+
+  // Everything except the constructor and IsUserInteractingWithView() must be
+  // called on the UI BrowserThread.
+  SEQUENCE_CHECKER(ui_sequence_checker_);
+
+  base::WeakPtrFactory<MouseCursorOverlayController> weak_factory_;
+
+  // Minium movement before the cursor has been considered intentionally moved
+  // by the user.
+  static constexpr int kMinMovementPixels = 15;
+
+  // Amount of time to elapse with no mouse activity before the cursor should
+  // stop showing.
+  static constexpr base::TimeDelta kIdleTimeout =
+      base::TimeDelta::FromSeconds(2);
+
+  DISALLOW_COPY_AND_ASSIGN(MouseCursorOverlayController);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_MOUSE_CURSOR_OVERLAY_CONTROLLER_H_
diff --git a/content/browser/media/capture/mouse_cursor_overlay_controller_aura.cc b/content/browser/media/capture/mouse_cursor_overlay_controller_aura.cc
new file mode 100644
index 0000000..0075be2
--- /dev/null
+++ b/content/browser/media/capture/mouse_cursor_overlay_controller_aura.cc
@@ -0,0 +1,226 @@
+// Copyright 2018 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/browser/media/capture/mouse_cursor_overlay_controller.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/cursor/cursor_loader.h"
+#include "ui/base/cursor/cursor_type.h"
+#include "ui/events/event.h"
+#include "ui/events/event_handler.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace content {
+
+namespace {
+
+ui::Cursor CreateDefaultPointerCursor() {
+  ui::Cursor cursor(ui::CursorType::kPointer);
+  std::unique_ptr<ui::CursorLoader> loader(ui::CursorLoader::Create());
+  loader->SetPlatformCursor(&cursor);
+  return cursor;
+}
+
+}  // namespace
+
+class MouseCursorOverlayController::Observer : public ui::EventHandler,
+                                               public aura::WindowObserver {
+ public:
+  explicit Observer(MouseCursorOverlayController* controller,
+                    aura::Window* window)
+      : controller_(controller), window_(window) {
+    DCHECK(controller_);
+    DCHECK(window_);
+    controller_->OnMouseHasGoneIdle();
+    window_->AddObserver(this);
+    window_->AddPreTargetHandler(this);
+  }
+
+  ~Observer() final {
+    if (window_) {
+      OnWindowDestroying(window_);
+    }
+  }
+
+  void StopTracking() {
+    if (window_) {
+      window_->RemovePreTargetHandler(this);
+      controller_->OnMouseHasGoneIdle();
+    }
+  }
+
+  static aura::Window* GetTargetWindow(
+      const std::unique_ptr<Observer>& observer) {
+    if (observer) {
+      return observer->window_;
+    }
+    return nullptr;
+  }
+
+ private:
+  bool IsWindowActive() const {
+    if (window_) {
+      if (auto* root_window = window_->GetRootWindow()) {
+        if (window_ == root_window) {
+          return true;
+        }
+        if (auto* client = wm::GetActivationClient(root_window)) {
+          if (auto* active_window = client->GetActiveWindow()) {
+            return active_window->Contains(window_);
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  gfx::PointF AsLocationInWindow(const ui::Event& event) const {
+    gfx::PointF location = event.AsLocatedEvent()->location_f();
+    if (event.target() != window_) {
+      aura::Window::ConvertPointToTarget(
+          static_cast<aura::Window*>(event.target()), window_, &location);
+    }
+    return location;
+  }
+
+  // ui::EventHandler overrides.
+  void OnEvent(ui::Event* event) final {
+    switch (event->type()) {
+      case ui::ET_MOUSE_DRAGGED:
+      case ui::ET_MOUSE_MOVED:
+      case ui::ET_MOUSE_ENTERED:
+      case ui::ET_MOUSE_EXITED:
+      case ui::ET_TOUCH_MOVED:
+        if (IsWindowActive()) {
+          controller_->OnMouseMoved(AsLocationInWindow(*event));
+        }
+        break;
+
+      case ui::ET_MOUSE_PRESSED:
+      case ui::ET_MOUSE_RELEASED:
+      case ui::ET_MOUSEWHEEL:
+      case ui::ET_TOUCH_PRESSED:
+      case ui::ET_TOUCH_RELEASED: {
+        controller_->OnMouseClicked(AsLocationInWindow(*event));
+        break;
+      }
+
+      default:
+        return;
+    }
+  }
+
+  // aura::WindowObserver overrides.
+  void OnWindowDestroying(aura::Window* window) final {
+    DCHECK_EQ(window_, window);
+    StopTracking();
+    window_->RemoveObserver(this);
+    window_ = nullptr;
+  }
+
+  MouseCursorOverlayController* const controller_;
+  aura::Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(Observer);
+};
+
+MouseCursorOverlayController::MouseCursorOverlayController()
+    : mouse_move_behavior_atomic_(kNotMoving), weak_factory_(this) {
+  // MouseCursorOverlayController can be constructed on any thread, but
+  // thereafter must be used according to class-level comments.
+  DETACH_FROM_SEQUENCE(ui_sequence_checker_);
+}
+
+MouseCursorOverlayController::~MouseCursorOverlayController() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  observer_.reset();
+  Stop();
+}
+
+void MouseCursorOverlayController::SetTargetView(aura::Window* window) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  observer_.reset();
+  if (window) {
+    observer_ = std::make_unique<Observer>(this, window);
+  }
+}
+
+gfx::NativeCursor MouseCursorOverlayController::GetCurrentCursorOrDefault()
+    const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  if (auto* window = Observer::GetTargetWindow(observer_)) {
+    if (auto* host = window->GetHost()) {
+      const gfx::NativeCursor cursor = host->last_cursor();
+      if (cursor != ui::CursorType::kNull) {
+        return cursor;
+      }
+    }
+  }
+
+  return CreateDefaultPointerCursor();
+}
+
+gfx::RectF MouseCursorOverlayController::ComputeRelativeBoundsForOverlay(
+    const gfx::NativeCursor& cursor,
+    const gfx::PointF& location) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  if (auto* window = Observer::GetTargetWindow(observer_)) {
+    const gfx::Size window_size = window->bounds().size();
+    if (!window_size.IsEmpty()) {
+      if (auto* root_window = window->GetRootWindow()) {
+        // Compute the cursor size in terms of DIP coordinates.
+        const SkBitmap& bitmap = cursor.GetBitmap();
+        const float scale_factor = cursor.device_scale_factor();
+        const gfx::SizeF size =
+            scale_factor > 0.0f
+                ? gfx::ScaleSize(gfx::SizeF(bitmap.width(), bitmap.height()),
+                                 1.0f / scale_factor)
+                : gfx::SizeF(bitmap.width(), bitmap.height());
+
+        // Compute the hotspot in terms of DIP coordinates.
+        const gfx::PointF hotspot =
+            scale_factor > 0.0f
+                ? gfx::ScalePoint(gfx::PointF(cursor.GetHotspot()),
+                                  1.0f / scale_factor)
+                : gfx::PointF(cursor.GetHotspot());
+
+        // Finally, put it all together: Scale the absolute bounds of the
+        // overlay by the window size to produce relative coordinates.
+        return gfx::ScaleRect(
+            gfx::RectF(location - hotspot.OffsetFromOrigin(), size),
+            1.0f / window_size.width(), 1.0f / window_size.height());
+      }
+    }
+  }
+
+  return gfx::RectF();
+}
+
+void MouseCursorOverlayController::DisconnectFromToolkitForTesting() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  observer_->StopTracking();
+
+  // The default cursor is ui::CursorType::kNone. Make it kPointer so the tests
+  // have a non-empty cursor bitmap to work with.
+  auto* const window = Observer::GetTargetWindow(observer_);
+  CHECK(window);
+  auto* const host = window->GetHost();
+  CHECK(host);
+  host->SetCursor(CreateDefaultPointerCursor());
+}
+
+// static
+SkBitmap MouseCursorOverlayController::GetCursorImage(
+    const gfx::NativeCursor& cursor) {
+  return cursor.GetBitmap();
+}
+
+}  // namespace content
diff --git a/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc b/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc
new file mode 100644
index 0000000..c4987dd
--- /dev/null
+++ b/content/browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc
@@ -0,0 +1,260 @@
+// Copyright 2018 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/browser/media/capture/mouse_cursor_overlay_controller.h"
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/shell/browser/shell.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_f.h"
+
+namespace content {
+namespace {
+
+class FakeOverlay : public MouseCursorOverlayController::Overlay {
+ public:
+  FakeOverlay() = default;
+  ~FakeOverlay() final = default;
+
+  const SkBitmap& image() const { return image_; }
+  const gfx::RectF& bounds() const { return bounds_; }
+
+  void SetImageAndBounds(const SkBitmap& image,
+                         const gfx::RectF& bounds) final {
+    image_ = image;
+    bounds_ = bounds;
+  }
+
+  void SetBounds(const gfx::RectF& bounds) final { bounds_ = bounds; }
+
+ private:
+  SkBitmap image_;
+  gfx::RectF bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeOverlay);
+};
+
+}  // namespace
+
+class MouseCursorOverlayControllerBrowserTest : public ContentBrowserTest {
+ public:
+  MouseCursorOverlayControllerBrowserTest() = default;
+  ~MouseCursorOverlayControllerBrowserTest() override = default;
+
+  void SetUpOnMainThread() final {
+    ContentBrowserTest::SetUpOnMainThread();
+    controller_.SetTargetView(shell()->web_contents()->GetNativeView());
+    controller_.DisconnectFromToolkitForTesting();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  FakeOverlay* Start() {
+    auto overlay_ptr = std::make_unique<FakeOverlay>();
+    FakeOverlay* const overlay = overlay_ptr.get();
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    controller_.Start(std::move(overlay_ptr),
+                      BrowserThread::GetTaskRunnerForThread(BrowserThread::UI));
+    return overlay;
+  }
+
+  gfx::PointF GetLowerRightMostPointInsideView() {
+    const gfx::Size& view_size = GetAbsoluteViewSize();
+    return gfx::PointF(1.0f - 1.0f / view_size.width(),
+                       1.0f - 1.0f / view_size.height());
+  }
+
+  void SimulateMouseTravel(float from_x, float from_y, float to_x, float to_y) {
+    constexpr int kNumMoves = 10;
+    for (int i = kNumMoves; i >= 0; --i) {
+      const float t = static_cast<float>(i) / kNumMoves;
+      const float x = t * from_x + (1.0f - t) * to_x;
+      const float y = t * from_y + (1.0f - t) * to_y;
+      controller_.OnMouseMoved(ToAbsoluteLocationInView(x, y));
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SimulateMouseClick(float x, float y) {
+    controller_.OnMouseClicked(ToAbsoluteLocationInView(x, y));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SimulateMouseHasGoneIdle() {
+    controller_.OnMouseHasGoneIdle();
+    base::RunLoop().RunUntilIdle();
+    EXPECT_FALSE(controller_.mouse_activity_ended_timer_.IsRunning());
+  }
+
+  void SimulateUnintentionalMouseMovement(float x, float y) {
+    const gfx::Size& view_size = GetAbsoluteViewSize();
+    const float distance_x =
+        (0.5f * MouseCursorOverlayController::kMinMovementPixels) /
+        view_size.width();
+    const float distance_y =
+        (0.5f * MouseCursorOverlayController::kMinMovementPixels) /
+        view_size.height();
+    DoSquareDance(x, y, distance_x, distance_y);
+  }
+
+  void SimulateBarelyIntentionalMouseMovement(float x, float y) {
+    const gfx::Size& view_size = GetAbsoluteViewSize();
+    const float distance_x =
+        (1.5f * MouseCursorOverlayController::kMinMovementPixels) /
+        view_size.width();
+    const float distance_y =
+        (1.5f * MouseCursorOverlayController::kMinMovementPixels) /
+        view_size.height();
+    DoSquareDance(x, y, distance_x, distance_y);
+  }
+
+  void ExpectOverlayPositionedAt(const FakeOverlay& overlay,
+                                 float expected_x,
+                                 float expected_y) {
+    const gfx::SizeF& overlay_size = GetExpectedOverlaySize();
+    // The position will be slightly off because of the hotspot offset.
+    EXPECT_NEAR(expected_x, overlay.bounds().x(), overlay_size.width() / 2.0f);
+    EXPECT_NEAR(expected_y, overlay.bounds().y(), overlay_size.height() / 2.0f);
+  }
+
+  void ExpectOverlaySizeMatchesCurrentCursor(const FakeOverlay& overlay) const {
+    const gfx::SizeF& expected_size = GetExpectedOverlaySize();
+    EXPECT_FALSE(expected_size.IsEmpty());
+    EXPECT_FALSE(overlay.image().drawsNothing());
+    EXPECT_NEAR(expected_size.width(), overlay.bounds().width(), 0.001);
+    EXPECT_NEAR(expected_size.height(), overlay.bounds().height(), 0.001);
+  }
+
+  bool IsUserInteractingWithView() const {
+    return controller_.IsUserInteractingWithView();
+  }
+
+ private:
+  gfx::Size GetAbsoluteViewSize() const {
+    const gfx::Size& view_size =
+        shell()->web_contents()->GetContainerBounds().size();
+    CHECK(!view_size.IsEmpty());
+    return view_size;
+  }
+
+  gfx::PointF ToAbsoluteLocationInView(float relative_x, float relative_y) {
+    const gfx::Size& view_size = GetAbsoluteViewSize();
+    return gfx::PointF(relative_x * view_size.width(),
+                       relative_y * view_size.height());
+  }
+
+  gfx::SizeF GetExpectedOverlaySize() const {
+    const gfx::Size& view_size = GetAbsoluteViewSize();
+    const SkBitmap image =
+        controller_.GetCursorImage(controller_.GetCurrentCursorOrDefault());
+    return gfx::SizeF(static_cast<float>(image.width()) / view_size.width(),
+                      static_cast<float>(image.height()) / view_size.height());
+  }
+
+  void DoSquareDance(float x, float y, float distance_x, float distance_y) {
+    SimulateMouseTravel(x, y, x + distance_x, y);
+    SimulateMouseTravel(x + distance_x, y, x + distance_x, y + distance_y);
+    SimulateMouseTravel(x + distance_x, y + distance_y, x, y + distance_y);
+    SimulateMouseTravel(x, y + distance_y, x, y);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  MouseCursorOverlayController controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(MouseCursorOverlayControllerBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(MouseCursorOverlayControllerBrowserTest,
+                       PositionsOverlayOnMouseMoves) {
+  FakeOverlay* const overlay = Start();
+
+  // Cursor not showing at start.
+  EXPECT_TRUE(overlay->image().drawsNothing());
+  EXPECT_TRUE(overlay->bounds().IsEmpty());
+  EXPECT_FALSE(IsUserInteractingWithView());
+
+  // Move to upper-leftmost corner.
+  {
+    SCOPED_TRACE(testing::Message() << "upper-leftmost corner of view");
+    SimulateMouseTravel(0.5f, 0.5f, 0.0f, 0.0f);
+    ExpectOverlayPositionedAt(*overlay, 0.0f, 0.0f);
+    ExpectOverlaySizeMatchesCurrentCursor(*overlay);
+    EXPECT_TRUE(IsUserInteractingWithView());
+  }
+
+  // Move to middle.
+  {
+    SCOPED_TRACE(testing::Message() << "center of view");
+    SimulateMouseTravel(0.0f, 0.0f, 0.5f, 0.5f);
+    ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f);
+    ExpectOverlaySizeMatchesCurrentCursor(*overlay);
+    EXPECT_TRUE(IsUserInteractingWithView());
+  }
+
+  // Move to lower-rightmost corner.
+  {
+    SCOPED_TRACE(testing::Message() << "lower-rightmost corner of view");
+    const gfx::PointF lower_right = GetLowerRightMostPointInsideView();
+    SimulateMouseTravel(0.5f, 0.5f, lower_right.x(), lower_right.y());
+    ExpectOverlayPositionedAt(*overlay, lower_right.x(), lower_right.y());
+    ExpectOverlaySizeMatchesCurrentCursor(*overlay);
+    EXPECT_TRUE(IsUserInteractingWithView());
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(MouseCursorOverlayControllerBrowserTest,
+                       PositionsOverlayOnMouseClicks) {
+  FakeOverlay* const overlay = Start();
+
+  // Cursor not showing at start.
+  EXPECT_TRUE(overlay->bounds().IsEmpty());
+  EXPECT_FALSE(IsUserInteractingWithView());
+
+  // Click in the middle of the view.
+  SimulateMouseClick(0.5f, 0.5f);
+  ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f);
+  ExpectOverlaySizeMatchesCurrentCursor(*overlay);
+  EXPECT_TRUE(IsUserInteractingWithView());
+}
+
+IN_PROC_BROWSER_TEST_F(MouseCursorOverlayControllerBrowserTest,
+                       CursorHidesWhenMouseStopsMoving) {
+  FakeOverlay* const overlay = Start();
+
+  // Cursor not showing at start.
+  EXPECT_TRUE(overlay->bounds().IsEmpty());
+  EXPECT_FALSE(IsUserInteractingWithView());
+
+  // Move to middle.
+  SimulateMouseTravel(0.0f, 0.0f, 0.5f, 0.5f);
+  ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f);
+  ExpectOverlaySizeMatchesCurrentCursor(*overlay);
+  EXPECT_TRUE(IsUserInteractingWithView());
+
+  // Simulate no movement for the timeout period.
+  SimulateMouseHasGoneIdle();
+  EXPECT_TRUE(overlay->bounds().IsEmpty());
+  EXPECT_FALSE(IsUserInteractingWithView());
+
+  // Move the mouse a little, but not enough to trip the "intentionally moved"
+  // logic.
+  SimulateUnintentionalMouseMovement(0.5f, 0.5f);
+  EXPECT_TRUE(overlay->bounds().IsEmpty());
+  EXPECT_FALSE(IsUserInteractingWithView());
+
+  // Move the mouse just a bit more, to trip the "intentionally moved" logic.
+  SimulateBarelyIntentionalMouseMovement(0.5f, 0.5f);
+  ExpectOverlayPositionedAt(*overlay, 0.5f, 0.5f);
+  ExpectOverlaySizeMatchesCurrentCursor(*overlay);
+  EXPECT_TRUE(IsUserInteractingWithView());
+}
+
+}  // namespace content
diff --git a/content/browser/media/capture/mouse_cursor_overlay_controller_mac.mm b/content/browser/media/capture/mouse_cursor_overlay_controller_mac.mm
new file mode 100644
index 0000000..fa6a82e6
--- /dev/null
+++ b/content/browser/media/capture/mouse_cursor_overlay_controller_mac.mm
@@ -0,0 +1,198 @@
+// Copyright 2018 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/browser/media/capture/mouse_cursor_overlay_controller.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "ui/base/cocoa/tracking_area.h"
+
+namespace {
+using LocationUpdateCallback = base::RepeatingCallback<void(const NSPoint&)>;
+}  // namespace;
+
+// Uses a CrTrackingArea to monitor for mouse events and forwards them to the
+// MouseCursorOverlayController::Observer.
+@interface MouseCursorOverlayTracker : NSObject {
+ @private
+  LocationUpdateCallback callback_;
+  ui::ScopedCrTrackingArea trackingArea_;
+}
+- (instancetype)initWithCallback:(LocationUpdateCallback)callback
+                         andView:(NSView*)nsView;
+- (void)stopTracking:(NSView*)nsView;
+@end
+
+@implementation MouseCursorOverlayTracker
+
+- (instancetype)initWithCallback:(LocationUpdateCallback)callback
+                         andView:(NSView*)nsView {
+  if ((self = [super init])) {
+    callback_ = std::move(callback);
+    constexpr NSTrackingAreaOptions kTrackingOptions =
+        NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited |
+        NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect |
+        NSTrackingEnabledDuringMouseDrag;
+    trackingArea_.reset([[CrTrackingArea alloc] initWithRect:NSZeroRect
+                                                     options:kTrackingOptions
+                                                       owner:self
+                                                    userInfo:nil]);
+    [nsView addTrackingArea:trackingArea_.get()];
+  }
+  return self;
+}
+
+- (void)stopTracking:(NSView*)nsView {
+  [nsView removeTrackingArea:trackingArea_.get()];
+  trackingArea_.reset();
+  callback_.Reset();
+}
+
+- (void)mouseMoved:(NSEvent*)theEvent {
+  callback_.Run([theEvent locationInWindow]);
+}
+
+- (void)mouseEntered:(NSEvent*)theEvent {
+  callback_.Run([theEvent locationInWindow]);
+}
+
+- (void)mouseExited:(NSEvent*)theEvent {
+  callback_.Run([theEvent locationInWindow]);
+}
+
+@end
+
+namespace content {
+
+class MouseCursorOverlayController::Observer {
+ public:
+  explicit Observer(MouseCursorOverlayController* controller, NSView* view)
+      : controller_(controller), view_([view retain]) {
+    DCHECK(controller_);
+    DCHECK(view_);
+    controller_->OnMouseHasGoneIdle();
+    mouse_tracker_.reset([[MouseCursorOverlayTracker alloc]
+        initWithCallback:base::BindRepeating(&Observer::OnMouseMoved,
+                                             base::Unretained(this))
+                 andView:view_.get()]);
+  }
+
+  ~Observer() { StopTracking(); }
+
+  void StopTracking() {
+    if (mouse_tracker_) {
+      [mouse_tracker_ stopTracking:view_.get()];
+      mouse_tracker_.reset();
+      controller_->OnMouseHasGoneIdle();
+    }
+  }
+
+  static NSView* GetTargetView(const std::unique_ptr<Observer>& observer) {
+    if (observer) {
+      return observer->view_.get();
+    }
+    return nil;
+  }
+
+ private:
+  void OnMouseMoved(const NSPoint& location_in_window) {
+    // Compute the location within the view using Aura conventions: (0,0) is the
+    // upper-left corner. So, if the NSView is flipped in Cocoa, it's not
+    // flipped in Aura.
+    NSPoint location_aura =
+        [view_ convertPoint:location_in_window fromView:nil];
+    if (![view_ isFlipped]) {
+      location_aura.y = NSHeight([view_ bounds]) - location_aura.y;
+    }
+    controller_->OnMouseMoved(gfx::PointF(location_aura.x, location_aura.y));
+  }
+
+  MouseCursorOverlayController* const controller_;
+  base::scoped_nsobject<NSView> view_;
+  base::scoped_nsobject<MouseCursorOverlayTracker> mouse_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(Observer);
+};
+
+MouseCursorOverlayController::MouseCursorOverlayController()
+    : mouse_move_behavior_atomic_(kNotMoving), weak_factory_(this) {
+  // MouseCursorOverlayController can be constructed on any thread, but
+  // thereafter must be used according to class-level comments.
+  DETACH_FROM_SEQUENCE(ui_sequence_checker_);
+}
+
+MouseCursorOverlayController::~MouseCursorOverlayController() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  observer_.reset();
+  Stop();
+}
+
+void MouseCursorOverlayController::SetTargetView(NSView* view) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  observer_.reset();
+  if (view) {
+    observer_ = std::make_unique<Observer>(this, view);
+  }
+}
+
+gfx::NativeCursor MouseCursorOverlayController::GetCurrentCursorOrDefault()
+    const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  NSCursor* cursor = [NSCursor currentCursor];
+  if (!cursor) {
+    cursor = [NSCursor arrowCursor];
+  }
+  return cursor;
+}
+
+gfx::RectF MouseCursorOverlayController::ComputeRelativeBoundsForOverlay(
+    const gfx::NativeCursor& cursor,
+    const gfx::PointF& location_aura) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  if (NSView* view = Observer::GetTargetView(observer_)) {
+    const NSRect view_bounds = [view bounds];
+    if (!NSIsEmptyRect(view_bounds)) {
+      // The documentation on NSCursor reference states that the hot spot is in
+      // flipped coordinates which, from the perspective of the Aura coordinate
+      // system, means it's not flipped.
+      const NSPoint hotspot = [cursor hotSpot];
+      const NSSize size = [[cursor image] size];
+      return gfx::ScaleRect(
+          gfx::RectF(location_aura.x() - hotspot.x,
+                     location_aura.y() - hotspot.y, size.width, size.height),
+          1.0 / NSWidth(view_bounds), 1.0 / NSHeight(view_bounds));
+    }
+  }
+
+  return gfx::RectF();
+}
+
+void MouseCursorOverlayController::DisconnectFromToolkitForTesting() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+
+  observer_->StopTracking();
+
+  // Note: Not overriding the mouse cursor since the default is already
+  // [NSCursor arrowCursor], which provides the tests a bitmap they can work
+  // with.
+}
+
+// static
+SkBitmap MouseCursorOverlayController::GetCursorImage(
+    const gfx::NativeCursor& cursor) {
+  return skia::NSImageToSkBitmapWithColorSpace(
+      [cursor image], /*is_opaque=*/false, base::mac::GetSystemColorSpace());
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.h b/content/browser/renderer_host/browser_compositor_view_mac.h
index 254be13..46be85e 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.h
+++ b/content/browser/renderer_host/browser_compositor_view_mac.h
@@ -148,7 +148,7 @@
 
   bool ForceNewSurfaceForTesting();
 
-  ui::Compositor* GetCompositor() const;
+  ui::Compositor* GetCompositorForTesting() const;
 
  private:
   // ui::LayerObserver implementation:
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.mm b/content/browser/renderer_host/browser_compositor_view_mac.mm
index 5bf4dbf4..9bb2156 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.mm
+++ b/content/browser/renderer_host/browser_compositor_view_mac.mm
@@ -487,9 +487,7 @@
   SetParentUiLayer(nullptr);
 }
 
-ui::Compositor* BrowserCompositorMac::GetCompositor() const {
-  if (parent_ui_layer_)
-    return parent_ui_layer_->GetCompositor();
+ui::Compositor* BrowserCompositorMac::GetCompositorForTesting() const {
   if (recyclable_compositor_)
     return recyclable_compositor_->compositor();
   return nullptr;
diff --git a/content/browser/renderer_host/input/fling_scheduler_mac.mm b/content/browser/renderer_host/input/fling_scheduler_mac.mm
index 83959b3e..f2ac740b 100644
--- a/content/browser/renderer_host/input/fling_scheduler_mac.mm
+++ b/content/browser/renderer_host/input/fling_scheduler_mac.mm
@@ -21,10 +21,14 @@
   // RWHV_child_frame doesn't have DelegatedFrameHost with ui::Compositor.
   if (host_->GetView()->IsRenderWidgetHostViewChildFrame())
     return nullptr;
-  RenderWidgetHostViewMac* view =
+
+  // TODO(sahel): Uncomment this once Viz is ready on Mac.
+  // https://crbug.com/833985
+  /* RenderWidgetHostViewMac* view =
       static_cast<RenderWidgetHostViewMac*>(host_->GetView());
   if (view->BrowserCompositor())
-    return view->BrowserCompositor()->GetCompositor();
+    return view->BrowserCompositor()->Compositor();
+  } */
 
   return nullptr;
 }
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index f56a974..3d10664 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -2329,8 +2329,6 @@
   // If window_android is null here, this is bad because we don't listen for it
   // being set, so we won't be able to construct the OverscrollController at the
   // proper time.
-  // TODO(rlanday): once we get WindowAndroid from ViewAndroid instead of
-  // ContentViewCore, listen for WindowAndroid being set and create the
   ui::WindowAndroid* window_android = view_.GetWindowAndroid();
   if (!window_android)
     return;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index ae59220..c5d40f6 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -482,7 +482,7 @@
   std::unique_ptr<TouchSelectionControllerClientManagerAndroid>
       touch_selection_controller_client_manager_;
 
-  // Bounds to use if we have no backing ContentViewCore
+  // Bounds to use if we have no backing WebContents.
   gfx::Rect default_bounds_;
 
   const bool using_browser_compositor_;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index a97ff3eb..4fceb1a 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -1837,11 +1837,11 @@
 
 TEST_F(RenderWidgetHostViewMacTest, ClearCompositorFrame) {
   BrowserCompositorMac* browser_compositor = rwhv_mac_->BrowserCompositor();
-  ui::Compositor* ui_compositor = browser_compositor->GetCompositor();
+  ui::Compositor* ui_compositor = browser_compositor->GetCompositorForTesting();
   EXPECT_NE(ui_compositor, nullptr);
   EXPECT_TRUE(ui_compositor->IsLocked());
   rwhv_mac_->ClearCompositorFrame();
-  EXPECT_EQ(browser_compositor->GetCompositor(), ui_compositor);
+  EXPECT_EQ(browser_compositor->GetCompositorForTesting(), ui_compositor);
   EXPECT_FALSE(ui_compositor->IsLocked());
 }
 
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 7c89dce..632b3a0 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -456,7 +456,7 @@
     "javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java",
     "javatests/src/org/chromium/content/browser/ClipboardTest.java",
     "javatests/src/org/chromium/content/browser/ContentCommandLineTest.java",
-    "javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java",
+    "javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java",
     "javatests/src/org/chromium/content/browser/ContentViewLocationTest.java",
     "javatests/src/org/chromium/content/browser/ContentViewPointerTypeTest.java",
     "javatests/src/org/chromium/content/browser/ContentViewPopupZoomerTest.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/RenderCoordinatesImpl.java b/content/public/android/java/src/org/chromium/content/browser/RenderCoordinatesImpl.java
index 9cacde5..3b5c756 100644
--- a/content/public/android/java/src/org/chromium/content/browser/RenderCoordinatesImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/RenderCoordinatesImpl.java
@@ -12,7 +12,7 @@
  * Cached copy of all positions and scales (CSS-to-DIP-to-physical pixels)
  * reported from the renderer.
  * Provides wrappers and a utility class to help with coordinate transforms on the client side.
- * Provides the internally-visible set of update methods (called from ContentViewCore).
+ * Provides the internally-visible set of update methods (called from GestureListenerManagerImpl).
  *
  * Unless stated otherwise, all coordinates are in CSS (document) coordinate space.
  */
@@ -43,7 +43,7 @@
         return ((WebContentsImpl) webContents).getRenderCoordinates();
     }
 
-    // Internally-visible set of update methods (used by ContentViewCore).
+    // Internally-visible set of update methods (used by WebContentsImpl).
     public void reset() {
         mScrollXCss = mScrollYCss = 0;
         mPageScaleFactor = 1.0f;
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
index 90d9d22..a72e768 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
@@ -593,8 +593,8 @@
         if (DEBUG_LOGS) Log.i(TAG, "hideKeyboard");
         View view = mViewDelegate.getContainerView();
         if (mInputMethodManagerWrapper.isActive(view)) {
-            // NOTE: we should not set ResultReceiver here. Otherwise, IMM will own ContentViewCore
-            // and ImeAdapter even after input method goes away and result gets received.
+            // NOTE: we should not set ResultReceiver here. Otherwise, IMM will own
+            // ImeAdapter even after input method goes away and result gets received.
             mInputMethodManagerWrapper.hideSoftInputFromWindow(view.getWindowToken(), 0, null);
         }
         // Detach input connection by returning null from onCreateInputConnection().
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java b/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
index 9a9c041e..317f5761 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
@@ -23,7 +23,7 @@
 @JNINamespace("content")
 public class LoadUrlParams {
     // Fields with counterparts in NavigationController::LoadURLParams.
-    // Package private so that ContentViewCore.loadUrl can pass them down to
+    // Package private so that NavigationController.loadUrl can pass them down to
     // native code. Should not be accessed directly anywhere else outside of
     // this class.
     String mUrl;
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHistory.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHistory.java
index 6338738ac..150937d 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHistory.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHistory.java
@@ -10,8 +10,8 @@
 
 /**
  * {@link NavigationHistory} captures a snapshot of the navigation history of a
- * {@link ContentViewCore}. It is a copy and will not be updated as navigation
- * occurs on the source {@link ContentViewCore}.
+ * {@link WebContents}. It is a copy and will not be updated as navigation
+ * occurs on the source {@link WebContents}.
  */
 public class NavigationHistory {
 
@@ -41,7 +41,7 @@
     }
 
     /**
-     * Returns the index of the entry the {@link ContentViewCore} was navigated to
+     * Returns the index of the entry the {@link WebContents} was navigated to
      * when the history was fetched.
      */
     public int getCurrentEntryIndex() {
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/SelectionPopupController.java b/content/public/android/java/src/org/chromium/content_public/browser/SelectionPopupController.java
index 836176d..069ba0fc2 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/SelectionPopupController.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/SelectionPopupController.java
@@ -128,8 +128,8 @@
      * handles and selection popups if focus is lost.
      * TODO(mdjones): This was added as a temporary measure to hide text UI while Reader Mode or
      * Contextual Search are showing. This should be removed in favor of proper focusing of the
-     * panel's ContentViewCore (which is currently not being added to the view hierarchy).
-     * @param focused If the ContentViewCore currently has focus.
+     * panel's WebContents (which is currently not being added to the view hierarchy).
+     * @param focused If the WebContents currently has focus.
      */
     void updateTextSelectionUI(boolean focused);
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java
similarity index 99%
rename from content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java
rename to content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java
index bb3b957..3c9a934 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentTextSelectionTest.java
@@ -42,7 +42,7 @@
  * Integration tests for text selection-related behavior.
  */
 @RunWith(ContentJUnit4ClassRunner.class)
-public class ContentViewCoreSelectionTest {
+public class ContentTextSelectionTest {
     @Rule
     public ContentShellActivityTestRule mActivityTestRule = new ContentShellActivityTestRule();
     private static final String DATA_URL = UrlUtils.encodeHtmlDataUri(
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java
index 37119d45..1740be3 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java
@@ -144,7 +144,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                // Synthesize joystick motion event and send to ContentViewCore.
+                // Synthesize joystick motion event and send to content layer.
                 MotionEvent leftJoystickMotionEvent =
                         MotionEvent.obtain(0, SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE,
                                 deltaAxisX, deltaAxisY, 0);
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java
index 71d42cfe..fd9a5709 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java
@@ -27,7 +27,7 @@
      * Calls {@link NavigationController#canGoBack()} on UI thread.
      *
      * @param instrumentation an Instrumentation instance.
-     * @param contentViewCore a ContentViewCore instance.
+     * @param webContents a WebContents instance.
      * @return result of {@link NavigationController#canGoBack()}
      * @throws Throwable
      */
@@ -46,7 +46,7 @@
      * Calls {@link NavigationController#canGoToOffset(int)} on UI thread.
      *
      * @param instrumentation an Instrumentation instance.
-     * @param contentViewCore a ContentViewCore instance.
+     * @param webContents a WebContents instance.
      * @param offset The number of steps to go on the UI thread, with negative
      *      representing going back.
      * @return result of {@link NavigationController#canGoToOffset(int)}
@@ -67,7 +67,7 @@
      * Calls {@link NavigationController#canGoForward()} on UI thread.
      *
      * @param instrumentation an Instrumentation instance.
-     * @param contentViewCore a ContentViewCore instance.
+     * @param webContents a WebContents instance.
      * @return result of {@link NavigationController#canGoForward()}
      * @throws Throwable
      */
@@ -86,7 +86,7 @@
      * Calls {@link NavigationController#clearHistory()} on UI thread.
      *
      * @param instrumentation an Instrumentation instance.
-     * @param contentViewCore a ContentViewCore instance.
+     * @param webContents a WebContents instance.
      * @throws Throwable
      */
     public static void clearHistoryOnUiThread(Instrumentation instrumentation,
@@ -103,8 +103,8 @@
      * Calls {@link WebContents#getLastCommittedUrl()} on UI Thread to get the current URL.
      *
      * @param instrumentation an Instrumentation instance.
-     * @param contentViewCore a ContentViewCore instance.
-     * @return the last committed URL of the provided ContentViewCore.
+     * @param webContents a WebContents instance.
+     * @return the last committed URL of the provided WebContents.
      * @throws Throwable
      */
     public static String getUrlOnUiThread(Instrumentation instrumentation,
@@ -123,9 +123,9 @@
      * it times out.
      *
      * @param instrumentation an Instrumentation instance.
-     * @param contentViewCore a ContentViewCore instance.
+     * @param webContents a WebContents instance.
      * @param onPageFinishedHelper the CallbackHelper instance associated with the onPageFinished
-     *                             callback of contentViewCore.
+     *                             callback of webContents.
      * @throws Throwable
      */
     public static void goBackSync(Instrumentation instrumentation,
@@ -148,7 +148,7 @@
      * it times out.
      *
      * @param instrumentation an Instrumentation instance.
-     * @param contentViewCore a ContentViewCore instance.
+     * @param webContents a WebContents instance.
      * @throws Throwable
      */
     public static void goForwardSync(Instrumentation instrumentation,
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/JavaScriptUtils.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/JavaScriptUtils.java
index 9bdfb05..e29f64b 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/JavaScriptUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/JavaScriptUtils.java
@@ -32,7 +32,7 @@
     }
 
     /**
-     * Executes the given snippet of JavaScript code within the given ContentViewCore.
+     * Executes the given snippet of JavaScript code within the given WebContents.
      * Does not depend on ContentView and TestCallbackHelperContainer.
      * Returns the result of its execution in JSON format.
      */
diff --git a/content/renderer/dom_storage/local_storage_cached_areas.cc b/content/renderer/dom_storage/local_storage_cached_areas.cc
index 7c2327ac..6ecf2ca4 100644
--- a/content/renderer/dom_storage/local_storage_cached_areas.cc
+++ b/content/renderer/dom_storage/local_storage_cached_areas.cc
@@ -181,7 +181,7 @@
 bool LocalStorageCachedAreas::DOMStorageNamespace::CleanUpUnusedAreas() {
   CheckPrefixes();
   base::EraseIf(cached_areas,
-                [](auto& pair) { return pair.second->HasOneRef(); });
+                [](const auto& pair) { return pair.second->HasOneRef(); });
   return cached_areas.empty();
 }
 
diff --git a/content/renderer/gpu/queue_message_swap_promise_unittest.cc b/content/renderer/gpu/queue_message_swap_promise_unittest.cc
index 098d92e..e6b911b 100644
--- a/content/renderer/gpu/queue_message_swap_promise_unittest.cc
+++ b/content/renderer/gpu/queue_message_swap_promise_unittest.cc
@@ -26,16 +26,6 @@
 
 namespace content {
 
-class TestRenderWidget : public RenderWidget {
- public:
-  using RenderWidget::QueueMessageImpl;
-
- private:
-  ~TestRenderWidget() override {}
-
-  DISALLOW_COPY_AND_ASSIGN(TestRenderWidget);
-};
-
 class TestSyncMessageFilter : public IPC::SyncMessageFilter {
  public:
   TestSyncMessageFilter() : IPC::SyncMessageFilter(nullptr) {}
@@ -90,7 +80,7 @@
       IPC::Message* msg,
       MessageDeliveryPolicy policy,
       int source_frame_number) {
-    return TestRenderWidget::QueueMessageImpl(
+    return RenderWidget::QueueMessageImpl(
         msg, policy, frame_swap_message_queue_.get(), sync_message_filter_,
         source_frame_number);
   }
diff --git a/content/renderer/media_recorder/vea_encoder.cc b/content/renderer/media_recorder/vea_encoder.cc
index ce04817..269eabb 100644
--- a/content/renderer/media_recorder/vea_encoder.cc
+++ b/content/renderer/media_recorder/vea_encoder.cc
@@ -55,14 +55,14 @@
   DCHECK(gpu_factories_);
   DCHECK_GE(size.width(), kVEAEncoderMinResolutionWidth);
   DCHECK_GE(size.height(), kVEAEncoderMinResolutionHeight);
+
+  encoding_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&VEAEncoder::ConfigureEncoderOnEncodingTaskRunner, this,
+                     size));
 }
 
 VEAEncoder::~VEAEncoder() {
-  if (encoding_task_runner_->BelongsToCurrentThread()) {
-    DestroyOnEncodingTaskRunner();
-    return;
-  }
-
   base::WaitableEvent release_waiter(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
@@ -79,13 +79,6 @@
   release_waiter.Wait();
 }
 
-void VEAEncoder::Initialize(const gfx::Size& resolution) {
-  encoding_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VEAEncoder::ConfigureEncoderOnEncodingTaskRunner, this,
-                     resolution));
-}
-
 void VEAEncoder::RequireBitstreamBuffers(unsigned int /*input_count*/,
                                          const gfx::Size& input_coded_size,
                                          size_t output_buffer_size) {
@@ -272,8 +265,7 @@
     base::WaitableEvent* async_waiter) {
   DCHECK(encoding_task_runner_->BelongsToCurrentThread());
   video_encoder_.reset();
-  if (async_waiter)
-    async_waiter->Signal();
+  async_waiter->Signal();
 }
 
 }  // namespace content
diff --git a/content/renderer/media_recorder/vea_encoder.h b/content/renderer/media_recorder/vea_encoder.h
index 3dc2acc..3d06e59 100644
--- a/content/renderer/media_recorder/vea_encoder.h
+++ b/content/renderer/media_recorder/vea_encoder.h
@@ -57,13 +57,12 @@
 
   // VideoTrackRecorder::Encoder implementation.
   ~VEAEncoder() override;
-  void Initialize(const gfx::Size& resolution) override;
   void EncodeOnEncodingTaskRunner(scoped_refptr<media::VideoFrame> frame,
                                   base::TimeTicks capture_timestamp) override;
 
   void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size);
 
-  void DestroyOnEncodingTaskRunner(base::WaitableEvent* async_waiter = nullptr);
+  void DestroyOnEncodingTaskRunner(base::WaitableEvent* async_waiter);
 
   media::GpuVideoAcceleratorFactories* const gpu_factories_;
 
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index fa20d4a..c2e5953 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -163,23 +163,6 @@
 
 }  // anonymous namespace
 
-VideoTrackRecorder::Counter::Counter() : count_(0u), weak_factory_(this) {}
-
-VideoTrackRecorder::Counter::~Counter() = default;
-
-void VideoTrackRecorder::Counter::IncreaseCount() {
-  count_++;
-}
-
-void VideoTrackRecorder::Counter::DecreaseCount() {
-  count_--;
-}
-
-base::WeakPtr<VideoTrackRecorder::Counter>
-VideoTrackRecorder::Counter::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
 VideoTrackRecorder::Encoder::Encoder(
     const OnEncodedVideoCB& on_encoded_video_callback,
     int32_t bits_per_second,
@@ -190,7 +173,7 @@
       paused_(false),
       on_encoded_video_callback_(on_encoded_video_callback),
       bits_per_second_(bits_per_second),
-      num_frames_in_encode_(std::make_unique<VideoTrackRecorder::Counter>()) {
+      num_frames_in_encode_(0) {
   DCHECK(!on_encoded_video_callback_.is_null());
   if (encoding_task_runner_)
     return;
@@ -201,10 +184,6 @@
 
 VideoTrackRecorder::Encoder::~Encoder() {
   main_task_runner_->DeleteSoon(FROM_HERE, video_renderer_.release());
-  if (origin_task_runner_ && !origin_task_runner_->BelongsToCurrentThread()) {
-    origin_task_runner_->DeleteSoon(FROM_HERE,
-                                    std::move(num_frames_in_encode_));
-  }
 }
 
 void VideoTrackRecorder::Encoder::StartFrameEncode(
@@ -225,7 +204,7 @@
     return;
   }
 
-  if (num_frames_in_encode_->count() > kMaxNumberOfFramesInEncode) {
+  if (num_frames_in_encode_ > kMaxNumberOfFramesInEncode) {
     DLOG(WARNING) << "Too many frames are queued up. Dropping this one.";
     return;
   }
@@ -247,13 +226,9 @@
         video_frame, video_frame->format(), video_frame->visible_rect(),
         video_frame->natural_size());
   }
-  wrapped_frame->AddDestructionObserver(media::BindToCurrentLoop(
-      base::BindOnce(&VideoTrackRecorder::Counter::DecreaseCount,
-                     num_frames_in_encode_->GetWeakPtr())));
-  wrapped_frame->AddDestructionObserver(
-      base::BindOnce([](const scoped_refptr<VideoFrame>& video_frame) {},
-                     std::move(video_frame)));
-  num_frames_in_encode_->IncreaseCount();
+  wrapped_frame->AddDestructionObserver(media::BindToCurrentLoop(base::Bind(
+      &VideoTrackRecorder::Encoder::FrameReleased, this, video_frame)));
+  ++num_frames_in_encode_;
 
   encoding_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&Encoder::EncodeOnEncodingTaskRunner, this,
@@ -374,6 +349,12 @@
   return false;
 }
 
+void VideoTrackRecorder::Encoder::FrameReleased(
+    const scoped_refptr<VideoFrame>& frame) {
+  DCHECK(origin_task_runner_->BelongsToCurrentThread());
+  --num_frames_in_encode_;
+}
+
 // static
 VideoTrackRecorder::CodecId VideoTrackRecorder::GetPreferredCodecId() {
   return GetCodecEnumerator()->GetPreferredCodecId();
@@ -396,7 +377,7 @@
     int32_t bits_per_second,
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
     : track_(track),
-      should_pause_encoder_on_initialization_(false),
+      paused_before_init_(false),
       main_task_runner_(std::move(main_task_runner)),
       weak_ptr_factory_(this) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
@@ -426,7 +407,7 @@
   if (encoder_)
     encoder_->SetPaused(true);
   else
-    should_pause_encoder_on_initialization_ = true;
+    paused_before_init_ = true;
 }
 
 void VideoTrackRecorder::Resume() {
@@ -434,7 +415,7 @@
   if (encoder_)
     encoder_->SetPaused(false);
   else
-    should_pause_encoder_on_initialization_ = false;
+    paused_before_init_ = false;
 }
 
 void VideoTrackRecorder::OnVideoFrameForTesting(
@@ -497,10 +478,9 @@
         NOTREACHED() << "Unsupported codec " << static_cast<int>(codec);
     }
   }
-  encoder_->Initialize(input_size);
 
-  if (should_pause_encoder_on_initialization_)
-    encoder_->SetPaused(should_pause_encoder_on_initialization_);
+  if (paused_before_init_)
+    encoder_->SetPaused(paused_before_init_);
 
   // StartFrameEncode() will be called on Render IO thread.
   MediaStreamVideoSink::ConnectToTrack(
diff --git a/content/renderer/media_recorder/video_track_recorder.h b/content/renderer/media_recorder/video_track_recorder.h
index 5370a2d..7316080 100644
--- a/content/renderer/media_recorder/video_track_recorder.h
+++ b/content/renderer/media_recorder/video_track_recorder.h
@@ -69,22 +69,6 @@
                           bool is_key_frame)>;
   using OnErrorCB = base::Closure;
 
-  // Wraps a counter in a class in order to enable use of base::WeakPtr<>.
-  // See https://crbug.com/859610 for why this was added.
-  class Counter {
-   public:
-    Counter();
-    ~Counter();
-    uint32_t count() const { return count_; }
-    void IncreaseCount();
-    void DecreaseCount();
-    base::WeakPtr<Counter> GetWeakPtr();
-
-   private:
-    uint32_t count_;
-    base::WeakPtrFactory<Counter> weak_factory_;
-  };
-
   // Base class to describe a generic Encoder, encapsulating all actual encoder
   // (re)configurations, encoding and delivery of received frames. This class is
   // ref-counted to allow the MediaStreamVideoTrack to hold a reference to it
@@ -107,11 +91,6 @@
             scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner =
                 nullptr);
 
-    // This must be called exactly once after the constructor and before any of
-    // the other public methods. Subclasses may choose to override this to
-    // perform initialization work that cannot be done in the constructor.
-    virtual void Initialize(const gfx::Size& resolution) {}
-
     // Start encoding |frame|, returning via |on_encoded_video_callback_|. This
     // call will also trigger an encode configuration upon first frame arrival
     // or parameter change, and an EncodeOnEncodingTaskRunner() to actually
@@ -139,13 +118,6 @@
     friend class base::RefCountedThreadSafe<Encoder>;
     friend class VideoTrackRecorderTest;
 
-    // This destructor may run on either |main_task_runner|,
-    // |encoding_task_runner|, or |origin_task_runner_|. Main ownership lies
-    // with VideoTrackRecorder. Shared ownership is handed out to
-    // asynchronous tasks running on |encoding_task_runner| for encoding. Shared
-    // ownership is also handed out to a MediaStreamVideoTrack which pushes
-    // frames on |origin_task_runner_|. Each of these may end up being the last
-    // reference.
     virtual ~Encoder();
 
     virtual void EncodeOnEncodingTaskRunner(
@@ -178,8 +150,7 @@
     const int32_t bits_per_second_;
 
     // Number of frames that we keep the reference alive for encode.
-    // Operated and released exclusively on |origin_task_runner_|.
-    std::unique_ptr<Counter> num_frames_in_encode_;
+    uint32_t num_frames_in_encode_;
 
     // Used to retrieve incoming opaque VideoFrames (i.e. VideoFrames backed by
     // textures). Created on-demand on |main_task_runner_|.
@@ -232,7 +203,8 @@
                       base::TimeTicks capture_time)>
       initialize_encoder_callback_;
 
-  bool should_pause_encoder_on_initialization_;
+  // Used to track the paused state during the initialization process.
+  bool paused_before_init_;
 
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
 
diff --git a/content/renderer/media_recorder/video_track_recorder_unittest.cc b/content/renderer/media_recorder/video_track_recorder_unittest.cc
index 877795c7..2eecab58 100644
--- a/content/renderer/media_recorder/video_track_recorder_unittest.cc
+++ b/content/renderer/media_recorder/video_track_recorder_unittest.cc
@@ -137,7 +137,7 @@
   }
 
   uint32_t NumFramesInEncode() {
-    return video_track_recorder_->encoder_->num_frames_in_encode_->count();
+    return video_track_recorder_->encoder_->num_frames_in_encode_;
   }
 
   // A ChildProcess is needed to fool the Tracks and Sources into believing they
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 688550b..20674ca6 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2103,7 +2103,7 @@
   // TODO(nasko): Remove the dependency on RenderViewImpl here and ref count
   // the process based on the lifetime of this RenderFrameImpl object.
   if (is_main_frame)
-    render_view->WasSwappedOut();
+    render_view->GetWidget()->WasSwappedOut();
 
   // Notify the browser that this frame was swapped. Use the RenderThread
   // directly because |this| is deleted.  Post a task to send the ACK, so that
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 3c3c84c..594a927 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -249,6 +249,24 @@
     return static_cast<TestRenderFrame*>(view()->GetMainRenderFrame());
   }
 
+  void ReceiveDisableDeviceEmulation(RenderViewImpl* view) {
+    // Emulates receiving an IPC message.
+    view->GetWidget()->OnDisableDeviceEmulation();
+  }
+
+  void ReceiveEnableDeviceEmulation(
+      RenderViewImpl* view,
+      const blink::WebDeviceEmulationParams& params) {
+    // Emulates receiving an IPC message.
+    view->GetWidget()->OnEnableDeviceEmulation(params);
+  }
+
+  void ReceiveSetTextDirection(RenderViewImpl* view,
+                               blink::WebTextDirection direction) {
+    // Emulates receiving an IPC message.
+    view->OnSetTextDirection(direction);
+  }
+
   void GoToOffsetWithParams(int offset,
                             const PageState& state,
                             const CommonNavigationParams common_params,
@@ -487,7 +505,7 @@
     params.view_size.width = width;
     params.view_size.height = height;
     params.device_scale_factor = dpr;
-    view()->OnEnableDeviceEmulation(params);
+    ReceiveEnableDeviceEmulation(view(), params);
     EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_width, &emulated_width));
     EXPECT_EQ(width, emulated_width);
     EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_height,
@@ -893,10 +911,10 @@
             view()->GetWidget()->GetOriginalScreenInfo().device_scale_factor);
   EXPECT_EQ(device_scale, child_proxy->screen_info().device_scale_factor);
 
-  view()->OnDisableDeviceEmulation();
+  ReceiveDisableDeviceEmulation(view());
 
   blink::WebDeviceEmulationParams params;
-  view()->OnEnableDeviceEmulation(params);
+  ReceiveEnableDeviceEmulation(view(), params);
   // Don't disable here to test that emulation is being shutdown properly.
 }
 
@@ -1458,7 +1476,7 @@
   for (size_t i = 0; i < arraysize(kTextDirection); ++i) {
     // Set the text direction of the <textarea> element.
     ExecuteJavaScriptForTests("document.getElementById('test').focus();");
-    view()->OnSetTextDirection(kTextDirection[i].direction);
+    ReceiveSetTextDirection(view(), kTextDirection[i].direction);
 
     // Write the values of its DOM 'dir' attribute and its CSS 'direction'
     // property to the <div> element.
@@ -1911,7 +1929,7 @@
 TEST_F(RenderViewImplTest, MessageOrderInDidChangeSelection) {
   LoadHTML("<textarea id=\"test\"></textarea>");
 
-  view()->SetHandlingInputEventForTesting(true);
+  view()->GetWidget()->SetHandlingInputEvent(true);
   ExecuteJavaScriptForTests("document.getElementById('test').focus();");
 
   bool is_input_type_called = false;
@@ -2599,10 +2617,10 @@
     TestEmulatedSizeDprDsf(1005, 1102, 3.f, 1.f);
   }
 
-  view()->OnDisableDeviceEmulation();
+  ReceiveDisableDeviceEmulation(view());
 
   blink::WebDeviceEmulationParams params;
-  view()->OnEnableDeviceEmulation(params);
+  ReceiveEnableDeviceEmulation(view(), params);
   // Don't disable here to test that emulation is being shutdown properly.
 }
 
@@ -2628,10 +2646,10 @@
     TestEmulatedSizeDprDsf(1005, 1102, 3.f, compositor_dsf);
   }
 
-  view()->OnDisableDeviceEmulation();
+  ReceiveDisableDeviceEmulation(view());
 
   blink::WebDeviceEmulationParams params;
-  view()->OnEnableDeviceEmulation(params);
+  ReceiveEnableDeviceEmulation(view(), params);
   // Don't disable here to test that emulation is being shutdown properly.
 }
 
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index c83399d19..85880a9 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -446,14 +446,14 @@
     mojom::CreateViewParamsPtr params,
     RenderWidget::ShowCallback show_callback,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  bool was_created_by_renderer = !show_callback.is_null();
+  bool has_show_callback = !!show_callback;
 #if defined(OS_ANDROID)
   // TODO(sgurun): crbug.com/325351 Needed only for android webview's deprecated
   // HandleNavigation codepath.
-  was_created_by_renderer_ = was_created_by_renderer;
+  was_created_by_renderer_ = has_show_callback;
 #endif
   renderer_wide_named_frame_lookup_ = params->renderer_wide_named_frame_lookup;
-  display_mode_ = params->visual_properties.display_mode;
+  RenderWidget::set_display_mode(params->visual_properties.display_mode);
 
   WebFrame* opener_frame =
       RenderFrameImpl::ResolveOpener(params->opener_frame_route_id);
@@ -476,7 +476,7 @@
   if (command_line.HasSwitch(switches::kStatsCollectionController))
     stats_collection_observer_.reset(new StatsCollectionObserver(this));
 
-  webview()->SetDisplayMode(display_mode_);
+  webview()->SetDisplayMode(GetWidget()->display_mode());
   webview()->GetSettings()->SetThreadedScrollingEnabled(
       !command_line.HasSwitch(switches::kDisableThreadedScrolling));
   webview()->SetShowFPSCounter(
@@ -528,9 +528,9 @@
         this, params->main_frame_routing_id,
         std::move(main_frame_interface_provider),
         params->main_frame_widget_routing_id, params->hidden,
-        GetWebScreenInfo(), compositor_deps_, opener_frame,
-        params->devtools_main_frame_token, params->replicated_frame_state,
-        params->has_committed_real_load);
+        GetWidget()->GetWebScreenInfo(), GetWidget()->compositor_deps(),
+        opener_frame, params->devtools_main_frame_token,
+        params->replicated_frame_state, params->has_committed_real_load);
   }
 
   // TODO(dcheng): Shouldn't these be mutually exclusive at this point? See
@@ -549,10 +549,11 @@
 
   // If this RenderView's creation was initiated by an opener page in this
   // process, (e.g. window.open()), we won't be visible until we ask the opener,
-  // via show_callback, to make us visible. Otherwise, we went through a
-  // browser-initiated creation, and show() won't be called.
-  if (!was_created_by_renderer)
-    did_show_ = true;
+  // via |show_callback|, to make us visible. Otherwise, we went through a
+  // browser-initiated creation, and Show() won't be called. In this case, no
+  // |show_callback| is given to inform that Show() won't be called.
+  if (!has_show_callback)
+    RenderWidget::set_did_show();
 
   // TODO(davidben): Move this state from Blink into content.
   if (params->window_was_created_with_opener)
@@ -1182,7 +1183,7 @@
   if (webview()) {
     WebLocalFrame* focused_frame = GetWebView()->FocusedFrame();
     if (focused_frame) {
-      input_handler_->set_handling_input_event(true);
+      GetWidget()->SetHandlingInputEvent(true);
       blink::WebRange initial_range = focused_frame->SelectionRange();
       if (!initial_range.IsNull())
         did_select = focused_frame->SelectWordAroundCaret();
@@ -1192,7 +1193,7 @@
             adjusted_range.StartOffset() - initial_range.StartOffset();
         end_adjust = adjusted_range.EndOffset() - initial_range.EndOffset();
       }
-      input_handler_->set_handling_input_event(false);
+      GetWidget()->SetHandlingInputEvent(false);
     }
   }
   Send(new ViewHostMsg_SelectWordAroundCaretAck(
@@ -1226,7 +1227,7 @@
 
 void RenderViewImpl::OnUpdateWindowScreenRect(gfx::Rect window_screen_rect) {
   // Defers to the RenderWidget message handler.
-  RenderWidget::OnUpdateWindowScreenRect(window_screen_rect);
+  GetWidget()->SetWindowScreenRect(window_screen_rect);
 }
 
 void RenderViewImpl::OnAudioStateChanged(bool is_audio_playing) {
@@ -1421,7 +1422,7 @@
   bool never_visible = false;
 
   VisualProperties visual_properties = VisualProperties();
-  visual_properties.screen_info = screen_info_;
+  visual_properties.screen_info = GetWidget()->screen_info();
 
   // The initial hidden state for the RenderViewImpl here has to match what the
   // browser will eventually decide for the given disposition. Since we have to
@@ -1461,7 +1462,8 @@
                      base::Unretained(creator_frame), opened_by_user_gesture);
 
   RenderViewImpl* view = RenderViewImpl::Create(
-      compositor_deps_, std::move(view_params), std::move(show_callback),
+      GetWidget()->compositor_deps(), std::move(view_params),
+      std::move(show_callback),
       creator->GetTaskRunner(blink::TaskType::kInternalDefault));
 
   return view->webview();
@@ -1469,16 +1471,14 @@
 
 WebWidget* RenderViewImpl::CreatePopup(blink::WebLocalFrame* creator,
                                        blink::WebPopupType popup_type) {
-  RenderWidget* widget = RenderWidget::CreateForPopup(
-      this, compositor_deps_, popup_type, screen_info_,
+  RenderWidget* popup_widget = RenderWidget::CreateForPopup(
+      this, GetWidget()->compositor_deps(), popup_type,
+      GetWidget()->screen_info(),
       creator->GetTaskRunner(blink::TaskType::kInternalDefault));
-  if (!widget)
+  if (!popup_widget)
     return nullptr;
-  if (screen_metrics_emulator_) {
-    widget->SetPopupOriginAdjustmentsForEmulation(
-        screen_metrics_emulator_.get());
-  }
-  return widget->GetWebWidget();
+  popup_widget->ApplyEmulatedScreenMetricsForPopupWidget(GetWidget());
+  return popup_widget->GetWebWidget();
 }
 
 base::StringPiece RenderViewImpl::GetSessionStorageNamespaceId() {
@@ -1498,7 +1498,7 @@
                         frame->Top()->IsWebRemoteFrame());
 
   RenderFrameImpl::FromWebFrame(frame)->ScriptedPrint(
-      input_handler_->handling_input_event());
+      GetWidget()->input_handler().handling_input_event());
 }
 
 bool RenderViewImpl::EnumerateChosenDirectory(
@@ -1926,9 +1926,11 @@
 
 #if defined(OS_MACOSX)
 void RenderViewImpl::OnClose() {
-  if (closing_)
+  if (GetWidget()->closing())
     RenderThread::Get()->Send(new ViewHostMsg_Close_ACK(GetRoutingID()));
-  // OnClose() is a protected member of RenderWidget.
+  // This method is protected for OS_MACOSX only, because the message gets sent
+  // to the RenderViewImpl instead of to the RenderWidget.
+  // TODO(danakj): Move this message to RenderWidget?
   RenderWidget::OnClose();
 }
 #endif
@@ -1972,8 +1974,9 @@
   // This IPC only updates the screen info on RenderViews that have a remote
   // main frame. For local main frames, the ScreenInfo is updated in
   // ViewMsg_Resize.
+  // TODO(danakj): Move this message to RenderWidget?
   if (!main_render_frame_)
-    screen_info_ = screen_info;
+    GetWidget()->set_screen_info(screen_info);
 }
 
 void RenderViewImpl::SetPageFrozen(bool frozen) {
@@ -2201,10 +2204,6 @@
   }
 }
 
-void RenderViewImpl::UseSynchronousResizeModeForTesting(bool enable) {
-  resizing_mode_selector_->set_is_synchronous_mode(enable);
-}
-
 void RenderViewImpl::OnResolveTapDisambiguation(
     base::TimeTicks timestamp,
     const gfx::Point& tap_viewport_offset,
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index dab34d99..ddcafbc 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -206,8 +206,6 @@
   // synchronously from the renderer.
   void SetFocusAndActivateForTesting(bool enable);
 
-  void UseSynchronousResizeModeForTesting(bool enable);
-
   void DidCommitProvisionalHistoryLoad();
 
   // IPC::Listener implementation (via RenderWidget inheritance).
@@ -350,7 +348,6 @@
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, OnHandleKeyboardEvent);
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, OnImeTypeChanged);
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, OnNavStateChanged);
-  FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, OnSetTextDirection);
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, OnUpdateWebPreferences);
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest,
                            SetEditableSelectionAndComposition);
@@ -362,12 +359,6 @@
                            GetCompositionCharacterBoundsTest);
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, OnNavigationHttpPost);
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, UpdateDSFAfterSwapIn);
-  FRIEND_TEST_ALL_PREFIXES(RenderViewImplScaleFactorTest,
-                           ScreenMetricsEmulationWithOriginalDSF1);
-  FRIEND_TEST_ALL_PREFIXES(RenderViewImplScaleFactorTest,
-                           ScreenMetricsEmulationWithOriginalDSF2);
-  FRIEND_TEST_ALL_PREFIXES(RenderViewImplScaleFactorTest,
-                           DeviceEmulationWithOOPIF);
   FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest,
                            DecideNavigationPolicyHandlesAllTopLevel);
 #if defined(OS_MACOSX)
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index fc5366e1..e2f4005 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -661,8 +661,12 @@
   RenderProcess::current()->ReleaseProcess();
 }
 
-void RenderWidget::SetPopupOriginAdjustmentsForEmulation(
-    RenderWidgetScreenMetricsEmulator* emulator) {
+void RenderWidget::ApplyEmulatedScreenMetricsForPopupWidget(
+    RenderWidget* origin_widget) {
+  RenderWidgetScreenMetricsEmulator* emulator =
+      origin_widget->screen_metrics_emulator_.get();
+  if (!emulator)
+    return;
   popup_origin_scale_for_emulation_ = emulator->scale();
   popup_view_origin_for_emulation_ = emulator->applied_widget_rect().origin();
   popup_screen_origin_for_emulation_ =
@@ -1588,6 +1592,10 @@
   return nullptr;
 }
 
+void RenderWidget::SetHandlingInputEvent(bool handling_input_event) {
+  input_handler_->set_handling_input_event(handling_input_event);
+}
+
 void RenderWidget::QueueMessage(IPC::Message* msg,
                                 MessageDeliveryPolicy policy) {
   // RenderThreadImpl::current() is NULL in some tests.
@@ -2022,14 +2030,6 @@
   Send(new ViewHostMsg_UpdateScreenRects_ACK(routing_id()));
 }
 
-void RenderWidget::OnUpdateWindowScreenRect(
-    const gfx::Rect& window_screen_rect) {
-  if (screen_metrics_emulator_)
-    screen_metrics_emulator_->OnUpdateWindowScreenRect(window_screen_rect);
-  else
-    window_screen_rect_ = window_screen_rect;
-}
-
 void RenderWidget::OnSetViewportIntersection(
     const gfx::Rect& viewport_intersection,
     const gfx::Rect& compositor_visible_rect) {
@@ -3042,6 +3042,7 @@
 }
 
 void RenderWidget::OnWaitNextFrameForTests(int routing_id) {
+  // Sends an ACK to the browser process during the next compositor frame.
   QueueMessage(new ViewHostMsg_WaitForNextFrameForTests_ACK(routing_id),
                MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE);
 }
@@ -3145,13 +3146,20 @@
   }
 }
 
+void RenderWidget::SetWindowScreenRect(const gfx::Rect& window_screen_rect) {
+  if (screen_metrics_emulator_)
+    screen_metrics_emulator_->OnUpdateWindowScreenRect(window_screen_rect);
+  else
+    window_screen_rect_ = window_screen_rect;
+}
+
 bool RenderWidget::IsSurfaceSynchronizationEnabled() const {
   return layer_tree_view_ &&
          layer_tree_view_->IsSurfaceSynchronizationEnabled();
 }
 
-void RenderWidget::SetHandlingInputEventForTesting(bool handling_input_event) {
-  input_handler_->set_handling_input_event(handling_input_event);
+void RenderWidget::UseSynchronousResizeModeForTesting(bool enable) {
+  resizing_mode_selector_->set_is_synchronous_mode(enable);
 }
 
 void RenderWidget::SetDeviceScaleFactorForTesting(float factor) {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index fecd455..132866f3 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -147,6 +147,12 @@
                               blink::WebNavigationPolicy policy,
                               const gfx::Rect& initial_rect)>;
 
+  // Time-To-First-Active-Paint(TTFAP) type
+  enum {
+    TTFAP_AFTER_PURGED,
+    TTFAP_5MIN_AFTER_BACKGROUNDED,
+  };
+
   // Creates a new RenderWidget for a popup. |opener| is the RenderView that
   // this widget lives inside.
   static RenderWidget* CreateForPopup(
@@ -177,6 +183,12 @@
       CreateRenderWidgetFunction create_render_widget,
       RenderWidgetInitializedCallback render_widget_initialized_callback);
 
+  void set_owner_delegate(RenderWidgetOwnerDelegate* owner_delegate) {
+    DCHECK(!owner_delegate_);
+    owner_delegate_ = owner_delegate;
+  }
+  RenderWidgetOwnerDelegate* owner_delegate() const { return owner_delegate_; }
+
   // Returns the RenderWidget for the given routing ID.
   static RenderWidget* FromRoutingID(int32_t routing_id);
 
@@ -214,11 +226,8 @@
     return visible_viewport_size_;
   }
 
-  void set_owner_delegate(RenderWidgetOwnerDelegate* owner_delegate) {
-    DCHECK(!owner_delegate_);
-    owner_delegate_ = owner_delegate;
-  }
-  RenderWidgetOwnerDelegate* owner_delegate() const { return owner_delegate_; }
+  ScreenInfo screen_info() const { return screen_info_; }
+  void set_screen_info(const ScreenInfo& info) { screen_info_ = info; }
 
   // Sets whether this RenderWidget has been swapped out to be displayed by
   // a RenderWidget in a different process.  If so, no new IPC messages will be
@@ -337,6 +346,10 @@
                      blink::WebDragOperationsMask mask,
                      const SkBitmap& drag_image,
                      const blink::WebPoint& image_offset) override;
+  void SetTouchAction(cc::TouchAction touch_action) override;
+  void RequestUnbufferedInputEvents() override;
+  void HasTouchEventHandlers(bool has_handlers) override;
+  void SetNeedsLowLatencyInput(bool) override;
 
   // Override point to obtain that the current input method state and caret
   // position.
@@ -363,15 +376,15 @@
   void WillCloseLayerTreeView();
 
   LayerTreeView* layer_tree_view() const { return layer_tree_view_.get(); }
-
   WidgetInputHandlerManager* widget_input_handler_manager() {
     return widget_input_handler_manager_.get();
   }
-
   const RenderWidgetInputHandler& input_handler() const {
     return *input_handler_;
   }
 
+  void SetHandlingInputEvent(bool handling_input_event);
+
   // Deliveres |message| together with compositor state change updates. The
   // exact behavior depends on |policy|.
   // This mechanism is not a drop-in replacement for IPC: messages sent this way
@@ -388,8 +401,7 @@
   void OnImeEventGuardStart(ImeEventGuard* guard);
   void OnImeEventGuardFinish(ImeEventGuard* guard);
 
-  void SetPopupOriginAdjustmentsForEmulation(
-      RenderWidgetScreenMetricsEmulator* emulator);
+  void ApplyEmulatedScreenMetricsForPopupWidget(RenderWidget* origin_widget);
 
   gfx::Rect AdjustValidationMessageAnchor(const gfx::Rect& anchor);
 
@@ -509,15 +521,11 @@
 
   void SetMouseCapture(bool capture);
 
-  // Time-To-First-Active-Paint(TTFAP) type
-  enum {
-    TTFAP_AFTER_PURGED,
-    TTFAP_5MIN_AFTER_BACKGROUNDED,
-  };
+  void SetWindowScreenRect(const gfx::Rect& window_screen_rect);
 
   bool IsSurfaceSynchronizationEnabled() const;
 
-  void SetHandlingInputEventForTesting(bool handling_input_event);
+  void UseSynchronousResizeModeForTesting(bool enable);
   void SetDeviceScaleFactorForTesting(float factor);
   void SetDeviceColorSpaceForTesting(const gfx::ColorSpace& color_space);
   void SetWindowRectSynchronouslyForTesting(const gfx::Rect& new_window_rect);
@@ -528,18 +536,6 @@
   base::WeakPtr<RenderWidget> AsWeakPtr();
 
  protected:
-  // Friend RefCounted so that the dtor can be non-public. Using this class
-  // without ref-counting is an error.
-  friend class base::RefCounted<RenderWidget>;
-
-  // For unit tests.
-  friend class RenderWidgetTest;
-
-  enum ResizeAck {
-    SEND_RESIZE_ACK,
-    NO_RESIZE_ACK,
-  };
-
   RenderWidget(int32_t widget_routing_id,
                CompositorDependencies* compositor_deps,
                blink::WebPopupType popup_type,
@@ -561,13 +557,57 @@
   static blink::WebWidget* CreateWebWidget(RenderWidget* render_widget);
 
   // Called by Create() functions and subclasses to finish initialization.
-  // |show_callback| will be invoked once WebWidgetClient::show() occurs, and
-  // should be null if show() won't be triggered for this widget.
+  // |show_callback| will be invoked once WebWidgetClient::Show() occurs, and
+  // should be null if Show() won't be triggered for this widget.
   void Init(ShowCallback show_callback, blink::WebWidget* web_widget);
 
+  // Initialization methods used by the RenderViewImpl subclass.
+  //
   // Idle user detector is optionally set up and destroyed during
   // initialization/teardown.
   void SetUpIdleUserDetector();
+  // Update the web view's device scale factor.
+  void UpdateWebViewWithDeviceScaleFactor();
+  // Set the display mode during initialization.
+  void set_display_mode(blink::WebDisplayMode mode) { display_mode_ = mode; }
+  // Informs that Show() will not happen.
+  void set_did_show() { did_show_ = true; }
+
+  // Close the underlying WebWidget and stop the compositor.
+  virtual void Close();
+
+  // Notify subclasses that we initiated the paint operation.
+  virtual void DidInitiatePaint() {}
+
+  // Gets the current URL or a placeholder constant for creating 3d contexts and
+  // attributing them back to a URL.
+  virtual GURL GetURLForGraphicsContext3D();
+
+  // RenderWidget IPC message handler that can be overridden by subclasses.
+  virtual void OnSynchronizeVisualProperties(const VisualProperties& params);
+
+#if defined(OS_MACOSX)
+  // On MacOSX this message arrives on RenderViewImpl instead of here, and it
+  // needs to be able to call back to the normal message handler here.
+  void OnClose();
+#endif
+
+ private:
+  // Friend RefCounted so that the dtor can be non-public. Using this class
+  // without ref-counting is an error.
+  friend class base::RefCounted<RenderWidget>;
+
+  // TODO(nasko): Temporarily friend RenderFrameImpl for WasSwappedOut(),
+  // while we move frame specific code away from RenderViewImpl/RenderWidget.
+  friend class RenderFrameImpl;
+
+  // For unit tests.
+  friend class InteractiveRenderWidget;
+  friend class PopupRenderWidget;
+  friend class QueueMessageSwapPromiseTest;
+  friend class RenderWidgetTest;
+  friend class RenderViewImplTest;
+  FRIEND_TEST_ALL_PREFIXES(RenderWidgetPopupUnittest, EmulatingPopupRect);
 
   // Allows the process to exit once the unload handler has finished, if there
   // are no other active RenderWidgets.
@@ -579,32 +619,27 @@
   gfx::Size GetSizeForWebWidget() const;
   void ResizeWebWidget();
 
-  // Close the underlying WebWidget and stop the compositor.
-  virtual void Close();
-
   // Just Close the WebWidget, in cases where the Close() will be deferred.
   // It is safe to call this multiple times, which happens in the case of
   // frame widgets beings closed, since subsequent calls are ignored.
   void CloseWebWidget();
 
-  // Update the web view's device scale factor.
-  void UpdateWebViewWithDeviceScaleFactor();
-
 #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
   void SetExternalPopupOriginAdjustmentsForEmulation(
       ExternalPopupMenu* popup,
       RenderWidgetScreenMetricsEmulator* emulator);
 #endif
 
-  // RenderWidget IPC message handlers
+  // RenderWidget IPC message handlers.
   void OnHandleInputEvent(
       const blink::WebInputEvent* event,
       const std::vector<const blink::WebInputEvent*>& coalesced_events,
       const ui::LatencyInfo& latency_info,
       InputEventDispatchType dispatch_type);
+#if !defined(OS_MACOSX)
   void OnClose();
+#endif
   void OnCreatingNewAck();
-  virtual void OnSynchronizeVisualProperties(const VisualProperties& params);
   void OnEnableDeviceEmulation(const blink::WebDeviceEmulationParams& params);
   void OnDisableDeviceEmulation();
   void OnWasHidden();
@@ -614,7 +649,6 @@
   void OnUpdateVideoAck(int32_t video_id);
   void OnRequestSetBoundsAck();
   void OnForceRedraw(int snapshot_id);
-  // Request from browser to show context menu.
   void OnShowContextMenu(ui::MenuSourceType source_type,
                          const gfx::Point& location);
 
@@ -622,14 +656,12 @@
   void OnGetFPS();
   void OnUpdateScreenRects(const gfx::Rect& view_screen_rect,
                            const gfx::Rect& window_screen_rect);
-  void OnUpdateWindowScreenRect(const gfx::Rect& window_screen_rect);
   void OnSetViewportIntersection(const gfx::Rect& viewport_intersection,
                                  const gfx::Rect& compositor_visible_rect);
   void OnSetIsInert(bool);
   void OnSetInheritedEffectiveTouchAction(cc::TouchAction touch_action);
   void OnUpdateRenderThrottlingStatus(bool is_throttled,
                                       bool subtree_throttled);
-  // Real data that is dragged is not included at DragEnter time.
   void OnDragTargetDragEnter(
       const std::vector<DropData::Metadata>& drop_meta_data,
       const gfx::PointF& client_pt,
@@ -651,13 +683,7 @@
                          blink::WebDragOperation drag_operation);
   void OnDragSourceSystemDragEnded();
   void OnOrientationChange();
-
-  // Notify subclasses that we initiated the paint operation.
-  virtual void DidInitiatePaint() {}
-
-  // Gets the current URL or a placeholder constant for creating 3d contexts and
-  // attributing them back to a URL.
-  virtual GURL GetURLForGraphicsContext3D();
+  void OnWaitNextFrameForTests(int routing_id);
 
   // Sets the "hidden" state of this widget.  All accesses to is_hidden_ should
   // use this method so that we can properly inform the RenderThread of our
@@ -702,21 +728,80 @@
   // GetWindowRect() we'll use this pending window rect as the size.
   void SetPendingWindowRect(const blink::WebRect& r);
 
-  // Check whether the WebWidget has any touch event handlers registered.
-  void HasTouchEventHandlers(bool has_handlers) override;
+  // TODO(ekaramad): This method should not be confused with its RenderView
+  // variant, GetWebFrameWidget(). Currently Cast and AndroidWebview's
+  // ContentRendererClients are the only users of the public variant. The public
+  // method will eventually be removed from RenderView and uses of the method
+  // will obtain WebFrameWidget from WebLocalFrame.
+  // Returns the WebFrameWidget associated with this RenderWidget if any.
+  // Returns nullptr if GetWebWidget() returns nullptr or returns a WebWidget
+  // that is not a WebFrameWidget. A WebFrameWidget only makes sense when there
+  // a local root associated with it. RenderWidgetFullscreenPepper and a swapped
+  // out RenderWidgets are amongst the cases where this method returns nullptr.
+  blink::WebFrameWidget* GetFrameWidget() const;
 
-  // Called to update whether low latency input mode is enabled or not.
-  void SetNeedsLowLatencyInput(bool) override;
+  // Applies/Removes the DevTools device emulation transformation to/from a
+  // window rect.
+  void ScreenRectToEmulatedIfNeeded(blink::WebRect* window_rect) const;
+  void EmulatedToScreenRectIfNeeded(blink::WebRect* window_rect) const;
 
-  // Requests unbuffered (ie. low latency) input until a pointerup
-  // event occurs.
-  void RequestUnbufferedInputEvents() override;
+  bool CreateWidget(int32_t opener_id,
+                    blink::WebPopupType popup_type,
+                    int32_t* routing_id);
 
-  // Tell the browser about the actions permitted for a new touch point.
-  void SetTouchAction(cc::TouchAction touch_action) override;
+  void UpdateSurfaceAndScreenInfo(
+      const viz::LocalSurfaceId& new_local_surface_id,
+      const gfx::Size& new_compositor_viewport_pixel_size,
+      const ScreenInfo& new_screen_info);
 
-  // Sends an ACK to the browser process during the next compositor frame.
-  void OnWaitNextFrameForTests(int routing_id);
+  // Used to force the size of a window when running layout tests.
+  void SetWindowRectSynchronously(const gfx::Rect& new_window_rect);
+
+  void UpdateCaptureSequenceNumber(uint32_t capture_sequence_number);
+
+  // A variant of Send but is fatal if it fails. The browser may
+  // be waiting for this IPC Message and if the send fails the browser will
+  // be left in a state waiting for something that never comes. And if it
+  // never comes then it may later determine this is a hung renderer; so
+  // instead fail right away.
+  void SendOrCrash(IPC::Message* msg);
+
+  // Determines whether or not RenderWidget should process IME events from the
+  // browser. It always returns true unless there is no WebFrameWidget to
+  // handle the event, or there is no page focus.
+  bool ShouldHandleImeEvents() const;
+
+  void UpdateTextInputStateInternal(bool show_virtual_keyboard,
+                                    bool reply_to_request);
+
+  gfx::ColorSpace GetRasterColorSpace() const;
+
+  void SendInputEventAck(blink::WebInputEvent::Type type,
+                         uint32_t touch_event_id,
+                         InputEventAckState ack_state,
+                         const ui::LatencyInfo& latency_info,
+                         std::unique_ptr<ui::DidOverscrollParams>,
+                         base::Optional<cc::TouchAction>);
+
+  void UpdateZoom(double zoom_level);
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+  // Returns the focused pepper plugin, if any, inside the WebWidget. That is
+  // the pepper plugin which is focused inside a frame which belongs to the
+  // local root associated with this RenderWidget.
+  PepperPluginInstanceImpl* GetFocusedPepperPluginInsideWidget();
+#endif
+  void RecordTimeToFirstActivePaint();
+
+  // Updates the URL used by the compositor for keying UKM metrics.
+  // Note that this uses the main frame's URL and only if its available in the
+  // current process. In the case where it is not available, no metrics will be
+  // recorded.
+  void UpdateURLForCompositorUkm();
+
+  // This method returns the WebLocalFrame which is currently focused and
+  // belongs to the frame tree associated with this RenderWidget.
+  blink::WebLocalFrame* GetFocusedWebLocalFrameInWidget() const;
 
   // Routing ID that allows us to communicate to the parent browser process
   // RenderWidgetHost.
@@ -889,82 +974,6 @@
 
   viz::LocalSurfaceId local_surface_id_from_parent_;
 
- private:
-  // TODO(ekaramad): This method should not be confused with its RenderView
-  // variant, GetWebFrameWidget(). Currently Cast and AndroidWebview's
-  // ContentRendererClients are the only users of the public variant. The public
-  // method will eventually be removed from RenderView and uses of the method
-  // will obtain WebFrameWidget from WebLocalFrame.
-  // Returns the WebFrameWidget associated with this RenderWidget if any.
-  // Returns nullptr if GetWebWidget() returns nullptr or returns a WebWidget
-  // that is not a WebFrameWidget. A WebFrameWidget only makes sense when there
-  // a local root associated with it. RenderWidgetFullscreenPepper and a swapped
-  // out RenderWidgets are amongst the cases where this method returns nullptr.
-  blink::WebFrameWidget* GetFrameWidget() const;
-
-  // Applies/Removes the DevTools device emulation transformation to/from a
-  // window rect.
-  void ScreenRectToEmulatedIfNeeded(blink::WebRect* window_rect) const;
-  void EmulatedToScreenRectIfNeeded(blink::WebRect* window_rect) const;
-
-  bool CreateWidget(int32_t opener_id,
-                    blink::WebPopupType popup_type,
-                    int32_t* routing_id);
-
-  void UpdateSurfaceAndScreenInfo(
-      const viz::LocalSurfaceId& new_local_surface_id,
-      const gfx::Size& new_compositor_viewport_pixel_size,
-      const ScreenInfo& new_screen_info);
-
-  // Used to force the size of a window when running layout tests.
-  void SetWindowRectSynchronously(const gfx::Rect& new_window_rect);
-
-  void UpdateCaptureSequenceNumber(uint32_t capture_sequence_number);
-
-  // A variant of Send but is fatal if it fails. The browser may
-  // be waiting for this IPC Message and if the send fails the browser will
-  // be left in a state waiting for something that never comes. And if it
-  // never comes then it may later determine this is a hung renderer; so
-  // instead fail right away.
-  void SendOrCrash(IPC::Message* msg);
-
-  // Determines whether or not RenderWidget should process IME events from the
-  // browser. It always returns true unless there is no WebFrameWidget to
-  // handle the event, or there is no page focus.
-  bool ShouldHandleImeEvents() const;
-
-  void UpdateTextInputStateInternal(bool show_virtual_keyboard,
-                                    bool reply_to_request);
-
-  gfx::ColorSpace GetRasterColorSpace() const;
-
-  void SendInputEventAck(blink::WebInputEvent::Type type,
-                         uint32_t touch_event_id,
-                         InputEventAckState ack_state,
-                         const ui::LatencyInfo& latency_info,
-                         std::unique_ptr<ui::DidOverscrollParams>,
-                         base::Optional<cc::TouchAction>);
-
-  void UpdateZoom(double zoom_level);
-
-#if BUILDFLAG(ENABLE_PLUGINS)
-  // Returns the focused pepper plugin, if any, inside the WebWidget. That is
-  // the pepper plugin which is focused inside a frame which belongs to the
-  // local root associated with this RenderWidget.
-  PepperPluginInstanceImpl* GetFocusedPepperPluginInsideWidget();
-#endif
-  void RecordTimeToFirstActivePaint();
-
-  // Updates the URL used by the compositor for keying UKM metrics.
-  // Note that this uses the main frame's URL and only if its available in the
-  // current process. In the case where it is not available, no metrics will be
-  // recorded.
-  void UpdateURLForCompositorUkm();
-
-  // This method returns the WebLocalFrame which is currently focused and
-  // belongs to the frame tree associated with this RenderWidget.
-  blink::WebLocalFrame* GetFocusedWebLocalFrameInWidget() const;
-
   // Indicates whether this widget has focus.
   bool has_focus_;
 
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index 28bf427..b3db9d7 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -295,7 +295,7 @@
 }
 
 void RenderWidgetFullscreenPepper::Invalidate() {
-  InvalidateRect(gfx::Rect(size_.width(), size_.height()));
+  InvalidateRect(gfx::Rect(size()));
 }
 
 void RenderWidgetFullscreenPepper::InvalidateRect(const blink::WebRect& rect) {
@@ -321,7 +321,7 @@
   // away.
   SetLayer(nullptr);
 
-  Send(new ViewHostMsg_Close(routing_id_));
+  Send(new ViewHostMsg_Close(routing_id()));
   Release();
 }
 
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 91392d4..53399da 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -26,6 +26,7 @@
 #include "content/renderer/devtools/render_widget_screen_metrics_emulator.h"
 #include "content/renderer/gpu/layer_tree_view.h"
 #include "content/renderer/input/widget_input_handler_manager.h"
+#include "content/renderer/render_widget_owner_delegate.h"
 #include "content/test/fake_compositor_dependencies.h"
 #include "content/test/mock_render_process.h"
 #include "ipc/ipc_test_sink.h"
@@ -406,8 +407,15 @@
       bool,
       const blink::WebDeviceEmulationParams&) override {}
 
+  // Shuts down the metrics emulator, the compositor, and destroys the internal
+  // WebWidget. Should be called before destroying the object.
+  void Shutdown() {
+    RenderWidget::Close();
+    shutdown_ = true;
+  }
+
  protected:
-  ~PopupRenderWidget() override { webwidget_internal_ = nullptr; }
+  ~PopupRenderWidget() override { DCHECK(shutdown_); }
 
   bool Send(IPC::Message* msg) override {
     sink_.OnMessageReceived(*msg);
@@ -416,6 +424,7 @@
   }
 
  private:
+  bool shutdown_ = false;
   IPC::TestSink sink_;
   MockWebWidget mock_webwidget_;
   static int routing_id_;
@@ -435,7 +444,7 @@
     widget_->Release();
     DCHECK(widget_->HasOneRef());
   }
-  ~RenderWidgetPopupUnittest() override {}
+  ~RenderWidgetPopupUnittest() override { widget_->Shutdown(); }
 
   PopupRenderWidget* widget() const { return widget_.get(); }
   FakeCompositorDependencies compositor_deps_;
@@ -451,6 +460,44 @@
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetPopupUnittest);
 };
 
+class StubRenderWidgetOwnerDelegate : public RenderWidgetOwnerDelegate {
+ public:
+  blink::WebWidget* GetWebWidgetForWidget() const override { return nullptr; }
+  bool RenderWidgetWillHandleMouseEventForWidget(
+      const blink::WebMouseEvent& event) override {
+    return false;
+  }
+  void SetActiveForWidget(bool active) override {}
+  void SetBackgroundOpaqueForWidget(bool opaque) override {}
+  bool SupportsMultipleWindowsForWidget() override { return true; }
+  void DidHandleGestureEventForWidget(
+      const blink::WebGestureEvent& event) override {}
+  void OverrideCloseForWidget() override {}
+  void DidCloseWidget() override {}
+  void ApplyNewSizeForWidget(const gfx::Size& old_size,
+                             const gfx::Size& new_size) override {}
+  void ApplyNewDisplayModeForWidget(
+      const blink::WebDisplayMode& new_display_mode) override {}
+  void ApplyAutoResizeLimitsForWidget(const gfx::Size& min_size,
+                                      const gfx::Size& max_size) override {}
+  void DisableAutoResizeForWidget() override {}
+  void ScrollFocusedNodeIntoViewForWidget() override {}
+  void DidReceiveSetFocusEventForWidget() override {}
+  void DidChangeFocusForWidget() override {}
+  GURL GetURLForGraphicsContext3DForWidget() override { return {}; }
+  void DidCommitCompositorFrameForWidget() override {}
+  void DidCompletePageScaleAnimationForWidget() override {}
+  void ResizeWebWidgetForWidget(
+      const gfx::Size& size,
+      float top_controls_height,
+      float bottom_controls_height,
+      bool browser_controls_shrink_blink_size) override {}
+  void RequestScheduleAnimationForWidget() override {}
+  void SetScreenMetricsEmulationParametersForWidget(
+      bool enabled,
+      const blink::WebDeviceEmulationParams& params) override {}
+};
+
 TEST_F(RenderWidgetPopupUnittest, EmulatingPopupRect) {
   blink::WebRect popup_screen_rect(200, 250, 100, 400);
   widget()->SetWindowRect(popup_screen_rect);
@@ -478,12 +525,16 @@
   scoped_refptr<PopupRenderWidget> parent_widget(
       new PopupRenderWidget(&compositor_deps_));
   parent_widget->Release();  // Balance Init().
-  RenderWidgetScreenMetricsEmulator emulator(
-      parent_widget.get(), emulation_params, visual_properties,
-      parent_window_rect, parent_window_rect);
-  emulator.Apply();
 
-  widget()->SetPopupOriginAdjustmentsForEmulation(&emulator);
+  // Emulation only happens for RenderWidgets with an owner delegate.
+  StubRenderWidgetOwnerDelegate delegate;
+  parent_widget->set_owner_delegate(&delegate);
+
+  // Setup emulation on the |parent_widget|.
+  parent_widget->OnSynchronizeVisualProperties(visual_properties);
+  parent_widget->OnEnableDeviceEmulation(emulation_params);
+  // Then use it for the popup widget under test.
+  widget()->ApplyEmulatedScreenMetricsForPopupWidget(parent_widget.get());
 
   // Position of the popup as seen by the emulated widget.
   gfx::Point emulated_position(
@@ -507,6 +558,8 @@
   EXPECT_EQ(popup_emulated_rect.y, widget()->WindowRect().y);
   EXPECT_EQ(popup_emulated_rect.x, widget()->ViewRect().x);
   EXPECT_EQ(popup_emulated_rect.y, widget()->ViewRect().y);
+
+  parent_widget->Shutdown();
 }
 
 // Verify desktop memory limit calculations.
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index d643f7e..651bcd6 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1028,6 +1028,7 @@
       "../browser/media/capture/fake_video_capture_stack.h",
       "../browser/media/capture/frame_test_util.cc",
       "../browser/media/capture/frame_test_util.h",
+      "../browser/media/capture/mouse_cursor_overlay_controller_browsertest.cc",
       "../browser/media/capture/web_contents_video_capture_device_browsertest.cc",
     ]
 
diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc
index 75199c79..f762f44 100644
--- a/content/test/layouttest_support.cc
+++ b/content/test/layouttest_support.cc
@@ -533,8 +533,9 @@
 }
 
 void UseSynchronousResizeMode(RenderView* render_view, bool enable) {
-  static_cast<RenderViewImpl*>(render_view)->
-      UseSynchronousResizeModeForTesting(enable);
+  RenderWidget* render_widget =
+      static_cast<RenderViewImpl*>(render_view)->GetWidget();
+  render_widget->UseSynchronousResizeModeForTesting(enable);
 }
 
 void EnableAutoResizeMode(RenderView* render_view,
diff --git a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h
index c4029b2f..b537070b 100644
--- a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h
+++ b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h
@@ -94,6 +94,9 @@
   // Sets the area on the screen that is occluded by the keyboard.
   virtual bool SetOccludedBounds(const std::vector<gfx::Rect>& bounds) = 0;
 
+  // Sets the areas on the keyboard window where events are handled.
+  virtual bool SetHitTestBounds(const std::vector<gfx::Rect>& bounds) = 0;
+
   // Restricts the virtual keyboard IME features.
   // Returns the values which were updated.
   virtual api::virtual_keyboard::FeatureRestrictions RestrictFeatures(
diff --git a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.cc b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.cc
index 9a0a218..efa172a 100644
--- a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.cc
+++ b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.cc
@@ -190,6 +190,22 @@
   return RespondNow(NoArguments());
 }
 
+ExtensionFunction::ResponseAction
+VirtualKeyboardPrivateSetHitTestBoundsFunction::Run() {
+  std::unique_ptr<keyboard::SetHitTestBounds::Params> params =
+      keyboard::SetHitTestBounds::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  std::vector<gfx::Rect> hit_test_bounds;
+  hit_test_bounds.reserve(params->bounds_list.size());
+  for (const auto& bounds : params->bounds_list)
+    hit_test_bounds.push_back(KeyboardBoundsToRect(bounds));
+
+  if (!delegate()->SetHitTestBounds(hit_test_bounds))
+    return RespondNow(Error(kVirtualKeyboardNotEnabled));
+  return RespondNow(NoArguments());
+}
+
 VirtualKeyboardAPI::VirtualKeyboardAPI(content::BrowserContext* context) {
   delegate_ =
       ExtensionsAPIClient::Get()->CreateVirtualKeyboardDelegate(context);
diff --git a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.h b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.h
index 7149d45..d4d862da 100644
--- a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.h
+++ b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.h
@@ -195,6 +195,19 @@
   ResponseAction Run() override;
 };
 
+class VirtualKeyboardPrivateSetHitTestBoundsFunction
+    : public VirtualKeyboardPrivateFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("virtualKeyboardPrivate.setHitTestBounds",
+                             VIRTUALKEYBOARDPRIVATE_SETHITTESTBOUNDS);
+
+ protected:
+  ~VirtualKeyboardPrivateSetHitTestBoundsFunction() override {}
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+};
+
 class VirtualKeyboardDelegate;
 
 class VirtualKeyboardAPI : public BrowserContextKeyedAPI {
diff --git a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api_unittest.cc b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api_unittest.cc
index c1d8d47..4f56bee 100644
--- a/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api_unittest.cc
+++ b/extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api_unittest.cc
@@ -54,6 +54,12 @@
   }
   const std::vector<gfx::Rect>& GetOccludedBounds() { return occluded_bounds_; }
 
+  bool SetHitTestBounds(const std::vector<gfx::Rect>& bounds) override {
+    hit_test_bounds_ = bounds;
+    return true;
+  }
+  const std::vector<gfx::Rect>& GetHitTestBounds() { return hit_test_bounds_; }
+
   api::virtual_keyboard::FeatureRestrictions RestrictFeatures(
       const api::virtual_keyboard::RestrictFeatures::Params& params) override {
     return api::virtual_keyboard::FeatureRestrictions();
@@ -61,6 +67,7 @@
 
  private:
   std::vector<gfx::Rect> occluded_bounds_;
+  std::vector<gfx::Rect> hit_test_bounds_;
 
   DISALLOW_COPY_AND_ASSIGN(MockVirtualKeyboardDelegate);
 };
@@ -117,9 +124,8 @@
 };
 
 TEST_F(VirtualKeyboardPrivateApiUnittest, SetOccludedBoundsWithOneBound) {
-  RunFunction(
-      new VirtualKeyboardPrivateSetOccludedBoundsFunction(),
-      "[[{ \"left\": 0, \"top\": 10, \"width\": 20, \"height\": 30 }]]");
+  RunFunction(new VirtualKeyboardPrivateSetOccludedBoundsFunction(),
+              R"([[{ "left": 0, "top": 10, "width": 20, "height": 30 }]])");
 
   const auto bounds = client()
                           .GetDelegateForBrowserContext(browser_context())
@@ -129,10 +135,9 @@
 };
 
 TEST_F(VirtualKeyboardPrivateApiUnittest, SetOccludedBoundsWithTwoBounds) {
-  RunFunction(
-      new VirtualKeyboardPrivateSetOccludedBoundsFunction(),
-      "[[{ \"left\": 0, \"top\": 10, \"width\": 20, \"height\": 30 }, "
-      "  { \"left\": 10, \"top\": 20, \"width\": 30, \"height\": 40 }]]");
+  RunFunction(new VirtualKeyboardPrivateSetOccludedBoundsFunction(),
+              R"([[{ "left": 0, "top": 10, "width": 20, "height": 30 },
+      { "left": 10, "top": 20, "width": 30, "height": 40 }]])");
 
   const auto bounds = client()
                           .GetDelegateForBrowserContext(browser_context())
@@ -142,4 +147,26 @@
   EXPECT_EQ(gfx::Rect(10, 20, 30, 40), bounds[1]);
 };
 
+TEST_F(VirtualKeyboardPrivateApiUnittest, SetHitTestBoundsWithNoBounds) {
+  RunFunction(new VirtualKeyboardPrivateSetHitTestBoundsFunction(), "[[]]");
+
+  const auto bounds = client()
+                          .GetDelegateForBrowserContext(browser_context())
+                          ->GetHitTestBounds();
+  EXPECT_EQ(0U, bounds.size());
+};
+
+TEST_F(VirtualKeyboardPrivateApiUnittest, SetHitTestBoundsWithMultipleBounds) {
+  RunFunction(new VirtualKeyboardPrivateSetHitTestBoundsFunction(),
+              R"([[{ "left": 0, "top": 10, "width": 20, "height": 30 },
+      { "left": 10, "top": 20, "width": 30, "height": 40 }]])");
+
+  const auto bounds = client()
+                          .GetDelegateForBrowserContext(browser_context())
+                          ->GetHitTestBounds();
+  ASSERT_EQ(2U, bounds.size());
+  EXPECT_EQ(gfx::Rect(0, 10, 20, 30), bounds[0]);
+  EXPECT_EQ(gfx::Rect(10, 20, 30, 40), bounds[1]);
+};
+
 }  // namespace extensions
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 26a29869..9ff1ebd 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1324,6 +1324,7 @@
   INPUTMETHODPRIVATE_GETSURROUNDINGTEXT = 1261,
   USERSPRIVATE_GETLOGINSTATUS = 1262,
   FILEMANAGERPRIVATEINTERNAL_INSTALLLINUXPACKAGE = 1263,
+  VIRTUALKEYBOARDPRIVATE_SETHITTESTBOUNDS = 1264,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/common/api/virtual_keyboard_private.json b/extensions/common/api/virtual_keyboard_private.json
index d240d8f..a08d1cca 100644
--- a/extensions/common/api/virtual_keyboard_private.json
+++ b/extensions/common/api/virtual_keyboard_private.json
@@ -254,6 +254,19 @@
             "items": { "$ref": "Bounds" }
           }
         ]
+      },
+      {
+        "name": "setHitTestBounds",
+        "type": "function",
+        "description": "Sets the areas on the keyboard window where events are handled. Any event outside of these areas are passed on to the window behind it.",
+        "parameters": [
+          {
+            "name": "boundsList",
+            "type": "array",
+            "description": "List of rectangles representing regions where events targeting the keyboard should be handled.",
+            "items": { "$ref": "Bounds" }
+          }
+        ]
       }
     ],
     "events": [
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 9128984d7..2f86366f 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -1131,6 +1131,11 @@
   // reloaded with a new messages map.
   EraseL10nMessagesMap(id);
 
+  // Update the origin access map so that any content scripts injected are no
+  // longer allowlisted for extra origins.
+  WebSecurityPolicy::RemoveAllOriginAccessWhitelistEntriesForOrigin(
+      Extension::GetBaseURLFromExtensionId(id));
+
   // We don't do anything with existing platform-app stylesheets. They will
   // stay resident, but the URL pattern corresponding to the unloaded
   // extension's URL just won't match anything anymore.
diff --git a/extensions/shell/browser/shell_virtual_keyboard_delegate.cc b/extensions/shell/browser/shell_virtual_keyboard_delegate.cc
index 0a56244..7948ef4d 100644
--- a/extensions/shell/browser/shell_virtual_keyboard_delegate.cc
+++ b/extensions/shell/browser/shell_virtual_keyboard_delegate.cc
@@ -82,6 +82,11 @@
   return false;
 }
 
+bool ShellVirtualKeyboardDelegate::SetHitTestBounds(
+    const std::vector<gfx::Rect>& bounds) {
+  return false;
+}
+
 api::virtual_keyboard::FeatureRestrictions
 ShellVirtualKeyboardDelegate::RestrictFeatures(
     const api::virtual_keyboard::RestrictFeatures::Params& params) {
diff --git a/extensions/shell/browser/shell_virtual_keyboard_delegate.h b/extensions/shell/browser/shell_virtual_keyboard_delegate.h
index e1307eef..9d16cd7 100644
--- a/extensions/shell/browser/shell_virtual_keyboard_delegate.h
+++ b/extensions/shell/browser/shell_virtual_keyboard_delegate.h
@@ -42,6 +42,7 @@
       const api::virtual_keyboard_private::Bounds& rect) override;
   bool SetRequestedKeyboardState(int state_enum) override;
   bool SetOccludedBounds(const std::vector<gfx::Rect>& bounds) override;
+  bool SetHitTestBounds(const std::vector<gfx::Rect>& bounds) override;
 
   api::virtual_keyboard::FeatureRestrictions RestrictFeatures(
       const api::virtual_keyboard::RestrictFeatures::Params& params) override;
diff --git a/extensions/shell/installer/linux/BUILD.gn b/extensions/shell/installer/linux/BUILD.gn
index bd60b49..1f07d353 100644
--- a/extensions/shell/installer/linux/BUILD.gn
+++ b/extensions/shell/installer/linux/BUILD.gn
@@ -136,10 +136,6 @@
       "//ppapi/native_client:irt",
     ]
   }
-
-  if (!libcpp_is_static && use_custom_libcxx) {
-    public_deps += [ "//buildtools/third_party/libc++:libc++" ]
-  }
 }
 
 # Creates .deb installer package.
@@ -166,10 +162,6 @@
     ]
   }
 
-  if (!libcpp_is_static && use_custom_libcxx) {
-    packaging_files_binaries += [ "$root_out_dir/libc++.so" ]
-  }
-
   deb_target_name = "${target_name}_deb"
   action(deb_target_name) {
     visibility = [ ":*" ]
diff --git a/google_apis/gaia/oauth2_access_token_fetcher_immediate_error.cc b/google_apis/gaia/oauth2_access_token_fetcher_immediate_error.cc
index 2259661..65b8c674e 100644
--- a/google_apis/gaia/oauth2_access_token_fetcher_immediate_error.cc
+++ b/google_apis/gaia/oauth2_access_token_fetcher_immediate_error.cc
@@ -12,10 +12,6 @@
 OAuth2AccessTokenFetcherImmediateError::FailCaller::FailCaller(
     OAuth2AccessTokenFetcherImmediateError* fetcher)
     : fetcher_(fetcher) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(&OAuth2AccessTokenFetcherImmediateError::FailCaller::run,
-                 this));
 }
 
 OAuth2AccessTokenFetcherImmediateError::FailCaller::~FailCaller() {
@@ -58,6 +54,10 @@
     const std::string& client_secret,
     const std::vector<std::string>& scopes) {
   failer_ = new FailCaller(this);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&OAuth2AccessTokenFetcherImmediateError::FailCaller::run,
+                     failer_));
 }
 
 void OAuth2AccessTokenFetcherImmediateError::Fail() {
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index b13d6d8..0c8590b 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -267,6 +267,7 @@
 #define glIsVertexArrayOES GLES2_GET_FUN(IsVertexArrayOES)
 #define glBindVertexArrayOES GLES2_GET_FUN(BindVertexArrayOES)
 #define glFramebufferParameteri GLES2_GET_FUN(FramebufferParameteri)
+#define glDispatchCompute GLES2_GET_FUN(DispatchCompute)
 #define glSwapBuffers GLES2_GET_FUN(SwapBuffers)
 #define glGetMaxValueInBufferCHROMIUM GLES2_GET_FUN(GetMaxValueInBufferCHROMIUM)
 #define glEnableFeatureCHROMIUM GLES2_GET_FUN(EnableFeatureCHROMIUM)
diff --git a/gpu/command_buffer/build_cmd_buffer_lib.py b/gpu/command_buffer/build_cmd_buffer_lib.py
index c6a2406..ba4cca5a 100644
--- a/gpu/command_buffer/build_cmd_buffer_lib.py
+++ b/gpu/command_buffer/build_cmd_buffer_lib.py
@@ -1089,6 +1089,12 @@
       f.write("""if (!feature_info_->IsWebGL2OrES3OrHigherContext())
           return error::kUnknownCommand;
         """)
+    if func.IsES31():
+      f.write("""return error::kUnknownCommand;
+        }
+
+        """)
+      return
     if func.GetCmdArgs():
       f.write("""const volatile %(prefix)s::cmds::%(name)s& c =
             *static_cast<const volatile %(prefix)s::cmds::%(name)s*>(cmd_data);
@@ -1123,6 +1129,8 @@
   def WriteServiceImplementation(self, func, f):
     """Writes the service implementation for a command."""
     self.WriteServiceHandlerFunctionHeader(func, f)
+    if func.IsES31():
+      return
     self.WriteHandlerExtensionCheck(func, f)
     self.WriteHandlerDeferReadWrite(func, f);
     self.WriteServiceHandlerArgGetCode(func, f)
@@ -1136,6 +1144,8 @@
   def WriteImmediateServiceImplementation(self, func, f):
     """Writes the service implementation for an immediate version of command."""
     self.WriteServiceHandlerFunctionHeader(func, f)
+    if func.IsES31():
+      return
     self.WriteHandlerExtensionCheck(func, f)
     self.WriteHandlerDeferReadWrite(func, f);
     self.WriteImmediateServiceHandlerArgGetCode(func, f)
@@ -1149,6 +1159,8 @@
   def WriteBucketServiceImplementation(self, func, f):
     """Writes the service implementation for a bucket version of command."""
     self.WriteServiceHandlerFunctionHeader(func, f)
+    if func.IsES31():
+      return
     self.WriteHandlerExtensionCheck(func, f)
     self.WriteHandlerDeferReadWrite(func, f);
     self.WriteBucketServiceHandlerArgGetCode(func, f)
@@ -1168,6 +1180,10 @@
       f.write("""if (!feature_info_->IsWebGL2OrES3OrHigherContext())
           return error::kUnknownCommand;
         """)
+    if func.IsES31():
+      f.write("""if (!feature_info_->IsWebGL2ComputeContext())
+          return error::kUnknownCommand;
+        """)
     if func.GetCmdArgs():
       f.write("""const volatile gles2::cmds::%(name)s& c =
             *static_cast<const volatile gles2::cmds::%(name)s*>(cmd_data);
@@ -2901,6 +2917,8 @@
   def WriteServiceImplementation(self, func, f):
     """Overrriden from TypeHandler."""
     self.WriteServiceHandlerFunctionHeader(func, f)
+    if func.IsES31():
+      return
     last_arg = func.GetLastOriginalArg()
     # All except shm_id and shm_offset.
     all_but_last_args = func.GetCmdArgs()[:-2]
@@ -4377,6 +4395,8 @@
   def WriteServiceImplementation(self, func, f):
     """Overrriden from TypeHandler."""
     self.WriteServiceHandlerFunctionHeader(func, f)
+    if func.IsES31():
+      return
     f.write("""
   GLuint bucket_id = static_cast<GLuint>(c.%(bucket_id)s);
   Bucket* bucket = GetBucket(bucket_id);
@@ -4458,6 +4478,8 @@
   def WriteServiceImplementation(self, func, f):
     """Overrriden from TypeHandler."""
     self.WriteServiceHandlerFunctionHeader(func, f)
+    if func.IsES31():
+      return
     self.WriteHandlerExtensionCheck(func, f)
     args = func.GetOriginalArgs()
     for arg in args:
@@ -5659,6 +5681,10 @@
     """Returns whether the function requires an ES3 context or not."""
     return self.GetInfo('es3', False)
 
+  def IsES31(self):
+    """Returns whether the function requires an ES31 context or not."""
+    return self.GetInfo('es31', False)
+
   def GetInfo(self, name, default = None):
     """Returns a value from the function info for this function."""
     if name in self.info:
@@ -5688,7 +5714,8 @@
   def IsCoreGLFunction(self):
     return (not self.IsExtension() and
             not self.GetInfo('pepper_interface') and
-            not self.IsES3())
+            not self.IsES3() and
+            not self.IsES31())
 
   def InPepperInterface(self, interface):
     ext = self.GetInfo('pepper_interface')
@@ -7321,6 +7348,7 @@
     for fname in ['third_party/khronos/GLES2/gl2.h',
                   'third_party/khronos/GLES2/gl2ext.h',
                   'third_party/khronos/GLES3/gl3.h',
+                  'third_party/khronos/GLES3/gl31.h',
                   'gpu/GLES2/gl2chromium.h',
                   'gpu/GLES2/gl2extchromium.h']:
       lines = open(fname).readlines()
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index d423a53..63e56109 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1753,6 +1753,8 @@
 #               'extension': True.
 # not_shared:   For GENn types, True if objects can't be shared between contexts
 # es3:          ES3 API. True if the function requires an ES3 or WebGL2 context.
+# es31:         ES31 API. True if the function requires an WebGL2Compute
+#               context.
 
 _FUNCTION_INFO = {
   'ActiveTexture': {
@@ -2179,6 +2181,12 @@
     'impl_func': False,
     'unit_test': False,
   },
+  'DispatchCompute': {
+    'cmd_args': 'GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z',
+    'trace_level': 2,
+    'es31': True,
+    'unit_test': False,
+  },
   'DrawArrays': {
     'type': 'Custom',
     'impl_func': False,
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index c507bcb..8bf28c9 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1213,6 +1213,12 @@
                                             GLint param) {
   gles2::GetGLContext()->FramebufferParameteri(target, pname, param);
 }
+void GL_APIENTRY GLES2DispatchCompute(GLuint num_groups_x,
+                                      GLuint num_groups_y,
+                                      GLuint num_groups_z) {
+  gles2::GetGLContext()->DispatchCompute(num_groups_x, num_groups_y,
+                                         num_groups_z);
+}
 void GL_APIENTRY GLES2SwapBuffers(GLuint64 swap_id, GLbitfield flags) {
   gles2::GetGLContext()->SwapBuffers(swap_id, flags);
 }
@@ -2725,6 +2731,10 @@
         reinterpret_cast<GLES2FunctionPointer>(glFramebufferParameteri),
     },
     {
+        "glDispatchCompute",
+        reinterpret_cast<GLES2FunctionPointer>(glDispatchCompute),
+    },
+    {
         "glSwapBuffers", reinterpret_cast<GLES2FunctionPointer>(glSwapBuffers),
     },
     {
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index c33a6154..fbb6f06 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -2370,6 +2370,15 @@
   }
 }
 
+void DispatchCompute(GLuint num_groups_x,
+                     GLuint num_groups_y,
+                     GLuint num_groups_z) {
+  gles2::cmds::DispatchCompute* c = GetCmdSpace<gles2::cmds::DispatchCompute>();
+  if (c) {
+    c->Init(num_groups_x, num_groups_y, num_groups_z);
+  }
+}
+
 void SwapBuffers(GLuint64 swap_id, GLbitfield flags) {
   gles2::cmds::SwapBuffers* c = GetCmdSpace<gles2::cmds::SwapBuffers>();
   if (c) {
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index a7ba01c..10306ea 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -847,6 +847,10 @@
 
 void FramebufferParameteri(GLenum target, GLenum pname, GLint param) override;
 
+void DispatchCompute(GLuint num_groups_x,
+                     GLuint num_groups_y,
+                     GLuint num_groups_z) override;
+
 void SwapBuffers(GLuint64 swap_id, GLbitfield flags = 0) override;
 
 GLuint GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
index 33e9604..d0b484d 100644
--- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -3038,6 +3038,16 @@
   CheckGLError();
 }
 
+void GLES2Implementation::DispatchCompute(GLuint num_groups_x,
+                                          GLuint num_groups_y,
+                                          GLuint num_groups_z) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDispatchCompute(" << num_groups_x
+                     << ", " << num_groups_y << ", " << num_groups_z << ")");
+  helper_->DispatchCompute(num_groups_x, num_groups_y, num_groups_z);
+  CheckGLError();
+}
+
 void GLES2Implementation::FlushMappedBufferRange(GLenum target,
                                                  GLintptr offset,
                                                  GLsizeiptr size) {
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
index 6e564b2c..552eb438 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -2690,6 +2690,17 @@
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
 
+TEST_F(GLES2ImplementationTest, DispatchCompute) {
+  struct Cmds {
+    cmds::DispatchCompute cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init(1, 2, 3);
+
+  gl_->DispatchCompute(1, 2, 3);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
 TEST_F(GLES2ImplementationTest, FlushMappedBufferRange) {
   struct Cmds {
     cmds::FlushMappedBufferRange cmd;
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index e3c0490..fbee76a3 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -620,6 +620,9 @@
 virtual void FramebufferParameteri(GLenum target,
                                    GLenum pname,
                                    GLint param) = 0;
+virtual void DispatchCompute(GLuint num_groups_x,
+                             GLuint num_groups_y,
+                             GLuint num_groups_z) = 0;
 virtual void SwapBuffers(GLuint64 swap_id, GLbitfield flags = 0) = 0;
 virtual GLuint GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
                                            GLsizei count,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index 20a0c3c..56c4d1d 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -597,6 +597,9 @@
 GLboolean IsVertexArrayOES(GLuint array) override;
 void BindVertexArrayOES(GLuint array) override;
 void FramebufferParameteri(GLenum target, GLenum pname, GLint param) override;
+void DispatchCompute(GLuint num_groups_x,
+                     GLuint num_groups_y,
+                     GLuint num_groups_z) override;
 void SwapBuffers(GLuint64 swap_id, GLbitfield flags) override;
 GLuint GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
                                    GLsizei count,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index c9a8b856..7125bec1 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -820,6 +820,9 @@
 void GLES2InterfaceStub::FramebufferParameteri(GLenum /* target */,
                                                GLenum /* pname */,
                                                GLint /* param */) {}
+void GLES2InterfaceStub::DispatchCompute(GLuint /* num_groups_x */,
+                                         GLuint /* num_groups_y */,
+                                         GLuint /* num_groups_z */) {}
 void GLES2InterfaceStub::SwapBuffers(GLuint64 /* swap_id */,
                                      GLbitfield /* flags */) {}
 GLuint GLES2InterfaceStub::GetMaxValueInBufferCHROMIUM(GLuint /* buffer_id */,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index c34598d..07a6f309 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -597,6 +597,9 @@
 GLboolean IsVertexArrayOES(GLuint array) override;
 void BindVertexArrayOES(GLuint array) override;
 void FramebufferParameteri(GLenum target, GLenum pname, GLint param) override;
+void DispatchCompute(GLuint num_groups_x,
+                     GLuint num_groups_y,
+                     GLuint num_groups_z) override;
 void SwapBuffers(GLuint64 swap_id, GLbitfield flags) override;
 GLuint GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
                                    GLsizei count,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 08823872..67a3c5d 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -1770,6 +1770,13 @@
   gl_->FramebufferParameteri(target, pname, param);
 }
 
+void GLES2TraceImplementation::DispatchCompute(GLuint num_groups_x,
+                                               GLuint num_groups_y,
+                                               GLuint num_groups_z) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::DispatchCompute");
+  gl_->DispatchCompute(num_groups_x, num_groups_y, num_groups_z);
+}
+
 void GLES2TraceImplementation::SwapBuffers(GLuint64 swap_id, GLbitfield flags) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::SwapBuffers");
   gl_->SwapBuffers(swap_id, flags);
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index cfb06974e..af427efd 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -11777,6 +11777,51 @@
 static_assert(offsetof(FramebufferParameteri, param) == 12,
               "offset of FramebufferParameteri param should be 12");
 
+struct DispatchCompute {
+  typedef DispatchCompute ValueType;
+  static const CommandId kCmdId = kDispatchCompute;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(2);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _num_groups_x, GLuint _num_groups_y, GLuint _num_groups_z) {
+    SetHeader();
+    num_groups_x = _num_groups_x;
+    num_groups_y = _num_groups_y;
+    num_groups_z = _num_groups_z;
+  }
+
+  void* Set(void* cmd,
+            GLuint _num_groups_x,
+            GLuint _num_groups_y,
+            GLuint _num_groups_z) {
+    static_cast<ValueType*>(cmd)->Init(_num_groups_x, _num_groups_y,
+                                       _num_groups_z);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t num_groups_x;
+  uint32_t num_groups_y;
+  uint32_t num_groups_z;
+};
+
+static_assert(sizeof(DispatchCompute) == 16,
+              "size of DispatchCompute should be 16");
+static_assert(offsetof(DispatchCompute, header) == 0,
+              "offset of DispatchCompute header should be 0");
+static_assert(offsetof(DispatchCompute, num_groups_x) == 4,
+              "offset of DispatchCompute num_groups_x should be 4");
+static_assert(offsetof(DispatchCompute, num_groups_y) == 8,
+              "offset of DispatchCompute num_groups_y should be 8");
+static_assert(offsetof(DispatchCompute, num_groups_z) == 12,
+              "offset of DispatchCompute num_groups_z should be 12");
+
 struct SwapBuffers {
   typedef SwapBuffers ValueType;
   static const CommandId kCmdId = kSwapBuffers;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index e7194bc..37deaf0 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -3952,6 +3952,19 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, DispatchCompute) {
+  cmds::DispatchCompute& cmd = *GetBufferAs<cmds::DispatchCompute>();
+  void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11),
+                           static_cast<GLuint>(12), static_cast<GLuint>(13));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::DispatchCompute::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.num_groups_x);
+  EXPECT_EQ(static_cast<GLuint>(12), cmd.num_groups_y);
+  EXPECT_EQ(static_cast<GLuint>(13), cmd.num_groups_z);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, SwapBuffers) {
   cmds::SwapBuffers& cmd = *GetBufferAs<cmds::SwapBuffers>();
   void* next_cmd =
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index ed9d7c6..59d10791 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -254,95 +254,96 @@
   OP(IsVertexArrayOES)                                     /* 495 */ \
   OP(BindVertexArrayOES)                                   /* 496 */ \
   OP(FramebufferParameteri)                                /* 497 */ \
-  OP(SwapBuffers)                                          /* 498 */ \
-  OP(GetMaxValueInBufferCHROMIUM)                          /* 499 */ \
-  OP(EnableFeatureCHROMIUM)                                /* 500 */ \
-  OP(MapBufferRange)                                       /* 501 */ \
-  OP(UnmapBuffer)                                          /* 502 */ \
-  OP(FlushMappedBufferRange)                               /* 503 */ \
-  OP(ResizeCHROMIUM)                                       /* 504 */ \
-  OP(GetRequestableExtensionsCHROMIUM)                     /* 505 */ \
-  OP(RequestExtensionCHROMIUM)                             /* 506 */ \
-  OP(GetProgramInfoCHROMIUM)                               /* 507 */ \
-  OP(GetUniformBlocksCHROMIUM)                             /* 508 */ \
-  OP(GetTransformFeedbackVaryingsCHROMIUM)                 /* 509 */ \
-  OP(GetUniformsES3CHROMIUM)                               /* 510 */ \
-  OP(DescheduleUntilFinishedCHROMIUM)                      /* 511 */ \
-  OP(GetTranslatedShaderSourceANGLE)                       /* 512 */ \
-  OP(PostSubBufferCHROMIUM)                                /* 513 */ \
-  OP(CopyTextureCHROMIUM)                                  /* 514 */ \
-  OP(CopySubTextureCHROMIUM)                               /* 515 */ \
-  OP(CompressedCopyTextureCHROMIUM)                        /* 516 */ \
-  OP(DrawArraysInstancedANGLE)                             /* 517 */ \
-  OP(DrawElementsInstancedANGLE)                           /* 518 */ \
-  OP(VertexAttribDivisorANGLE)                             /* 519 */ \
-  OP(ProduceTextureDirectCHROMIUMImmediate)                /* 520 */ \
-  OP(CreateAndConsumeTextureINTERNALImmediate)             /* 521 */ \
-  OP(BindUniformLocationCHROMIUMBucket)                    /* 522 */ \
-  OP(BindTexImage2DCHROMIUM)                               /* 523 */ \
-  OP(BindTexImage2DWithInternalformatCHROMIUM)             /* 524 */ \
-  OP(ReleaseTexImage2DCHROMIUM)                            /* 525 */ \
-  OP(TraceBeginCHROMIUM)                                   /* 526 */ \
-  OP(TraceEndCHROMIUM)                                     /* 527 */ \
-  OP(DiscardFramebufferEXTImmediate)                       /* 528 */ \
-  OP(LoseContextCHROMIUM)                                  /* 529 */ \
-  OP(InsertFenceSyncCHROMIUM)                              /* 530 */ \
-  OP(WaitSyncTokenCHROMIUM)                                /* 531 */ \
-  OP(UnpremultiplyAndDitherCopyCHROMIUM)                   /* 532 */ \
-  OP(DrawBuffersEXTImmediate)                              /* 533 */ \
-  OP(DiscardBackbufferCHROMIUM)                            /* 534 */ \
-  OP(ScheduleOverlayPlaneCHROMIUM)                         /* 535 */ \
-  OP(ScheduleCALayerSharedStateCHROMIUM)                   /* 536 */ \
-  OP(ScheduleCALayerCHROMIUM)                              /* 537 */ \
-  OP(ScheduleCALayerInUseQueryCHROMIUMImmediate)           /* 538 */ \
-  OP(CommitOverlayPlanesCHROMIUM)                          /* 539 */ \
-  OP(FlushDriverCachesCHROMIUM)                            /* 540 */ \
-  OP(ScheduleDCLayerSharedStateCHROMIUM)                   /* 541 */ \
-  OP(ScheduleDCLayerCHROMIUM)                              /* 542 */ \
-  OP(MatrixLoadfCHROMIUMImmediate)                         /* 543 */ \
-  OP(MatrixLoadIdentityCHROMIUM)                           /* 544 */ \
-  OP(GenPathsCHROMIUM)                                     /* 545 */ \
-  OP(DeletePathsCHROMIUM)                                  /* 546 */ \
-  OP(IsPathCHROMIUM)                                       /* 547 */ \
-  OP(PathCommandsCHROMIUM)                                 /* 548 */ \
-  OP(PathParameterfCHROMIUM)                               /* 549 */ \
-  OP(PathParameteriCHROMIUM)                               /* 550 */ \
-  OP(PathStencilFuncCHROMIUM)                              /* 551 */ \
-  OP(StencilFillPathCHROMIUM)                              /* 552 */ \
-  OP(StencilStrokePathCHROMIUM)                            /* 553 */ \
-  OP(CoverFillPathCHROMIUM)                                /* 554 */ \
-  OP(CoverStrokePathCHROMIUM)                              /* 555 */ \
-  OP(StencilThenCoverFillPathCHROMIUM)                     /* 556 */ \
-  OP(StencilThenCoverStrokePathCHROMIUM)                   /* 557 */ \
-  OP(StencilFillPathInstancedCHROMIUM)                     /* 558 */ \
-  OP(StencilStrokePathInstancedCHROMIUM)                   /* 559 */ \
-  OP(CoverFillPathInstancedCHROMIUM)                       /* 560 */ \
-  OP(CoverStrokePathInstancedCHROMIUM)                     /* 561 */ \
-  OP(StencilThenCoverFillPathInstancedCHROMIUM)            /* 562 */ \
-  OP(StencilThenCoverStrokePathInstancedCHROMIUM)          /* 563 */ \
-  OP(BindFragmentInputLocationCHROMIUMBucket)              /* 564 */ \
-  OP(ProgramPathFragmentInputGenCHROMIUM)                  /* 565 */ \
-  OP(CoverageModulationCHROMIUM)                           /* 566 */ \
-  OP(BlendBarrierKHR)                                      /* 567 */ \
-  OP(ApplyScreenSpaceAntialiasingCHROMIUM)                 /* 568 */ \
-  OP(BindFragDataLocationIndexedEXTBucket)                 /* 569 */ \
-  OP(BindFragDataLocationEXTBucket)                        /* 570 */ \
-  OP(GetFragDataIndexEXT)                                  /* 571 */ \
-  OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 572 */ \
-  OP(OverlayPromotionHintCHROMIUM)                         /* 573 */ \
-  OP(SwapBuffersWithBoundsCHROMIUMImmediate)               /* 574 */ \
-  OP(SetDrawRectangleCHROMIUM)                             /* 575 */ \
-  OP(SetEnableDCLayersCHROMIUM)                            /* 576 */ \
-  OP(InitializeDiscardableTextureCHROMIUM)                 /* 577 */ \
-  OP(UnlockDiscardableTextureCHROMIUM)                     /* 578 */ \
-  OP(LockDiscardableTextureCHROMIUM)                       /* 579 */ \
-  OP(TexStorage2DImageCHROMIUM)                            /* 580 */ \
-  OP(SetColorSpaceMetadataCHROMIUM)                        /* 581 */ \
-  OP(WindowRectanglesEXTImmediate)                         /* 582 */ \
-  OP(CreateGpuFenceINTERNAL)                               /* 583 */ \
-  OP(WaitGpuFenceCHROMIUM)                                 /* 584 */ \
-  OP(DestroyGpuFenceCHROMIUM)                              /* 585 */ \
-  OP(SetReadbackBufferShadowAllocationINTERNAL)            /* 586 */
+  OP(DispatchCompute)                                      /* 498 */ \
+  OP(SwapBuffers)                                          /* 499 */ \
+  OP(GetMaxValueInBufferCHROMIUM)                          /* 500 */ \
+  OP(EnableFeatureCHROMIUM)                                /* 501 */ \
+  OP(MapBufferRange)                                       /* 502 */ \
+  OP(UnmapBuffer)                                          /* 503 */ \
+  OP(FlushMappedBufferRange)                               /* 504 */ \
+  OP(ResizeCHROMIUM)                                       /* 505 */ \
+  OP(GetRequestableExtensionsCHROMIUM)                     /* 506 */ \
+  OP(RequestExtensionCHROMIUM)                             /* 507 */ \
+  OP(GetProgramInfoCHROMIUM)                               /* 508 */ \
+  OP(GetUniformBlocksCHROMIUM)                             /* 509 */ \
+  OP(GetTransformFeedbackVaryingsCHROMIUM)                 /* 510 */ \
+  OP(GetUniformsES3CHROMIUM)                               /* 511 */ \
+  OP(DescheduleUntilFinishedCHROMIUM)                      /* 512 */ \
+  OP(GetTranslatedShaderSourceANGLE)                       /* 513 */ \
+  OP(PostSubBufferCHROMIUM)                                /* 514 */ \
+  OP(CopyTextureCHROMIUM)                                  /* 515 */ \
+  OP(CopySubTextureCHROMIUM)                               /* 516 */ \
+  OP(CompressedCopyTextureCHROMIUM)                        /* 517 */ \
+  OP(DrawArraysInstancedANGLE)                             /* 518 */ \
+  OP(DrawElementsInstancedANGLE)                           /* 519 */ \
+  OP(VertexAttribDivisorANGLE)                             /* 520 */ \
+  OP(ProduceTextureDirectCHROMIUMImmediate)                /* 521 */ \
+  OP(CreateAndConsumeTextureINTERNALImmediate)             /* 522 */ \
+  OP(BindUniformLocationCHROMIUMBucket)                    /* 523 */ \
+  OP(BindTexImage2DCHROMIUM)                               /* 524 */ \
+  OP(BindTexImage2DWithInternalformatCHROMIUM)             /* 525 */ \
+  OP(ReleaseTexImage2DCHROMIUM)                            /* 526 */ \
+  OP(TraceBeginCHROMIUM)                                   /* 527 */ \
+  OP(TraceEndCHROMIUM)                                     /* 528 */ \
+  OP(DiscardFramebufferEXTImmediate)                       /* 529 */ \
+  OP(LoseContextCHROMIUM)                                  /* 530 */ \
+  OP(InsertFenceSyncCHROMIUM)                              /* 531 */ \
+  OP(WaitSyncTokenCHROMIUM)                                /* 532 */ \
+  OP(UnpremultiplyAndDitherCopyCHROMIUM)                   /* 533 */ \
+  OP(DrawBuffersEXTImmediate)                              /* 534 */ \
+  OP(DiscardBackbufferCHROMIUM)                            /* 535 */ \
+  OP(ScheduleOverlayPlaneCHROMIUM)                         /* 536 */ \
+  OP(ScheduleCALayerSharedStateCHROMIUM)                   /* 537 */ \
+  OP(ScheduleCALayerCHROMIUM)                              /* 538 */ \
+  OP(ScheduleCALayerInUseQueryCHROMIUMImmediate)           /* 539 */ \
+  OP(CommitOverlayPlanesCHROMIUM)                          /* 540 */ \
+  OP(FlushDriverCachesCHROMIUM)                            /* 541 */ \
+  OP(ScheduleDCLayerSharedStateCHROMIUM)                   /* 542 */ \
+  OP(ScheduleDCLayerCHROMIUM)                              /* 543 */ \
+  OP(MatrixLoadfCHROMIUMImmediate)                         /* 544 */ \
+  OP(MatrixLoadIdentityCHROMIUM)                           /* 545 */ \
+  OP(GenPathsCHROMIUM)                                     /* 546 */ \
+  OP(DeletePathsCHROMIUM)                                  /* 547 */ \
+  OP(IsPathCHROMIUM)                                       /* 548 */ \
+  OP(PathCommandsCHROMIUM)                                 /* 549 */ \
+  OP(PathParameterfCHROMIUM)                               /* 550 */ \
+  OP(PathParameteriCHROMIUM)                               /* 551 */ \
+  OP(PathStencilFuncCHROMIUM)                              /* 552 */ \
+  OP(StencilFillPathCHROMIUM)                              /* 553 */ \
+  OP(StencilStrokePathCHROMIUM)                            /* 554 */ \
+  OP(CoverFillPathCHROMIUM)                                /* 555 */ \
+  OP(CoverStrokePathCHROMIUM)                              /* 556 */ \
+  OP(StencilThenCoverFillPathCHROMIUM)                     /* 557 */ \
+  OP(StencilThenCoverStrokePathCHROMIUM)                   /* 558 */ \
+  OP(StencilFillPathInstancedCHROMIUM)                     /* 559 */ \
+  OP(StencilStrokePathInstancedCHROMIUM)                   /* 560 */ \
+  OP(CoverFillPathInstancedCHROMIUM)                       /* 561 */ \
+  OP(CoverStrokePathInstancedCHROMIUM)                     /* 562 */ \
+  OP(StencilThenCoverFillPathInstancedCHROMIUM)            /* 563 */ \
+  OP(StencilThenCoverStrokePathInstancedCHROMIUM)          /* 564 */ \
+  OP(BindFragmentInputLocationCHROMIUMBucket)              /* 565 */ \
+  OP(ProgramPathFragmentInputGenCHROMIUM)                  /* 566 */ \
+  OP(CoverageModulationCHROMIUM)                           /* 567 */ \
+  OP(BlendBarrierKHR)                                      /* 568 */ \
+  OP(ApplyScreenSpaceAntialiasingCHROMIUM)                 /* 569 */ \
+  OP(BindFragDataLocationIndexedEXTBucket)                 /* 570 */ \
+  OP(BindFragDataLocationEXTBucket)                        /* 571 */ \
+  OP(GetFragDataIndexEXT)                                  /* 572 */ \
+  OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 573 */ \
+  OP(OverlayPromotionHintCHROMIUM)                         /* 574 */ \
+  OP(SwapBuffersWithBoundsCHROMIUMImmediate)               /* 575 */ \
+  OP(SetDrawRectangleCHROMIUM)                             /* 576 */ \
+  OP(SetEnableDCLayersCHROMIUM)                            /* 577 */ \
+  OP(InitializeDiscardableTextureCHROMIUM)                 /* 578 */ \
+  OP(UnlockDiscardableTextureCHROMIUM)                     /* 579 */ \
+  OP(LockDiscardableTextureCHROMIUM)                       /* 580 */ \
+  OP(TexStorage2DImageCHROMIUM)                            /* 581 */ \
+  OP(SetColorSpaceMetadataCHROMIUM)                        /* 582 */ \
+  OP(WindowRectanglesEXTImmediate)                         /* 583 */ \
+  OP(CreateGpuFenceINTERNAL)                               /* 584 */ \
+  OP(WaitGpuFenceCHROMIUM)                                 /* 585 */ \
+  OP(DestroyGpuFenceCHROMIUM)                              /* 586 */ \
+  OP(SetReadbackBufferShadowAllocationINTERNAL)            /* 587 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index 0fd4a01..3e3d8a5 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -475,9 +475,18 @@
         0x100, "GL_GLYPH_HAS_KERNING_BIT_NV",
     },
     {
+        0x1000, "GL_TEXTURE_WIDTH",
+    },
+    {
         0x10000000, "GL_FONT_HAS_KERNING_BIT_NV",
     },
     {
+        0x1001, "GL_TEXTURE_HEIGHT",
+    },
+    {
+        0x1003, "GL_TEXTURE_INTERNAL_FORMAT",
+    },
+    {
         0x1004, "GL_TEXTURE_BORDER_COLOR_OES",
     },
     {
@@ -865,6 +874,18 @@
         0x805B, "GL_RGBA16_EXT",
     },
     {
+        0x805C, "GL_TEXTURE_RED_SIZE",
+    },
+    {
+        0x805D, "GL_TEXTURE_GREEN_SIZE",
+    },
+    {
+        0x805E, "GL_TEXTURE_BLUE_SIZE",
+    },
+    {
+        0x805F, "GL_TEXTURE_ALPHA_SIZE",
+    },
+    {
         0x8069, "GL_TEXTURE_BINDING_2D",
     },
     {
@@ -880,6 +901,9 @@
         0x806F, "GL_TEXTURE_3D_OES",
     },
     {
+        0x8071, "GL_TEXTURE_DEPTH",
+    },
+    {
         0x8072, "GL_TEXTURE_WRAP_R_OES",
     },
     {
@@ -1177,6 +1201,24 @@
         0x8261, "GL_NO_RESET_NOTIFICATION_KHR",
     },
     {
+        0x8262, "GL_MAX_COMPUTE_SHARED_MEMORY_SIZE",
+    },
+    {
+        0x8263, "GL_MAX_COMPUTE_UNIFORM_COMPONENTS",
+    },
+    {
+        0x8264, "GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS",
+    },
+    {
+        0x8265, "GL_MAX_COMPUTE_ATOMIC_COUNTERS",
+    },
+    {
+        0x8266, "GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS",
+    },
+    {
+        0x8267, "GL_COMPUTE_WORK_GROUP_SIZE",
+    },
+    {
         0x8268, "GL_DEBUG_TYPE_MARKER_KHR",
     },
     {
@@ -1195,6 +1237,30 @@
         0x826D, "GL_DEBUG_GROUP_STACK_DEPTH_KHR",
     },
     {
+        0x826E, "GL_MAX_UNIFORM_LOCATIONS",
+    },
+    {
+        0x82D4, "GL_VERTEX_ATTRIB_BINDING",
+    },
+    {
+        0x82D5, "GL_VERTEX_ATTRIB_RELATIVE_OFFSET",
+    },
+    {
+        0x82D6, "GL_VERTEX_BINDING_DIVISOR",
+    },
+    {
+        0x82D7, "GL_VERTEX_BINDING_OFFSET",
+    },
+    {
+        0x82D8, "GL_VERTEX_BINDING_STRIDE",
+    },
+    {
+        0x82D9, "GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET",
+    },
+    {
+        0x82DA, "GL_MAX_VERTEX_ATTRIB_BINDINGS",
+    },
+    {
         0x82DB, "GL_TEXTURE_VIEW_MIN_LEVEL_OES",
     },
     {
@@ -1225,6 +1291,9 @@
         0x82E4, "GL_PROGRAM_PIPELINE_KHR",
     },
     {
+        0x82E5, "GL_MAX_VERTEX_ATTRIB_STRIDE",
+    },
+    {
         0x82E6, "GL_SAMPLER_KHR",
     },
     {
@@ -1498,6 +1567,9 @@
         0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER",
     },
     {
+        0x86A1, "GL_TEXTURE_COMPRESSED",
+    },
+    {
         0x86A2, "GL_NUM_COMPRESSED_TEXTURE_FORMATS",
     },
     {
@@ -1630,6 +1702,9 @@
         0x883D, "GL_BLEND_EQUATION_ALPHA",
     },
     {
+        0x884A, "GL_TEXTURE_DEPTH_SIZE",
+    },
+    {
         0x884C, "GL_TEXTURE_COMPARE_MODE_EXT",
     },
     {
@@ -1690,6 +1765,9 @@
         0x88B9, "GL_WRITE_ONLY_OES",
     },
     {
+        0x88BA, "GL_READ_WRITE",
+    },
+    {
         0x88BB, "GL_BUFFER_ACCESS_OES",
     },
     {
@@ -1747,6 +1825,9 @@
         0x88F0, "GL_DEPTH24_STENCIL8_OES",
     },
     {
+        0x88F1, "GL_TEXTURE_STENCIL_SIZE",
+    },
+    {
         0x88F9, "GL_SRC1_COLOR_EXT",
     },
     {
@@ -2191,6 +2272,21 @@
         0x8C0A, "GL_SGX_BINARY_IMG",
     },
     {
+        0x8C10, "GL_TEXTURE_RED_TYPE",
+    },
+    {
+        0x8C11, "GL_TEXTURE_GREEN_TYPE",
+    },
+    {
+        0x8C12, "GL_TEXTURE_BLUE_TYPE",
+    },
+    {
+        0x8C13, "GL_TEXTURE_ALPHA_TYPE",
+    },
+    {
+        0x8C16, "GL_TEXTURE_DEPTH_TYPE",
+    },
+    {
         0x8C17, "GL_UNSIGNED_NORMALIZED_EXT",
     },
     {
@@ -2236,6 +2332,9 @@
         0x8C3E, "GL_UNSIGNED_INT_5_9_9_9_REV_APPLE",
     },
     {
+        0x8C3F, "GL_TEXTURE_SHARED_SIZE",
+    },
+    {
         0x8C40, "GL_SRGB_EXT",
     },
     {
@@ -2800,6 +2899,15 @@
         0x8E50, "GL_SAMPLE_LOCATION_NV",
     },
     {
+        0x8E51, "GL_SAMPLE_MASK",
+    },
+    {
+        0x8E52, "GL_SAMPLE_MASK_VALUE",
+    },
+    {
+        0x8E59, "GL_MAX_SAMPLE_MASK_WORDS",
+    },
+    {
         0x8E5A, "GL_MAX_GEOMETRY_SHADER_INVOCATIONS_OES",
     },
     {
@@ -2812,6 +2920,12 @@
         0x8E5D, "GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES",
     },
     {
+        0x8E5E, "GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET",
+    },
+    {
+        0x8E5F, "GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET",
+    },
+    {
         0x8E72, "GL_PATCH_VERTICES_OES",
     },
     {
@@ -2929,6 +3043,36 @@
         0x8F37, "GL_COPY_WRITE_BUFFER_NV",
     },
     {
+        0x8F38, "GL_MAX_IMAGE_UNITS",
+    },
+    {
+        0x8F39, "GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES",
+    },
+    {
+        0x8F3A, "GL_IMAGE_BINDING_NAME",
+    },
+    {
+        0x8F3B, "GL_IMAGE_BINDING_LEVEL",
+    },
+    {
+        0x8F3C, "GL_IMAGE_BINDING_LAYERED",
+    },
+    {
+        0x8F3D, "GL_IMAGE_BINDING_LAYER",
+    },
+    {
+        0x8F3E, "GL_IMAGE_BINDING_ACCESS",
+    },
+    {
+        0x8F3F, "GL_DRAW_INDIRECT_BUFFER",
+    },
+    {
+        0x8F43, "GL_DRAW_INDIRECT_BUFFER_BINDING",
+    },
+    {
+        0x8F4F, "GL_VERTEX_BINDING_BUFFER",
+    },
+    {
         0x8F60, "GL_MALI_SHADER_BINARY_ARM",
     },
     {
@@ -3103,24 +3247,63 @@
         0x900F, "GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_OES",
     },
     {
+        0x904D, "GL_IMAGE_2D",
+    },
+    {
+        0x904E, "GL_IMAGE_3D",
+    },
+    {
+        0x9050, "GL_IMAGE_CUBE",
+    },
+    {
         0x9051, "GL_IMAGE_BUFFER_OES",
     },
     {
+        0x9053, "GL_IMAGE_2D_ARRAY",
+    },
+    {
         0x9054, "GL_IMAGE_CUBE_MAP_ARRAY_OES",
     },
     {
+        0x9058, "GL_INT_IMAGE_2D",
+    },
+    {
+        0x9059, "GL_INT_IMAGE_3D",
+    },
+    {
+        0x905B, "GL_INT_IMAGE_CUBE",
+    },
+    {
         0x905C, "GL_INT_IMAGE_BUFFER_OES",
     },
     {
+        0x905E, "GL_INT_IMAGE_2D_ARRAY",
+    },
+    {
         0x905F, "GL_INT_IMAGE_CUBE_MAP_ARRAY_OES",
     },
     {
+        0x9063, "GL_UNSIGNED_INT_IMAGE_2D",
+    },
+    {
+        0x9064, "GL_UNSIGNED_INT_IMAGE_3D",
+    },
+    {
+        0x9066, "GL_UNSIGNED_INT_IMAGE_CUBE",
+    },
+    {
         0x9067, "GL_UNSIGNED_INT_IMAGE_BUFFER_OES",
     },
     {
+        0x9069, "GL_UNSIGNED_INT_IMAGE_2D_ARRAY",
+    },
+    {
         0x906A, "GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_OES",
     },
     {
+        0x906E, "GL_IMAGE_BINDING_FORMAT",
+    },
+    {
         0x906F, "GL_RGB10_A2UI",
     },
     {
@@ -3331,6 +3514,18 @@
         0x90BF, "GL_PATH_COVER_DEPTH_FUNC_NV",
     },
     {
+        0x90C7, "GL_IMAGE_FORMAT_COMPATIBILITY_TYPE",
+    },
+    {
+        0x90C8, "GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE",
+    },
+    {
+        0x90C9, "GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS",
+    },
+    {
+        0x90CA, "GL_MAX_VERTEX_IMAGE_UNIFORMS",
+    },
+    {
         0x90CB, "GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_OES",
     },
     {
@@ -3340,6 +3535,27 @@
         0x90CD, "GL_MAX_GEOMETRY_IMAGE_UNIFORMS_OES",
     },
     {
+        0x90CE, "GL_MAX_FRAGMENT_IMAGE_UNIFORMS",
+    },
+    {
+        0x90CF, "GL_MAX_COMBINED_IMAGE_UNIFORMS",
+    },
+    {
+        0x90D2, "GL_SHADER_STORAGE_BUFFER",
+    },
+    {
+        0x90D3, "GL_SHADER_STORAGE_BUFFER_BINDING",
+    },
+    {
+        0x90D4, "GL_SHADER_STORAGE_BUFFER_START",
+    },
+    {
+        0x90D5, "GL_SHADER_STORAGE_BUFFER_SIZE",
+    },
+    {
+        0x90D6, "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS",
+    },
+    {
         0x90D7, "GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_OES",
     },
     {
@@ -3349,6 +3565,36 @@
         0x90D9, "GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_OES",
     },
     {
+        0x90DA, "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS",
+    },
+    {
+        0x90DB, "GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS",
+    },
+    {
+        0x90DC, "GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS",
+    },
+    {
+        0x90DD, "GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS",
+    },
+    {
+        0x90DE, "GL_MAX_SHADER_STORAGE_BLOCK_SIZE",
+    },
+    {
+        0x90DF, "GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT",
+    },
+    {
+        0x90EA, "GL_DEPTH_STENCIL_TEXTURE_MODE",
+    },
+    {
+        0x90EB, "GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS",
+    },
+    {
+        0x90EE, "GL_DISPATCH_INDIRECT_BUFFER",
+    },
+    {
+        0x90EF, "GL_DISPATCH_INDIRECT_BUFFER_BINDING",
+    },
+    {
         0x90F0, "GL_COLOR_ATTACHMENT_EXT",
     },
     {
@@ -3373,9 +3619,27 @@
         0x9102, "GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES",
     },
     {
+        0x9104, "GL_TEXTURE_BINDING_2D_MULTISAMPLE",
+    },
+    {
         0x9105, "GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY_OES",
     },
     {
+        0x9106, "GL_TEXTURE_SAMPLES",
+    },
+    {
+        0x9107, "GL_TEXTURE_FIXED_SAMPLE_LOCATIONS",
+    },
+    {
+        0x9108, "GL_SAMPLER_2D_MULTISAMPLE",
+    },
+    {
+        0x9109, "GL_INT_SAMPLER_2D_MULTISAMPLE",
+    },
+    {
+        0x910A, "GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE",
+    },
+    {
         0x910B, "GL_SAMPLER_2D_MULTISAMPLE_ARRAY_OES",
     },
     {
@@ -3385,6 +3649,15 @@
         0x910D, "GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES",
     },
     {
+        0x910E, "GL_MAX_COLOR_TEXTURE_SAMPLES",
+    },
+    {
+        0x910F, "GL_MAX_DEPTH_TEXTURE_SAMPLES",
+    },
+    {
+        0x9110, "GL_MAX_INTEGER_SAMPLES",
+    },
+    {
         0x9111, "GL_MAX_SERVER_WAIT_TIMEOUT_APPLE",
     },
     {
@@ -3559,6 +3832,24 @@
         0x91AA, "GL_NUM_SPARSE_LEVELS_EXT",
     },
     {
+        0x91B9, "GL_COMPUTE_SHADER",
+    },
+    {
+        0x91BB, "GL_MAX_COMPUTE_UNIFORM_BLOCKS",
+    },
+    {
+        0x91BC, "GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS",
+    },
+    {
+        0x91BD, "GL_MAX_COMPUTE_IMAGE_UNIFORMS",
+    },
+    {
+        0x91BE, "GL_MAX_COMPUTE_WORK_GROUP_COUNT",
+    },
+    {
+        0x91BF, "GL_MAX_COMPUTE_WORK_GROUP_SIZE",
+    },
+    {
         0x9243, "GL_UNPACK_COLORSPACE_CONVERSION_CHROMIUM",
     },
     {
@@ -3769,6 +4060,21 @@
         0x92BE, "GL_PRIMITIVE_BOUNDING_BOX_OES",
     },
     {
+        0x92C0, "GL_ATOMIC_COUNTER_BUFFER",
+    },
+    {
+        0x92C1, "GL_ATOMIC_COUNTER_BUFFER_BINDING",
+    },
+    {
+        0x92C2, "GL_ATOMIC_COUNTER_BUFFER_START",
+    },
+    {
+        0x92C3, "GL_ATOMIC_COUNTER_BUFFER_SIZE",
+    },
+    {
+        0x92CC, "GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS",
+    },
+    {
         0x92CD, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_OES",
     },
     {
@@ -3778,6 +4084,15 @@
         0x92CF, "GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_OES",
     },
     {
+        0x92D0, "GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS",
+    },
+    {
+        0x92D1, "GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS",
+    },
+    {
+        0x92D2, "GL_MAX_VERTEX_ATOMIC_COUNTERS",
+    },
+    {
         0x92D3, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_OES",
     },
     {
@@ -3787,6 +4102,24 @@
         0x92D5, "GL_MAX_GEOMETRY_ATOMIC_COUNTERS_OES",
     },
     {
+        0x92D6, "GL_MAX_FRAGMENT_ATOMIC_COUNTERS",
+    },
+    {
+        0x92D7, "GL_MAX_COMBINED_ATOMIC_COUNTERS",
+    },
+    {
+        0x92D8, "GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE",
+    },
+    {
+        0x92D9, "GL_ACTIVE_ATOMIC_COUNTER_BUFFERS",
+    },
+    {
+        0x92DB, "GL_UNSIGNED_INT_ATOMIC_COUNTER",
+    },
+    {
+        0x92DC, "GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS",
+    },
+    {
         0x92DD, "GL_FRAGMENT_COVERAGE_TO_COLOR_NV",
     },
     {
@@ -3796,9 +4129,81 @@
         0x92E0, "GL_DEBUG_OUTPUT_KHR",
     },
     {
+        0x92E1, "GL_UNIFORM",
+    },
+    {
+        0x92E2, "GL_UNIFORM_BLOCK",
+    },
+    {
+        0x92E3, "GL_PROGRAM_INPUT",
+    },
+    {
+        0x92E4, "GL_PROGRAM_OUTPUT",
+    },
+    {
+        0x92E5, "GL_BUFFER_VARIABLE",
+    },
+    {
+        0x92E6, "GL_SHADER_STORAGE_BLOCK",
+    },
+    {
         0x92E7, "GL_IS_PER_PATCH_OES",
     },
     {
+        0x92F4, "GL_TRANSFORM_FEEDBACK_VARYING",
+    },
+    {
+        0x92F5, "GL_ACTIVE_RESOURCES",
+    },
+    {
+        0x92F6, "GL_MAX_NAME_LENGTH",
+    },
+    {
+        0x92F7, "GL_MAX_NUM_ACTIVE_VARIABLES",
+    },
+    {
+        0x92F9, "GL_NAME_LENGTH",
+    },
+    {
+        0x92FA, "GL_TYPE",
+    },
+    {
+        0x92FB, "GL_ARRAY_SIZE",
+    },
+    {
+        0x92FC, "GL_OFFSET",
+    },
+    {
+        0x92FD, "GL_BLOCK_INDEX",
+    },
+    {
+        0x92FE, "GL_ARRAY_STRIDE",
+    },
+    {
+        0x92FF, "GL_MATRIX_STRIDE",
+    },
+    {
+        0x9300, "GL_IS_ROW_MAJOR",
+    },
+    {
+        0x9301, "GL_ATOMIC_COUNTER_BUFFER_INDEX",
+    },
+    {
+        0x9302, "GL_BUFFER_BINDING",
+    },
+    {
+        0x9303, "GL_BUFFER_DATA_SIZE",
+    },
+    {
+        0x9304, "GL_NUM_ACTIVE_VARIABLES",
+    },
+    {
+        0x9305, "GL_ACTIVE_VARIABLES",
+    },
+    {
+        0x9306, "GL_REFERENCED_BY_VERTEX_SHADER",
+    },
+    {
         0x9307, "GL_REFERENCED_BY_TESS_CONTROL_SHADER_OES",
     },
     {
@@ -3808,15 +4213,51 @@
         0x9309, "GL_REFERENCED_BY_GEOMETRY_SHADER_OES",
     },
     {
+        0x930A, "GL_REFERENCED_BY_FRAGMENT_SHADER",
+    },
+    {
+        0x930B, "GL_REFERENCED_BY_COMPUTE_SHADER",
+    },
+    {
+        0x930C, "GL_TOP_LEVEL_ARRAY_SIZE",
+    },
+    {
+        0x930D, "GL_TOP_LEVEL_ARRAY_STRIDE",
+    },
+    {
+        0x930E, "GL_LOCATION",
+    },
+    {
         0x930F, "GL_LOCATION_INDEX_EXT",
     },
     {
+        0x9310, "GL_FRAMEBUFFER_DEFAULT_WIDTH",
+    },
+    {
+        0x9311, "GL_FRAMEBUFFER_DEFAULT_HEIGHT",
+    },
+    {
         0x9312, "GL_FRAMEBUFFER_DEFAULT_LAYERS_OES",
     },
     {
+        0x9313, "GL_FRAMEBUFFER_DEFAULT_SAMPLES",
+    },
+    {
+        0x9314, "GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS",
+    },
+    {
+        0x9315, "GL_MAX_FRAMEBUFFER_WIDTH",
+    },
+    {
+        0x9316, "GL_MAX_FRAMEBUFFER_HEIGHT",
+    },
+    {
         0x9317, "GL_MAX_FRAMEBUFFER_LAYERS_OES",
     },
     {
+        0x9318, "GL_MAX_FRAMEBUFFER_SAMPLES",
+    },
+    {
         0x9327, "GL_RASTER_MULTISAMPLE_EXT",
     },
     {
diff --git a/gpu/command_buffer/gles2_cmd_buffer_functions.txt b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
index dc957cc..c2d9431 100644
--- a/gpu/command_buffer/gles2_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
@@ -255,6 +255,8 @@
 GL_APICALL void         GL_APIENTRY glBindVertexArrayOES (GLidBindVertexArray array);
 GL_APICALL void         GL_APIENTRY glFramebufferParameteri (GLenumFramebufferTarget target, GLenumFramebufferParameter pname, GLint param);
 
+GL_APICALL void         GL_APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);
+
 // Non-GL commands.
 GL_APICALL void         GL_APIENTRY glSwapBuffers (GLuint64 swap_id, GLbitfieldSwapBuffersFlags flags = 0);
 GL_APICALL GLuint       GL_APIENTRY glGetMaxValueInBufferCHROMIUM (GLidBuffer buffer_id, GLsizei count, GLenumGetMaxIndexType type, GLuint offset);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index ec73874..70ac070 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -4525,6 +4525,12 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderImpl::HandleDispatchCompute(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  return error::kUnknownCommand;
+}
+
 error::Error GLES2DecoderImpl::HandleSwapBuffers(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
index e106a21..4b1cebe 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -162,6 +162,9 @@
 error::Error DoDetachShader(GLuint program, GLuint shader);
 error::Error DoDisable(GLenum cap);
 error::Error DoDisableVertexAttribArray(GLuint index);
+error::Error DoDispatchCompute(GLuint num_groups_x,
+                               GLuint num_groups_y,
+                               GLuint num_groups_z);
 error::Error DoDrawArrays(GLenum mode, GLint first, GLsizei count);
 error::Error DoDrawElements(GLenum mode,
                             GLsizei count,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 1dc0a5a..1a1778e 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -1039,6 +1039,15 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderPassthroughImpl::DoDispatchCompute(
+    GLuint num_groups_x,
+    GLuint num_groups_y,
+    GLuint num_groups_z) {
+  BindPendingImagesForSamplersIfNeeded();
+  api()->glDispatchComputeFn(num_groups_x, num_groups_y, num_groups_z);
+  return error::kNoError;
+}
+
 error::Error GLES2DecoderPassthroughImpl::DoDrawArrays(GLenum mode,
                                                        GLint first,
                                                        GLsizei count) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
index 1ac64d5..5aad66f7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
@@ -3925,6 +3925,24 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderPassthroughImpl::HandleDispatchCompute(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  if (!feature_info_->IsWebGL2ComputeContext())
+    return error::kUnknownCommand;
+  const volatile gles2::cmds::DispatchCompute& c =
+      *static_cast<const volatile gles2::cmds::DispatchCompute*>(cmd_data);
+  GLuint num_groups_x = static_cast<GLuint>(c.num_groups_x);
+  GLuint num_groups_y = static_cast<GLuint>(c.num_groups_y);
+  GLuint num_groups_z = static_cast<GLuint>(c.num_groups_z);
+  error::Error error =
+      DoDispatchCompute(num_groups_x, num_groups_y, num_groups_z);
+  if (error != error::kNoError) {
+    return error;
+  }
+  return error::kNoError;
+}
+
 error::Error GLES2DecoderPassthroughImpl::HandleSwapBuffers(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.mm b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
index e2ea9d5..e6681e5 100644
--- a/gpu/ipc/service/image_transport_surface_overlay_mac.mm
+++ b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/debug/crash_logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
@@ -80,15 +79,12 @@
 }
 
 void ImageTransportSurfaceOverlayMac::ApplyBackpressure() {
+  // TODO(ccameron): This early-out is to determine if https://crbug.com/863817
+  // is caused by ApplyBackpressure, or if ApplyBackpressure happens to be
+  // what triggers a crash that was already going to happen.
+  return;
+
   TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::ApplyBackpressure");
-  static auto* crash_key = base::debug::AllocateCrashKeyString(
-      "mac_swap", base::debug::CrashKeySize::Size64);
-  std::stringstream crash_info;
-  crash_info << "pixel_size:" << pixel_size_.ToString() << " ";
-  crash_info << "scale:" << scale_factor_ << " ";
-  crash_info << "has_tree:"
-             << ca_layer_tree_coordinator_->HasPendingCARendererLayerTree();
-  base::debug::SetCrashKeyString(crash_key, crash_info.str());
 
   gl::GLContext* current_context = gl::GLContext::GetCurrent();
   // TODO(ccameron): Remove these CHECKs.
@@ -100,8 +96,6 @@
   uint64_t this_frame_fence = current_context->BackpressureFenceCreate();
   current_context->BackpressureFenceWait(previous_frame_fence_);
   previous_frame_fence_ = this_frame_fence;
-
-  base::debug::ClearCrashKeyString(crash_key);
 }
 
 void ImageTransportSurfaceOverlayMac::BufferPresented(
@@ -233,6 +227,10 @@
     const gfx::RectF& crop_rect,
     bool enable_blend,
     std::unique_ptr<gfx::GpuFence> gpu_fence) {
+  // Temporary flush for debugging https://crbug.com/863817.
+  // TODO(ccameron): Remove this.
+  glFlush();
+
   if (transform != gfx::OVERLAY_TRANSFORM_NONE) {
     DLOG(ERROR) << "Invalid overlay plane transform.";
     return false;
@@ -264,6 +262,10 @@
 
 bool ImageTransportSurfaceOverlayMac::ScheduleCALayer(
     const ui::CARendererLayerParams& params) {
+  // Temporary flush for debugging https://crbug.com/863817.
+  // TODO(ccameron): Remove this.
+  glFlush();
+
   if (params.image) {
     gl::GLImageIOSurface* io_surface_image =
         gl::GLImageIOSurface::FromGLImage(params.image);
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 85f54f0..ad6dd164 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -277,6 +277,11 @@
   }
 }
 
+std::string HeadlessContentBrowserClient::GetAcceptLangs(
+    content::BrowserContext* context) {
+  return browser_->options()->accept_language;
+}
+
 void HeadlessContentBrowserClient::AllowCertificateError(
     content::WebContents* web_contents,
     int cert_error,
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h
index 2effa5f..bfc51d1d 100644
--- a/headless/lib/browser/headless_content_browser_client.h
+++ b/headless/lib/browser/headless_content_browser_client.h
@@ -40,7 +40,7 @@
 #endif
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
-
+  std::string GetAcceptLangs(content::BrowserContext* context) override;
   void AllowCertificateError(
       content::WebContents* web_contents,
       int cert_error,
@@ -51,15 +51,12 @@
       bool expired_previous_decision,
       const base::Callback<void(content::CertificateRequestResultType)>&
           callback) override;
-
   void SelectClientCertificate(
       content::WebContents* web_contents,
       net::SSLCertRequestInfo* cert_request_info,
       net::ClientCertIdentityList client_certs,
       std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
-
   void ResourceDispatcherHostCreated() override;
-
   bool DoesSiteRequireDedicatedProcess(content::BrowserContext* browser_context,
                                        const GURL& effective_site_url) override;
 
diff --git a/headless/test/data/protocol/sanity/renderer-content-security-policy-expected.txt b/headless/test/data/protocol/sanity/renderer-content-security-policy-expected.txt
new file mode 100644
index 0000000..528537c2
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-content-security-policy-expected.txt
@@ -0,0 +1,5 @@
+Tests renderer: content security policy.
+requested url: http://example.com/
+pass256
+pass384
+pass512
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/renderer-content-security-policy.js b/headless/test/data/protocol/sanity/renderer-content-security-policy.js
new file mode 100644
index 0000000..af749c5
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-content-security-policy.js
@@ -0,0 +1,51 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: content security policy.');
+
+  let RendererTestHelper =
+      await testRunner.loadScript('../helpers/renderer-test-helper.js');
+  let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+      await (new RendererTestHelper(testRunner, dp, page)).init();
+
+  dp.Runtime.enable();
+  dp.Runtime.onConsoleAPICalled(data => {
+    const text = data.params.args[0].value;
+    testRunner.log(text);
+  });
+
+  // Only first 3 scripts of 4 on the page are white listed for execution.
+  // Therefore only 3 lines in the log are expected.
+  httpInterceptor.addResponse(`http://example.com/`,
+      `<!DOCTYPE html>` +
+      `<script>console.log('pass256');</script>` +
+      `<script>console.log('pass384');</script>` +
+      `<script>console.log('pass512');</script>` +
+      `<script>console.log('fail');</script>`,
+      [`HTTP/1.1 200 OK`,
+       `Content-Type: text/html`,
+       `Content-Security-Policy: script-src` +
+       ` 'sha256-INSsCHXoo4K3+jDRF8FSvl13GP22I9vcqcJjkq35Y20='` +
+       ` 'sha384-77lSn5Q6V979pJ8W2TXc6Lrj98LughR0ofkFwa+qOEtlcofEdLPkOPtp` +
+       `JF8QQMev'` +
+       ` 'sha512-2cS3KZwfnxFo6lvBvAl113f5N3QCRgtRJBbtFaQHKOhk36sdYYKFvhCq` +
+       `GTvbN7pBKUfsjfCQgFF4MSbCQuvT8A=='`,
+      ]);
+
+  // Regenerate sha256 hash with:
+  // echo -n "console.log('pass256');" \
+  //   | openssl sha256 -binary \
+  //   | openssl base64
+
+  await virtualTimeController.grantInitialTime(500, 1000,
+    null,
+    async () => {
+      testRunner.completeTest();
+    }
+  );
+
+  await frameNavigationHelper.navigate('http://example.com/');
+})
diff --git a/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-expected.txt b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-expected.txt
new file mode 100644
index 0000000..124f345
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-expected.txt
@@ -0,0 +1,3 @@
+Tests renderer: cookie set from js.
+requested url: http://www.example.com/
+pass
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-no-cookies-expected.txt b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-no-cookies-expected.txt
new file mode 100644
index 0000000..64fa496
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-no-cookies-expected.txt
@@ -0,0 +1,3 @@
+Tests renderer: cookie set from js with cookies disabled.
+requested url: http://www.example.com/
+pass
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-no-cookies.js b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-no-cookies.js
new file mode 100644
index 0000000..80cb243d
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js-no-cookies.js
@@ -0,0 +1,38 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: cookie set from js with cookies disabled.');
+
+  let RendererTestHelper =
+      await testRunner.loadScript('../helpers/renderer-test-helper.js');
+  let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+      await (new RendererTestHelper(testRunner, dp, page)).init();
+
+  httpInterceptor.addResponse(
+      `http://www.example.com/`,
+      `<html>
+        <head>
+          <script>
+            document.cookie = 'SessionID=123';
+          </script>
+        </head>
+        <body>Hello, World!</body>
+      </html>`);
+
+  await dp.Emulation.setDocumentCookieDisabled({disabled: true});
+
+  await virtualTimeController.grantInitialTime(5000, 1000,
+    null,
+    async () => {
+      const cookieIndex =
+          await session.evaluate(`document.cookie.indexOf('SessionID')`);
+      testRunner.log(cookieIndex < 0 ? 'pass' : 'FAIL');
+      testRunner.completeTest();
+    }
+  );
+
+  await frameNavigationHelper.navigate('http://www.example.com/');
+})
diff --git a/headless/test/data/protocol/sanity/renderer-cookie-set-from-js.js b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js.js
new file mode 100644
index 0000000..555e6bc
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-cookie-set-from-js.js
@@ -0,0 +1,38 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: cookie set from js.');
+
+  let RendererTestHelper =
+      await testRunner.loadScript('../helpers/renderer-test-helper.js');
+  let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+      await (new RendererTestHelper(testRunner, dp, page)).init();
+
+  httpInterceptor.addResponse(
+      `http://www.example.com/`,
+      `<html>
+        <head>
+          <script>
+            document.cookie = 'SessionID=123';
+          </script>
+        </head>
+        <body>Hello, World!</body>
+      </html>`);
+
+  await dp.Emulation.setDocumentCookieDisabled({disabled: false});
+
+  await virtualTimeController.grantInitialTime(5000, 1000,
+    null,
+    async () => {
+      const cookieIndex =
+          await session.evaluate(`document.cookie.indexOf('SessionID')`);
+      testRunner.log(cookieIndex < 0 ? 'FAIL' : 'pass');
+      testRunner.completeTest();
+    }
+  );
+
+  await frameNavigationHelper.navigate('http://www.example.com/');
+})
diff --git a/headless/test/data/protocol/sanity/renderer-cookie-updated-from-js-expected.txt b/headless/test/data/protocol/sanity/renderer-cookie-updated-from-js-expected.txt
new file mode 100644
index 0000000..e18668b8
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-cookie-updated-from-js-expected.txt
@@ -0,0 +1,4 @@
+Tests renderer: cookie updated from js.
+requested url: http://www.example.com/
+foo=barbaz
+Pass
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/renderer-cookie-updated-from-js.js b/headless/test/data/protocol/sanity/renderer-cookie-updated-from-js.js
new file mode 100644
index 0000000..0b89453
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-cookie-updated-from-js.js
@@ -0,0 +1,40 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: cookie updated from js.');
+
+  let RendererTestHelper =
+      await testRunner.loadScript('../helpers/renderer-test-helper.js');
+  let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+      await (new RendererTestHelper(testRunner, dp, page)).init();
+
+  httpInterceptor.addResponse(
+      `http://www.example.com/`,
+      `<html>
+        <head>
+          <script>
+            let x = document.cookie;
+            document.cookie = x + 'baz';
+            document.title = document.cookie;
+          </script>
+       </head>
+      <body>Pass</body>
+      </html>`);
+
+  await dp.Network.setCookie({url: 'http://www.example.com/',
+      name: 'foo', value: 'bar'});
+
+  await virtualTimeController.grantInitialTime(5000, 1000,
+    null,
+    async () => {
+      testRunner.log(await session.evaluate('document.title'));
+      testRunner.log(await session.evaluate('document.body.innerText'));
+      testRunner.completeTest();
+    }
+  );
+
+  await frameNavigationHelper.navigate('http://www.example.com/');
+})
diff --git a/headless/test/data/protocol/sanity/renderer-frame-load-events-expected.txt b/headless/test/data/protocol/sanity/renderer-frame-load-events-expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-frame-load-events-expected.txt
diff --git a/headless/test/data/protocol/sanity/renderer-frame-load-events.js b/headless/test/data/protocol/sanity/renderer-frame-load-events.js
new file mode 100644
index 0000000..de2c55a
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-frame-load-events.js
@@ -0,0 +1,60 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: content security policy.');
+
+  let RendererTestHelper =
+      await testRunner.loadScript('../helpers/renderer-test-helper.js');
+  let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+      await (new RendererTestHelper(testRunner, dp, page)).init();
+
+  httpInterceptor.addResponse(`http://example.com/`, null,
+      ['HTTP/1.1 302 Found', 'Location: http://example.com/1']);
+
+  httpInterceptor.addResponse(`http://example.com/1`,
+      `<html><frameset>
+        <frame src="http://example.com/frameA/" id="frameA">
+        <frame src="http://example.com/frameB/" id="frameB">
+      </frameset></html>`);
+
+  httpInterceptor.addResponse(`http://example.com/frameA/`,
+      `<html><head><script>
+        document.location="http://example.com/frameA/1"
+      </script></head></html>`);
+
+  httpInterceptor.addResponse(`http://example.com/frameB/`,
+      `<html><head><script>
+        document.location="http://example.com/frameB/1"
+      </script></head></html>`);
+
+  httpInterceptor.addResponse(`http://example.com/frameA/1`,
+      `<html><body>FRAME A 1</body></html>`);
+
+  httpInterceptor.addResponse(`http://example.com/frameB/1`,
+      `<html><body>FRAME B 1
+        <iframe src="http://example.com/frameB/1/iframe/" id="iframe"></iframe>
+      </body></html>`);
+
+  httpInterceptor.addResponse(`http://example.com/frameB/1/iframe/`,
+      `<html><head><script>
+        document.location="http://example.com/frameB/1/iframe/1"
+      </script></head></html>`);
+
+  httpInterceptor.addResponse(`http://example.com/frameB/1/iframe/1`,
+      `<html><body>IFRAME 1</body><html>`);
+
+  await virtualTimeController.grantInitialTime(500, 1000,
+    null,
+    async () => {
+      testRunner.log(await session.evaluate('document.body.innerHTML'));
+      frameNavigationHelper.logFrames();
+      frameNavigationHelper.logScheduledNavigations();
+      testRunner.completeTest();
+    }
+  );
+
+  await frameNavigationHelper.navigate('http://example.com/');
+})
diff --git a/headless/test/data/protocol/sanity/renderer-hello-world-expected.txt b/headless/test/data/protocol/sanity/renderer-hello-world-expected.txt
index 1bc14c0..3169729 100644
--- a/headless/test/data/protocol/sanity/renderer-hello-world-expected.txt
+++ b/headless/test/data/protocol/sanity/renderer-hello-world-expected.txt
@@ -1,7 +1,7 @@
 Tests renderer: hello world.
-requested url: http://example.com/foobar
+requested url: http://www.example.com/
 <h1>Hello headless world!</h1>
 Frames: 1
  frameId=MainFrame
-  url=http://example.com/foobar
+  url=http://www.example.com/
 ScheduledNavigations: 0
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/renderer-hello-world.js b/headless/test/data/protocol/sanity/renderer-hello-world.js
index 92adb12a..1bae742 100644
--- a/headless/test/data/protocol/sanity/renderer-hello-world.js
+++ b/headless/test/data/protocol/sanity/renderer-hello-world.js
@@ -12,8 +12,8 @@
       await (new RendererTestHelper(testRunner, dp, page)).init();
 
   httpInterceptor.addResponse(
-    `http://example.com/foobar`,
-    `<!doctype html><h1>Hello headless world!</h1>`);
+      `http://www.example.com/`,
+      `<!doctype html><h1>Hello headless world!</h1>`);
 
   await virtualTimeController.grantInitialTime(500, 1000,
     null,
@@ -25,5 +25,5 @@
     }
   );
 
-  await frameNavigationHelper.navigate('http://example.com/foobar');
+  await frameNavigationHelper.navigate('http://www.example.com/');
 })
diff --git a/headless/test/data/protocol/sanity/renderer-in-cross-origin-object-expected.txt b/headless/test/data/protocol/sanity/renderer-in-cross-origin-object-expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-in-cross-origin-object-expected.txt
diff --git a/headless/test/data/protocol/sanity/renderer-in-cross-origin-object.js b/headless/test/data/protocol/sanity/renderer-in-cross-origin-object.js
new file mode 100644
index 0000000..4610754
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-in-cross-origin-object.js
@@ -0,0 +1,53 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: in cross origin object.');
+
+  let RendererTestHelper =
+      await testRunner.loadScript('../helpers/renderer-test-helper.js');
+  let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+      await (new RendererTestHelper(testRunner, dp, page)).init();
+
+    dp.Runtime.enable();
+    dp.Runtime.onConsoleAPICalled(data => {
+      const text = data.params.args[0].value;
+      testRunner.log(text);
+    });
+
+  httpInterceptor.addResponse(
+      `http://foo.com/`,
+      `<html>
+        <body>
+          <iframe id='myframe' src='http://bar.com/'></iframe>
+          <script>
+            window.onload = function() {
+              console.log('onLoad');
+              try {
+                var a = 0 in document.getElementById('myframe').contentWindow;
+              } catch (e) {
+                console.log('caught: ' + e.message);
+              }
+            };
+          </script>
+          <p>Pass</p>
+        </body>
+      </html>`);
+
+  httpInterceptor.addResponse(
+      `http://bar.com/`,
+      `<html></html>`);
+
+  await virtualTimeController.grantInitialTime(500, 1000,
+    null,
+    async () => {
+      testRunner.log(await session.evaluate('document.title'));
+      testRunner.log(await session.evaluate('document.body.innerText'));
+      testRunner.completeTest();
+    }
+  );
+
+  await frameNavigationHelper.navigate('http://foo.com/');
+})
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index b609778..f6aeb8f6 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -341,6 +341,24 @@
 HEADLESS_PROTOCOL_COMPOSITOR_TEST(
     RendererClientRedirectChainNoJs,
     "sanity/renderer-client-redirect-chain-no-js.js");
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(RendererCookieSetFromJs,
+                                  "sanity/renderer-cookie-set-from-js.js");
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(
+    RendererCookieSetFromJsNoCookies,
+    "sanity/renderer-cookie-set-from-js-no-cookies.js");
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(RendererCookieUpdatedFromJs,
+                                  "sanity/renderer-cookie-updated-from-js.js");
+
+// TODO(kvitekp): investigating...
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(DISABLED_RendererInCrossOriginObject,
+                                  "sanity/renderer-in-cross-origin-object.js");
+
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(RendererContentSecurityPolicy,
+                                  "sanity/renderer-content-security-policy.js");
+
+// TODO(crbug.com/867274): frame navigation is currently broken.
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(DISABLED_RendererFrameLoadEvents,
+                                  "sanity/renderer-frame-load-events.js");
 HEADLESS_PROTOCOL_COMPOSITOR_TEST(RendererCssUrlFilter,
                                   "sanity/renderer-css-url-filter.js");
 HEADLESS_PROTOCOL_COMPOSITOR_TEST(RendererCanvas, "sanity/renderer-canvas.js");
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index 42417b7..a5b89bc 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -49,6 +49,7 @@
   "+components/open_from_clipboard",
   "+components/password_manager/core/browser",
   "+components/password_manager/core/common",
+  "+components/password_manager/ios",
   "+components/password_manager/sync/browser",
   "+components/payments/core",
   "+components/payments/mojom",
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 61423cf..27c6ecab 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -29,8 +29,6 @@
     "ios_chrome_update_password_infobar_delegate.mm",
     "js_credential_manager.h",
     "js_credential_manager.mm",
-    "js_password_manager.h",
-    "js_password_manager.mm",
     "notify_auto_signin_view_controller.h",
     "notify_auto_signin_view_controller.mm",
     "password_controller.h",
@@ -62,6 +60,7 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser/form_parsing",
     "//components/password_manager/core/common",
+    "//components/password_manager/ios",
     "//components/password_manager/sync/browser",
     "//components/prefs",
     "//components/security_state/core",
@@ -134,6 +133,7 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser:test_support",
     "//components/password_manager/core/common",
+    "//components/password_manager/ios",
     "//components/prefs",
     "//components/prefs:test_support",
     "//components/security_state/ios",
@@ -172,6 +172,7 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser:test_support",
     "//components/password_manager/core/common",
+    "//components/password_manager/ios",
     "//components/prefs",
     "//components/prefs:test_support",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 590ac3c..d68aa7d7 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -35,6 +35,7 @@
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
+#import "components/password_manager/ios/js_password_manager.h"
 #include "components/sync/driver/sync_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
@@ -43,7 +44,6 @@
 #include "ios/chrome/browser/passwords/credential_manager_features.h"
 #import "ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h"
 #import "ios/chrome/browser/passwords/ios_chrome_update_password_infobar_delegate.h"
-#import "ios/chrome/browser/passwords/js_password_manager.h"
 #import "ios/chrome/browser/passwords/notify_auto_signin_view_controller.h"
 #import "ios/chrome/browser/passwords/password_form_filler.h"
 #import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
diff --git a/ios/chrome/browser/passwords/password_controller_js_unittest.mm b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
index c411a354..4ab50c3 100644
--- a/ios/chrome/browser/passwords/password_controller_js_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
@@ -4,7 +4,7 @@
 
 #import <Foundation/Foundation.h>
 
-#import "ios/chrome/browser/passwords/js_password_manager.h"
+#import "components/password_manager/ios/js_password_manager.h"
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index eb974a9b..51710fb0 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -22,13 +22,13 @@
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
+#import "components/password_manager/ios/js_password_manager.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/security_state/ios/ssl_status_input_event_data.h"
 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#import "ios/chrome/browser/passwords/js_password_manager.h"
 #import "ios/chrome/browser/passwords/password_form_filler.h"
 #include "ios/chrome/browser/passwords/test_helpers.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index faeb2ae..cf27b16 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -4557,7 +4557,8 @@
       TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
           self.browserState);
   sessions::TabRestoreService* restoreService =
-      IOSChromeTabRestoreServiceFactory::GetForBrowserState(self.browserState);
+      IOSChromeTabRestoreServiceFactory::GetForBrowserState(
+          self.browserState->GetOriginalChromeBrowserState());
   restoreService->RestoreEntryById(delegate, sessionID,
                                    WindowOpenDisposition::CURRENT_TAB);
 }
diff --git a/ios/chrome/browser/ui/history/history_table_view_controller.mm b/ios/chrome/browser/ui/history/history_table_view_controller.mm
index 7bf194f..59e3b6e 100644
--- a/ios/chrome/browser/ui/history/history_table_view_controller.mm
+++ b/ios/chrome/browser/ui/history/history_table_view_controller.mm
@@ -271,57 +271,54 @@
   NSString* searchQuery =
       [base::SysUTF16ToNSString(queryResultsInfo.search_text) copy];
 
-  void (^tableUpdates)(void) = ^{
-    // There should always be at least a header section present.
-    DCHECK([[self tableViewModel] numberOfSections]);
-    for (const BrowsingHistoryService::HistoryEntry& entry : results) {
-      HistoryEntryItem* item =
-          [[HistoryEntryItem alloc] initWithType:ItemTypeHistoryEntry
-                           accessibilityDelegate:self];
-      item.text = [history::FormattedTitle(entry.title, entry.url) copy];
-      item.detailText =
-          [base::SysUTF8ToNSString(entry.url.GetOrigin().spec()) copy];
-      item.timeText = [base::SysUTF16ToNSString(
-          base::TimeFormatTimeOfDay(entry.time)) copy];
-      item.URL = entry.url;
-      item.timestamp = entry.time;
-      [resultsItems addObject:item];
-    }
-
-    [self updateToolbarButtons];
-
-    if ((self.searchInProgress && [searchQuery length] > 0 &&
-         [self.currentQuery isEqualToString:searchQuery]) ||
-        self.filterQueryResult) {
-      // If in search mode, filter out entries that are not part of the
-      // search result.
-      [self filterForHistoryEntries:resultsItems];
-      [self deleteItemsFromTableViewModelWithIndex:
-                self.filteredOutEntriesIndexPaths];
-      // Clear all objects that were just deleted from the tableViewModel.
-      [self.filteredOutEntriesIndexPaths removeAllObjects];
-      self.filterQueryResult = NO;
-    }
-    // Wait to insert until after the deletions are done, this is needed
-    // because performBatchUpdates processes deletion indexes first, and
-    // then inserts.
-    for (HistoryEntryItem* item in resultsItems) {
-      [self.entryInserter insertHistoryEntryItem:item];
-    }
-  };
-
-  // If iOS11+ use performBatchUpdates: instead of beginUpdates/endUpdates.
-  if (@available(iOS 11, *)) {
-    [self.tableView performBatchUpdates:tableUpdates
-                             completion:^(BOOL) {
-                               [self updateTableViewAfterDeletingEntries];
-                             }];
-  } else {
-    [self.tableView beginUpdates];
-    tableUpdates();
-    [self updateTableViewAfterDeletingEntries];
-    [self.tableView endUpdates];
+  // There should always be at least a header section present.
+  DCHECK([[self tableViewModel] numberOfSections]);
+  for (const BrowsingHistoryService::HistoryEntry& entry : results) {
+    HistoryEntryItem* item =
+        [[HistoryEntryItem alloc] initWithType:ItemTypeHistoryEntry
+                         accessibilityDelegate:self];
+    item.text = [history::FormattedTitle(entry.title, entry.url) copy];
+    item.detailText =
+        [base::SysUTF8ToNSString(entry.url.GetOrigin().spec()) copy];
+    item.timeText =
+        [base::SysUTF16ToNSString(base::TimeFormatTimeOfDay(entry.time)) copy];
+    item.URL = entry.url;
+    item.timestamp = entry.time;
+    [resultsItems addObject:item];
   }
+
+  [self updateToolbarButtons];
+
+  if ((self.searchInProgress && [searchQuery length] > 0 &&
+       [self.currentQuery isEqualToString:searchQuery]) ||
+      self.filterQueryResult) {
+    // If in search mode, filter out entries that are not part of the
+    // search result.
+    [self filterForHistoryEntries:resultsItems];
+    [self
+        deleteItemsFromTableViewModelWithIndex:self.filteredOutEntriesIndexPaths
+                      deleteItemsFromTableView:NO];
+    // Clear all objects that were just deleted from the tableViewModel.
+    [self.filteredOutEntriesIndexPaths removeAllObjects];
+    self.filterQueryResult = NO;
+  }
+
+  // Insert result items into the model.
+  for (HistoryEntryItem* item in resultsItems) {
+    [self.entryInserter insertHistoryEntryItem:item];
+  }
+
+  // Save the currently selected rows to preserve its state after the tableView
+  // is reloaded. Since a query with selected rows can only happen when
+  // scrolling down the tableView this should be safe. If this changes in the
+  // future e.g. being able to search while selected rows exist, we should
+  // update this.
+  NSIndexPath* currentSelectedCells = [self.tableView indexPathForSelectedRow];
+  [self.tableView reloadData];
+  [self.tableView selectRowAtIndexPath:currentSelectedCells
+                              animated:NO
+                        scrollPosition:UITableViewScrollPositionNone];
+  [self updateTableViewAfterDeletingEntries];
 }
 
 - (void)showNoticeAboutOtherFormsOfBrowsingHistory:(BOOL)shouldShowNotice {
@@ -352,20 +349,20 @@
 
 - (void)historyEntryInserter:(HistoryEntryInserter*)inserter
     didInsertItemAtIndexPath:(NSIndexPath*)indexPath {
-  [self.tableView insertRowsAtIndexPaths:@[ indexPath ]
-                        withRowAnimation:UITableViewRowAnimationNone];
+  // NO-OP since [self.tableView reloadData] will be called after the inserter
+  // has completed its updates.
 }
 
 - (void)historyEntryInserter:(HistoryEntryInserter*)inserter
      didInsertSectionAtIndex:(NSInteger)sectionIndex {
-  [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
-                withRowAnimation:UITableViewRowAnimationNone];
+  // NO-OP since [self.tableView reloadData] will be called after the inserter
+  // has completed its updates.
 }
 
 - (void)historyEntryInserter:(HistoryEntryInserter*)inserter
      didRemoveSectionAtIndex:(NSInteger)sectionIndex {
-  [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
-                withRowAnimation:UITableViewRowAnimationNone];
+  // NO-OP since [self.tableView reloadData] will be called after the inserter
+  // has completed its updates.
 }
 
 #pragma mark HistoryEntryItemDelegate
@@ -471,7 +468,8 @@
   // If iOS11+ use performBatchUpdates: instead of beginUpdates/endUpdates.
   if (@available(iOS 11, *)) {
     [self.tableView performBatchUpdates:^{
-      [self deleteItemsFromTableViewModelWithIndex:toDeleteIndexPaths];
+      [self deleteItemsFromTableViewModelWithIndex:toDeleteIndexPaths
+                          deleteItemsFromTableView:YES];
     }
         completion:^(BOOL) {
           [self updateTableViewAfterDeletingEntries];
@@ -479,7 +477,8 @@
         }];
   } else {
     [self.tableView beginUpdates];
-    [self deleteItemsFromTableViewModelWithIndex:toDeleteIndexPaths];
+    [self deleteItemsFromTableViewModelWithIndex:toDeleteIndexPaths
+                        deleteItemsFromTableView:YES];
     [self updateTableViewAfterDeletingEntries];
     [self configureViewsForNonEditModeWithAnimation:YES];
     [self.tableView endUpdates];
@@ -778,8 +777,10 @@
 }
 
 // Deletes all items in the tableView which indexes are included in indexArray,
-// needs to be run inside a performBatchUpdates block.
-- (void)deleteItemsFromTableViewModelWithIndex:(NSArray*)indexArray {
+// if |deleteItemsFromTableView| is YES this method needs to be run inside a
+// performBatchUpdates block.
+- (void)deleteItemsFromTableViewModelWithIndex:(NSArray*)indexArray
+                      deleteItemsFromTableView:(BOOL)deleteItemsFromTableView {
   NSArray* sortedIndexPaths =
       [indexArray sortedArrayUsingSelector:@selector(compare:)];
   for (NSIndexPath* indexPath in [sortedIndexPaths reverseObjectEnumerator]) {
@@ -792,14 +793,18 @@
                   fromSectionWithIdentifier:sectionIdentifier
                                     atIndex:index];
   }
-  [self.tableView deleteRowsAtIndexPaths:indexArray
-                        withRowAnimation:UITableViewRowAnimationNone];
+  if (deleteItemsFromTableView)
+    [self.tableView deleteRowsAtIndexPaths:indexArray
+                          withRowAnimation:UITableViewRowAnimationNone];
 
   // Remove any empty sections, except the header section.
   for (int section = self.tableView.numberOfSections - 1; section > 0;
        --section) {
     if (![self.tableViewModel numberOfItemsInSection:section]) {
       [self.entryInserter removeSection:section];
+      if (deleteItemsFromTableView)
+        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:section]
+                      withRowAnimation:UITableViewRowAnimationAutomatic];
     }
   }
 }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
index ea3b39c..6c44017 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_disclosure_header_footer_item.mm
@@ -97,20 +97,22 @@
         fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
     _titleLabel.font =
         [UIFont fontWithDescriptor:styleDescriptor size:kUseDefaultFontSize];
+    [_titleLabel
+        setContentCompressionResistancePriority:UILayoutPriorityRequired
+                                        forAxis:UILayoutConstraintAxisVertical];
 
     _subtitleLabel = [[UILabel alloc] init];
     _subtitleLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     _subtitleLabel.textColor = [UIColor lightGrayColor];
     [_subtitleLabel
-        setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh
+        setContentCompressionResistancePriority:UILayoutPriorityRequired
                                         forAxis:UILayoutConstraintAxisVertical];
 
     // Vertical StackView.
     UIStackView* verticalStack = [[UIStackView alloc]
         initWithArrangedSubviews:@[ _titleLabel, _subtitleLabel ]];
     verticalStack.axis = UILayoutConstraintAxisVertical;
-    verticalStack.distribution = UIStackViewDistributionFillProportionally;
 
     // Disclosure ImageView. Initial pointing direction is to the right.
     _disclosureImageView = [[UIImageView alloc]
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 24abaff1..c29e0506 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -32,6 +32,7 @@
 #include "media/filters/ffmpeg_demuxer.h"
 #include "media/filters/file_data_source.h"
 #include "media/formats/mp4/avc.h"
+#include "media/formats/mp4/bitstream_converter.h"
 #include "media/media_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -1199,8 +1200,8 @@
     subsamples = buffer->decrypt_config()->subsamples();
 
   bool is_valid =
-      mp4::AVC::IsValidAnnexB(buffer->data(), buffer->data_size(),
-                              subsamples);
+      mp4::AVC::AnalyzeAnnexB(buffer->data(), buffer->data_size(), subsamples)
+          .is_conformant.value_or(false);
   EXPECT_TRUE(is_valid);
 
   if (!is_valid) {
diff --git a/media/formats/mp4/avc.cc b/media/formats/mp4/avc.cc
index 509335b..235795f7 100644
--- a/media/formats/mp4/avc.cc
+++ b/media/formats/mp4/avc.cc
@@ -171,15 +171,21 @@
   return true;
 }
 
-// Verifies AnnexB NALU order according to ISO/IEC 14496-10 Section 7.4.1.2.3
-bool AVC::IsValidAnnexB(const uint8_t* buffer,
-                        size_t size,
-                        const std::vector<SubsampleEntry>& subsamples) {
+// static
+BitstreamConverter::AnalysisResult AVC::AnalyzeAnnexB(
+    const uint8_t* buffer,
+    size_t size,
+    const std::vector<SubsampleEntry>& subsamples) {
   DVLOG(3) << __func__;
   DCHECK(buffer);
 
-  if (size == 0)
-    return true;
+  BitstreamConverter::AnalysisResult result;
+  result.is_conformant = false;  // Will change if needed before return.
+
+  if (size == 0) {
+    result.is_conformant = true;
+    return result;
+  }
 
   H264Parser parser;
   parser.SetEncryptedStream(buffer, size, subsamples);
@@ -205,7 +211,7 @@
           case H264NALU::kAUD:
             if (order_state > kAUDAllowed) {
               DVLOG(1) << "Unexpected AUD in order_state " << order_state;
-              return false;
+              return result;
             }
             order_state = kBeforeFirstVCL;
             break;
@@ -221,7 +227,7 @@
             if (order_state > kBeforeFirstVCL) {
               DVLOG(1) << "Unexpected NALU type " << nalu.nal_unit_type
                        << " in order_state " << order_state;
-              return false;
+              return result;
             }
             order_state = kBeforeFirstVCL;
             break;
@@ -229,7 +235,7 @@
           case H264NALU::kSPSExt:
             if (last_nalu_type != H264NALU::kSPS) {
               DVLOG(1) << "SPS extension does not follow an SPS.";
-              return false;
+              return result;
             }
             break;
 
@@ -240,22 +246,26 @@
           case H264NALU::kIDRSlice:
             if (order_state > kAfterFirstVCL) {
               DVLOG(1) << "Unexpected VCL in order_state " << order_state;
-              return false;
+              return result;
             }
+
+            if (!result.is_keyframe.has_value())
+              result.is_keyframe = nalu.nal_unit_type == H264NALU::kIDRSlice;
+
             order_state = kAfterFirstVCL;
             break;
 
           case H264NALU::kCodedSliceAux:
             if (order_state != kAfterFirstVCL) {
               DVLOG(1) << "Unexpected extension in order_state " << order_state;
-              return false;
+              return result;
             }
             break;
 
           case H264NALU::kEOSeq:
             if (order_state != kAfterFirstVCL) {
               DVLOG(1) << "Unexpected EOSeq in order_state " << order_state;
-              return false;
+              return result;
             }
             order_state = kEOStreamAllowed;
             break;
@@ -263,7 +273,7 @@
           case H264NALU::kEOStream:
             if (order_state < kAfterFirstVCL) {
               DVLOG(1) << "Unexpected EOStream in order_state " << order_state;
-              return false;
+              return result;
             }
             order_state = kNoMoreDataAllowed;
             break;
@@ -274,7 +284,7 @@
                   order_state < kEOStreamAllowed)) {
               DVLOG(1) << "Unexpected NALU type " << nalu.nal_unit_type
                        << " in order_state " << order_state;
-              return false;
+              return result;
             }
             break;
 
@@ -284,25 +294,30 @@
                 order_state != kAfterFirstVCL) {
               DVLOG(1) << "Unexpected NALU type " << nalu.nal_unit_type
                        << " in order_state " << order_state;
-              return false;
+              return result;
             }
         }
         last_nalu_type = nalu.nal_unit_type;
         break;
 
       case H264Parser::kInvalidStream:
-        return false;
+        return result;
 
       case H264Parser::kUnsupportedStream:
         NOTREACHED() << "AdvanceToNextNALU() returned kUnsupportedStream!";
-        return false;
+        return result;
 
       case H264Parser::kEOStream:
         done = true;
     }
   }
 
-  return order_state >= kAfterFirstVCL;
+  if (order_state < kAfterFirstVCL)
+    return result;
+
+  result.is_conformant = true;
+  DCHECK(result.is_keyframe.has_value());
+  return result;
 }
 
 AVCBitstreamConverter::AVCBitstreamConverter(
@@ -338,14 +353,16 @@
   return true;
 }
 
-bool AVCBitstreamConverter::IsValid(
+BitstreamConverter::AnalysisResult AVCBitstreamConverter::Analyze(
     std::vector<uint8_t>* frame_buf,
     std::vector<SubsampleEntry>* subsamples) const {
 #if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
-  if (disable_validation_)
-    return true;
+  if (disable_validation_) {
+    BitstreamConverter::AnalysisResult result;
+    return result;
+  }
 #endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
-  return AVC::IsValidAnnexB(frame_buf->data(), frame_buf->size(), *subsamples);
+  return AVC::AnalyzeAnnexB(frame_buf->data(), frame_buf->size(), *subsamples);
 }
 
 }  // namespace mp4
diff --git a/media/formats/mp4/avc.h b/media/formats/mp4/avc.h
index 655aa2f..444d219 100644
--- a/media/formats/mp4/avc.h
+++ b/media/formats/mp4/avc.h
@@ -44,14 +44,17 @@
       const AVCDecoderConfigurationRecord& avc_config,
       std::vector<uint8_t>* buffer);
 
-  // Verifies that the contents of |buffer| conform to
-  // Section 7.4.1.2.3 of ISO/IEC 14496-10.
+  // Analyzes the contents of |buffer| for conformance to Section 7.4.1.2.3 of
+  // ISO/IEC 14496-10. Also analyzes |buffer| and reports if it looks like a
+  // keyframe, if such can be determined. Determination of keyframe-ness is done
+  // only if |buffer| is conformant or if lack of conformance is detected after
+  // detecting keyframe-ness.
   // |subsamples| contains the information about what parts of the buffer are
   // encrypted and which parts are clear.
-  // Returns true if |buffer| contains conformant Annex B data
-  static bool IsValidAnnexB(const uint8_t* buffer,
-                            size_t size,
-                            const std::vector<SubsampleEntry>& subsamples);
+  static BitstreamConverter::AnalysisResult AnalyzeAnnexB(
+      const uint8_t* buffer,
+      size_t size,
+      const std::vector<SubsampleEntry>& subsamples);
 
   // Given a |buffer| and |subsamples| information and |pts| pointer into the
   // |buffer| finds the index of the subsample |ptr| is pointing into.
@@ -81,8 +84,9 @@
                     bool is_keyframe,
                     std::vector<SubsampleEntry>* subsamples) const override;
 
-  bool IsValid(std::vector<uint8_t>* frame_buf,
-               std::vector<SubsampleEntry>* subsamples) const override;
+  AnalysisResult Analyze(
+      std::vector<uint8_t>* frame_buf,
+      std::vector<SubsampleEntry>* subsamples) const override;
 
  private:
   ~AVCBitstreamConverter() override;
diff --git a/media/formats/mp4/avc_unittest.cc b/media/formats/mp4/avc_unittest.cc
index 0bca2a02..944ca1b 100644
--- a/media/formats/mp4/avc_unittest.cc
+++ b/media/formats/mp4/avc_unittest.cc
@@ -6,12 +6,16 @@
 #include <stdint.h>
 #include <string.h>
 
+#include <ostream>
+
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "media/base/decrypt_config.h"
 #include "media/base/stream_parser_buffer.h"
 #include "media/formats/mp4/avc.h"
+#include "media/formats/mp4/bitstream_converter.h"
 #include "media/formats/mp4/box_definitions.h"
 #include "media/video/h264_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -36,6 +40,15 @@
   if (name == "I")
     return H264NALU::kIDRSlice;
 
+  if (name == "SDA")
+    return H264NALU::kSliceDataA;
+
+  if (name == "SDB")
+    return H264NALU::kSliceDataB;
+
+  if (name == "SDC")
+    return H264NALU::kSliceDataC;
+
   if (name == "SEI")
     return H264NALU::kSEIMessage;
 
@@ -132,9 +145,9 @@
 // The output buffer will contain a valid-looking Annex B (it's valid-looking in
 // the sense that it has start codes and correct NALU types, but the actual NALU
 // payload is junk).
-void StringToAnnexB(const std::string& str,
-                    std::vector<uint8_t>* buffer,
-                    std::vector<SubsampleEntry>* subsamples) {
+static void StringToAnnexB(const std::string& str,
+                           std::vector<uint8_t>* buffer,
+                           std::vector<SubsampleEntry>* subsamples) {
   DCHECK(!str.empty());
 
   std::vector<std::string> subsample_specs = base::SplitString(
@@ -176,8 +189,30 @@
   }
 }
 
-std::string AnnexBToString(const std::vector<uint8_t>& buffer,
-                           const std::vector<SubsampleEntry>& subsamples) {
+// Helper to compare two results of AVC::Analyze().
+static bool AnalysesMatch(const BitstreamConverter::AnalysisResult& r1,
+                          const BitstreamConverter::AnalysisResult& r2) {
+  return r1.is_conformant == r2.is_conformant &&
+         r1.is_keyframe == r2.is_keyframe;
+}
+
+// Helper output operator, for debugging/testability.
+std::ostream& operator<<(std::ostream& os,
+                         const BitstreamConverter::AnalysisResult& r) {
+  os << "{ is_conformant: "
+     << (r.is_conformant.has_value()
+             ? (r.is_conformant.value() ? "true" : "false")
+             : "nullopt/unknown")
+     << ", is_keyframe: "
+     << (r.is_keyframe.has_value() ? (r.is_keyframe.value() ? "true" : "false")
+                                   : "nullopt/unknown")
+     << " }";
+  return os;
+}
+
+static std::string AnnexBToString(
+    const std::vector<uint8_t>& buffer,
+    const std::vector<SubsampleEntry>& subsamples) {
   std::stringstream ss;
 
   H264Parser parser;
@@ -230,7 +265,14 @@
   std::vector<SubsampleEntry> subsamples;
   MakeInputForLength(GetParam(), &buf);
   EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf, &subsamples));
-  EXPECT_TRUE(AVC::IsValidAnnexB(buf.data(), buf.size(), subsamples));
+
+  BitstreamConverter::AnalysisResult expected;
+  expected.is_conformant = true;
+  expected.is_keyframe = false;
+  EXPECT_PRED2(AnalysesMatch,
+               AVC::AnalyzeAnnexB(buf.data(), buf.size(), subsamples),
+               expected);
+
   EXPECT_EQ(buf.size(), sizeof(kExpected));
   EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected)));
   EXPECT_EQ("P,SDC", AnnexBToString(buf, subsamples));
@@ -350,63 +392,107 @@
   std::vector<uint8_t> buf;
   std::vector<SubsampleEntry> subsamples;
   StringToAnnexB(str, &buf, &subsamples);
-  EXPECT_TRUE(AVC::IsValidAnnexB(buf.data(), buf.size(), subsamples));
+
+  BitstreamConverter::AnalysisResult expected;
+  expected.is_conformant = true;
+  expected.is_keyframe = true;
+  EXPECT_PRED2(AnalysesMatch,
+               AVC::AnalyzeAnnexB(buf.data(), buf.size(), subsamples),
+               expected);
 
   EXPECT_EQ(str, AnnexBToString(buf, subsamples));
 }
 
 TEST_F(AVCConversionTest, ValidAnnexBConstructs) {
-  const char* test_cases[] = {
-    "I",
-    "I I I I",
-    "AUD I",
-    "AUD SPS PPS I",
-    "I EOSeq",
-    "I EOSeq EOStr",
-    "I EOStr",
-    "P",
-    "P P P P",
-    "AUD SPS PPS P",
-    "SEI SEI I",
-    "SEI SEI R14 I",
-    "SPS SPSExt SPS PPS I P",
-    "R14 SEI I",
-    "AUD,I",
-    "AUD,SEI I",
-    "AUD,SEI,SPS,PPS,I"
+  struct {
+    const char* case_string;
+    const bool is_keyframe;
+  } test_cases[] = {
+      {"I", true},
+      {"I I I I", true},
+      {"AUD I", true},
+      {"AUD SPS PPS I", true},
+      {"I EOSeq", true},
+      {"I EOSeq EOStr", true},
+      {"I EOStr", true},
+      {"P", false},
+      {"P P P P", false},
+      {"AUD SPS PPS P", false},
+      {"SEI SEI I", true},
+      {"SEI SEI R14 I", true},
+      {"SPS SPSExt SPS PPS I P", true},
+      {"R14 SEI I", true},
+      {"AUD,I", true},
+      {"AUD,SEI I", true},
+      {"AUD,SEI,SPS,PPS,I", true},
+
+      // In reality, these might not always be conformant/valid, but assuming
+      // they are, they're not keyframes because a non-IDR slice preceded the
+      // IDR slice, if any.
+      {"SDA SDB SDC", false},
+      {"P I", false},
+      {"SDA I", false},
+      {"SDB I", false},
+      {"SDC I", false},
   };
 
   for (size_t i = 0; i < arraysize(test_cases); ++i) {
     std::vector<uint8_t> buf;
     std::vector<SubsampleEntry> subsamples;
-    StringToAnnexB(test_cases[i], &buf, NULL);
-    EXPECT_TRUE(AVC::IsValidAnnexB(buf.data(), buf.size(), subsamples))
-        << "'" << test_cases[i] << "' failed";
+    StringToAnnexB(test_cases[i].case_string, &buf, NULL);
+
+    BitstreamConverter::AnalysisResult expected;
+    expected.is_conformant = true;
+    expected.is_keyframe = test_cases[i].is_keyframe;
+    EXPECT_PRED2(AnalysesMatch,
+                 AVC::AnalyzeAnnexB(buf.data(), buf.size(), subsamples),
+                 expected)
+        << "'" << test_cases[i].case_string << "' failed";
   }
 }
 
 TEST_F(AVCConversionTest, InvalidAnnexBConstructs) {
-  static const char* test_cases[] = {
-    "AUD",  // No VCL present.
-    "AUD,SEI", // No VCL present.
-    "SPS PPS",  // No VCL present.
-    "SPS PPS AUD I",  // Parameter sets must come after AUD.
-    "SPSExt SPS P",  // SPS must come before SPSExt.
-    "SPS PPS SPSExt P",  // SPSExt must follow an SPS.
-    "EOSeq",  // EOSeq must come after a VCL.
-    "EOStr",  // EOStr must come after a VCL.
-    "I EOStr EOSeq",  // EOSeq must come before EOStr.
-    "I R14",  // Reserved14-18 must come before first VCL.
-    "I SEI",  // SEI must come before first VCL.
-    "P SPS P", // SPS after first VCL would indicate a new access unit.
+  struct {
+    const char* case_string;
+    const base::Optional<bool> is_keyframe;
+  } test_cases[] = {
+      // For these cases, lack of conformance is determined before detecting any
+      // IDR or non-IDR slices, so the non-conformant frames' keyframe analysis
+      // reports base::nullopt (which means undetermined analysis result).
+      {"AUD", base::nullopt},            // No VCL present.
+      {"AUD,SEI", base::nullopt},        // No VCL present.
+      {"SPS PPS", base::nullopt},        // No VCL present.
+      {"SPS PPS AUD I", base::nullopt},  // Parameter sets must come after AUD.
+      {"SPSExt SPS P", base::nullopt},   // SPS must come before SPSExt.
+      {"SPS PPS SPSExt P", base::nullopt},  // SPSExt must follow an SPS.
+      {"EOSeq", base::nullopt},             // EOSeq must come after a VCL.
+      {"EOStr", base::nullopt},             // EOStr must come after a VCL.
+
+      // For these cases, IDR slice is first VCL and is detected before
+      // conformance failure, so the non-conformant frame is reported as a
+      // keyframe.
+      {"I EOStr EOSeq", true},  // EOSeq must come before EOStr.
+      {"I R14", true},          // Reserved14-18 must come before first VCL.
+      {"I SEI", true},          // SEI must come before first VCL.
+
+      // For this case, P slice is first VCL and is detected before conformance
+      // failure, so the non-conformant frame is reported as a non-keyframe.
+      {"P SPS P",
+       false},  // SPS after first VCL would indicate a new access unit.
   };
 
+  BitstreamConverter::AnalysisResult expected;
+  expected.is_conformant = false;
+
   for (size_t i = 0; i < arraysize(test_cases); ++i) {
     std::vector<uint8_t> buf;
     std::vector<SubsampleEntry> subsamples;
-    StringToAnnexB(test_cases[i], &buf, NULL);
-    EXPECT_FALSE(AVC::IsValidAnnexB(buf.data(), buf.size(), subsamples))
-        << "'" << test_cases[i] << "' failed";
+    StringToAnnexB(test_cases[i].case_string, &buf, NULL);
+    expected.is_keyframe = test_cases[i].is_keyframe;
+    EXPECT_PRED2(AnalysesMatch,
+                 AVC::AnalyzeAnnexB(buf.data(), buf.size(), subsamples),
+                 expected)
+        << "'" << test_cases[i].case_string << "' failed";
   }
 }
 
@@ -442,6 +528,10 @@
   avc_config.pps_list[0].push_back(0x56);
   avc_config.pps_list[0].push_back(0x78);
 
+  BitstreamConverter::AnalysisResult expected;
+  expected.is_conformant = true;
+  expected.is_keyframe = true;
+
   for (size_t i = 0; i < arraysize(test_cases); ++i) {
     std::vector<uint8_t> buf;
     std::vector<SubsampleEntry> subsamples;
@@ -450,7 +540,9 @@
 
     EXPECT_TRUE(AVC::InsertParamSetsAnnexB(avc_config, &buf, &subsamples))
         << "'" << test_cases[i].input << "' insert failed.";
-    EXPECT_TRUE(AVC::IsValidAnnexB(buf.data(), buf.size(), subsamples))
+    EXPECT_PRED2(AnalysesMatch,
+                 AVC::AnalyzeAnnexB(buf.data(), buf.size(), subsamples),
+                 expected)
         << "'" << test_cases[i].input << "' created invalid AnnexB.";
     EXPECT_EQ(test_cases[i].expected, AnnexBToString(buf, subsamples))
         << "'" << test_cases[i].input << "' generated unexpected output.";
diff --git a/media/formats/mp4/bitstream_converter.cc b/media/formats/mp4/bitstream_converter.cc
index e6cd01a..812f682 100644
--- a/media/formats/mp4/bitstream_converter.cc
+++ b/media/formats/mp4/bitstream_converter.cc
@@ -7,6 +7,13 @@
 namespace media {
 namespace mp4 {
 
+BitstreamConverter::AnalysisResult::AnalysisResult(){};
+
+BitstreamConverter::AnalysisResult::AnalysisResult(const AnalysisResult& other)
+    : is_conformant(other.is_conformant), is_keyframe(other.is_keyframe) {}
+
+BitstreamConverter::AnalysisResult::~AnalysisResult() = default;
+
 BitstreamConverter::~BitstreamConverter() = default;
 
 }  // namespace mp4
diff --git a/media/formats/mp4/bitstream_converter.h b/media/formats/mp4/bitstream_converter.h
index 3f95d172..5d3c360 100644
--- a/media/formats/mp4/bitstream_converter.h
+++ b/media/formats/mp4/bitstream_converter.h
@@ -10,6 +10,8 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "media/base/media_export.h"
 
 namespace media {
 
@@ -20,9 +22,21 @@
 // BitstreamConverter provides a unified interface for performing some common
 // bitstream conversions (e.g. H.264 MP4 bitstream to Annex B, and elementary
 // AAC stream to ADTS).
-class BitstreamConverter
+class MEDIA_EXPORT BitstreamConverter
     : public base::RefCountedThreadSafe<BitstreamConverter> {
  public:
+  // Describes the result of Analyze(). Not all analyses are implemented or
+  // enabled across mp4::BitstreamConverter implementations, hence the use of
+  // base::Optional<>.
+  struct MEDIA_EXPORT AnalysisResult {
+    AnalysisResult();
+    AnalysisResult(const AnalysisResult&);
+    ~AnalysisResult();
+
+    base::Optional<bool> is_conformant;
+    base::Optional<bool> is_keyframe;
+  };
+
   // Converts a single frame/buffer |frame_buf| into the output format.
   // Returns true iff the conversion was successful.
   // |frame_buf| is an input/output parameter, it contains input frame data and
@@ -37,11 +51,12 @@
                             bool is_keyframe,
                             std::vector<SubsampleEntry>* subsamples) const = 0;
 
-  // Checks a converted frame for conformance.
-  // Note: may return true even if the frame is not conformant; the checks may
-  // not be exhaustive (or implemented at all).
-  virtual bool IsValid(std::vector<uint8_t>* frame_buf,
-                       std::vector<SubsampleEntry>* subsamples) const = 0;
+  // Inspects an already converted frame for conformance. If conformant,
+  // inspects further to see if the converted frame appears to be a keyframe.
+  // Note, the checks may not be exhaustive (or implemented at all).
+  virtual AnalysisResult Analyze(
+      std::vector<uint8_t>* frame_buf,
+      std::vector<SubsampleEntry>* subsamples) const = 0;
 
  protected:
   friend class base::RefCountedThreadSafe<BitstreamConverter>;
diff --git a/media/formats/mp4/hevc.cc b/media/formats/mp4/hevc.cc
index ecf813d..8e8c8ea 100644
--- a/media/formats/mp4/hevc.cc
+++ b/media/formats/mp4/hevc.cc
@@ -137,11 +137,13 @@
 static const uint8_t kAnnexBStartCode[] = {0, 0, 0, 1};
 static const int kAnnexBStartCodeSize = 4;
 
+// static
 bool HEVC::InsertParamSetsAnnexB(
     const HEVCDecoderConfigurationRecord& hevc_config,
     std::vector<uint8_t>* buffer,
     std::vector<SubsampleEntry>* subsamples) {
-  DCHECK(HEVC::IsValidAnnexB(buffer->data(), buffer->size(), *subsamples));
+  DCHECK(HEVC::AnalyzeAnnexB(buffer->data(), buffer->size(), *subsamples)
+             .is_conformant.value_or(true));
 
   std::unique_ptr<H265Parser> parser(new H265Parser());
   const uint8_t* start = buffer->data();
@@ -179,10 +181,12 @@
   buffer->insert(config_insert_point,
                  param_sets.begin(), param_sets.end());
 
-  DCHECK(HEVC::IsValidAnnexB(buffer->data(), buffer->size(), *subsamples));
+  DCHECK(HEVC::AnalyzeAnnexB(buffer->data(), buffer->size(), *subsamples)
+             .is_conformant.value_or(true));
   return true;
 }
 
+// static
 bool HEVC::ConvertConfigToAnnexB(
     const HEVCDecoderConfigurationRecord& hevc_config,
     std::vector<uint8_t>* buffer) {
@@ -204,17 +208,23 @@
   return true;
 }
 
-// Verifies AnnexB NALU order according to section 7.4.2.4.4 of ISO/IEC 23008-2.
-bool HEVC::IsValidAnnexB(const uint8_t* buffer,
-                         size_t size,
-                         const std::vector<SubsampleEntry>& subsamples) {
+// static
+BitstreamConverter::AnalysisResult HEVC::AnalyzeAnnexB(
+    const uint8_t* buffer,
+    size_t size,
+    const std::vector<SubsampleEntry>& subsamples) {
   DCHECK(buffer);
 
-  if (size == 0)
-    return true;
+  BitstreamConverter::AnalysisResult result;
 
-  // TODO(servolk): Implement this, see crbug.com/527595
-  return true;
+  if (size == 0) {
+    result.is_conformant = true;
+    return result;
+  }
+
+  // TODO(servolk): Implement this, see https://crbug.com/527595. For now, we
+  // report that neither conformance nor keyframe analyses were performed.
+  return result;
 }
 
 HEVCBitstreamConverter::HEVCBitstreamConverter(
@@ -243,10 +253,10 @@
   return true;
 }
 
-bool HEVCBitstreamConverter::IsValid(
+BitstreamConverter::AnalysisResult HEVCBitstreamConverter::Analyze(
     std::vector<uint8_t>* frame_buf,
     std::vector<SubsampleEntry>* subsamples) const {
-  return HEVC::IsValidAnnexB(frame_buf->data(), frame_buf->size(), *subsamples);
+  return HEVC::AnalyzeAnnexB(frame_buf->data(), frame_buf->size(), *subsamples);
 }
 
 }  // namespace mp4
diff --git a/media/formats/mp4/hevc.h b/media/formats/mp4/hevc.h
index f3576f9..87ec4ff 100644
--- a/media/formats/mp4/hevc.h
+++ b/media/formats/mp4/hevc.h
@@ -78,14 +78,15 @@
       std::vector<uint8_t>* buffer,
       std::vector<SubsampleEntry>* subsamples);
 
-  // Verifies that the contents of |buffer| conform to
-  // Section 7.4.2.4.4 of ISO/IEC 23008-2.
+  // Analyzes the contents of |buffer| for conformance to
+  // Section 7.4.2.4.4 of ISO/IEC 23008-2, and if conformant, further inspects
+  // |buffer| to report whether or not it looks like a keyframe.
   // |subsamples| contains the information about what parts of the buffer are
   // encrypted and which parts are clear.
-  // Returns true if |buffer| contains conformant Annex B data
-  static bool IsValidAnnexB(const uint8_t* buffer,
-                            size_t size,
-                            const std::vector<SubsampleEntry>& subsamples);
+  static BitstreamConverter::AnalysisResult AnalyzeAnnexB(
+      const uint8_t* buffer,
+      size_t size,
+      const std::vector<SubsampleEntry>& subsamples);
 };
 
 class HEVCBitstreamConverter : public BitstreamConverter {
@@ -98,8 +99,9 @@
                     bool is_keyframe,
                     std::vector<SubsampleEntry>* subsamples) const override;
 
-  bool IsValid(std::vector<uint8_t>* frame_buf,
-               std::vector<SubsampleEntry>* subsamples) const override;
+  AnalysisResult Analyze(
+      std::vector<uint8_t>* frame_buf,
+      std::vector<SubsampleEntry>* subsamples) const override;
 
  private:
   ~HEVCBitstreamConverter() override;
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index dc7645a..3517906 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -41,6 +41,7 @@
 
 const int kMaxEmptySampleLogs = 20;
 const int kMaxInvalidConversionLogs = 20;
+const int kMaxVideoKeyframeMismatchLogs = 10;
 
 // Caller should be prepared to handle return of Unencrypted() in case of
 // unsupported scheme.
@@ -90,8 +91,8 @@
       has_sbr_(has_sbr),
       has_flac_(has_flac),
       num_empty_samples_skipped_(0),
-      num_invalid_conversions_(0) {
-}
+      num_invalid_conversions_(0),
+      num_video_keyframe_mismatches_(0) {}
 
 MP4StreamParser::~MP4StreamParser() = default;
 
@@ -802,12 +803,31 @@
             << "Failed to prepare video sample for decode";
         return ParseResult::kError;
       }
-      if (!runs_->video_description().frame_bitstream_converter->IsValid(
-              &frame_buf, &subsamples)) {
+      BitstreamConverter::AnalysisResult analysis =
+          runs_->video_description().frame_bitstream_converter->Analyze(
+              &frame_buf, &subsamples);
+      // If conformance analysis was not actually performed, assume the frame is
+      // conformant.  If it was performed and found to be non-conformant, log
+      // it.
+      if (!analysis.is_conformant.value_or(true)) {
         LIMITED_MEDIA_LOG(DEBUG, media_log_, num_invalid_conversions_,
                           kMaxInvalidConversionLogs)
             << "Prepared video sample is not conformant";
       }
+
+      // Use |analysis.is_keyframe|, if it was actually determined, for logging
+      // if the analysis mismatches the container's keyframe metadata for
+      // |frame_buf|.
+      if (analysis.is_keyframe.has_value() &&
+          runs_->is_keyframe() != analysis.is_keyframe.value()) {
+        LIMITED_MEDIA_LOG(DEBUG, media_log_, num_video_keyframe_mismatches_,
+                          kMaxVideoKeyframeMismatchLogs)
+            << "ISO-BMFF container metadata for video frame indicates that the "
+               "frame is "
+            << (runs_->is_keyframe() ? "" : "not ")
+            << "a keyframe, but the video frame contents indicate the "
+               "opposite.";
+      }
     }
   }
 
diff --git a/media/formats/mp4/mp4_stream_parser.h b/media/formats/mp4/mp4_stream_parser.h
index 45ee6f9..909bf55 100644
--- a/media/formats/mp4/mp4_stream_parser.h
+++ b/media/formats/mp4/mp4_stream_parser.h
@@ -148,6 +148,9 @@
   // Tracks the number of MEDIA_LOGS for invalid bitstream conversion.
   int num_invalid_conversions_;
 
+  // Tracks the number of MEDIA_LOGS for video keyframe MP4<->frame mismatch.
+  int num_video_keyframe_mismatches_;
+
   DISALLOW_COPY_AND_ASSIGN(MP4StreamParser);
 };
 
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc
index 5ce3fd8..ccb0800 100644
--- a/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -50,7 +50,11 @@
 }
 
 MATCHER_P(ErrorLog, error_string, "") {
-  return CONTAINS_STRING(arg, error_string);
+  return CONTAINS_STRING(arg, error_string) && CONTAINS_STRING(arg, "error");
+}
+
+MATCHER_P(DebugLog, debug_string, "") {
+  return CONTAINS_STRING(arg, debug_string) && CONTAINS_STRING(arg, "debug");
 }
 
 class MP4StreamParserTest : public testing::Test {
@@ -287,6 +291,51 @@
       512);
 }
 
+TEST_F(MP4StreamParserTest, AVC_KeyAndNonKeyframeness_Match_Container) {
+  // Both AVC video frames' keyframe-ness metadata matches the MP4:
+  // Frame 0: AVC IDR, trun.first_sample_flags: sync sample that doesn't
+  //          depend on others.
+  // Frame 1: AVC Non-IDR, tfhd.default_sample_flags: not sync sample, depends
+  //          on others.
+  // This is the base case; see also the "Mismatches" cases, below.
+  auto params = GetDefaultInitParametersExpectations();
+  params.detected_audio_track_count = 0;
+  InitializeParserWithInitParametersExpectations(params);
+  ParseMP4File("bear-640x360-v-2frames_frag.mp4", 512);
+}
+
+TEST_F(MP4StreamParserTest, AVC_Keyframeness_Mismatches_Container) {
+  // The first AVC video frame's keyframe-ness metadata matches the MP4:
+  // Frame 0: AVC IDR, trun.first_sample_flags: NOT sync sample, DEPENDS on
+  //          others.
+  // Frame 1: AVC Non-IDR, tfhd.default_sample_flags: not sync sample, depends
+  //          on others.
+  auto params = GetDefaultInitParametersExpectations();
+  params.detected_audio_track_count = 0;
+  InitializeParserWithInitParametersExpectations(params);
+  EXPECT_MEDIA_LOG(DebugLog(
+      "ISO-BMFF container metadata for video frame indicates that the frame is "
+      "not a keyframe, but the video frame contents indicate the opposite."));
+  ParseMP4File("bear-640x360-v-2frames-keyframe-is-non-sync-sample_frag.mp4",
+               512);
+}
+
+TEST_F(MP4StreamParserTest, AVC_NonKeyframeness_Mismatches_Container) {
+  // The second AVC video frame's keyframe-ness metadata matches the MP4:
+  // Frame 0: AVC IDR, trun.first_sample_flags: sync sample that doesn't
+  //          depend on others.
+  // Frame 1: AVC Non-IDR, tfhd.default_sample_flags: SYNC sample, DOES NOT
+  //          depend on others.
+  auto params = GetDefaultInitParametersExpectations();
+  params.detected_audio_track_count = 0;
+  InitializeParserWithInitParametersExpectations(params);
+  EXPECT_MEDIA_LOG(DebugLog(
+      "ISO-BMFF container metadata for video frame indicates that the frame is "
+      "a keyframe, but the video frame contents indicate the opposite."));
+  ParseMP4File("bear-640x360-v-2frames-nonkeyframe-is-sync-sample_frag.mp4",
+               512);
+}
+
 TEST_F(MP4StreamParserTest, MPEG2_AAC_LC) {
   InSequence s;
   std::set<int> audio_object_types;
diff --git a/media/test/data/README b/media/test/data/README
index 2568798..4347682 100644
--- a/media/test/data/README
+++ b/media/test/data/README
@@ -74,6 +74,20 @@
 vorbis-packet-2  - timestamp: 0ms, duration: 0ms
 vorbis-packet-3  - timestamp: 2902ms, duration: 0ms
 
+// MSE MP4 keyframe-metadata versus encoded AVC keyframe-ness test media:
+bear-640x360-v-2frames_frag.mp4 - Just first 2 video frames of bear-640x360-v_frag.mp4, created with:
+  ffmpeg -i bear-640x360-v_frag.mp4 -vcodec copy -movflags frag_keyframe+empty_moov+default_base_moof \
+    -vframes 2 bear-640x360-v-2frames_frag.mp4
+  It's 1 keyframe + 1 non-keyframe, with container's frame keyframe-ness correct.
+bear-640x360-v-2frames-keyframe-is-non-sync-sample_frag.mp4
+  This is bear-640x360-v-2frames_frag.mp4, with manually updated trun.first_sample_flags:
+    s/0x02000000/0x01010000 (first frame is non-sync-sample, depends on another
+    frame, mismatches compressed h264 first frame's keyframe-ness).
+bear-640x360-v-2frames-nonkeyframe-is-sync-sample_frag.mp4
+  This is bear-640x360-v-2frames_frag.mp4, with manually updated tfhd.default_sample_flags:
+    s/0x01010000/0x02000000 (second frame is sync-sample, doesn't depend on other
+    frames, mismatches compressed h264 second frame's nonkeyframe-ness).
+
 // 10-bit test file(s)
 bear-320x180-hi10p.mp4
 bear-320x240-vp9_profile2.webm - VP9 encoded video with profile 2 (10-bit, 4:2:0). Codec string: vp09.02.10.10.01.02.02.02.00.
diff --git a/media/test/data/bear-640x360-v-2frames-keyframe-is-non-sync-sample_frag.mp4 b/media/test/data/bear-640x360-v-2frames-keyframe-is-non-sync-sample_frag.mp4
new file mode 100644
index 0000000..dac67566
--- /dev/null
+++ b/media/test/data/bear-640x360-v-2frames-keyframe-is-non-sync-sample_frag.mp4
Binary files differ
diff --git a/media/test/data/bear-640x360-v-2frames-nonkeyframe-is-sync-sample_frag.mp4 b/media/test/data/bear-640x360-v-2frames-nonkeyframe-is-sync-sample_frag.mp4
new file mode 100644
index 0000000..3dddd57
--- /dev/null
+++ b/media/test/data/bear-640x360-v-2frames-nonkeyframe-is-sync-sample_frag.mp4
Binary files differ
diff --git a/media/test/data/bear-640x360-v-2frames_frag.mp4 b/media/test/data/bear-640x360-v-2frames_frag.mp4
new file mode 100644
index 0000000..3d21750
--- /dev/null
+++ b/media/test/data/bear-640x360-v-2frames_frag.mp4
Binary files differ
diff --git a/net/BUILD.gn b/net/BUILD.gn
index aa90b645..1d4e113 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1561,20 +1561,14 @@
       "third_party/quic/platform/impl/quic_url_impl.h",
       "third_party/quic/platform/impl/quic_url_utils_impl.cc",
       "third_party/quic/platform/impl/quic_url_utils_impl.h",
-      "third_party/quic/quartc/quartc_clock_interface.h",
       "third_party/quic/quartc/quartc_factory.cc",
       "third_party/quic/quartc/quartc_factory.h",
-      "third_party/quic/quartc/quartc_factory_interface.cc",
-      "third_party/quic/quartc/quartc_factory_interface.h",
       "third_party/quic/quartc/quartc_packet_writer.cc",
       "third_party/quic/quartc/quartc_packet_writer.h",
       "third_party/quic/quartc/quartc_session.cc",
       "third_party/quic/quartc/quartc_session.h",
-      "third_party/quic/quartc/quartc_session_interface.h",
       "third_party/quic/quartc/quartc_stream.cc",
       "third_party/quic/quartc/quartc_stream.h",
-      "third_party/quic/quartc/quartc_stream_interface.h",
-      "third_party/quic/quartc/quartc_task_runner_interface.h",
       "third_party/spdy/core/fuzzing/hpack_fuzz_util.cc",
       "third_party/spdy/core/fuzzing/hpack_fuzz_util.h",
       "third_party/spdy/core/hpack/hpack_constants.cc",
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index cbf36fe..c261f55 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -54576,6 +54576,8 @@
     { "name": "www.zdnet.com", "policy": "custom", "mode": "force-https", "include_subdomains": true },
     { "name": "downloads.zdnet.com", "policy": "custom", "mode": "force-https", "include_subdomains": true },
     { "name": "www.techrepublic.com", "policy": "custom", "mode": "force-https", "include_subdomains": true },
+    { "name": "aka.ms", "policy": "custom", "mode": "force-https", "include_subdomains": true },
+    { "name": "go.microsoft.com", "policy": "custom", "mode": "force-https", "include_subdomains": true },
     // IP Address
     { "name": "1.0.0.1", "policy": "custom", "mode": "force-https", "include_subdomains": false },
     // No subdomains
diff --git a/net/network_error_logging/network_error_logging_service.cc b/net/network_error_logging/network_error_logging_service.cc
index 1a95e58..2b39fad 100644
--- a/net/network_error_logging/network_error_logging_service.cc
+++ b/net/network_error_logging/network_error_logging_service.cc
@@ -203,6 +203,7 @@
     }
 
     OriginPolicy policy;
+    policy.origin = origin;
     policy.received_ip_address = received_ip_address;
     HeaderOutcome outcome =
         ParseHeader(value, tick_clock_->NowTicks(), &policy);
@@ -239,8 +240,8 @@
       return;
     }
 
-    const OriginPolicy* policy =
-        FindPolicyForOrigin(url::Origin::Create(details.uri));
+    auto report_origin = url::Origin::Create(details.uri);
+    const OriginPolicy* policy = FindPolicyForOrigin(report_origin);
     if (!policy) {
       RecordRequestOutcome(RequestOutcome::DISCARDED_NO_ORIGIN_POLICY);
       return;
@@ -291,7 +292,8 @@
 
     // include_subdomains policies are only allowed to report on DNS resolution
     // errors.
-    if (phase_string != kDnsPhase && policy->include_subdomains) {
+    if (phase_string != kDnsPhase && policy->include_subdomains &&
+        !(policy->origin == report_origin)) {
       RecordRequestOutcome(RequestOutcome::DISCARDED_NON_DNS_SUBDOMAIN_REPORT);
       return;
     }
@@ -370,6 +372,7 @@
  private:
   // NEL Policy set by an origin.
   struct OriginPolicy {
+    url::Origin origin;
     IPAddress received_ip_address;
 
     // Reporting API endpoint group to which reports should be sent.
diff --git a/net/network_error_logging/network_error_logging_service_unittest.cc b/net/network_error_logging/network_error_logging_service_unittest.cc
index da785393..3f542c0a2e 100644
--- a/net/network_error_logging/network_error_logging_service_unittest.cc
+++ b/net/network_error_logging/network_error_logging_service_unittest.cc
@@ -718,6 +718,19 @@
   EXPECT_TRUE(reports().empty());
 }
 
+TEST_F(NetworkErrorLoggingServiceTest,
+       IncludeSubdomainsReportsSameOriginSuccess) {
+  static const std::string kHeaderIncludeSubdomainsSuccess1 =
+      "{\"report_to\":\"group\",\"max_age\":86400,"
+      "\"include_subdomains\":true,\"success_fraction\":1.0}";
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomainsSuccess1);
+
+  service()->OnRequest(MakeRequestDetails(kUrl_, OK));
+
+  ASSERT_EQ(1u, reports().size());
+  EXPECT_EQ(kUrl_, reports()[0].url);
+}
+
 TEST_F(NetworkErrorLoggingServiceTest, RemoveAllBrowsingData) {
   service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
diff --git a/net/third_party/quic/platform/api/quic_mutex.h b/net/third_party/quic/platform/api/quic_mutex.h
index 3637ce59..d1defb395 100644
--- a/net/third_party/quic/platform/api/quic_mutex.h
+++ b/net/third_party/quic/platform/api/quic_mutex.h
@@ -66,6 +66,24 @@
   QuicMutex* const lock_;
 };
 
+// A Notification allows threads to receive notification of a single occurrence
+// of a single event.
+class QUIC_EXPORT_PRIVATE QuicNotification {
+ public:
+  QuicNotification() = default;
+  QuicNotification(const QuicNotification&) = delete;
+  QuicNotification& operator=(const QuicNotification&) = delete;
+
+  bool HasBeenNotified() { return impl_.HasBeenNotified(); }
+
+  void Notify() { impl_.Notify(); }
+
+  void WaitForNotification() { impl_.WaitForNotification(); }
+
+ private:
+  QuicNotificationImpl impl_;
+};
+
 }  // namespace quic
 
 #endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_MUTEX_H_
diff --git a/net/third_party/quic/platform/api/quic_thread.h b/net/third_party/quic/platform/api/quic_thread.h
new file mode 100644
index 0000000..df89ee7f
--- /dev/null
+++ b/net/third_party/quic/platform/api/quic_thread.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2018 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 NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_THREAD_H_
+#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_THREAD_H_
+
+#include "net/third_party/quic/platform/api/quic_string.h"
+#include "net/third_party/quic/platform/impl/quic_thread_impl.h"
+
+namespace quic {
+
+// A class representing a thread of execution in QUIC.
+class QuicThread : public QuicThreadImpl {
+ public:
+  QuicThread(const QuicString& string) : QuicThreadImpl(string) {}
+  QuicThread(const QuicThread&) = delete;
+  QuicThread& operator=(const QuicThread&) = delete;
+
+  // Impl defines a virtual void Run() method which subclasses
+  // must implement.
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_THREAD_H_
diff --git a/net/third_party/quic/platform/impl/quic_mutex_impl.h b/net/third_party/quic/platform/impl/quic_mutex_impl.h
index b396cbe7..2fdac4dd 100644
--- a/net/third_party/quic/platform/impl/quic_mutex_impl.h
+++ b/net/third_party/quic/platform/impl/quic_mutex_impl.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 
 #ifndef EXCLUSIVE_LOCK_FUNCTION
@@ -73,6 +74,26 @@
   DISALLOW_COPY_AND_ASSIGN(QuicLockImpl);
 };
 
+// A Notification allows threads to receive notification of a single occurrence
+// of a single event.
+class QUIC_EXPORT_PRIVATE QuicNotificationImpl {
+ public:
+  QuicNotificationImpl()
+      : event_(base::WaitableEvent::ResetPolicy::MANUAL,
+               base::WaitableEvent::InitialState::NOT_SIGNALED) {}
+  QuicNotificationImpl(const QuicNotificationImpl&) = delete;
+  QuicNotificationImpl& operator=(const QuicNotificationImpl&) = delete;
+
+  bool HasBeenNotified() { return event_.IsSignaled(); }
+
+  void Notify() { event_.Signal(); }
+
+  void WaitForNotification() { event_.Wait(); }
+
+ private:
+  base::WaitableEvent event_;
+};
+
 }  // namespace quic
 
 #endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_MUTEX_IMPL_H_
diff --git a/net/third_party/quic/platform/impl/quic_thread_impl.h b/net/third_party/quic/platform/impl/quic_thread_impl.h
new file mode 100644
index 0000000..a8e1f9a
--- /dev/null
+++ b/net/third_party/quic/platform/impl/quic_thread_impl.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2018 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 NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_THREAD_IMPL_H_
+#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_THREAD_IMPL_H_
+
+#include "base/threading/simple_thread.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
+
+namespace quic {
+
+// A class representing a thread of execution in QUIC.
+class QuicThreadImpl : public base::SimpleThread {
+ public:
+  QuicThreadImpl(const QuicString& string) : base::SimpleThread(string) {}
+  QuicThreadImpl(const QuicThreadImpl&) = delete;
+  QuicThreadImpl& operator=(const QuicThreadImpl&) = delete;
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_THREAD_IMPL_H_
diff --git a/net/third_party/quic/quartc/quartc_clock_interface.h b/net/third_party/quic/quartc/quartc_clock_interface.h
deleted file mode 100644
index 53366d70..0000000
--- a/net/third_party/quic/quartc/quartc_clock_interface.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_CLOCK_INTERFACE_H_
-#define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_CLOCK_INTERFACE_H_
-
-#include <stdint.h>
-
-namespace quic {
-
-// Implemented by the Quartc API user to provide a timebase.
-class QuartcClockInterface {
- public:
-  virtual ~QuartcClockInterface() {}
-  virtual int64_t NowMicroseconds() = 0;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_CLOCK_INTERFACE_H_
diff --git a/net/third_party/quic/quartc/quartc_factory.cc b/net/third_party/quic/quartc/quartc_factory.cc
index 099808c..e5e0287e 100644
--- a/net/third_party/quic/quartc/quartc_factory.cc
+++ b/net/third_party/quic/quartc/quartc_factory.cc
@@ -11,106 +11,17 @@
 
 namespace quic {
 
-namespace {
-
-// Implements the QuicAlarm with QuartcTaskRunnerInterface for the Quartc
-//  users other than Chromium. For example, WebRTC will create QuartcAlarm with
-// a QuartcTaskRunner implemented by WebRTC.
-class QuartcAlarm : public QuicAlarm, public QuartcTaskRunnerInterface::Task {
- public:
-  QuartcAlarm(const QuicClock* clock,
-              QuartcTaskRunnerInterface* task_runner,
-              QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
-      : QuicAlarm(std::move(delegate)),
-        clock_(clock),
-        task_runner_(task_runner) {}
-
-  ~QuartcAlarm() override {
-    // Cancel the scheduled task before getting deleted.
-    CancelImpl();
-  }
-
-  // QuicAlarm overrides.
-  void SetImpl() override {
-    DCHECK(deadline().IsInitialized());
-    // Cancel it if already set.
-    CancelImpl();
-
-    int64_t delay_ms = (deadline() - (clock_->Now())).ToMilliseconds();
-    if (delay_ms < 0) {
-      delay_ms = 0;
-    }
-
-    DCHECK(task_runner_);
-    DCHECK(!scheduled_task_);
-    scheduled_task_ = task_runner_->Schedule(this, delay_ms);
-  }
-
-  void CancelImpl() override {
-    if (scheduled_task_) {
-      scheduled_task_->Cancel();
-      scheduled_task_.reset();
-    }
-  }
-
-  // QuartcTaskRunner::Task overrides.
-  void Run() override {
-    // The alarm may have been cancelled.
-    if (!deadline().IsInitialized()) {
-      return;
-    }
-
-    // The alarm may have been re-set to a later time.
-    if (clock_->Now() < deadline()) {
-      SetImpl();
-      return;
-    }
-
-    Fire();
-  }
-
- private:
-  // Not owned by QuartcAlarm. Owned by the QuartcFactory.
-  const QuicClock* clock_;
-  // Not owned by QuartcAlarm. Owned by the QuartcFactory.
-  QuartcTaskRunnerInterface* task_runner_;
-  // Owned by QuartcAlarm.
-  std::unique_ptr<QuartcTaskRunnerInterface::ScheduledTask> scheduled_task_;
-};
-
-// Adapts QuartcClockInterface (provided by the user) to QuicClock
-// (expected by QUIC).
-class QuartcClock : public QuicClock {
- public:
-  explicit QuartcClock(QuartcClockInterface* clock) : clock_(clock) {}
-  QuicTime ApproximateNow() const override { return Now(); }
-  QuicTime Now() const override {
-    return QuicTime::Zero() +
-           QuicTime::Delta::FromMicroseconds(clock_->NowMicroseconds());
-  }
-  QuicWallTime WallNow() const override {
-    return QuicWallTime::FromUNIXMicroseconds(clock_->NowMicroseconds());
-  }
-
- private:
-  QuartcClockInterface* clock_;
-};
-
-}  // namespace
-
 QuartcFactory::QuartcFactory(const QuartcFactoryConfig& factory_config)
-    : task_runner_(factory_config.task_runner),
-      clock_(new QuartcClock(factory_config.clock)) {}
+    : alarm_factory_(factory_config.alarm_factory),
+      clock_(factory_config.clock) {}
 
 QuartcFactory::~QuartcFactory() {}
 
-std::unique_ptr<QuartcSessionInterface> QuartcFactory::CreateQuartcSession(
+std::unique_ptr<QuartcSession> QuartcFactory::CreateQuartcSession(
     const QuartcSessionConfig& quartc_session_config) {
   DCHECK(quartc_session_config.packet_transport);
 
-  Perspective perspective = quartc_session_config.is_server
-                                ? Perspective::IS_SERVER
-                                : Perspective::IS_CLIENT;
+  Perspective perspective = quartc_session_config.perspective;
 
   // QuartcSession will eventually own both |writer| and |quic_connection|.
   auto writer =
@@ -135,53 +46,50 @@
   // Enable time-based loss detection.
   copt.push_back(kTIME);
 
-  if (quartc_session_config.congestion_control ==
-      QuartcCongestionControl::kBBR) {
-    copt.push_back(kTBBR);
+  copt.push_back(kTBBR);
 
-    // Note: These settings have no effect for Exoblaze builds since
-    // SetQuicReloadableFlag() gets stubbed out.
-    SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true);
-    SetQuicReloadableFlag(quic_unified_iw_options, true);
-    for (const auto option : quartc_session_config.bbr_options) {
-      switch (option) {
-        case (QuartcBbrOptions::kSlowerStartup):
-          copt.push_back(kBBRS);
-          break;
-        case (QuartcBbrOptions::kFullyDrainQueue):
-          copt.push_back(kBBR3);
-          break;
-        case (QuartcBbrOptions::kReduceProbeRtt):
-          copt.push_back(kBBR6);
-          break;
-        case (QuartcBbrOptions::kSkipProbeRtt):
-          copt.push_back(kBBR7);
-          break;
-        case (QuartcBbrOptions::kSkipProbeRttAggressively):
-          copt.push_back(kBBR8);
-          break;
-        case (QuartcBbrOptions::kFillUpLinkDuringProbing):
-          quic_connection->set_fill_up_link_during_probing(true);
-          break;
-        case (QuartcBbrOptions::kInitialWindow3):
-          copt.push_back(kIW03);
-          break;
-        case (QuartcBbrOptions::kInitialWindow10):
-          copt.push_back(kIW10);
-          break;
-        case (QuartcBbrOptions::kInitialWindow20):
-          copt.push_back(kIW20);
-          break;
-        case (QuartcBbrOptions::kInitialWindow50):
-          copt.push_back(kIW50);
-          break;
-        case (QuartcBbrOptions::kStartup1RTT):
-          copt.push_back(k1RTT);
-          break;
-        case (QuartcBbrOptions::kStartup2RTT):
-          copt.push_back(k2RTT);
-          break;
-      }
+  // Note: These settings have no effect for Exoblaze builds since
+  // SetQuicReloadableFlag() gets stubbed out.
+  SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true);
+  SetQuicReloadableFlag(quic_unified_iw_options, true);
+  for (const auto option : quartc_session_config.bbr_options) {
+    switch (option) {
+      case (QuartcBbrOptions::kSlowerStartup):
+        copt.push_back(kBBRS);
+        break;
+      case (QuartcBbrOptions::kFullyDrainQueue):
+        copt.push_back(kBBR3);
+        break;
+      case (QuartcBbrOptions::kReduceProbeRtt):
+        copt.push_back(kBBR6);
+        break;
+      case (QuartcBbrOptions::kSkipProbeRtt):
+        copt.push_back(kBBR7);
+        break;
+      case (QuartcBbrOptions::kSkipProbeRttAggressively):
+        copt.push_back(kBBR8);
+        break;
+      case (QuartcBbrOptions::kFillUpLinkDuringProbing):
+        quic_connection->set_fill_up_link_during_probing(true);
+        break;
+      case (QuartcBbrOptions::kInitialWindow3):
+        copt.push_back(kIW03);
+        break;
+      case (QuartcBbrOptions::kInitialWindow10):
+        copt.push_back(kIW10);
+        break;
+      case (QuartcBbrOptions::kInitialWindow20):
+        copt.push_back(kIW20);
+        break;
+      case (QuartcBbrOptions::kInitialWindow50):
+        copt.push_back(kIW50);
+        break;
+      case (QuartcBbrOptions::kStartup1RTT):
+        copt.push_back(k1RTT);
+        break;
+      case (QuartcBbrOptions::kStartup2RTT):
+        copt.push_back(k2RTT);
+        break;
     }
   }
   QuicConfig quic_config;
@@ -202,15 +110,15 @@
       kStreamReceiveWindowLimit);
   quic_config.SetConnectionOptionsToSend(copt);
   quic_config.SetClientConnectionOptions(copt);
-  if (quartc_session_config.max_time_before_crypto_handshake_secs > 0) {
+  if (quartc_session_config.max_time_before_crypto_handshake >
+      QuicTime::Delta::Zero()) {
     quic_config.set_max_time_before_crypto_handshake(
-        QuicTime::Delta::FromSeconds(
-            quartc_session_config.max_time_before_crypto_handshake_secs));
+        quartc_session_config.max_time_before_crypto_handshake);
   }
-  if (quartc_session_config.max_idle_time_before_crypto_handshake_secs > 0) {
+  if (quartc_session_config.max_idle_time_before_crypto_handshake >
+      QuicTime::Delta::Zero()) {
     quic_config.set_max_idle_time_before_crypto_handshake(
-        QuicTime::Delta::FromSeconds(
-            quartc_session_config.max_idle_time_before_crypto_handshake_secs));
+        quartc_session_config.max_idle_time_before_crypto_handshake);
   }
   if (quartc_session_config.idle_network_timeout > QuicTime::Delta::Zero()) {
     quic_config.SetIdleNetworkTimeout(
@@ -224,7 +132,7 @@
   return QuicMakeUnique<QuartcSession>(
       std::move(quic_connection), quic_config,
       quartc_session_config.unique_remote_server_id, perspective,
-      this /*QuicConnectionHelperInterface*/, clock_.get(), std::move(writer));
+      this /*QuicConnectionHelperInterface*/, clock_, std::move(writer));
 }
 
 std::unique_ptr<QuicConnection> QuartcFactory::CreateQuicConnection(
@@ -236,28 +144,12 @@
   QuicSocketAddress dummy_address(QuicIpAddress::Any4(), 0 /*Port*/);
   return QuicMakeUnique<QuicConnection>(
       dummy_id, dummy_address, this, /*QuicConnectionHelperInterface*/
-      this /*QuicAlarmFactory*/, packet_writer, /*owns_writer=*/false,
+      alarm_factory_ /*QuicAlarmFactory*/, packet_writer, /*owns_writer=*/false,
       perspective, CurrentSupportedVersions());
 }
 
-QuicAlarm* QuartcFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
-  return new QuartcAlarm(GetClock(), task_runner_,
-                         QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
-}
-
-QuicArenaScopedPtr<QuicAlarm> QuartcFactory::CreateAlarm(
-    QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
-    QuicConnectionArena* arena) {
-  if (arena != nullptr) {
-    return arena->New<QuartcAlarm>(GetClock(), task_runner_,
-                                   std::move(delegate));
-  }
-  return QuicArenaScopedPtr<QuicAlarm>(
-      new QuartcAlarm(GetClock(), task_runner_, std::move(delegate)));
-}
-
 const QuicClock* QuartcFactory::GetClock() const {
-  return clock_.get();
+  return clock_;
 }
 
 QuicRandom* QuartcFactory::GetRandomGenerator() {
@@ -268,10 +160,9 @@
   return &buffer_allocator_;
 }
 
-std::unique_ptr<QuartcFactoryInterface> CreateQuartcFactory(
+std::unique_ptr<QuartcFactory> CreateQuartcFactory(
     const QuartcFactoryConfig& factory_config) {
-  return std::unique_ptr<QuartcFactoryInterface>(
-      new QuartcFactory(factory_config));
+  return std::unique_ptr<QuartcFactory>(new QuartcFactory(factory_config));
 }
 
 }  // namespace quic
diff --git a/net/third_party/quic/quartc/quartc_factory.h b/net/third_party/quic/quartc/quartc_factory.h
index f56c2499..8db0340 100644
--- a/net/third_party/quic/quartc/quartc_factory.h
+++ b/net/third_party/quic/quartc/quartc_factory.h
@@ -8,34 +8,83 @@
 #include "net/third_party/quic/core/quic_alarm_factory.h"
 #include "net/third_party/quic/core/quic_connection.h"
 #include "net/third_party/quic/core/quic_simple_buffer_allocator.h"
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/quartc/quartc_factory_interface.h"
 #include "net/third_party/quic/quartc/quartc_packet_writer.h"
-#include "net/third_party/quic/quartc/quartc_task_runner_interface.h"
+#include "net/third_party/quic/quartc/quartc_session.h"
 
 namespace quic {
 
-// Implements the QuartcFactoryInterface to create the instances of
-// QuartcSessionInterface. Implements the QuicAlarmFactory to create alarms
-// using the QuartcTaskRunner. Implements the QuicConnectionHelperInterface used
-// by the QuicConnections. Only one QuartcFactory is expected to be created.
-class QUIC_EXPORT_PRIVATE QuartcFactory : public QuartcFactoryInterface,
-                                          public QuicAlarmFactory,
-                                          public QuicConnectionHelperInterface {
+// Options that control the BBR algorithm.
+enum class QuartcBbrOptions {
+  kSlowerStartup,    // Once a loss is encountered in STARTUP,
+                     // switches startup to a 1.5x pacing gain.
+  kFullyDrainQueue,  // Fully drains the queue once per cycle.
+  kReduceProbeRtt,   // Probe RTT reduces CWND to 0.75 * BDP instead of 4
+                     // packets.
+  kSkipProbeRtt,     // Skip Probe RTT and extend the existing min_rtt if a
+                     // recent min_rtt is within 12.5% of the current min_rtt.
+  kSkipProbeRttAggressively,  //  Skip ProbeRTT and extend the existing min_rtt
+                              //  as long as you've been app limited at least
+                              //  once.
+  kFillUpLinkDuringProbing,   // Sends probing retransmissions whenever we
+                              // become application limited.
+  kInitialWindow3,            // Use a 3-packet initial congestion window.
+  kInitialWindow10,           // Use a 10-packet initial congestion window.
+  kInitialWindow20,           // Use a 20-packet initial congestion window.
+  kInitialWindow50,           // Use a 50-packet initial congestion window.
+  kStartup1RTT,               // Stay in STARTUP for 1 RTT.
+  kStartup2RTT,               // Stay in STARTUP for 2 RTTs.
+};
+
+// The configuration for creating a QuartcFactory.
+struct QuartcFactoryConfig {
+  // Factory for |QuicAlarm|s. Implemented by the Quartc user with different
+  // mechanisms. For example in WebRTC, it is implemented with rtc::Thread.
+  // Owned by the user, and needs to stay alive for as long as the QuartcFactory
+  // exists.
+  QuicAlarmFactory* alarm_factory = nullptr;
+  // The clock used by |QuicAlarm|s. Implemented by the Quartc user. Owned by
+  // the user, and needs to stay alive for as long as the QuartcFactory exists.
+  QuicClock* clock = nullptr;
+};
+
+struct QuartcSessionConfig {
+  // When using Quartc, there are two endpoints. The QuartcSession on one
+  // endpoint must act as a server and the one on the other side must act as a
+  // client.
+  Perspective perspective = Perspective::IS_CLIENT;
+  // This is only needed when is_server = false.  It must be unique
+  // for each endpoint the local endpoint may communicate with. For example,
+  // a WebRTC client could use the remote endpoint's crypto fingerprint
+  QuicString unique_remote_server_id;
+  // The way the QuicConnection will send and receive packets, like a virtual
+  // UDP socket. For WebRTC, this will typically be an IceTransport.
+  QuartcPacketTransport* packet_transport = nullptr;
+  // The maximum size of the packet can be written with the packet writer.
+  // 1200 bytes by default.
+  QuicPacketLength max_packet_size = 1200;
+  // Options to control the BBR algorithm. In case the congestion control is
+  // set to anything but BBR, these options are ignored.
+  std::vector<QuartcBbrOptions> bbr_options;
+  // Timeouts for the crypto handshake. Set them to higher values to
+  // prevent closing the session before it started on a slow network.
+  // Zero entries are ignored and QUIC defaults are used in that case.
+  QuicTime::Delta max_idle_time_before_crypto_handshake =
+      QuicTime::Delta::Zero();
+  QuicTime::Delta max_time_before_crypto_handshake = QuicTime::Delta::Zero();
+  QuicTime::Delta idle_network_timeout = QuicTime::Delta::Zero();
+};
+
+// Factory that creates instances of QuartcSession.  Implements the
+// QuicConnectionHelperInterface used by the QuicConnections. Only one
+// QuartcFactory is expected to be created.
+class QUIC_EXPORT_PRIVATE QuartcFactory : public QuicConnectionHelperInterface {
  public:
   explicit QuartcFactory(const QuartcFactoryConfig& factory_config);
   ~QuartcFactory() override;
 
-  // QuartcFactoryInterface overrides.
-  std::unique_ptr<QuartcSessionInterface> CreateQuartcSession(
-      const QuartcSessionConfig& quartc_session_config) override;
-
-  // QuicAlarmFactory overrides.
-  QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
-
-  QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
-      QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
-      QuicConnectionArena* arena) override;
+  // Creates a new QuartcSession using the given configuration.
+  std::unique_ptr<QuartcSession> CreateQuartcSession(
+      const QuartcSessionConfig& quartc_session_config);
 
   // QuicConnectionHelperInterface overrides.
   const QuicClock* GetClock() const override;
@@ -49,15 +98,19 @@
       Perspective perspective,
       QuartcPacketWriter* packet_writer);
 
-  // Used to implement QuicAlarmFactory..
-  QuartcTaskRunnerInterface* task_runner_;
-  // Used to implement the QuicConnectionHelperInterface.
-  // The QuicClock wrapper held in this variable is owned by QuartcFactory,
-  // but the QuartcClockInterface inside of it belongs to the user!
-  std::unique_ptr<QuicClock> clock_;
+  // Used to implement QuicAlarmFactory.  Owned by the user and must outlive
+  // QuartcFactory.
+  QuicAlarmFactory* alarm_factory_;
+  // Used to implement the QuicConnectionHelperInterface.  Owned by the user and
+  // must outlive QuartcFactory.
+  QuicClock* clock_;
   SimpleBufferAllocator buffer_allocator_;
 };
 
+// Creates a new instance of QuartcFactory.
+std::unique_ptr<QuartcFactory> CreateQuartcFactory(
+    const QuartcFactoryConfig& factory_config);
+
 }  // namespace quic
 
 #endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_FACTORY_H_
diff --git a/net/third_party/quic/quartc/quartc_factory_interface.cc b/net/third_party/quic/quartc/quartc_factory_interface.cc
deleted file mode 100644
index 12897fa..0000000
--- a/net/third_party/quic/quartc/quartc_factory_interface.cc
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/third_party/quic/quartc/quartc_factory_interface.h"
-
-namespace quic {
-
-QuartcFactoryInterface::QuartcSessionConfig::QuartcSessionConfig() = default;
-QuartcFactoryInterface::QuartcSessionConfig::~QuartcSessionConfig() = default;
-
-}  // namespace quic
diff --git a/net/third_party/quic/quartc/quartc_factory_interface.h b/net/third_party/quic/quartc/quartc_factory_interface.h
deleted file mode 100644
index 8013bc9..0000000
--- a/net/third_party/quic/quartc/quartc_factory_interface.h
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_FACTORY_INTERFACE_H_
-#define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_FACTORY_INTERFACE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/quartc/quartc_clock_interface.h"
-#include "net/third_party/quic/quartc/quartc_session_interface.h"
-#include "net/third_party/quic/quartc/quartc_task_runner_interface.h"
-
-namespace quic {
-
-// Algorithm to use for congestion control.
-enum class QuartcCongestionControl {
-  kDefault,  // Use an arbitrary algorithm chosen by QUIC.
-  kBBR,      // Use BBR.
-};
-
-// Options that control the BBR algorithm.
-enum class QuartcBbrOptions {
-  kSlowerStartup,    // Once a loss is encountered in STARTUP,
-                     // switches startup to a 1.5x pacing gain.
-  kFullyDrainQueue,  // Fully drains the queue once per cycle.
-  kReduceProbeRtt,   // Probe RTT reduces CWND to 0.75 * BDP instead of 4
-                     // packets.
-  kSkipProbeRtt,     // Skip Probe RTT and extend the existing min_rtt if a
-                     // recent min_rtt is within 12.5% of the current min_rtt.
-  kSkipProbeRttAggressively,  //  Skip ProbeRTT and extend the existing min_rtt
-                              //  as long as you've been app limited at least
-                              //  once.
-  kFillUpLinkDuringProbing,   // Sends probing retransmissions whenever we
-                              // become application limited.
-  kInitialWindow3,            // Use a 3-packet initial congestion window.
-  kInitialWindow10,           // Use a 10-packet initial congestion window.
-  kInitialWindow20,           // Use a 20-packet initial congestion window.
-  kInitialWindow50,           // Use a 50-packet initial congestion window.
-  kStartup1RTT,               // Stay in STARTUP for 1 RTT.
-  kStartup2RTT,               // Stay in STARTUP for 2 RTTs.
-};
-
-// Used to create instances for Quartc objects such as QuartcSession.
-class QUIC_EXPORT_PRIVATE QuartcFactoryInterface {
- public:
-  virtual ~QuartcFactoryInterface() {}
-
-  struct QuartcSessionConfig {
-    QuartcSessionConfig();
-    ~QuartcSessionConfig();
-
-    // When using Quartc, there are two endpoints. The QuartcSession on one
-    // endpoint must act as a server and the one on the other side must act as a
-    // client.
-    bool is_server = false;
-    // This is only needed when is_server = false.  It must be unique
-    // for each endpoint the local endpoint may communicate with. For example,
-    // a WebRTC client could use the remote endpoint's crypto fingerprint
-    std::string unique_remote_server_id;
-    // The way the QuicConnection will send and receive packets, like a virtual
-    // UDP socket. For WebRTC, this will typically be an IceTransport.
-    QuartcPacketTransport* packet_transport = nullptr;
-    // The maximum size of the packet can be written with the packet writer.
-    // 1200 bytes by default.
-    uint64_t max_packet_size = 1200;
-    // Algorithm to use for congestion control.  By default, uses an arbitrary
-    // congestion control algorithm chosen by QUIC.
-    QuartcCongestionControl congestion_control =
-        QuartcCongestionControl::kDefault;
-    // Options to control the BBR algorithm. In case the congestion control is
-    // set to anything but BBR, these options are ignored.
-    std::vector<QuartcBbrOptions> bbr_options;
-    // Timeouts for the crypto handshake. Set them to higher values to
-    // prevent closing the session before it started on a slow network.
-    // Zero entries are ignored and QUIC defaults are used in that case.
-    uint32_t max_idle_time_before_crypto_handshake_secs = 0;
-    uint32_t max_time_before_crypto_handshake_secs = 0;
-    QuicTime::Delta idle_network_timeout = QuicTime::Delta::Zero();
-  };
-
-  virtual std::unique_ptr<QuartcSessionInterface> CreateQuartcSession(
-      const QuartcSessionConfig& quartc_config) = 0;
-};
-
-// The configuration for creating a QuartcFactory.
-struct QuartcFactoryConfig {
-  // The task runner used by the QuartcAlarm. Implemented by the Quartc user
-  // with different mechanism. For example in WebRTC, it is implemented with
-  // rtc::Thread. Owned by the user, and needs to stay alive for as long
-  // as the QuartcFactory exists.
-  QuartcTaskRunnerInterface* task_runner = nullptr;
-  // The clock used by QuartcAlarms. Implemented by the Quartc user. Owned by
-  // the user, and needs to stay alive for as long as the QuartcFactory exists.
-  QuartcClockInterface* clock = nullptr;
-};
-
-// Creates a new instance of QuartcFactoryInterface.
-std::unique_ptr<QuartcFactoryInterface> CreateQuartcFactory(
-    const QuartcFactoryConfig& factory_config);
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_FACTORY_INTERFACE_H_
diff --git a/net/third_party/quic/quartc/quartc_packet_writer.h b/net/third_party/quic/quartc/quartc_packet_writer.h
index a1cabb7..a4dca96 100644
--- a/net/third_party/quic/quartc/quartc_packet_writer.h
+++ b/net/third_party/quic/quartc/quartc_packet_writer.h
@@ -5,12 +5,31 @@
 #ifndef NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_
 #define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_
 
+#include "net/third_party/quic/core/quic_connection.h"
 #include "net/third_party/quic/core/quic_packet_writer.h"
+#include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/quartc/quartc_session_interface.h"
 
 namespace quic {
 
+// Send and receive packets, like a virtual UDP socket. For example, this
+// could be implemented by WebRTC's IceTransport.
+class QUIC_EXPORT_PRIVATE QuartcPacketTransport {
+ public:
+  // Additional metadata provided for each packet written.
+  struct PacketInfo {
+    QuicPacketNumber packet_number;
+  };
+
+  virtual ~QuartcPacketTransport() {}
+
+  // Called by the QuartcPacketWriter when writing packets to the network.
+  // Return the number of written bytes. Return 0 if the write is blocked.
+  virtual int Write(const char* buffer,
+                    size_t buf_len,
+                    const PacketInfo& info) = 0;
+};
+
 // Implements a QuicPacketWriter using a QuartcPacketTransport, which allows a
 // QuicConnection to use (for example), a WebRTC IceTransport.
 class QUIC_EXPORT_PRIVATE QuartcPacketWriter : public QuicPacketWriter {
@@ -20,7 +39,7 @@
   ~QuartcPacketWriter() override {}
 
   // The QuicConnection calls WritePacket and the QuicPacketWriter writes them
-  // to the QuartcSessionInterface::PacketTransport.
+  // to the QuartcSession::PacketTransport.
   WriteResult WritePacket(const char* buffer,
                           size_t buf_len,
                           const QuicIpAddress& self_address,
diff --git a/net/third_party/quic/quartc/quartc_session.cc b/net/third_party/quic/quartc/quartc_session.cc
index c8d48c2..f61da4e 100644
--- a/net/third_party/quic/quartc/quartc_session.cc
+++ b/net/third_party/quic/quartc/quartc_session.cc
@@ -8,8 +8,6 @@
 #include "net/third_party/quic/core/tls_server_handshaker.h"
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
 
-using std::string;
-
 namespace quic {
 
 namespace {
@@ -31,14 +29,14 @@
 
   // ProofSource override.
   void GetProof(const QuicSocketAddress& server_addr,
-                const string& hostname,
-                const string& server_config,
+                const QuicString& hostname,
+                const QuicString& server_config,
                 QuicTransportVersion transport_version,
                 QuicStringPiece chlo_hash,
                 std::unique_ptr<Callback> callback) override {
     QuicReferenceCountedPointer<ProofSource::Chain> chain;
     QuicCryptoProof proof;
-    std::vector<string> certs;
+    std::vector<QuicString> certs;
     certs.push_back("Dummy cert");
     chain = new ProofSource::Chain(certs);
     proof.signature = "Dummy signature";
@@ -48,13 +46,13 @@
 
   QuicReferenceCountedPointer<Chain> GetCertChain(
       const QuicSocketAddress& server_address,
-      const string& hostname) override {
+      const QuicString& hostname) override {
     return QuicReferenceCountedPointer<Chain>();
   }
 
   void ComputeTlsSignature(
       const QuicSocketAddress& server_address,
-      const string& hostname,
+      const QuicString& hostname,
       uint16_t signature_algorithm,
       QuicStringPiece in,
       std::unique_ptr<SignatureCallback> callback) override {
@@ -72,26 +70,26 @@
 
   // ProofVerifier override.
   QuicAsyncStatus VerifyProof(
-      const string& hostname,
+      const QuicString& hostname,
       const uint16_t port,
-      const string& server_config,
+      const QuicString& server_config,
       QuicTransportVersion transport_version,
       QuicStringPiece chlo_hash,
-      const std::vector<string>& certs,
-      const string& cert_sct,
-      const string& signature,
+      const std::vector<QuicString>& certs,
+      const QuicString& cert_sct,
+      const QuicString& signature,
       const ProofVerifyContext* context,
-      string* error_details,
+      QuicString* error_details,
       std::unique_ptr<ProofVerifyDetails>* verify_details,
       std::unique_ptr<ProofVerifierCallback> callback) override {
     return QUIC_SUCCESS;
   }
 
   QuicAsyncStatus VerifyCertChain(
-      const string& hostname,
-      const std::vector<string>& certs,
+      const QuicString& hostname,
+      const std::vector<QuicString>& certs,
       const ProofVerifyContext* context,
-      string* error_details,
+      QuicString* error_details,
       std::unique_ptr<ProofVerifyDetails>* details,
       std::unique_ptr<ProofVerifierCallback> callback) override {
     return QUIC_SUCCESS;
@@ -114,65 +112,13 @@
     const QuicSocketAddress& client_address,
     const QuicSocketAddress& peer_address,
     const QuicSocketAddress& self_address,
-    string* error_details) const {
+    QuicString* error_details) const {
   return true;
 }
 
-QuartcSessionVisitorAdapter::~QuartcSessionVisitorAdapter() {}
-
-QuartcSessionVisitorAdapter::QuartcSessionVisitorAdapter() {}
-
-void QuartcSessionVisitorAdapter::OnPacketSent(
-    const SerializedPacket& serialized_packet,
-    QuicPacketNumber original_packet_number,
-    TransmissionType transmission_type,
-    QuicTime sent_time) {
-  for (QuartcSessionVisitor* visitor : visitors_) {
-    visitor->OnPacketSent(serialized_packet, original_packet_number,
-                          transmission_type, sent_time);
-  }
-}
-
-void QuartcSessionVisitorAdapter::OnIncomingAck(
-    const QuicAckFrame& ack_frame,
-    QuicTime ack_receive_time,
-    QuicPacketNumber largest_observed,
-    bool rtt_updated,
-    QuicPacketNumber least_unacked_sent_packet) {
-  for (QuartcSessionVisitor* visitor : visitors_) {
-    visitor->OnIncomingAck(ack_frame, ack_receive_time, largest_observed,
-                           rtt_updated, least_unacked_sent_packet);
-  }
-}
-
-void QuartcSessionVisitorAdapter::OnPacketLoss(
-    QuicPacketNumber lost_packet_number,
-    TransmissionType transmission_type,
-    QuicTime detection_time) {
-  for (QuartcSessionVisitor* visitor : visitors_) {
-    visitor->OnPacketLoss(lost_packet_number, transmission_type,
-                          detection_time);
-  }
-}
-
-void QuartcSessionVisitorAdapter::OnWindowUpdateFrame(
-    const QuicWindowUpdateFrame& frame,
-    const QuicTime& receive_time) {
-  for (QuartcSessionVisitor* visitor : visitors_) {
-    visitor->OnWindowUpdateFrame(frame, receive_time);
-  }
-}
-
-void QuartcSessionVisitorAdapter::OnSuccessfulVersionNegotiation(
-    const ParsedQuicVersion& version) {
-  for (QuartcSessionVisitor* visitor : visitors_) {
-    visitor->OnSuccessfulVersionNegotiation(version);
-  }
-}
-
 QuartcSession::QuartcSession(std::unique_ptr<QuicConnection> connection,
                              const QuicConfig& config,
-                             const string& unique_remote_server_id,
+                             const QuicString& unique_remote_server_id,
                              Perspective perspective,
                              QuicConnectionHelperInterface* helper,
                              QuicClock* clock,
@@ -200,7 +146,7 @@
     helper_->GetRandomGenerator()->RandBytes(source_address_token_secret,
                                              kInputKeyingMaterialLength);
     quic_crypto_server_config_ = QuicMakeUnique<QuicCryptoServerConfig>(
-        string(source_address_token_secret, kInputKeyingMaterialLength),
+        QuicString(source_address_token_secret, kInputKeyingMaterialLength),
         helper_->GetRandomGenerator(), std::move(proof_source),
         TlsServerHandshaker::CreateSslCtx());
     // Provide server with serialized config string to prove ownership.
@@ -241,15 +187,6 @@
   }
 }
 
-void QuartcSession::CloseStream(QuicStreamId stream_id) {
-  if (IsClosedStream(stream_id)) {
-    // When CloseStream has been called recursively (via
-    // QuicStream::OnClose), the stream is already closed so return.
-    return;
-  }
-  QuicSession::CloseStream(stream_id);
-}
-
 void QuartcSession::CancelStream(QuicStreamId stream_id) {
   ResetStream(stream_id, QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
 }
@@ -265,21 +202,12 @@
   }
 }
 
-bool QuartcSession::IsOpenStream(QuicStreamId stream_id) {
-  return QuicSession::IsOpenStream(stream_id);
-}
-
-QuicConnectionStats QuartcSession::GetStats() {
-  return connection_->GetStats();
-}
-
 void QuartcSession::OnConnectionClosed(QuicErrorCode error,
-                                       const string& error_details,
+                                       const QuicString& error_details,
                                        ConnectionCloseSource source) {
   QuicSession::OnConnectionClosed(error, error_details, source);
   DCHECK(session_delegate_);
-  session_delegate_->OnConnectionClosed(
-      error, source == ConnectionCloseSource::FROM_PEER);
+  session_delegate_->OnConnectionClosed(error, error_details, source);
 }
 
 void QuartcSession::SetPreSharedKey(QuicStringPiece key) {
@@ -313,35 +241,13 @@
   }
 }
 
-bool QuartcSession::ExportKeyingMaterial(const string& label,
-                                         const uint8_t* context,
-                                         size_t context_len,
-                                         bool used_context,
-                                         uint8_t* result,
-                                         size_t result_len) {
-  string quic_context(reinterpret_cast<const char*>(context), context_len);
-  string quic_result;
-  bool success = crypto_stream_->ExportKeyingMaterial(label, quic_context,
-                                                      result_len, &quic_result);
-  quic_result.copy(reinterpret_cast<char*>(result), result_len);
-  DCHECK(quic_result.length() == result_len);
-  return success;
-}
-
-void QuartcSession::CloseConnection(const string& details) {
+void QuartcSession::CloseConnection(const QuicString& details) {
   connection_->CloseConnection(
       QuicErrorCode::QUIC_CONNECTION_CANCELLED, details,
       ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
 }
 
-QuartcStreamInterface* QuartcSession::CreateOutgoingStream(
-    const OutgoingStreamParameters& param) {
-  // The |param| is for forward-compatibility. Not used for now.
-  return CreateOutgoingDynamicStream();
-}
-
-void QuartcSession::SetDelegate(
-    QuartcSessionInterface::Delegate* session_delegate) {
+void QuartcSession::SetDelegate(Delegate* session_delegate) {
   if (session_delegate_) {
     LOG(WARNING) << "The delegate for the session has already been set.";
   }
@@ -349,25 +255,6 @@
   DCHECK(session_delegate_);
 }
 
-void QuartcSession::AddSessionVisitor(QuartcSessionVisitor* visitor) {
-  // If there aren't any visitors yet, install the adapter as a connection debug
-  // visitor to delegate any future calls.
-  if (session_visitor_adapter_.visitors().empty()) {
-    connection_->set_debug_visitor(&session_visitor_adapter_);
-  }
-  session_visitor_adapter_.mutable_visitors().insert(visitor);
-  visitor->OnQuicConnection(connection_.get());
-}
-
-void QuartcSession::RemoveSessionVisitor(QuartcSessionVisitor* visitor) {
-  session_visitor_adapter_.mutable_visitors().erase(visitor);
-  // If the last visitor is removed, uninstall the connection debug visitor to
-  // avoid delegating debug calls unnecessarily.
-  if (session_visitor_adapter_.visitors().empty()) {
-    connection_->set_debug_visitor(nullptr);
-  }
-}
-
 void QuartcSession::OnTransportCanWrite() {
   connection()->writer()->SetWritable();
   if (HasDataToWrite()) {
@@ -376,29 +263,12 @@
 }
 
 bool QuartcSession::OnTransportReceived(const char* data, size_t data_len) {
-  // If the session is currently bundling packets, it must stop and flush writes
-  // before processing incoming data.  QUIC expects pending packets to be
-  // written before receiving data, because received data may change the
-  // contents of ACK frames in pending packets.
-  FlushWrites();
-
   QuicReceivedPacket packet(data, data_len, clock_->Now());
   ProcessUdpPacket(connection()->self_address(), connection()->peer_address(),
                    packet);
   return true;
 }
 
-void QuartcSession::BundleWrites() {
-  if (!packet_flusher_) {
-    packet_flusher_ = QuicMakeUnique<QuicConnection::ScopedPacketFlusher>(
-        connection_.get(), QuicConnection::SEND_ACK_IF_QUEUED);
-  }
-}
-
-void QuartcSession::FlushWrites() {
-  packet_flusher_ = nullptr;
-}
-
 void QuartcSession::OnProofValid(
     const QuicCryptoClientConfig::CachedState& cached) {
   // TODO(zhihuang): Handle the proof verification.
@@ -409,16 +279,6 @@
   // TODO(zhihuang): Handle the proof verification.
 }
 
-void QuartcSession::SetClientCryptoConfig(
-    QuicCryptoClientConfig* client_config) {
-  quic_crypto_client_config_.reset(client_config);
-}
-
-void QuartcSession::SetServerCryptoConfig(
-    QuicCryptoServerConfig* server_config) {
-  quic_crypto_server_config_.reset(server_config);
-}
-
 QuicStream* QuartcSession::CreateIncomingDynamicStream(QuicStreamId id) {
   return ActivateDataStream(CreateDataStream(id, QuicStream::kDefaultPriority));
 }
diff --git a/net/third_party/quic/quartc/quartc_session.h b/net/third_party/quic/quartc/quartc_session.h
index ac1eb24c..82a43338 100644
--- a/net/third_party/quic/quartc/quartc_session.h
+++ b/net/third_party/quic/quartc/quartc_session.h
@@ -11,9 +11,7 @@
 #include "net/third_party/quic/core/quic_error_codes.h"
 #include "net/third_party/quic/core/quic_session.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/quartc/quartc_clock_interface.h"
 #include "net/third_party/quic/quartc/quartc_packet_writer.h"
-#include "net/third_party/quic/quartc/quartc_session_interface.h"
 #include "net/third_party/quic/quartc/quartc_stream.h"
 
 namespace quic {
@@ -28,58 +26,23 @@
                             const QuicSocketAddress& client_address,
                             const QuicSocketAddress& peer_address,
                             const QuicSocketAddress& self_address,
-                            std::string* error_details) const override;
+                            QuicString* error_details) const override;
 };
 
-// Adapts |QuartcSessionVisitor|s to the |QuicConnectionDebugVisitor| interface.
-// Keeps a set of |QuartcSessionVisitor|s and forwards QUIC debug callbacks to
-// each visitor in the set.
-class QuartcSessionVisitorAdapter : public QuicConnectionDebugVisitor {
- public:
-  QuartcSessionVisitorAdapter();
-  ~QuartcSessionVisitorAdapter() override;
-
-  void OnPacketSent(const SerializedPacket& serialized_packet,
-                    QuicPacketNumber original_packet_number,
-                    TransmissionType transmission_type,
-                    QuicTime sent_time) override;
-  void OnIncomingAck(const QuicAckFrame& ack_frame,
-                     QuicTime ack_receive_time,
-                     QuicPacketNumber largest_observed,
-                     bool rtt_updated,
-                     QuicPacketNumber least_unacked_sent_packet) override;
-  void OnPacketLoss(QuicPacketNumber lost_packet_number,
-                    TransmissionType transmission_type,
-                    QuicTime detection_time) override;
-  void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
-                           const QuicTime& receive_time) override;
-  void OnSuccessfulVersionNegotiation(
-      const ParsedQuicVersion& version) override;
-
-  const std::set<QuartcSessionVisitor*>& visitors() const { return visitors_; }
-  std::set<QuartcSessionVisitor*>& mutable_visitors() { return visitors_; }
-
-  // Disallow copy and assign.
-  QuartcSessionVisitorAdapter(const QuartcSessionVisitorAdapter&) = delete;
-  QuartcSessionVisitorAdapter operator=(const QuartcSessionVisitorAdapter&) =
-      delete;
-
- private:
-  std::set<QuartcSessionVisitor*> visitors_;
-};
-
+// QuartcSession owns and manages a QUIC connection.
 class QUIC_EXPORT_PRIVATE QuartcSession
     : public QuicSession,
-      public QuartcSessionInterface,
       public QuicCryptoClientStream::ProofHandler {
  public:
   QuartcSession(std::unique_ptr<QuicConnection> connection,
                 const QuicConfig& config,
-                const std::string& unique_remote_server_id,
+                const QuicString& unique_remote_server_id,
                 Perspective perspective,
                 QuicConnectionHelperInterface* helper,
                 QuicClock* clock,
                 std::unique_ptr<QuartcPacketWriter> packet_writer);
+  QuartcSession(const QuartcSession&) = delete;
+  QuartcSession& operator=(const QuartcSession&) = delete;
   ~QuartcSession() override;
 
   // QuicSession overrides.
@@ -91,48 +54,67 @@
 
   void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
 
-  void CloseStream(QuicStreamId stream_id) override;
-
   // QuicConnectionVisitorInterface overrides.
   void OnConnectionClosed(QuicErrorCode error,
-                          const std::string& error_details,
+                          const QuicString& error_details,
                           ConnectionCloseSource source) override;
 
-  // QuartcSessionInterface overrides
-  void SetPreSharedKey(QuicStringPiece key) override;
+  // QuartcSession methods.
 
-  void StartCryptoHandshake() override;
+  // Sets a pre-shared key for use during the crypto handshake.  Must be set
+  // before StartCryptoHandshake() is called.
+  void SetPreSharedKey(QuicStringPiece key);
 
-  bool ExportKeyingMaterial(const std::string& label,
-                            const uint8_t* context,
-                            size_t context_len,
-                            bool used_context,
-                            uint8_t* result,
-                            size_t result_len) override;
+  void StartCryptoHandshake();
 
-  void CloseConnection(const std::string& details) override;
+  // Closes the connection with the given human-readable error details.
+  // The connection closes with the QUIC_CONNECTION_CANCELLED error code to
+  // indicate the application closed it.
+  //
+  // Informs the peer that the connection has been closed.  This prevents the
+  // peer from waiting until the connection times out.
+  //
+  // Cleans up the underlying QuicConnection's state.  Closing the connection
+  // makes it safe to delete the QuartcSession.
+  void CloseConnection(const QuicString& details);
 
-  QuartcStreamInterface* CreateOutgoingStream(
-      const OutgoingStreamParameters& param) override;
+  // If the given stream is still open, sends a reset frame to cancel it.
+  // Note:  This method cancels a stream by QuicStreamId rather than by pointer
+  // (or by a method on QuartcStream) because QuartcSession (and not
+  // the caller) owns the streams.  Streams may finish and be deleted before the
+  // caller tries to cancel them, rendering the caller's pointers invalid.
+  void CancelStream(QuicStreamId stream_id);
 
-  void CancelStream(QuicStreamId stream_id) override;
+  // Callbacks called by the QuartcSession to notify the user of the
+  // QuartcSession of certain events.
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
 
-  bool IsOpenStream(QuicStreamId stream_id) override;
+    // Called when the crypto handshake is complete.
+    virtual void OnCryptoHandshakeComplete() = 0;
 
-  QuicConnectionStats GetStats() override;
+    // Called when a new stream is received from the remote endpoint.
+    virtual void OnIncomingStream(QuartcStream* stream) = 0;
 
-  void SetDelegate(QuartcSessionInterface::Delegate* session_delegate) override;
+    // Called when the connection is closed. This means all of the streams will
+    // be closed and no new streams can be created.
+    virtual void OnConnectionClosed(QuicErrorCode error_code,
+                                    const QuicString& error_details,
+                                    ConnectionCloseSource source) = 0;
 
-  void AddSessionVisitor(QuartcSessionVisitor* visitor) override;
-  void RemoveSessionVisitor(QuartcSessionVisitor* visitor) override;
+    // TODO(zhihuang): Add proof verification.
+  };
 
-  void OnTransportCanWrite() override;
+  // The |delegate| is not owned by QuartcSession.
+  void SetDelegate(Delegate* session_delegate);
 
-  // Decrypts an incoming QUIC packet to a data stream.
-  bool OnTransportReceived(const char* data, size_t data_len) override;
+  // Called when CanWrite() changes from false to true.
+  void OnTransportCanWrite();
 
-  void BundleWrites() override;
-  void FlushWrites() override;
+  // Called when a packet has been received and should be handled by the
+  // QuicConnection.
+  bool OnTransportReceived(const char* data, size_t data_len);
 
   // ProofHandler overrides.
   void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
@@ -143,12 +125,6 @@
   void OnProofVerifyDetailsAvailable(
       const ProofVerifyDetails& verify_details) override;
 
-  // Override the default crypto configuration.
-  // The session will take the ownership of the configurations.
-  void SetClientCryptoConfig(QuicCryptoClientConfig* client_config);
-
-  void SetServerCryptoConfig(QuicCryptoServerConfig* server_config);
-
  protected:
   // QuicSession override.
   QuicStream* CreateIncomingDynamicStream(QuicStreamId id) override;
@@ -164,7 +140,7 @@
  private:
   // For crypto handshake.
   std::unique_ptr<QuicCryptoStream> crypto_stream_;
-  const std::string unique_remote_server_id_;
+  const QuicString unique_remote_server_id_;
   Perspective perspective_;
 
   // Packet writer used by |connection_|.
@@ -179,7 +155,7 @@
   // For recording packet receipt time
   QuicClock* clock_;
   // Not owned by QuartcSession.
-  QuartcSessionInterface::Delegate* session_delegate_ = nullptr;
+  Delegate* session_delegate_ = nullptr;
   // Used by QUIC crypto server stream to track most recently compressed certs.
   std::unique_ptr<QuicCompressedCertsCache> quic_compressed_certs_cache_;
   // This helper is needed when create QuicCryptoServerStream.
@@ -188,14 +164,6 @@
   std::unique_ptr<QuicCryptoClientConfig> quic_crypto_client_config_;
   // Config for QUIC crypto server stream, used by the server.
   std::unique_ptr<QuicCryptoServerConfig> quic_crypto_server_config_;
-
-  // Holds pointers to QuartcSessionVisitors and adapts them to the
-  // QuicConnectionDebugVisitor interface.
-  QuartcSessionVisitorAdapter session_visitor_adapter_;
-
-  std::unique_ptr<QuicConnection::ScopedPacketFlusher> packet_flusher_;
-
-  DISALLOW_COPY_AND_ASSIGN(QuartcSession);
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/quartc/quartc_session_interface.h b/net/third_party/quic/quartc/quartc_session_interface.h
deleted file mode 100644
index 2db5f35..0000000
--- a/net/third_party/quic/quartc/quartc_session_interface.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_INTERFACE_H_
-#define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_INTERFACE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <string>
-
-#include "net/third_party/quic/core/quic_bandwidth.h"
-#include "net/third_party/quic/core/quic_error_codes.h"
-#include "net/third_party/quic/core/quic_time.h"
-#include "net/third_party/quic/core/quic_types.h"
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/quartc/quartc_session_visitor_interface.h"
-#include "net/third_party/quic/quartc/quartc_stream_interface.h"
-
-namespace quic {
-
-// Send and receive packets, like a virtual UDP socket. For example, this
-// could be implemented by WebRTC's IceTransport.
-class QUIC_EXPORT_PRIVATE QuartcPacketTransport {
- public:
-  // Additional metadata provided for each packet written.
-  struct PacketInfo {
-    QuicPacketNumber packet_number;
-  };
-
-  virtual ~QuartcPacketTransport() {}
-
-  // Called by the QuartcPacketWriter when writing packets to the network.
-  // Return the number of written bytes. Return 0 if the write is blocked.
-  virtual int Write(const char* buffer,
-                    size_t buf_len,
-                    const PacketInfo& info) = 0;
-};
-
-// Given a PacketTransport, provides a way to send and receive separate streams
-// of reliable, in-order, encrypted data. For example, this can build on top of
-// a WebRTC IceTransport for sending and receiving data over QUIC.
-class QUIC_EXPORT_PRIVATE QuartcSessionInterface {
- public:
-  virtual ~QuartcSessionInterface() {}
-
-  // Sets a pre-shared key for use during the crypto handshake.  Must be set
-  // before StartCryptoHandshake() is called.
-  virtual void SetPreSharedKey(QuicStringPiece key) = 0;
-
-  virtual void StartCryptoHandshake() = 0;
-
-  // Only needed when using SRTP with QuicTransport
-  // Key Exporter interface from RFC 5705
-  // Arguments are:
-  // label               -- the exporter label.
-  //                        part of the RFC defining each exporter usage (IN)
-  // context/context_len -- a context to bind to for this connection;
-  //                        optional, can be NULL, 0 (IN)
-  // use_context         -- whether to use the context value
-  //                        (needed to distinguish no context from
-  //                        zero-length ones).
-  // result              -- where to put the computed value
-  // result_len          -- the length of the computed value
-  virtual bool ExportKeyingMaterial(const std::string& label,
-                                    const uint8_t* context,
-                                    size_t context_len,
-                                    bool used_context,
-                                    uint8_t* result,
-                                    size_t result_len) = 0;
-
-  // Closes the connection with the given human-readable error details.
-  // The connection closes with the QUIC_CONNECTION_CANCELLED error code to
-  // indicate the application closed it.
-  //
-  // Informs the peer that the connection has been closed.  This prevents the
-  // peer from waiting until the connection times out.
-  //
-  // Cleans up the underlying QuicConnection's state.  Closing the connection
-  // makes it safe to delete the QuartcSession.
-  virtual void CloseConnection(const std::string& error_details) = 0;
-
-  // For forward-compatibility. More parameters could be added through the
-  // struct without changing the API.
-  struct OutgoingStreamParameters {};
-
-  virtual QuartcStreamInterface* CreateOutgoingStream(
-      const OutgoingStreamParameters& params) = 0;
-
-  // If the given stream is still open, sends a reset frame to cancel it.
-  // Note:  This method cancels a stream by QuicStreamId rather than by pointer
-  // (or by a method on QuartcStreamInterface) because QuartcSession (and not
-  // the caller) owns the streams.  Streams may finish and be deleted before the
-  // caller tries to cancel them, rendering the caller's pointers invalid.
-  virtual void CancelStream(QuicStreamId stream_id) = 0;
-
-  // This method verifies if a stream is still open and stream pointer can be
-  // used. When true is returned, the interface pointer is good for making a
-  // call immediately on the same thread, but may be rendered invalid by ANY
-  // other QUIC activity.
-  virtual bool IsOpenStream(QuicStreamId stream_id) = 0;
-
-  // Gets stats associated with the current QUIC connection.
-  virtual QuicConnectionStats GetStats() = 0;
-
-  // Called when CanWrite() changes from false to true.
-  virtual void OnTransportCanWrite() = 0;
-
-  // Called when a packet has been received and should be handled by the
-  // QuicConnection.
-  virtual bool OnTransportReceived(const char* data, size_t data_len) = 0;
-
-  // Bundles subsequent writes on a best-effort basis.
-  // Data is sent whenever enough data is accumulated to fill a packet.
-  // The session stops bundling writes and sends data immediately as soon as
-  // FlushWrites() is called or a packet is received.
-  virtual void BundleWrites() = 0;
-
-  // Stop bundling writes and flush any pending writes immediately.
-  virtual void FlushWrites() = 0;
-
-  // Callbacks called by the QuartcSession to notify the user of the
-  // QuartcSession of certain events.
-  class Delegate {
-   public:
-    virtual ~Delegate() {}
-
-    // Called when the crypto handshake is complete.
-    virtual void OnCryptoHandshakeComplete() = 0;
-
-    // Called when a new stream is received from the remote endpoint.
-    virtual void OnIncomingStream(QuartcStreamInterface* stream) = 0;
-
-    // Called when the connection is closed. This means all of the streams will
-    // be closed and no new streams can be created.
-    // TODO(zhihuang): Create mapping from integer error code to WebRTC error
-    // code.
-    virtual void OnConnectionClosed(int error_code, bool from_remote) = 0;
-
-    // TODO(zhihuang): Add proof verification.
-  };
-
-  // The |delegate| is not owned by QuartcSession.
-  virtual void SetDelegate(Delegate* delegate) = 0;
-
-  // Add or remove session visitors.  Session visitors observe internals of the
-  // Quartc/QUIC session for the purpose of gathering metrics or debug
-  // information.
-  virtual void AddSessionVisitor(QuartcSessionVisitor* visitor) = 0;
-  virtual void RemoveSessionVisitor(QuartcSessionVisitor* visitor) = 0;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_INTERFACE_H_
diff --git a/net/third_party/quic/quartc/quartc_session_test.cc b/net/third_party/quic/quartc/quartc_session_test.cc
index aedd3675..3aa46a5 100644
--- a/net/third_party/quic/quartc/quartc_session_test.cc
+++ b/net/third_party/quic/quartc/quartc_session_test.cc
@@ -13,229 +13,97 @@
 #include "net/third_party/quic/platform/api/quic_test.h"
 #include "net/third_party/quic/platform/api/quic_test_mem_slice_vector.h"
 #include "net/third_party/quic/quartc/quartc_factory.h"
-#include "net/third_party/quic/quartc/quartc_factory_interface.h"
 #include "net/third_party/quic/quartc/quartc_packet_writer.h"
-#include "net/third_party/quic/quartc/quartc_stream_interface.h"
 #include "net/third_party/quic/test_tools/mock_clock.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using std::string;
-
 namespace quic {
 
 namespace {
 
-static const char kExporterLabel[] = "label";
-static const uint8_t kExporterContext[] = "context";
-static const size_t kExporterContextLen = sizeof(kExporterContext);
-static const size_t kOutputKeyLength = 20;
-static QuartcStreamInterface::WriteParameters kDefaultWriteParam;
-static QuartcSessionInterface::OutgoingStreamParameters kDefaultStreamParam;
 static QuicByteCount kDefaultMaxPacketSize = 1200;
 
-// Single-threaded scheduled task runner based on a MockClock.
+// Single-threaded alarm implementation based on a MockClock.
 //
-// Simulates asynchronous execution on a single thread by holding scheduled
-// tasks until Run() is called. Performs no synchronization, assumes that
-// Schedule() and Run() are called on the same thread.
-class FakeTaskRunner : public QuartcTaskRunnerInterface {
+// Simulates asynchronous execution on a single thread by holding alarms
+// until Run() is called. Performs no synchronization, assumes that
+// CreateAlarm(), Set(), Cancel(), and Run() are called on the same thread.
+class FakeAlarmFactory : public QuicAlarmFactory {
  public:
-  explicit FakeTaskRunner(MockClock* clock)
-      : tasks_([this](const TaskType& l, const TaskType& r) {
-          // Items at a later time should run after items at an earlier time.
-          // Priority queue comparisons should return true if l appears after r.
-          return l->time() > r->time();
-        }),
-        clock_(clock) {}
+  class FakeAlarm : public QuicAlarm {
+   public:
+    FakeAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+              FakeAlarmFactory* parent)
+        : QuicAlarm(std::move(delegate)), parent_(parent) {}
 
-  ~FakeTaskRunner() override {}
+    ~FakeAlarm() override { parent_->RemoveAlarm(this); }
 
-  // Runs all tasks scheduled in the next total_ms milliseconds.  Advances the
+    void SetImpl() override { parent_->AddAlarm(this); }
+
+    void CancelImpl() override { parent_->RemoveAlarm(this); }
+
+    void Run() { Fire(); }
+
+   private:
+    FakeAlarmFactory* parent_;
+  };
+
+  explicit FakeAlarmFactory(MockClock* clock) : clock_(clock) {}
+  FakeAlarmFactory(const FakeAlarmFactory&) = delete;
+  FakeAlarmFactory& operator=(const FakeAlarmFactory&) = delete;
+
+  QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override {
+    return new FakeAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate),
+                         this);
+  }
+
+  QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+      QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+      QuicConnectionArena* arena) override {
+    return arena->New<FakeAlarm>(std::move(delegate), this);
+  }
+
+  // Runs all alarms scheduled in the next total_ms milliseconds.  Advances the
   // clock by total_ms.  Runs tasks in time order.  Executes tasks scheduled at
   // the same in an arbitrary order.
   void Run(uint32_t total_ms) {
     for (uint32_t i = 0; i < total_ms; ++i) {
-      while (!tasks_.empty() && tasks_.top()->time() <= clock_->Now()) {
-        tasks_.top()->Run();
-        tasks_.pop();
+      while (!alarms_.empty() && alarms_.top()->deadline() <= clock_->Now()) {
+        alarms_.top()->Run();
+        alarms_.pop();
       }
       clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
     }
   }
 
  private:
-  class InnerTask {
-   public:
-    InnerTask(std::function<void()> task, QuicTime time)
-        : task_(std::move(task)), time_(time) {}
-
-    void Cancel() { cancelled_ = true; }
-
-    void Run() {
-      if (!cancelled_) {
-        task_();
+  void RemoveAlarm(FakeAlarm* alarm) {
+    std::vector<FakeAlarm*> leftovers;
+    while (!alarms_.empty()) {
+      FakeAlarm* top = alarms_.top();
+      alarms_.pop();
+      if (top == alarm) {
+        break;
       }
+      leftovers.push_back(top);
     }
-
-    QuicTime time() const { return time_; }
-
-   private:
-    bool cancelled_ = false;
-    std::function<void()> task_;
-    QuicTime time_;
-  };
-
- public:
-  // Hook for cancelling a scheduled task.
-  class ScheduledTask : public QuartcTaskRunnerInterface::ScheduledTask {
-   public:
-    explicit ScheduledTask(std::shared_ptr<InnerTask> inner)
-        : inner_(std::move(inner)) {}
-
-    // Cancel if the caller deletes the ScheduledTask.  This behavior is
-    // consistent with the actual task runner Quartc uses.
-    ~ScheduledTask() override { Cancel(); }
-
-    // ScheduledTask implementation.
-    void Cancel() override { inner_->Cancel(); }
-
-   private:
-    std::shared_ptr<InnerTask> inner_;
-  };
-
-  // See QuartcTaskRunnerInterface.
-  std::unique_ptr<QuartcTaskRunnerInterface::ScheduledTask> Schedule(
-      Task* task,
-      uint64_t delay_ms) override {
-    auto inner = std::shared_ptr<InnerTask>(new InnerTask(
-        [task] { task->Run(); },
-        clock_->Now() + QuicTime::Delta::FromMilliseconds(delay_ms)));
-    tasks_.push(inner);
-    return std::unique_ptr<QuartcTaskRunnerInterface::ScheduledTask>(
-        new ScheduledTask(inner));
-  }
-
-  // Schedules a function to run immediately.
-  void Schedule(std::function<void()> task) {
-    tasks_.push(std::shared_ptr<InnerTask>(
-        new InnerTask(std::move(task), clock_->Now())));
-  }
-
- private:
-  // InnerTasks are shared by the queue and ScheduledTask (which hooks into it
-  // to implement Cancel()).
-  using TaskType = std::shared_ptr<InnerTask>;
-  std::priority_queue<TaskType,
-                      std::vector<TaskType>,
-                      std::function<bool(const TaskType&, const TaskType&)>>
-      tasks_;
-  MockClock* clock_;
-};
-
-// QuartcClock that wraps a MockClock.
-//
-// This is silly because Quartc wraps it as a QuicClock, and MockClock is
-// already a QuicClock.  But we don't have much choice.  We need to pass a
-// QuartcClockInterface into the Quartc wrappers.
-class MockQuartcClock : public QuartcClockInterface {
- public:
-  explicit MockQuartcClock(MockClock* clock) : clock_(clock) {}
-
-  int64_t NowMicroseconds() override {
-    return clock_->WallNow().ToUNIXMicroseconds();
-  }
-
- private:
-  MockClock* clock_;
-};
-
-// Used by QuicCryptoServerConfig to provide server credentials, returning a
-// canned response equal to |success|.
-class FakeProofSource : public ProofSource {
- public:
-  explicit FakeProofSource(bool success) : success_(success) {}
-
-  // ProofSource override.
-  void GetProof(const QuicSocketAddress& server_ip,
-                const string& hostname,
-                const string& server_config,
-                QuicTransportVersion transport_version,
-                QuicStringPiece chlo_hash,
-                std::unique_ptr<Callback> callback) override {
-    QuicReferenceCountedPointer<ProofSource::Chain> chain;
-    QuicCryptoProof proof;
-    if (success_) {
-      std::vector<string> certs;
-      certs.push_back("Required to establish handshake");
-      chain = new ProofSource::Chain(certs);
-      proof.signature = "Signature";
-      proof.leaf_cert_scts = "Time";
+    for (FakeAlarm* leftover : leftovers) {
+      alarms_.push(leftover);
     }
-    callback->Run(success_, chain, proof, nullptr /* details */);
   }
 
-  QuicReferenceCountedPointer<Chain> GetCertChain(
-      const QuicSocketAddress& server_address,
-      const string& hostname) override {
-    return QuicReferenceCountedPointer<Chain>();
-  }
+  void AddAlarm(FakeAlarm* alarm) { alarms_.push(alarm); }
 
-  void ComputeTlsSignature(
-      const QuicSocketAddress& server_address,
-      const string& hostname,
-      uint16_t signature_algorithm,
-      QuicStringPiece in,
-      std::unique_ptr<SignatureCallback> callback) override {
-    callback->Run(true, "Signature");
-  }
+  MockClock* clock_;
 
- private:
-  // Whether or not obtaining proof source succeeds.
-  bool success_;
-};
-
-// Used by QuicCryptoClientConfig to verify server credentials, returning a
-// canned response of QUIC_SUCCESS if |success| is true.
-class FakeProofVerifier : public ProofVerifier {
- public:
-  explicit FakeProofVerifier(bool success) : success_(success) {}
-
-  // ProofVerifier override
-  QuicAsyncStatus VerifyProof(
-      const string& hostname,
-      const uint16_t port,
-      const string& server_config,
-      QuicTransportVersion transport_version,
-      QuicStringPiece chlo_hash,
-      const std::vector<string>& certs,
-      const string& cert_sct,
-      const string& signature,
-      const ProofVerifyContext* context,
-      string* error_details,
-      std::unique_ptr<ProofVerifyDetails>* verify_details,
-      std::unique_ptr<ProofVerifierCallback> callback) override {
-    return success_ ? QUIC_SUCCESS : QUIC_FAILURE;
-  }
-
-  QuicAsyncStatus VerifyCertChain(
-      const string& hostname,
-      const std::vector<string>& certs,
-      const ProofVerifyContext* context,
-      string* error_details,
-      std::unique_ptr<ProofVerifyDetails>* details,
-      std::unique_ptr<ProofVerifierCallback> callback) override {
-    LOG(INFO) << "VerifyProof() ignoring credentials and returning success";
-    return success_ ? QUIC_SUCCESS : QUIC_FAILURE;
-  }
-
-  std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override {
-    return nullptr;
-  }
-
- private:
-  // Whether or not proof verification succeeds.
-  bool success_;
+  using AlarmCompare = std::function<bool(const FakeAlarm*, const FakeAlarm*)>;
+  const AlarmCompare alarm_later_ = [](const FakeAlarm* l, const FakeAlarm* r) {
+    // Sort alarms so that the earliest deadline appears first.
+    return l->deadline() > r->deadline();
+  };
+  std::priority_queue<FakeAlarm*, std::vector<FakeAlarm*>, AlarmCompare>
+      alarms_{alarm_later_};
 };
 
 // Used by the FakeTransportChannel.
@@ -244,15 +112,17 @@
   virtual ~FakeTransportChannelObserver() {}
 
   // Called when the other peer is trying to send message.
-  virtual void OnTransportChannelReadPacket(const string& data) = 0;
+  virtual void OnTransportChannelReadPacket(const QuicString& data) = 0;
 };
 
 // Simulate the P2P communication transport. Used by the
-// QuartcSessionInterface::Transport.
-class FakeTransportChannel {
+// QuartcSession::Transport.
+class FakeTransportChannel : QuicAlarm::Delegate {
  public:
-  explicit FakeTransportChannel(FakeTaskRunner* task_runner, MockClock* clock)
-      : task_runner_(task_runner), clock_(clock) {}
+  explicit FakeTransportChannel(QuicAlarmFactory* alarm_factory,
+                                MockClock* clock)
+      : alarm_(alarm_factory->CreateAlarm(new AlarmDelegate(this))),
+        clock_(clock) {}
 
   void SetDestination(FakeTransportChannel* dest) {
     if (!dest_) {
@@ -268,21 +138,16 @@
     }
     // Advance the time 10us to ensure the RTT is never 0ms.
     clock_->AdvanceTime(QuicTime::Delta::FromMicroseconds(10));
-    if (async_ && task_runner_) {
-      string packet(data, len);
-      task_runner_->Schedule([this, packet] { send(packet); });
+    if (async_) {
+      packet_queue_.emplace_back(data, len);
+      alarm_->Cancel();
+      alarm_->Set(clock_->Now());
     } else {
-      send(string(data, len));
+      Send(QuicString(data, len));
     }
     return static_cast<int>(len);
   }
 
-  void send(const string& data) {
-    DCHECK(dest_);
-    DCHECK(dest_->observer());
-    dest_->observer()->OnTransportChannelReadPacket(data);
-  }
-
   FakeTransportChannelObserver* observer() { return observer_; }
 
   void SetObserver(FakeTransportChannelObserver* observer) {
@@ -292,14 +157,43 @@
   void SetAsync(bool async) { async_ = async; }
 
  private:
+  class AlarmDelegate : public QuicAlarm::Delegate {
+   public:
+    explicit AlarmDelegate(FakeTransportChannel* channel) : channel_(channel) {}
+
+    void OnAlarm() override { channel_->OnAlarm(); }
+
+   private:
+    FakeTransportChannel* channel_;
+  };
+
+  void Send(const QuicString& data) {
+    DCHECK(dest_);
+    DCHECK(dest_->observer());
+    dest_->observer()->OnTransportChannelReadPacket(data);
+  }
+
+  void OnAlarm() override {
+    QUIC_LOG(WARNING) << "Sending packet: " << packet_queue_.front();
+    Send(packet_queue_.front());
+    packet_queue_.pop_front();
+
+    if (!packet_queue_.empty()) {
+      alarm_->Cancel();
+      alarm_->Set(clock_->Now());
+    }
+  }
+
   // The writing destination of this channel.
   FakeTransportChannel* dest_ = nullptr;
   // The observer of this channel. Called when the received the data.
   FakeTransportChannelObserver* observer_ = nullptr;
   // If async, will send packets by running asynchronous tasks.
   bool async_ = false;
-  // Used to send data asynchronously.
-  FakeTaskRunner* task_runner_;
+  // If async, packets are queued here to send.
+  QuicDeque<QuicString> packet_queue_;
+  // Alarm used to send data asynchronously.
+  QuicArenaScopedPtr<QuicAlarm> alarm_;
   // The test clock.  Used to ensure the RTT is not 0.
   MockClock* clock_;
 };
@@ -331,51 +225,52 @@
   QuicPacketCount packets_to_lose_ = 0;
 };
 
-class FakeQuartcSessionDelegate : public QuartcSessionInterface::Delegate {
+class FakeQuartcSessionDelegate : public QuartcSession::Delegate {
  public:
-  explicit FakeQuartcSessionDelegate(
-      QuartcStreamInterface::Delegate* stream_delegate)
+  explicit FakeQuartcSessionDelegate(QuartcStream::Delegate* stream_delegate)
       : stream_delegate_(stream_delegate) {}
   // Called when peers have established forward-secure encryption
   void OnCryptoHandshakeComplete() override {
     LOG(INFO) << "Crypto handshake complete!";
   }
   // Called when connection closes locally, or remotely by peer.
-  void OnConnectionClosed(int error_code, bool from_remote) override {
+  void OnConnectionClosed(QuicErrorCode error_code,
+                          const QuicString& error_details,
+                          ConnectionCloseSource source) override {
     connected_ = false;
   }
   // Called when an incoming QUIC stream is created.
-  void OnIncomingStream(QuartcStreamInterface* quartc_stream) override {
+  void OnIncomingStream(QuartcStream* quartc_stream) override {
     last_incoming_stream_ = quartc_stream;
     last_incoming_stream_->SetDelegate(stream_delegate_);
   }
 
-  QuartcStreamInterface* incoming_stream() { return last_incoming_stream_; }
+  QuartcStream* incoming_stream() { return last_incoming_stream_; }
 
   bool connected() { return connected_; }
 
  private:
-  QuartcStreamInterface* last_incoming_stream_;
+  QuartcStream* last_incoming_stream_;
   bool connected_ = true;
   QuartcStream::Delegate* stream_delegate_;
 };
 
-class FakeQuartcStreamDelegate : public QuartcStreamInterface::Delegate {
+class FakeQuartcStreamDelegate : public QuartcStream::Delegate {
  public:
-  void OnReceived(QuartcStreamInterface* stream,
+  void OnReceived(QuartcStream* stream,
                   const char* data,
                   size_t size) override {
-    received_data_[stream->stream_id()] += string(data, size);
+    received_data_[stream->id()] += QuicString(data, size);
   }
 
-  void OnClose(QuartcStreamInterface* stream) override {}
+  void OnClose(QuartcStream* stream) override {}
 
-  void OnBufferChanged(QuartcStreamInterface* stream) override {}
+  void OnBufferChanged(QuartcStream* stream) override {}
 
-  std::map<QuicStreamId, string> data() { return received_data_; }
+  std::map<QuicStreamId, QuicString> data() { return received_data_; }
 
  private:
-  std::map<QuicStreamId, string> received_data_;
+  std::map<QuicStreamId, QuicString> received_data_;
 };
 
 class QuartcSessionForTest : public QuartcSession,
@@ -383,7 +278,7 @@
  public:
   QuartcSessionForTest(std::unique_ptr<QuicConnection> connection,
                        const QuicConfig& config,
-                       const string& remote_fingerprint_value,
+                       const QuicString& remote_fingerprint_value,
                        Perspective perspective,
                        QuicConnectionHelperInterface* helper,
                        QuicClock* clock,
@@ -403,11 +298,11 @@
   }
 
   // QuartcPacketWriter override.
-  void OnTransportChannelReadPacket(const string& data) override {
+  void OnTransportChannelReadPacket(const QuicString& data) override {
     OnTransportReceived(data.c_str(), data.length());
   }
 
-  std::map<QuicStreamId, string> data() { return stream_delegate_->data(); }
+  std::map<QuicStreamId, QuicString> data() { return stream_delegate_->data(); }
 
   bool has_data() { return !data().empty(); }
 
@@ -431,9 +326,9 @@
     // Quic crashes if packets are sent at time 0, and the clock defaults to 0.
     clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
     client_channel_ =
-        QuicMakeUnique<FakeTransportChannel>(&task_runner_, &clock_);
+        QuicMakeUnique<FakeTransportChannel>(&alarm_factory_, &clock_);
     server_channel_ =
-        QuicMakeUnique<FakeTransportChannel>(&task_runner_, &clock_);
+        QuicMakeUnique<FakeTransportChannel>(&alarm_factory_, &clock_);
     // Make the channel asynchronous so that two peer will not keep calling each
     // other when they exchange information.
     client_channel_->SetAsync(true);
@@ -453,8 +348,7 @@
 
   // The parameters are used to control whether the handshake will success or
   // not.
-  void CreateClientAndServerSessions(bool client_handshake_success = true,
-                                     bool server_handshake_success = true) {
+  void CreateClientAndServerSessions() {
     Init();
     client_peer_ =
         CreateSession(Perspective::IS_CLIENT, std::move(client_writer_));
@@ -463,26 +357,6 @@
 
     client_channel_->SetObserver(client_peer_.get());
     server_channel_->SetObserver(server_peer_.get());
-
-    client_peer_->SetClientCryptoConfig(new QuicCryptoClientConfig(
-        std::unique_ptr<ProofVerifier>(
-            new FakeProofVerifier(client_handshake_success)),
-        TlsClientHandshaker::CreateSslCtx()));
-
-    QuicCryptoServerConfig* server_config = new QuicCryptoServerConfig(
-        "TESTING", QuicRandom::GetInstance(),
-        std::unique_ptr<FakeProofSource>(
-            new FakeProofSource(server_handshake_success)),
-        TlsServerHandshaker::CreateSslCtx());
-    // Provide server with serialized config string to prove ownership.
-    QuicCryptoServerConfig::ConfigOptions options;
-    std::unique_ptr<QuicServerConfigProtobuf> primary_config(
-        server_config->GenerateConfig(QuicRandom::GetInstance(), &clock_,
-                                      options));
-    std::unique_ptr<CryptoHandshakeMessage> message(
-        server_config->AddConfig(std::move(primary_config), clock_.WallNow()));
-
-    server_peer_->SetServerCryptoConfig(server_config);
   }
 
   std::unique_ptr<QuartcSessionForTest> CreateSession(
@@ -490,7 +364,7 @@
       std::unique_ptr<QuartcPacketWriter> writer) {
     std::unique_ptr<QuicConnection> quic_connection =
         CreateConnection(perspective, writer.get());
-    string remote_fingerprint_value = "value";
+    QuicString remote_fingerprint_value = "value";
     QuicConfig config;
     return QuicMakeUnique<QuartcSessionForTest>(
         std::move(quic_connection), config, remote_fingerprint_value,
@@ -501,22 +375,14 @@
                                                    QuartcPacketWriter* writer) {
     QuicIpAddress ip;
     ip.FromString("0.0.0.0");
-    if (!alarm_factory_) {
-      // QuartcFactory is only used as an alarm factory.
-      QuartcFactoryConfig config;
-      config.clock = &quartc_clock_;
-      config.task_runner = &task_runner_;
-      alarm_factory_ = QuicMakeUnique<QuartcFactory>(config);
-    }
-
     return QuicMakeUnique<QuicConnection>(
         0, QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
-        alarm_factory_.get(), writer, /*owns_writer=*/false, perspective,
+        &alarm_factory_, writer, /*owns_writer=*/false, perspective,
         CurrentSupportedVersions());
   }
 
   // Runs all tasks scheduled in the next 200 ms.
-  void RunTasks() { task_runner_.Run(200); }
+  void RunTasks() { alarm_factory_.Run(200); }
 
   void StartHandshake() {
     server_peer_->StartCryptoHandshake();
@@ -532,23 +398,9 @@
     ASSERT_TRUE(server_peer_->IsEncryptionEstablished());
     ASSERT_TRUE(client_peer_->IsEncryptionEstablished());
 
-    uint8_t server_key[kOutputKeyLength];
-    uint8_t client_key[kOutputKeyLength];
-    bool use_context = true;
-    bool server_success = server_peer_->ExportKeyingMaterial(
-        kExporterLabel, kExporterContext, kExporterContextLen, use_context,
-        server_key, kOutputKeyLength);
-    ASSERT_TRUE(server_success);
-    bool client_success = client_peer_->ExportKeyingMaterial(
-        kExporterLabel, kExporterContext, kExporterContextLen, use_context,
-        client_key, kOutputKeyLength);
-    ASSERT_TRUE(client_success);
-    EXPECT_EQ(0, memcmp(server_key, client_key, sizeof(server_key)));
-
     // Now we can establish encrypted outgoing stream.
-    QuartcStreamInterface* outgoing_stream =
-        server_peer_->CreateOutgoingStream(kDefaultStreamParam);
-    QuicStreamId stream_id = outgoing_stream->stream_id();
+    QuartcStream* outgoing_stream = server_peer_->CreateOutgoingDynamicStream();
+    QuicStreamId stream_id = outgoing_stream->id();
     ASSERT_NE(nullptr, outgoing_stream);
     EXPECT_TRUE(server_peer_->HasOpenDynamicStreams());
 
@@ -558,16 +410,16 @@
     char kTestMessage[] = "Hello";
     test::QuicTestMemSliceVector data(
         {std::make_pair(kTestMessage, strlen(kTestMessage))});
-    outgoing_stream->Write(data.span(), kDefaultWriteParam);
+    outgoing_stream->WriteMemSlices(data.span(), /*fin=*/false);
     RunTasks();
 
     // Wait for peer 2 to receive messages.
     ASSERT_TRUE(client_peer_->has_data());
 
-    QuartcStreamInterface* incoming =
+    QuartcStream* incoming =
         client_peer_->session_delegate()->incoming_stream();
     ASSERT_TRUE(incoming);
-    EXPECT_EQ(incoming->stream_id(), stream_id);
+    EXPECT_EQ(incoming->id(), stream_id);
     EXPECT_TRUE(client_peer_->HasOpenDynamicStreams());
 
     EXPECT_EQ(client_peer_->data()[stream_id], kTestMessage);
@@ -575,7 +427,7 @@
     char kTestResponse[] = "Response";
     test::QuicTestMemSliceVector response(
         {std::make_pair(kTestResponse, strlen(kTestResponse))});
-    incoming->Write(response.span(), kDefaultWriteParam);
+    incoming->WriteMemSlices(response.span(), /*fin=*/false);
     RunTasks();
     // Wait for peer 1 to receive messages.
     ASSERT_TRUE(server_peer_->has_data());
@@ -606,10 +458,9 @@
   }
 
  protected:
-  std::unique_ptr<QuicAlarmFactory> alarm_factory_;
-  SimpleBufferAllocator buffer_allocator_;
   MockClock clock_;
-  MockQuartcClock quartc_clock_{&clock_};
+  FakeAlarmFactory alarm_factory_{&clock_};
+  SimpleBufferAllocator buffer_allocator_;
 
   std::unique_ptr<FakeTransportChannel> client_channel_;
   std::unique_ptr<FakeTransportChannel> server_channel_;
@@ -619,8 +470,6 @@
   std::unique_ptr<QuartcPacketWriter> server_writer_;
   std::unique_ptr<QuartcSessionForTest> client_peer_;
   std::unique_ptr<QuartcSessionForTest> server_peer_;
-
-  FakeTaskRunner task_runner_{&clock_};
 };
 
 TEST_F(QuartcSessionTest, StreamConnection) {
@@ -629,42 +478,19 @@
   TestStreamConnection();
 }
 
-TEST_F(QuartcSessionTest, ClientRejection) {
-  CreateClientAndServerSessions(false /*client_handshake_success*/,
-                                true /*server_handshake_success*/);
+TEST_F(QuartcSessionTest, PreSharedKeyHandshake) {
+  CreateClientAndServerSessions();
+  client_peer_->SetPreSharedKey("foo");
+  server_peer_->SetPreSharedKey("foo");
   StartHandshake();
-  TestDisconnectAfterFailedHandshake();
-}
-
-TEST_F(QuartcSessionTest, ServerRejection) {
-  CreateClientAndServerSessions(true /*client_handshake_success*/,
-                                false /*server_handshake_success*/);
-  StartHandshake();
-  TestDisconnectAfterFailedHandshake();
+  TestStreamConnection();
 }
 
 // Test that data streams are not created before handshake.
 TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) {
   CreateClientAndServerSessions();
-  EXPECT_EQ(nullptr, server_peer_->CreateOutgoingStream(kDefaultStreamParam));
-  EXPECT_EQ(nullptr, client_peer_->CreateOutgoingStream(kDefaultStreamParam));
-}
-
-TEST_F(QuartcSessionTest, CloseQuartcStream) {
-  CreateClientAndServerSessions();
-  StartHandshake();
-  ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
-  ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
-  QuartcStreamInterface* stream =
-      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
-  ASSERT_NE(nullptr, stream);
-
-  uint32_t id = stream->stream_id();
-  EXPECT_FALSE(client_peer_->IsClosedStream(id));
-  stream->SetDelegate(client_peer_->stream_delegate());
-  stream->Close();
-  RunTasks();
-  EXPECT_TRUE(client_peer_->IsClosedStream(id));
+  EXPECT_EQ(nullptr, server_peer_->CreateOutgoingDynamicStream());
+  EXPECT_EQ(nullptr, client_peer_->CreateOutgoingDynamicStream());
 }
 
 TEST_F(QuartcSessionTest, CancelQuartcStream) {
@@ -673,11 +499,10 @@
   ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
   ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
 
-  QuartcStreamInterface* stream =
-      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
+  QuartcStream* stream = client_peer_->CreateOutgoingDynamicStream();
   ASSERT_NE(nullptr, stream);
 
-  uint32_t id = stream->stream_id();
+  uint32_t id = stream->id();
   EXPECT_FALSE(client_peer_->IsClosedStream(id));
   stream->SetDelegate(client_peer_->stream_delegate());
   client_peer_->CancelStream(id);
@@ -686,103 +511,19 @@
   EXPECT_TRUE(client_peer_->IsClosedStream(id));
 }
 
-TEST_F(QuartcSessionTest, BundleWrites) {
-  CreateClientAndServerSessions();
-  StartHandshake();
-  ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
-  ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
-
-  client_peer_->BundleWrites();
-  QuartcStreamInterface* first =
-      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
-  QuicStreamId first_id = first->stream_id();
-  first->SetDelegate(client_peer_->stream_delegate());
-
-  char kFirstMessage[] = "Hello";
-  test::QuicTestMemSliceVector first_data(
-      {std::make_pair(kFirstMessage, strlen(kFirstMessage))});
-  first->Write(first_data.span(), kDefaultWriteParam);
-  RunTasks();
-
-  // Server should not receive any data until the client flushes writes.
-  EXPECT_FALSE(server_peer_->has_data());
-
-  QuartcStreamInterface* second =
-      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
-  QuicStreamId second_id = second->stream_id();
-  second->SetDelegate(client_peer_->stream_delegate());
-
-  char kSecondMessage[] = "World";
-  test::QuicTestMemSliceVector second_data(
-      {std::make_pair(kSecondMessage, strlen(kSecondMessage))});
-  second->Write(second_data.span(), kDefaultWriteParam);
-  RunTasks();
-
-  EXPECT_FALSE(server_peer_->has_data());
-
-  client_peer_->FlushWrites();
-  RunTasks();
-
-  ASSERT_TRUE(server_peer_->has_data());
-  EXPECT_EQ(server_peer_->data()[first_id], kFirstMessage);
-  EXPECT_EQ(server_peer_->data()[second_id], kSecondMessage);
-}
-
-TEST_F(QuartcSessionTest, StopBundlingOnIncomingData) {
-  CreateClientAndServerSessions();
-  StartHandshake();
-  ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
-  ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
-
-  client_peer_->BundleWrites();
-  QuartcStreamInterface* first =
-      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
-  QuicStreamId first_id = first->stream_id();
-  first->SetDelegate(client_peer_->stream_delegate());
-
-  char kFirstMessage[] = "Hello";
-  test::QuicTestMemSliceVector first_data(
-      {std::make_pair(kFirstMessage, strlen(kFirstMessage))});
-  first->Write(first_data.span(), kDefaultWriteParam);
-  RunTasks();
-
-  // Server should not receive any data until the client flushes writes.
-  EXPECT_FALSE(server_peer_->has_data());
-
-  QuartcStreamInterface* second =
-      server_peer_->CreateOutgoingStream(kDefaultStreamParam);
-  QuicStreamId second_id = second->stream_id();
-  second->SetDelegate(server_peer_->stream_delegate());
-
-  char kSecondMessage[] = "World";
-  test::QuicTestMemSliceVector second_data(
-      {std::make_pair(kSecondMessage, strlen(kSecondMessage))});
-  second->Write(second_data.span(), kDefaultWriteParam);
-  RunTasks();
-
-  ASSERT_TRUE(client_peer_->has_data());
-  EXPECT_EQ(client_peer_->data()[second_id], kSecondMessage);
-
-  // Server should receive data as well, since the client stops bundling to
-  // process incoming packets.
-  ASSERT_TRUE(server_peer_->has_data());
-  EXPECT_EQ(server_peer_->data()[first_id], kFirstMessage);
-}
-
 TEST_F(QuartcSessionTest, WriterGivesPacketNumberToTransport) {
   CreateClientAndServerSessions();
   StartHandshake();
   ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
   ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
 
-  QuartcStreamInterface* stream =
-      client_peer_->CreateOutgoingStream(kDefaultStreamParam);
+  QuartcStream* stream = client_peer_->CreateOutgoingDynamicStream();
   stream->SetDelegate(client_peer_->stream_delegate());
 
   char kClientMessage[] = "Hello";
   test::QuicTestMemSliceVector stream_data(
       {std::make_pair(kClientMessage, strlen(kClientMessage))});
-  stream->Write(stream_data.span(), kDefaultWriteParam);
+  stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
   RunTasks();
 
   // The transport should see the latest packet number sent by QUIC.
@@ -791,33 +532,6 @@
       client_peer_->connection()->sent_packet_manager().GetLargestSentPacket());
 }
 
-TEST_F(QuartcSessionTest, GetStats) {
-  CreateClientAndServerSessions();
-  StartHandshake();
-  ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
-  ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
-
-  QuicConnectionStats stats = server_peer_->GetStats();
-  EXPECT_GT(stats.estimated_bandwidth, QuicBandwidth::Zero());
-  EXPECT_GT(stats.srtt_us, 0);
-  EXPECT_GT(stats.packets_sent, 0u);
-  EXPECT_EQ(stats.packets_lost, 0u);
-}
-
-TEST_F(QuartcSessionTest, DISABLED_PacketLossStats) {
-  CreateClientAndServerSessions();
-  StartHandshake();
-  ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
-  ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
-
-  // Packet loss doesn't count until the handshake is done.
-  server_transport_->set_packets_to_lose(1);
-  TestStreamConnection();
-
-  QuicConnectionStats stats = server_peer_->GetStats();
-  EXPECT_EQ(stats.packets_lost, 1u);
-}
-
 TEST_F(QuartcSessionTest, CloseConnection) {
   CreateClientAndServerSessions();
   StartHandshake();
diff --git a/net/third_party/quic/quartc/quartc_session_visitor_interface.h b/net/third_party/quic/quartc/quartc_session_visitor_interface.h
deleted file mode 100644
index 7cd75c6..0000000
--- a/net/third_party/quic/quartc/quartc_session_visitor_interface.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2018 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 NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_VISITOR_INTERFACE_H_
-#define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_VISITOR_INTERFACE_H_
-
-#include "net/third_party/quic/core/quic_connection.h"
-#include "net/third_party/quic/platform/api/quic_export.h"
-
-namespace quic {
-
-// QuartcSessionVisitor observes internals of a Quartc/QUIC session for the
-// purpose of gathering metrics or debug information.
-class QUIC_EXPORT_PRIVATE QuartcSessionVisitor {
- public:
-  virtual ~QuartcSessionVisitor() {}
-
-  // Informs this visitor of a |QuicConnection| for the session.
-  // Called once when the visitor is attached to a QuartcSession, or when a new
-  // |QuicConnection| starts.
-  virtual void OnQuicConnection(QuicConnection* connection) {}
-
-  // Called when a packet has been sent.
-  virtual void OnPacketSent(const SerializedPacket& serialized_packet,
-                            QuicPacketNumber original_packet_number,
-                            TransmissionType transmission_type,
-                            QuicTime sent_time) {}
-
-  // Called when an ack is received.
-  virtual void OnIncomingAck(const QuicAckFrame& ack_frame,
-                             QuicTime ack_receive_time,
-                             QuicPacketNumber largest_observed,
-                             bool rtt_updated,
-                             QuicPacketNumber least_unacked_sent_packet) {}
-
-  // Called when a packet is lost.
-  virtual void OnPacketLoss(QuicPacketNumber lost_packet_number,
-                            TransmissionType transmission_type,
-                            QuicTime detection_time) {}
-
-  // Called when a WindowUpdateFrame is received.
-  virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
-                                   const QuicTime& receive_time) {}
-
-  // Called when version negotiation succeeds.
-  virtual void OnSuccessfulVersionNegotiation(
-      const ParsedQuicVersion& version) {}
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_VISITOR_INTERFACE_H_
diff --git a/net/third_party/quic/quartc/quartc_stream.cc b/net/third_party/quic/quartc/quartc_stream.cc
index 8c628cf..6cbce4a 100644
--- a/net/third_party/quic/quartc/quartc_stream.cc
+++ b/net/third_party/quic/quartc/quartc_stream.cc
@@ -50,39 +50,11 @@
   delegate_->OnBufferChanged(this);
 }
 
-uint32_t QuartcStream::stream_id() {
-  return id();
-}
-
-uint64_t QuartcStream::bytes_buffered() {
-  return BufferedDataBytes();
-}
-
-bool QuartcStream::fin_sent() {
-  return QuicStream::fin_sent();
-}
-
-int QuartcStream::stream_error() {
-  return QuicStream::stream_error();
-}
-
-void QuartcStream::Write(QuicMemSliceSpan data, const WriteParameters& param) {
-  WriteMemSlices(data, param.fin);
-}
-
 void QuartcStream::FinishWriting() {
   WriteOrBufferData(QuicStringPiece(nullptr, 0), true, nullptr);
 }
 
-void QuartcStream::FinishReading() {
-  QuicStream::StopReading();
-}
-
-void QuartcStream::Close() {
-  QuicStream::session()->CloseStream(id());
-}
-
-void QuartcStream::SetDelegate(QuartcStreamInterface::Delegate* delegate) {
+void QuartcStream::SetDelegate(Delegate* delegate) {
   if (delegate_) {
     LOG(WARNING) << "The delegate for Stream " << id()
                  << " has already been set.";
diff --git a/net/third_party/quic/quartc/quartc_stream.h b/net/third_party/quic/quartc/quartc_stream.h
index 7a7173e..830d45f 100644
--- a/net/third_party/quic/quartc/quartc_stream.h
+++ b/net/third_party/quic/quartc/quartc_stream.h
@@ -8,13 +8,15 @@
 #include "net/third_party/quic/core/quic_session.h"
 #include "net/third_party/quic/core/quic_stream.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/quartc/quartc_stream_interface.h"
+#include "net/third_party/quic/platform/api/quic_mem_slice_span.h"
 
 namespace quic {
 
-// Implements a QuartcStreamInterface using a QuicStream.
-class QUIC_EXPORT_PRIVATE QuartcStream : public QuicStream,
-                                         public QuartcStreamInterface {
+// Sends and receives data with a particular QUIC stream ID, reliably and
+// in-order. To send/receive data out of order, use separate streams. To
+// send/receive unreliably, close a stream after reliability is no longer
+// needed.
+class QUIC_EXPORT_PRIVATE QuartcStream : public QuicStream {
  public:
   QuartcStream(QuicStreamId id, QuicSession* session);
 
@@ -33,27 +35,43 @@
       const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener)
       override;
 
-  // QuartcStreamInterface overrides.
-  uint32_t stream_id() override;
+  // QuartcStream interface methods.
 
-  uint64_t bytes_buffered() override;
+  // Marks this stream as finished writing.  Asynchronously sends a FIN and
+  // closes the write-side.  It is not necessary to call FinishWriting() if the
+  // last call to Write() sends a FIN.
+  void FinishWriting();
 
-  bool fin_sent() override;
+  // Implemented by the user of the QuartcStream to receive incoming
+  // data and be notified of state changes.
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
 
-  int stream_error() override;
+    // Called when the stream receives data.  Called with |size| == 0 after all
+    // stream data has been delivered (once the stream receives a FIN bit).
+    // Note that the same packet may include both data and a FIN bit, causing
+    // this method to be called twice.
+    virtual void OnReceived(QuartcStream* stream,
+                            const char* data,
+                            size_t size) = 0;
 
-  void Write(QuicMemSliceSpan data, const WriteParameters& param) override;
+    // Called when the stream is closed, either locally or by the remote
+    // endpoint.  Streams close when (a) fin bits are both sent and received,
+    // (b) Close() is called, or (c) the stream is reset.
+    // TODO(zhihuang) Creates a map from the integer error_code to WebRTC native
+    // error code.
+    virtual void OnClose(QuartcStream* stream) = 0;
 
-  void FinishWriting() override;
+    // Called when the contents of the stream's buffer changes.
+    virtual void OnBufferChanged(QuartcStream* stream) = 0;
+  };
 
-  void FinishReading() override;
-
-  void Close() override;
-
-  void SetDelegate(QuartcStreamInterface::Delegate* delegate) override;
+  // The |delegate| is not owned by QuartcStream.
+  void SetDelegate(Delegate* delegate);
 
  private:
-  QuartcStreamInterface::Delegate* delegate_ = nullptr;
+  Delegate* delegate_ = nullptr;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/quartc/quartc_stream_interface.h b/net/third_party/quic/quartc/quartc_stream_interface.h
deleted file mode 100644
index 9f3f600..0000000
--- a/net/third_party/quic/quartc/quartc_stream_interface.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_STREAM_INTERFACE_H_
-#define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_STREAM_INTERFACE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/platform/api/quic_mem_slice_span.h"
-
-namespace quic {
-
-// Sends and receives data with a particular QUIC stream ID, reliably and
-// in-order. To send/receive data out of order, use separate streams. To
-// send/receive unreliably, close a stream after reliability is no longer
-// needed.
-class QUIC_EXPORT_PRIVATE QuartcStreamInterface {
- public:
-  virtual ~QuartcStreamInterface() {}
-
-  // The QUIC stream ID.
-  virtual uint32_t stream_id() = 0;
-
-  // The amount of data buffered on this stream.
-  virtual uint64_t bytes_buffered() = 0;
-
-  // Return true if the FIN has been sent. Used by the outgoing streams to
-  // determine if all the data has been sent
-  virtual bool fin_sent() = 0;
-
-  virtual int stream_error() = 0;
-
-  struct WriteParameters {
-    // |fin| is set to be true when there is no more data need to be send
-    // through a particular stream. The receiving side will used it to determine
-    // if the sender finish sending data.
-    bool fin = false;
-  };
-
-  // Sends data reliably and in-order.  Returns the amount sent.
-  // Does not buffer data.
-  virtual void Write(QuicMemSliceSpan data, const WriteParameters& param) = 0;
-
-  // Marks this stream as finished writing.  Asynchronously sends a FIN and
-  // closes the write-side.  The stream will no longer call OnCanWrite().
-  // It is not necessary to call FinishWriting() if the last call to Write()
-  // sends a FIN.
-  virtual void FinishWriting() = 0;
-
-  // Marks this stream as finished reading.  Further incoming data is discarded.
-  // The stream will no longer call OnReceived().
-  // It is never necessary to call FinishReading().  The read-side closes when a
-  // FIN is received, regardless of whether FinishReading() has been called.
-  virtual void FinishReading() = 0;
-
-  // Once Close is called, no more data can be sent, all buffered data will be
-  // dropped and no data will be retransmitted.
-  virtual void Close() = 0;
-
-  // Implemented by the user of the QuartcStreamInterface to receive incoming
-  // data and be notified of state changes.
-  class Delegate {
-   public:
-    virtual ~Delegate() {}
-
-    // Called when the stream receives the data.  Called with |size| == 0 after
-    // all stream data has been delivered.
-    virtual void OnReceived(QuartcStreamInterface* stream,
-                            const char* data,
-                            size_t size) = 0;
-
-    // Called when the stream is closed, either locally or by the remote
-    // endpoint.  Streams close when (a) fin bits are both sent and received,
-    // (b) Close() is called, or (c) the stream is reset.
-    // TODO(zhihuang) Creates a map from the integer error_code to WebRTC native
-    // error code.
-    virtual void OnClose(QuartcStreamInterface* stream) = 0;
-
-    // Called when the contents of the stream's buffer changes.
-    virtual void OnBufferChanged(QuartcStreamInterface* stream) = 0;
-  };
-
-  // The |delegate| is not owned by QuartcStream.
-  virtual void SetDelegate(Delegate* delegate) = 0;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_STREAM_INTERFACE_H_
diff --git a/net/third_party/quic/quartc/quartc_stream_test.cc b/net/third_party/quic/quartc/quartc_stream_test.cc
index 66e3673..353990ec 100644
--- a/net/third_party/quic/quartc/quartc_stream_test.cc
+++ b/net/third_party/quic/quartc/quartc_stream_test.cc
@@ -11,9 +11,9 @@
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 #include "net/third_party/quic/platform/api/quic_test_mem_slice_vector.h"
-#include "net/third_party/quic/quartc/quartc_clock_interface.h"
 #include "net/third_party/quic/quartc/quartc_factory.h"
 #include "net/third_party/quic/test_tools/mock_clock.h"
+#include "net/third_party/quic/test_tools/quic_test_utils.h"
 #include "net/third_party/spdy/core/spdy_protocol.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,7 +23,6 @@
 namespace {
 
 static const QuicStreamId kStreamId = 5;
-static const QuartcStreamInterface::WriteParameters kDefaultParam;
 
 // MockQuicSession that does not create streams and writes data from
 // QuicStream to a string.
@@ -31,7 +30,7 @@
  public:
   MockQuicSession(QuicConnection* connection,
                   const QuicConfig& config,
-                  std::string* write_buffer)
+                  QuicString* write_buffer)
       : QuicSession(connection, nullptr /*visitor*/, config),
         write_buffer_(write_buffer) {}
 
@@ -92,7 +91,7 @@
 
  private:
   // Stores written data from ReliableQuicStreamAdapter.
-  std::string* write_buffer_;
+  QuicString* write_buffer_;
   // Whether data is written to write_buffer_.
   bool writable_ = true;
 };
@@ -132,23 +131,23 @@
   WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); }
 };
 
-class MockQuartcStreamDelegate : public QuartcStreamInterface::Delegate {
+class MockQuartcStreamDelegate : public QuartcStream::Delegate {
  public:
-  MockQuartcStreamDelegate(int id, std::string* read_buffer)
+  MockQuartcStreamDelegate(int id, QuicString* read_buffer)
       : id_(id), read_buffer_(read_buffer) {}
 
-  void OnBufferChanged(QuartcStreamInterface* stream) override {
-    last_bytes_buffered_ = stream->bytes_buffered();
+  void OnBufferChanged(QuartcStream* stream) override {
+    last_bytes_buffered_ = stream->BufferedDataBytes();
   }
 
-  void OnReceived(QuartcStreamInterface* stream,
+  void OnReceived(QuartcStream* stream,
                   const char* data,
                   size_t size) override {
-    EXPECT_EQ(id_, stream->stream_id());
+    EXPECT_EQ(id_, stream->id());
     read_buffer_->append(data, size);
   }
 
-  void OnClose(QuartcStreamInterface* stream) override { closed_ = true; }
+  void OnClose(QuartcStream* stream) override { closed_ = true; }
 
   bool closed() { return closed_; }
 
@@ -157,7 +156,7 @@
  protected:
   uint32_t id_;
   // Data read by the QuicStream.
-  std::string* read_buffer_;
+  QuicString* read_buffer_;
   // Whether the QuicStream is closed.
   bool closed_ = false;
 
@@ -174,9 +173,7 @@
     ip.FromString("0.0.0.0");
     bool owns_writer = true;
 
-    // We only use QuartcFactory for its role as an alarm factory.
-    QuartcFactoryConfig config;
-    alarm_factory_ = QuicMakeUnique<QuartcFactory>(config);
+    alarm_factory_ = QuicMakeUnique<test::MockAlarmFactory>();
 
     connection_ = QuicMakeUnique<QuicConnection>(
         0, QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
@@ -210,9 +207,9 @@
   std::unique_ptr<MockQuartcStreamDelegate> mock_stream_delegate_;
   std::unique_ptr<MockQuicSession> session_;
   // Data written by the ReliableQuicStreamAdapterTest.
-  std::string write_buffer_;
+  QuicString write_buffer_;
   // Data read by the ReliableQuicStreamAdapterTest.
-  std::string read_buffer_;
+  QuicString read_buffer_;
   std::unique_ptr<QuicAlarmFactory> alarm_factory_;
   std::unique_ptr<QuicConnection> connection_;
   // Used to implement the QuicConnectionHelperInterface.
@@ -225,7 +222,7 @@
   CreateReliableQuicStream();
   char message[] = "Foo bar";
   test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
-  stream_->Write(data.span(), kDefaultParam);
+  stream_->WriteMemSlices(data.span(), /*fin=*/false);
   EXPECT_EQ("Foo bar", write_buffer_);
 }
 
@@ -234,7 +231,7 @@
   CreateReliableQuicStream();
   char message[] = "Foo bar";
   test::QuicTestMemSliceVector data({std::make_pair(message, 5)});
-  stream_->Write(data.span(), kDefaultParam);
+  stream_->WriteMemSlices(data.span(), /*fin=*/false);
   EXPECT_EQ("Foo b", write_buffer_);
 }
 
@@ -247,11 +244,11 @@
 
   // The stream is not yet writable, so data will be buffered.
   session_->set_writable(false);
-  stream_->Write(data.span(), kDefaultParam);
+  stream_->WriteMemSlices(data.span(), /*fin=*/false);
 
   // Check that data is buffered.
   EXPECT_TRUE(stream_->HasBufferedData());
-  EXPECT_EQ(7u, stream_->bytes_buffered());
+  EXPECT_EQ(7u, stream_->BufferedDataBytes());
 
   // Check that the stream told its delegate about the buffer change.
   EXPECT_EQ(7u, mock_stream_delegate_->last_bytes_buffered());
@@ -265,10 +262,10 @@
   test::QuicTestMemSliceVector data1({std::make_pair(message1, 5)});
 
   // More writes go into the buffer.
-  stream_->Write(data1.span(), kDefaultParam);
+  stream_->WriteMemSlices(data1.span(), /*fin=*/false);
 
   EXPECT_TRUE(stream_->HasBufferedData());
-  EXPECT_EQ(12u, stream_->bytes_buffered());
+  EXPECT_EQ(12u, stream_->BufferedDataBytes());
   EXPECT_EQ(12u, mock_stream_delegate_->last_bytes_buffered());
   EXPECT_EQ(0ul, write_buffer_.size());
 
@@ -277,7 +274,7 @@
   stream_->OnCanWrite();
 
   EXPECT_FALSE(stream_->HasBufferedData());
-  EXPECT_EQ(0u, stream_->bytes_buffered());
+  EXPECT_EQ(0u, stream_->BufferedDataBytes());
   EXPECT_EQ(0u, mock_stream_delegate_->last_bytes_buffered());
   EXPECT_EQ("Foo barxyzzy", write_buffer_);
 }
@@ -317,10 +314,11 @@
   EXPECT_EQ("Hello", read_buffer_);
 }
 
-// Streams do not call OnReceived() after FinishReading().
-TEST_F(QuartcStreamTest, FinishReading) {
+// Streams do not call OnReceived() after StopReading().
+// Note: this is tested here because Quartc relies on this behavior.
+TEST_F(QuartcStreamTest, StopReading) {
   CreateReliableQuicStream();
-  stream_->FinishReading();
+  stream_->StopReading();
 
   QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
   stream_->OnStreamFrame(frame);
@@ -348,10 +346,8 @@
   QuicStreamFrame frame(kStreamId, true, 0, 0);
   stream_->OnStreamFrame(frame);
 
-  QuartcStreamInterface::WriteParameters param;
-  param.fin = true;
   test::QuicTestMemSliceVector data({});
-  stream_->Write(data.span(), param);
+  stream_->WriteMemSlices(data.span(), /*fin=*/true);
 
   // Check that the OnClose() callback occurred.
   EXPECT_TRUE(mock_stream_delegate_->closed());
diff --git a/net/third_party/quic/quartc/quartc_task_runner_interface.h b/net/third_party/quic/quartc/quartc_task_runner_interface.h
deleted file mode 100644
index b3560b3c..0000000
--- a/net/third_party/quic/quartc/quartc_task_runner_interface.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_TASK_RUNNER_INTERFACE_H_
-#define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_TASK_RUNNER_INTERFACE_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-namespace quic {
-
-// Used by platform specific QuicAlarms. For example, WebRTC will use it to set
-// and cancel an alarm. When setting an alarm, the task runner will schedule a
-// task on rtc::Thread. When canceling an alarm, the canceler for that task will
-// be called.
-class QuartcTaskRunnerInterface {
- public:
-  virtual ~QuartcTaskRunnerInterface() {}
-
-  class Task {
-   public:
-    virtual ~Task() {}
-
-    // Called when it's time to start the task.
-    virtual void Run() = 0;
-  };
-
-  // A handler used to cancel a scheduled task. In some cases, a task cannot
-  // be directly canceled with its pointer. For example, in WebRTC, the task
-  // will be scheduled on rtc::Thread. When canceling a task, its pointer cannot
-  // locate the scheduled task in the thread message queue. So when scheduling a
-  // task, an additional handler (ScheduledTask) will be returned.
-  class ScheduledTask {
-   public:
-    virtual ~ScheduledTask() {}
-
-    // Cancels a scheduled task, meaning the task will not be run.
-    virtual void Cancel() = 0;
-  };
-
-  // Schedules a task, which will be run after the given delay. A ScheduledTask
-  // may be used to cancel the task.
-  virtual std::unique_ptr<ScheduledTask> Schedule(Task* task,
-                                                  uint64_t delay_ms) = 0;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_TASK_RUNNER_INTERFACE_H_
diff --git a/net/third_party/quic/test_tools/server_thread.cc b/net/third_party/quic/test_tools/server_thread.cc
index ef9f734..016c6d20 100644
--- a/net/third_party/quic/test_tools/server_thread.cc
+++ b/net/third_party/quic/test_tools/server_thread.cc
@@ -13,17 +13,7 @@
 namespace test {
 
 ServerThread::ServerThread(QuicServer* server, const QuicSocketAddress& address)
-    : SimpleThread("server_thread"),
-      confirmed_(base::WaitableEvent::ResetPolicy::MANUAL,
-                 base::WaitableEvent::InitialState::NOT_SIGNALED),
-      pause_(base::WaitableEvent::ResetPolicy::MANUAL,
-             base::WaitableEvent::InitialState::NOT_SIGNALED),
-      paused_(base::WaitableEvent::ResetPolicy::MANUAL,
-              base::WaitableEvent::InitialState::NOT_SIGNALED),
-      resume_(base::WaitableEvent::ResetPolicy::MANUAL,
-              base::WaitableEvent::InitialState::NOT_SIGNALED),
-      quit_(base::WaitableEvent::ResetPolicy::MANUAL,
-            base::WaitableEvent::InitialState::NOT_SIGNALED),
+    : QuicThread("server_thread"),
       server_(server),
       address_(address),
       port_(0),
@@ -49,10 +39,10 @@
     Initialize();
   }
 
-  while (!quit_.IsSignaled()) {
-    if (pause_.IsSignaled() && !resume_.IsSignaled()) {
-      paused_.Signal();
-      resume_.Wait();
+  while (!quit_.HasBeenNotified()) {
+    if (pause_.HasBeenNotified() && !resume_.HasBeenNotified()) {
+      paused_.Notify();
+      resume_.WaitForNotification();
     }
     server_->WaitForEvents();
     ExecuteScheduledActions();
@@ -69,36 +59,38 @@
 }
 
 void ServerThread::Schedule(std::function<void()> action) {
-  DCHECK(!quit_.IsSignaled());
+  DCHECK(!quit_.HasBeenNotified());
   QuicWriterMutexLock lock(&scheduled_actions_lock_);
   scheduled_actions_.push_back(std::move(action));
 }
 
 void ServerThread::WaitForCryptoHandshakeConfirmed() {
-  confirmed_.Wait();
+  confirmed_.WaitForNotification();
 }
 
 void ServerThread::Pause() {
-  DCHECK(!pause_.IsSignaled());
-  pause_.Signal();
-  paused_.Wait();
+  DCHECK(!pause_.HasBeenNotified());
+  pause_.Notify();
+  paused_.WaitForNotification();
 }
 
 void ServerThread::Resume() {
-  DCHECK(!resume_.IsSignaled());
-  DCHECK(pause_.IsSignaled());
-  resume_.Signal();
+  DCHECK(!resume_.HasBeenNotified());
+  DCHECK(pause_.HasBeenNotified());
+  resume_.Notify();
 }
 
 void ServerThread::Quit() {
-  if (pause_.IsSignaled() && !resume_.IsSignaled()) {
-    resume_.Signal();
+  if (pause_.HasBeenNotified() && !resume_.HasBeenNotified()) {
+    resume_.Notify();
   }
-  quit_.Signal();
+  if (!quit_.HasBeenNotified()) {
+    quit_.Notify();
+  }
 }
 
 void ServerThread::MaybeNotifyOfHandshakeConfirmation() {
-  if (confirmed_.IsSignaled()) {
+  if (confirmed_.HasBeenNotified()) {
     // Only notify once.
     return;
   }
@@ -109,7 +101,7 @@
   }
   QuicSession* session = dispatcher->session_map().begin()->second.get();
   if (session->IsCryptoHandshakeConfirmed()) {
-    confirmed_.Signal();
+    confirmed_.Notify();
   }
 }
 
diff --git a/net/third_party/quic/test_tools/server_thread.h b/net/third_party/quic/test_tools/server_thread.h
index 4046e8ac..67dfbb7 100644
--- a/net/third_party/quic/test_tools/server_thread.h
+++ b/net/third_party/quic/test_tools/server_thread.h
@@ -7,20 +7,18 @@
 
 #include <memory>
 
-#include "base/macros.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/simple_thread.h"
 #include "net/third_party/quic/core/quic_config.h"
 #include "net/third_party/quic/platform/api/quic_containers.h"
 #include "net/third_party/quic/platform/api/quic_mutex.h"
 #include "net/third_party/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quic/platform/api/quic_thread.h"
 #include "net/third_party/quic/tools/quic_server.h"
 
 namespace quic {
 namespace test {
 
 // Simple wrapper class to run QuicServer in a dedicated thread.
-class ServerThread : public base::SimpleThread {
+class ServerThread : public QuicThread {
  public:
   ServerThread(QuicServer* server, const QuicSocketAddress& address);
   ServerThread(const ServerThread&) = delete;
@@ -65,12 +63,12 @@
   void MaybeNotifyOfHandshakeConfirmation();
   void ExecuteScheduledActions();
 
-  base::WaitableEvent
-      confirmed_;  // Notified when the first handshake is confirmed.
-  base::WaitableEvent pause_;   // Notified when the server should pause.
-  base::WaitableEvent paused_;  // Notitied when the server has paused
-  base::WaitableEvent resume_;  // Notified when the server should resume.
-  base::WaitableEvent quit_;    // Notified when the server should quit.
+  QuicNotification
+      confirmed_;            // Notified when the first handshake is confirmed.
+  QuicNotification pause_;   // Notified when the server should pause.
+  QuicNotification paused_;  // Notitied when the server has paused
+  QuicNotification resume_;  // Notified when the server should resume.
+  QuicNotification quit_;    // Notified when the server should quit.
 
   std::unique_ptr<QuicServer> server_;
   QuicSocketAddress address_;
diff --git a/remoting/host/linux/BUILD.gn b/remoting/host/linux/BUILD.gn
index e0a4627..32ccdcc 100644
--- a/remoting/host/linux/BUILD.gn
+++ b/remoting/host/linux/BUILD.gn
@@ -62,10 +62,10 @@
     if (is_component_build) {
       sources += [ "$root_build_dir/libbase.so" ]
       deps += [ "//base:base" ]
-    }
-    if (!libcpp_is_static && use_custom_libcxx) {
-      sources += [ "$root_build_dir/libc++.so" ]
-      deps += [ "//buildtools/third_party/libc++" ]
+      if (use_custom_libcxx) {
+        sources += [ "$root_build_dir/libc++.so" ]
+        deps += [ "//buildtools/third_party/libc++" ]
+      }
     }
   }
 
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 9df659f..86d6409 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -231,7 +231,8 @@
       : algorithm_perferences_(algorithm_perferences),
         ssl_private_key_(std::move(ssl_private_key)) {
     ssl_private_key_.set_connection_error_handler(
-        base::BindOnce(&SSLPrivateKeyInternal::HandleSSLPrivateKeyError, this));
+        base::BindOnce(&SSLPrivateKeyInternal::HandleSSLPrivateKeyError,
+                       base::Unretained(this)));
   }
 
   // net::SSLPrivateKey:
diff --git a/services/ui/test_ws/test_ws.cc b/services/ui/test_ws/test_ws.cc
index 9df5344..79cccf37 100644
--- a/services/ui/test_ws/test_ws.cc
+++ b/services/ui/test_ws/test_ws.cc
@@ -142,6 +142,7 @@
     std::unique_ptr<aura::Window> top_level =
         std::make_unique<aura::Window>(nullptr);
     top_level->Init(LAYER_NOT_DRAWN);
+    aura_test_helper_->root_window()->AddChild(top_level.get());
     for (auto property : properties) {
       property_converter->SetPropertyFromTransportValue(
           top_level.get(), property.first, &property.second);
diff --git a/services/ui/ws2/focus_handler.cc b/services/ui/ws2/focus_handler.cc
index db164f03..c6249cf 100644
--- a/services/ui/ws2/focus_handler.cc
+++ b/services/ui/ws2/focus_handler.cc
@@ -54,8 +54,10 @@
   ClientChange change(window_tree_->property_change_tracker_.get(), window,
                       ClientChangeType::kFocus);
   focus_client->FocusWindow(window);
-  if (focus_client->GetFocusedWindow() != window)
+  if (focus_client->GetFocusedWindow() != window) {
+    DVLOG(1) << "SetFocus failed (FocusClient::FocusWindow call failed)";
     return false;
+  }
 
   if (server_window)
     server_window->set_focus_owner(window_tree_);
@@ -76,7 +78,7 @@
     return true;  // Used to clear focus.
 
   if (!window->IsVisible() || !window->GetRootWindow())
-    return false;  // The window must be drawn an in attached to a root.
+    return false;  // The window must be drawn and attached to a root.
 
   return (window_tree_->IsClientCreatedWindow(window) ||
           window_tree_->IsClientRootWindow(window));
diff --git a/testing/buildbot/filters/viz.content_browsertests.filter b/testing/buildbot/filters/viz.content_browsertests.filter
index c961980b..bf0ea54 100644
--- a/testing/buildbot/filters/viz.content_browsertests.filter
+++ b/testing/buildbot/filters/viz.content_browsertests.filter
@@ -1 +1,7 @@
 ### Mac
+# BrowserSideFlingBrowserTest timing out https://crbug.com/842325
+-BrowserSideFlingBrowserTest.AutoscrollFling
+-BrowserSideFlingBrowserTest.TouchpadFling
+-BrowserSideFlingBrowserTest.TouchscreenFling
+-SitePerProcessBrowserTest.TouchpadGestureFlingStart
+-SitePerProcessBrowserTest.TouchscreenGestureFlingStart
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 93f52cb2..117a7dc 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -725,27 +725,6 @@
             ]
         }
     ],
-    "BetterMultiTabsLoading": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "PageAlmostIdleSignalEnabled2",
-                    "params": {
-                        "mainThreadTaskLoadLowThreshold": "25"
-                    },
-                    "enable_features": [
-                        "PageAlmostIdle"
-                    ]
-                }
-            ]
-        }
-    ],
     "BlinkSchedulerDedicatedWorkerThrottling": [
         {
             "platforms": [
@@ -3195,6 +3174,32 @@
             ]
         }
     ],
+    "ProactiveTabFreezeAndDiscard": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "FreezeAndDiscardWithUnfreeze",
+                    "params": {
+                        "ShouldPeriodicallyUnfreeze": "true",
+                        "ShouldProactivelyDiscard": "true",
+                        "mainThreadTaskLoadLowThreshold": "25"
+                    },
+                    "enable_features": [
+                        "PageAlmostIdle",
+                        "PageLifecycle",
+                        "ProactiveTabFreezeAndDiscard",
+                        "SiteCharacteristicsDatabase"
+                    ]
+                }
+            ]
+        }
+    ],
     "ProgressBarAnimationAndroid": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 3169640..c7bdd71 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -583,7 +583,6 @@
 crbug.com/591099 fast/writing-mode/border-radius-clipping-vertical-lr.html [ Failure ]
 crbug.com/714962 fast/writing-mode/border-styles-vertical-lr.html [ Failure ]
 crbug.com/591099 fast/writing-mode/fieldsets.html [ Failure ]
-crbug.com/714962 fast/writing-mode/flipped-blocks-hit-test-line-edges.html [ Failure ]
 crbug.com/591099 fast/writing-mode/percentage-height-orthogonal-writing-modes.html [ Failure ]
 crbug.com/591099 fast/writing-mode/table-percent-width-quirk.html [ Pass ]
 crbug.com/591099 fullscreen/full-screen-with-flex-item.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 36f82ef..c44a5d46 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -490,7 +490,6 @@
 Bug(none) fast/events/touch/compositor-touch-hit-rects-transform-changed-nolayout.html [ Failure ]
 Bug(none) fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
 Bug(none) fast/events/touch/gesture/gesture-scroll-by-page.html [ Failure ]
-Bug(none) fast/events/touch/gesture/gesture-scroll-by-pixel.html [ Failure ]
 Bug(none) fast/events/touch/touch-handler-assert-input-range.html [ Failure ]
 Bug(none) fast/events/touch/touch-rect-assert-first-layer-special.html [ Failure ]
 Bug(none) fast/events/touch/touch-rect-crash-on-unpromote-layer.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index da1a2c3..e653287f 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -139,24 +139,20 @@
 crbug.com/680635 external/wpt/offscreen-canvas/text/2d.text.draw.stroke.basic-manual.worker.js [ Skip ]
 
 # With removing fling handling logic from renderer Layouttests that are using event sender to send fling events are failing.
-crbug.com/857490 fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
 crbug.com/857490 fast/events/touch/gesture/pad-gesture-cancel.html [ Skip ]
 crbug.com/857490 fast/events/touch/gesture/pad-gesture-fling.html [ Skip ]
 crbug.com/857490 fast/events/touch/gesture/touch-gesture-fling-with-page-scale.html [ Skip ]
 crbug.com/857490 fast/events/wheel/mainthread-touchpad-fling-latching.html [ Skip ]
 crbug.com/857490 fast/events/wheel/wheel-fling-cancel.html [ Skip ]
-crbug.com/857490 virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
 crbug.com/857490 virtual/mouseevent_fractional/fast/events/touch/gesture/pad-gesture-cancel.html [ Skip ]
 crbug.com/857490 virtual/mouseevent_fractional/fast/events/touch/gesture/pad-gesture-fling.html [ Skip ]
 crbug.com/857490 virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-fling-with-page-scale.html [ Skip ]
 crbug.com/857490 virtual/mouseevent_fractional/fast/events/wheel/mainthread-touchpad-fling-latching.html [ Skip ]
 crbug.com/857490 virtual/mouseevent_fractional/fast/events/wheel/wheel-fling-cancel.html [ Skip ]
-crbug.com/857490 virtual/scroll_customization/fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
 crbug.com/857490 virtual/scroll_customization/fast/events/touch/gesture/pad-gesture-cancel.html [ Skip ]
 crbug.com/857490 virtual/scroll_customization/fast/events/touch/gesture/pad-gesture-fling.html [ Skip ]
 crbug.com/857490 virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-fling-with-page-scale.html [ Skip ]
 crbug.com/857490 virtual/scroll_customization/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html [ Skip ]
-crbug.com/857490 virtual/user-activation-v2/fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
 crbug.com/857490 virtual/user-activation-v2/fast/events/touch/gesture/pad-gesture-cancel.html [ Skip ]
 crbug.com/857490 virtual/user-activation-v2/fast/events/touch/gesture/pad-gesture-fling.html [ Skip ]
 crbug.com/857490 virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-fling-with-page-scale.html [ Skip ]
@@ -2245,6 +2241,90 @@
 crbug.com/613672 [ Mac ] fast/events/pointerevents/multi-touch-events.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/pointerevents/multi-touch-events.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/multi-touch-events.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/gesture-scrollbar.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-iframe.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-input-field.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/gesture-scrollbar.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-iframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-input-field.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/gesture-scrollbar.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-iframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-input-field.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scrollbar-fling.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/gesture-scrollbar.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-iframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-input-field.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/gesture-scroll.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/gesture-scrollbar-mainframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/gesture-scrollbar.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-iframe.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-input-field.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/paint-touchaction-rects/fast/events/touch/gesture/touch-gesture-scroll-page.html [ Skip ]
 
 crbug.com/802067 [ Mac ] external/wpt/pointerlock/movementX_Y_basic-manual.html [ Failure ]
 crbug.com/802067 [ Mac ] external/wpt/pointerevents/pointerlock/pointerevent_movementxy-manual.html [ Failure ]
@@ -3484,9 +3564,6 @@
 crbug.com/636055 external/wpt/css/css-multicol/multicol-span-all-margin-nested-002.xht [ Failure ]
 crbug.com/792446 external/wpt/css/css-multicol/multicol-span-float-001.xht [ Failure ]
 
-# Skip a virtual test that wpt-importer is unable to update baseline for correctly.
-crbug.com/866802 virtual/video-surface-layer/external/wpt/picture-in-picture/idlharness.window.html [ Skip ]
-
 # This test times out on debug builds, see https://crbug.com/755810
 crbug.com/626703 [ Debug ] external/wpt/html/semantics/tabular-data/processing-model-1/span-limits.html [ Skip ]
 
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index b08feee..98760b6f 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -161553,6 +161553,11 @@
      {}
     ]
    ],
+   "pointerevents/idlharness.window-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "pointerevents/pointerevent_styles.css": [
     [
      {}
@@ -238811,9 +238816,9 @@
      {}
     ]
    ],
-   "pointerevents/idlharness.html": [
+   "pointerevents/idlharness.window.js": [
     [
-     "/pointerevents/idlharness.html",
+     "/pointerevents/idlharness.window.html",
      {}
     ]
    ],
@@ -378885,7 +378890,7 @@
    "support"
   ],
   "interfaces/pointerevents.idl": [
-   "5bf09b0822cbd8ecd479092842de1cd80b891269",
+   "b95c7ec12ac6319cebb9ba64eeae4d9dd0c621fc",
    "support"
   ],
   "interfaces/pointerlock.idl": [
@@ -389760,8 +389765,12 @@
    "1f705f9b11e30d844845ff8638b2a60796ecc83e",
    "support"
   ],
-  "pointerevents/idlharness.html": [
-   "590e7c7c798d0b136a50cd65939cae11eaad9f5c",
+  "pointerevents/idlharness.window-expected.txt": [
+   "075be5958698b22a3a3ea958e52b6d9262ab2fcf",
+   "support"
+  ],
+  "pointerevents/idlharness.window.js": [
+   "1e717ff6c68e7c3e7971f1296aa2824510176857",
    "testharness"
   ],
   "pointerevents/pointerevent_attributes_hoverable_pointers-manual.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-expected.txt
index bb6dba0c..4d8f637 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding-expected.txt
@@ -2,5 +2,6 @@
 PASS U+00FF should find U+00FF
 PASS Percent-encoded UTF-8 BOM should find U+FEFF as BOM is not stripped when decoding
 FAIL %FF should not find U+00FF as decoding it gives U+FFFD assert_equals: #%FF expected 0 but got 9416
+PASS Valid UTF-8 + invalid UTF-8 should not be matched to the utf8-decoded former + the isomorphic-decoded latter
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html
index c971911f..21fbd46 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/scroll-to-fragid/fragment-and-encoding.html
@@ -7,6 +7,7 @@
 <div style=height:10000px></div>
 <div id=&#xFF;></div>
 <div id=&#xFEFF;></div>
+<div id=&#x2661;&#x00FF;><div>
 <script>
 function goToTop() {
   location.hash = "top";
@@ -34,4 +35,16 @@
   location.hash = "%FF";
   assert_equals(self.scrollY, 0, "#%FF");
 }, "%FF should not find U+00FF as decoding it gives U+FFFD");
+
+test(() => {
+  goToTop();
+
+  // U+2661 in UTF-8 + %FF.
+  // Chrome had an issue that the following fragment was decoded as U+2661 U+00FF.
+  // https://github.com/whatwg/html/pull/3111
+  location.hash = "%E2%99%A1%FF";
+  assert_equals(self.scrollY, 0, "%E2%99%A1%FF");
+
+  goToTop();
+}, "Valid UTF-8 + invalid UTF-8 should not be matched to the utf8-decoded former + the isomorphic-decoded latter");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/pointerevents.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/pointerevents.idl
index ef577f8..be4fdd51 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/pointerevents.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/pointerevents.idl
@@ -4,37 +4,38 @@
 // See: https://w3c.github.io/pointerevents/
 
 dictionary PointerEventInit : MouseEventInit {
-    long      pointerId = 0;
-    double    width = 1;
-    double    height = 1;
-    float     pressure = 0;
-    float     tangentialPressure = 0;
-    long      tiltX = 0;
-    long      tiltY = 0;
-    long      twist = 0;
+    long pointerId = 0;
+    double width = 1;
+    double height = 1;
+    float pressure = 0;
+    float tangentialPressure = 0;
+    long tiltX = 0;
+    long tiltY = 0;
+    long twist = 0;
     DOMString pointerType = "";
-    boolean   isPrimary = false;
+    boolean isPrimary = false;
 };
 
-[Constructor(DOMString type, optional PointerEventInit eventInitDict),
- Exposed=Window]
+[Constructor(DOMString type, optional PointerEventInit eventInitDict), Exposed=Window]
 interface PointerEvent : MouseEvent {
-    readonly attribute long      pointerId;
-    readonly attribute double    width;
-    readonly attribute double    height;
-    readonly attribute float     pressure;
-    readonly attribute float     tangentialPressure;
-    readonly attribute long      tiltX;
-    readonly attribute long      tiltY;
-    readonly attribute long      twist;
-    readonly attribute DOMString pointerType;
-    readonly attribute boolean   isPrimary;
+    readonly        attribute long pointerId;
+    readonly        attribute double width;
+    readonly        attribute double height;
+    readonly        attribute float pressure;
+    readonly        attribute float tangentialPressure;
+    readonly        attribute long tiltX;
+    readonly        attribute long tiltY;
+    readonly        attribute long twist;
+    readonly        attribute DOMString pointerType;
+    readonly        attribute boolean isPrimary;
 };
+
 partial interface Element {
-    void    setPointerCapture(long pointerId);
-    void    releasePointerCapture(long pointerId);
-    boolean hasPointerCapture(long pointerId);
+  void setPointerCapture(long pointerId);
+  void releasePointerCapture(long pointerId);
+  boolean hasPointerCapture(long pointerId);
 };
+
 partial interface GlobalEventHandlers {
     attribute EventHandler ongotpointercapture;
     attribute EventHandler onlostpointercapture;
@@ -47,6 +48,7 @@
     attribute EventHandler onpointerenter;
     attribute EventHandler onpointerleave;
 };
+
 partial interface Navigator {
-    readonly attribute long maxTouchPoints;
+    readonly  attribute long maxTouchPoints;
 };
diff --git a/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.html b/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.html
deleted file mode 100644
index 90f3c1cd..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>idlharness test</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script src="/resources/WebIDLParser.js"></script>
-<script src="/resources/idlharness.js"></script>
-
-<pre id='untested_idl' style='display:none'>
-[Global=Window, Exposed=Window]
-interface Window {
-};
-
-[TreatNonObjectAsNull]
-callback EventHandlerNonNull = any (Event event);
-typedef EventHandlerNonNull? EventHandler;
-
-[NoInterfaceObject]
-interface GlobalEventHandlers {
-};
-Window implements GlobalEventHandlers;
-
-interface Navigator {
-};
-
-interface Element {
-};
-
-interface HTMLElement : Element {
-};
-HTMLElement implements GlobalEventHandlers;
-
-interface Document {
-};
-Document implements GlobalEventHandlers;
-
-interface MouseEvent {
-};
-</pre>
-
-<script>
-  promise_test(async function() {
-    const dom = await fetch('/interfaces/dom.idl').then(r => r.text());
-    const uievents = await fetch('/interfaces/uievents.idl').then(r => r.text());
-    const idl = await fetch('/interfaces/pointerevents.idl').then(r => r.text());
-
-    const idl_array = new IdlArray();
-    idl_array.add_untested_idls(dom, { only: ['EventInit'] });
-    idl_array.add_untested_idls(uievents, { only: [
-      'UIEventInit',
-      'MouseEventInit',
-      'EventModifierInit']
-    });
-    idl_array.add_untested_idls(
-        document.getElementById("untested_idl").textContent);
-    idl_array.add_idls(document.getElementById("idl").textContent);
-
-    // Note that I don't bother including Document here because there are still
-    // a bunch of differences between browsers around Document vs HTMLDocument.
-    idl_array.add_objects({
-      Window: ["window"],
-      Navigator: ["navigator"]
-    });
-    idl_array.test();
-  }, 'pointerevents interfaces');
-</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.window-expected.txt
new file mode 100644
index 0000000..768dea4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.window-expected.txt
@@ -0,0 +1,61 @@
+This is a testharness.js-based test.
+Found 57 tests; 51 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS pointerevents interfaces
+PASS Partial interface Element: original interface defined
+PASS Partial interface GlobalEventHandlers: original interface defined
+PASS Partial interface Navigator: original interface defined
+PASS PointerEvent interface: existence and properties of interface object
+PASS PointerEvent interface object length
+PASS PointerEvent interface object name
+PASS PointerEvent interface: existence and properties of interface prototype object
+PASS PointerEvent interface: existence and properties of interface prototype object's "constructor" property
+PASS PointerEvent interface: existence and properties of interface prototype object's @@unscopables property
+PASS PointerEvent interface: attribute pointerId
+PASS Unscopable handled correctly for pointerId property on PointerEvent
+PASS PointerEvent interface: attribute width
+PASS Unscopable handled correctly for width property on PointerEvent
+PASS PointerEvent interface: attribute height
+PASS Unscopable handled correctly for height property on PointerEvent
+PASS PointerEvent interface: attribute pressure
+PASS Unscopable handled correctly for pressure property on PointerEvent
+PASS PointerEvent interface: attribute tangentialPressure
+PASS Unscopable handled correctly for tangentialPressure property on PointerEvent
+PASS PointerEvent interface: attribute tiltX
+PASS Unscopable handled correctly for tiltX property on PointerEvent
+PASS PointerEvent interface: attribute tiltY
+PASS Unscopable handled correctly for tiltY property on PointerEvent
+PASS PointerEvent interface: attribute twist
+PASS Unscopable handled correctly for twist property on PointerEvent
+PASS PointerEvent interface: attribute pointerType
+PASS Unscopable handled correctly for pointerType property on PointerEvent
+PASS PointerEvent interface: attribute isPrimary
+PASS Unscopable handled correctly for isPrimary property on PointerEvent
+PASS PointerEvent must be primary interface of new PointerEvent("type")
+PASS Stringification of new PointerEvent("type")
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "pointerId" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "width" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "height" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "pressure" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "tangentialPressure" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "tiltX" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "tiltY" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "twist" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "pointerType" with the proper type
+PASS PointerEvent interface: new PointerEvent("type") must inherit property "isPrimary" with the proper type
+PASS Element interface: operation setPointerCapture(long)
+PASS Unscopable handled correctly for setPointerCapture(long) on Element
+PASS Element interface: operation releasePointerCapture(long)
+PASS Unscopable handled correctly for releasePointerCapture(long) on Element
+PASS Element interface: operation hasPointerCapture(long)
+PASS Unscopable handled correctly for hasPointerCapture(long) on Element
+FAIL Element interface: document must inherit property "setPointerCapture(long)" with the proper type assert_inherits: property "setPointerCapture" not found in prototype chain
+FAIL Element interface: calling setPointerCapture(long) on document with too few arguments must throw TypeError assert_inherits: property "setPointerCapture" not found in prototype chain
+FAIL Element interface: document must inherit property "releasePointerCapture(long)" with the proper type assert_inherits: property "releasePointerCapture" not found in prototype chain
+FAIL Element interface: calling releasePointerCapture(long) on document with too few arguments must throw TypeError assert_inherits: property "releasePointerCapture" not found in prototype chain
+FAIL Element interface: document must inherit property "hasPointerCapture(long)" with the proper type assert_inherits: property "hasPointerCapture" not found in prototype chain
+FAIL Element interface: calling hasPointerCapture(long) on document with too few arguments must throw TypeError assert_inherits: property "hasPointerCapture" not found in prototype chain
+PASS Navigator interface: attribute maxTouchPoints
+PASS Unscopable handled correctly for maxTouchPoints property on Navigator
+PASS Navigator interface: navigator must inherit property "maxTouchPoints" with the proper type
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.window.js b/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.window.js
new file mode 100644
index 0000000..aa9b11c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/pointerevents/idlharness.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+'use strict';
+
+// https://w3c.github.io/pointerevents/
+
+idl_test(
+  ['pointerevents'],
+  ['uievents', 'dom', 'html'],
+  idl_array => {
+    idl_array.add_objects({
+      Element: ['document'],
+      Window: ['window'],
+      Navigator: ['navigator'],
+      PointerEvent: ['new PointerEvent("type")']
+    });
+  },
+  'pointerevents interfaces'
+);
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-with-negative-child-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-with-negative-child-expected.txt
new file mode 100644
index 0000000..8163fbd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-with-negative-child-expected.txt
@@ -0,0 +1,5 @@
+This tests verifies the hit test regions given to the compositor for an element with child at negative offset. It can only be run in DumpRenderTree.
+
+negativeOffsetChild: DIV#withNegativeOffsetChild[109,0] (0, 1, 102, 12)
+
+
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-with-negative-child.html b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-with-negative-child.html
new file mode 100644
index 0000000..b97255a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/compositor-touch-hit-rects-with-negative-child.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/compositor-touch-hit-rects.css">
+<style>
+#withNegativeOffsetChild {
+    position: relative;
+    width: 100px;
+    height: 10px;
+    border: 1px dotted blue;
+    transform: translateX(calc(120%)) translateZ(0);
+    clear: both;
+}
+#negativeOffsetChild {
+    position: absolute;
+    width: 100px;
+    height: 10px;
+    left: calc(-110%);
+    top: 0px;
+}
+</style>
+</head>
+<body>
+<p id="description">
+This tests verifies the hit test regions given to the compositor for an element with child
+at negative offset. It can only be run in DumpRenderTree.</p>
+
+<div id="tests">
+  <div id="withNegativeOffsetChild">
+      <div id="negativeOffsetChild" class="testcase"></div>
+  </div>
+</div>
+
+<div id="console"></div>
+<script src="resources/compositor-touch-hit-rects.js"></script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-pixel-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-pixel-expected.txt
deleted file mode 100644
index c557902..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-pixel-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This tests gesture scrolling by non-precise pixels.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS document.scrollingElement.scrollTop became 295
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-pixel.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-pixel.html
deleted file mode 100644
index 14eaff6..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-by-pixel.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<style type="text/css">
-::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
-}
-
-#greenbox {
-  width: 100px;
-  height: 2000px;
-  background: green;
-}
-#redbox {
-  width: 100px;
-  height: 2000px;
-  background: red;
-}
-
-</style>
-</head>
-<body style="margin:0" onload="runTest();">
-
-<div id="greenbox"></div>
-<div id="redbox"></div>
-
-<p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
-
-function gestureScroll()
-{
-    eventSender.gestureScrollBegin("touchpad", 10, 20);
-    eventSender.gestureScrollUpdate("touchpad", 0, -100, "Pixels");
-    eventSender.gestureScrollUpdate("touchpad", 0, -215, "Pixels");
-    eventSender.gestureScrollUpdate("touchpad", 0, 20, "Pixels");
-    eventSender.gestureScrollEnd("touchpad", 0, 0);
-
-    shouldBecomeEqual("document.scrollingElement.scrollTop", "295", finishJSTest, 1000);
-}
-
-jsTestIsAsync = true;
-
-function runTest()
-{
-    if (window.eventSender) {
-        description('This tests gesture scrolling by non-precise pixels.');
-        gestureScroll();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
-</script>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-expected.txt
deleted file mode 100644
index a4d0faf37..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-This tests scroll gesture events. Square is (mostly) green on pass
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movingdiv.scrollTop is 0
-PASS movingdiv.scrollLeft is 90
-second gesture
-PASS movingdiv.scrollTop is 95
-PASS movingdiv.scrollLeft is 90
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-object-crash-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-object-crash-expected.txt
deleted file mode 100644
index 4dd7b7d..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-object-crash-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Verifies that scrolling on top of a LayoutEmbeddedContent with null widget works and doesn't crash.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS sentEvents is true
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-object-crash.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-object-crash.html
index 8ea6dac..96dce76 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-object-crash.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll-object-crash.html
@@ -1,4 +1,7 @@
 <!DOCTYPE html>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style>
 #spacer {
   height: 1000px;
@@ -10,27 +13,27 @@
 </style>
 <object id=target></object>
 <div id=spacer></div>
-<script src="../../../../resources/js-test.js"></script>
+
 <script>
-description("Verifies that scrolling on top of a LayoutEmbeddedContent with null widget works and doesn't crash.");
+// This promise gets resolved in the event handler.
+var scrollHandlerResolve;
+scrollHandlerPromise = new Promise(function(resolve) {
+  scrollHandlerResolve = resolve;
+});
 
-var sentEvents = false;
+window.addEventListener('scroll', function(e) {
+    scrollHandlerResolve();
+});
 
-onload = function() {
+promise_test (async () => {
     var target = document.getElementById('target');
     var rect = target.getBoundingClientRect();
     var targetX = rect.left + rect.width / 2;
     var targetY = rect.top + rect.height / 2;
-    eventSender.gestureScrollBegin(targetX, targetY);
-    eventSender.gestureScrollUpdate(0, -20);
-    eventSender.gestureScrollEnd(0,0);
-    sentEvents = true;
-}
-
-window.addEventListener('scroll', function(e) {
-    shouldBeTrue('sentEvents');
-    setTimeout(finishJSTest, 0);
-});
-
-jsTestIsAsync = true;
+    await smoothScroll(20, targetX, targetY, GestureSourceType.TOUCH_INPUT,
+                      "down", SPEED_INSTANT);
+    await waitFor( () => { return document.scrollingElement.scrollTop > 0; });
+    await scrollHandlerPromise;
+}, "Verifies that scrolling on top of a LayoutEmbeddedContent with null " +
+   "widget works and doesn't crash.");
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll.html
index 0c388b9..a63d859 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scroll.html
@@ -1,7 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #gesturetarget {
   width: 100px;
@@ -11,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #movingbox {
@@ -45,8 +45,8 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
+
+<body style="margin:0">
 <div id="gesturetarget">
   <div id="movingbox">
     <table border="0" cellspacing="0px">
@@ -61,87 +61,38 @@
     </table>
   </div>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movingdiv;
+var movingdiv = document.getElementById('movingbox');
 var expectedScrollsTotal = 2;
 var scrollsOccurred = 0;
-var scrollAmountX = ['90', '90'];
-var scrollAmountY = ['0', '95'];
+var scrollAmountX = [90, 90];
+var scrollAmountY = [0, 95];
 
-function checkScrollOffset()
-{
-    movingdiv = document.getElementById('movingbox');
-    if (window.eventSender) {
-        shouldBe('movingdiv.scrollTop', scrollAmountY[scrollsOccurred]);
-        shouldBe('movingdiv.scrollLeft', scrollAmountX[scrollsOccurred]);
-        scrollsOccurred++;
-    }
-
-    if (scrollsOccurred == expectedScrollsTotal) {
-        // If we've got here, we've passed.
-        isSuccessfullyParsed();
-        if (window.testRunner)
-            testRunner.notifyDone();
-    } else {
-        secondGestureScrollSequence();
-    }
+function checkScrollOffset() {
+  // Allow up to two pixels off to avoid flakiness.
+  var pixels = 2;
+  return approx_equals(movingdiv.scrollTop, 
+                       scrollAmountY[scrollsOccurred], pixels) &&
+      approx_equals(movingdiv.scrollLeft,
+                    scrollAmountX[scrollsOccurred], pixels);
 }
 
-function firstGestureScrollSequence()
-{
-    debug("first gesture");
+promise_test (async () => {
+  var x = 95;
+  var y = 12;
+  await smoothScroll(90, x, y, GestureSourceType.TOUCH_INPUT, "right",
+                     SPEED_INSTANT);
+  await waitFor(checkScrollOffset);
+  scrollsOccurred++;
+}, "firstGestureScrollSequence");
 
-    eventSender.gestureScrollBegin(95, 12);
-    eventSender.gestureScrollUpdate(-90, 0);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-function secondGestureScrollSequence()
-{
-    debug("second gesture");
-
-    eventSender.gestureScrollBegin(12, 97);
-    eventSender.gestureScrollUpdate(0, -95);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-function exitIfNecessary()
-{
-    debug('Gesture events not implemented on this platform or broken');
-    isSuccessfullyParsed();
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    var movingdiv = document.getElementById('movingbox');
-
-    if (window.eventSender) {
-        description('This tests scroll gesture events. ' +
-            'Square is (mostly) green on pass');
-
-        if (eventSender.clearTouchPoints)
-            firstGestureScrollSequence();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.");
-    }
-}
+promise_test (async () => {
+  var x = 12;
+  var y = 97;
+  await smoothScroll(95, x, y, GestureSourceType.TOUCH_INPUT, "down",
+                     SPEED_INSTANT);
+  await waitFor(checkScrollOffset);
+}, "secondGestureScrollSequence");
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-expected.txt
deleted file mode 100644
index 8222327..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests scroll gesture events on a scroll bar. The scrollable div below should be slightly scrolled down if successful.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS movingDiv.scrollTop is 0
-PASS movingDiv.scrollTop is 0
-PASS movingDiv.scrollTop is >= 20
-PASS movingDiv.scrollTop is >= 85
-PASS movingDiv.scrollTop is >= 85
-PASS movingDiv.scrollLeft is 0
-PASS movingDiv.scrollLeft is 0
-PASS movingDiv.scrollLeft is >= 20
-PASS movingDiv.scrollLeft is >= 85
-PASS movingDiv.scrollLeft is >= 85
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-fling-expected.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-fling-expected.html
deleted file mode 100644
index 20ceaa9..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-fling-expected.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-
-<!-- This tests that the scrollbar thumb is deselected on a fling start -->
-
-<head>
-<style type="text/css">
-::-webkit-scrollbar {
-  background-color: #ccc;
-  height: 15px;
-  width: 15px;
-}
-
-::-webkit-scrollbar-button {
-  display: none;
-}
-
-::-webkit-scrollbar-thumb {
-  background-color: #777;
-  height: 15px;
-  width: 15px;
-}
-
-::-webkit-scrollbar-thumb:active {
-  background-color: #333;
-}
-
-#scrollable {
-    height: 300px;
-    width: 300px;
-    overflow: scroll;
-}
-
-.large {
-    height: 600px;
-    width: 600px;
-}
-</style>
-</head>
-<body>
-<div id="scrollable">
-  <div class="large">
-  </div>
-</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-fling.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-fling.html
index 026a0c2..7c4c3d5f 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-fling.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-fling.html
@@ -1,9 +1,10 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 
 <!-- This tests that the scrollbar thumb is deselected on a fling start -->
 
-<head>
 <style type="text/css">
 ::-webkit-scrollbar {
   background-color: #ccc;
@@ -26,61 +27,31 @@
 }
 
 #scrollable {
-    height: 300px;
-    width: 300px;
-    overflow: scroll;
+  height: 300px;
+  width: 300px;
+  overflow: scroll;
 }
 
 .large {
-    height: 600px;
-    width: 600px;
+  height: 600px;
+  width: 600px;
 }
 </style>
-</head>
-<body onload="runTest();">
+
 <div id="scrollable">
   <div class="large">
   </div>
 </div>
-<div id="console"></div>
 
 <script type="text/javascript">
+internals.settings.setMockScrollbarsEnabled(true);
 
-
-function scrollTest() {
-    var movingDiv;
-    movingDiv = document.getElementById('scrollable');
-    var scrollbarX = movingDiv.offsetLeft + movingDiv.offsetWidth - 5;
-    var scrollThumbSafeOffset = 80;
-    var scrollbarY = movingDiv.offsetTop + scrollThumbSafeOffset;
-
-    // Ensure we use a touch with an area to test under touch adjustment
-    var touchWidth = 25;
-    var touchHeight = 25;
-
-    eventSender.gestureTapDown(scrollbarX, scrollbarY, touchWidth, touchHeight);
-    eventSender.gestureFlingStart(0, 0, 1, 0, "touchscreen");
-}
-
-function exitIfNecessary()
-{
-    debug('Gesture events not implemented on this platform or broken');
-    isSuccessfullyParsed();
-}
-
-function runTest()
-{
-    internals.settings.setMockScrollbarsEnabled(true);
-
-    if (window.eventSender) {
-        if (eventSender.clearTouchPoints)
-            scrollTest();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.");
-    }
-}
+promise_test (async () => {
+  var movingDiv = document.getElementById('scrollable');
+  var scrollbarX = movingDiv.offsetLeft + movingDiv.offsetWidth - 5;
+  var scrollThumbSafeOffset = 80;
+  var scrollbarY = movingDiv.offsetTop + scrollThumbSafeOffset;
+  await swipe(10, scrollbarX, scrollbarY, "down");
+  await waitFor(() => {return movingDiv.scrollTop > 90; })
+});
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-mainframe.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-mainframe.html
index 042c1b3..193b240 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-mainframe.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-mainframe.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <script src="../../../../resources/testharness.js"></script>
 <script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style>
 ::-webkit-scrollbar {
   background-color: #ccc;
@@ -18,55 +19,35 @@
 }
 
 body {
-    margin: 0;
+  margin: 0;
 }
 
 .large {
-    height: 2000px;
-    width: 600px;
+  height: 2000px;
+  width: 600px;
 }
 </style>
-<div id="console"></div>
-<div class="large">
+<div class="large"></div>
 
 <script type="text/javascript">
-
-// Ensure there's a candidate for touch adjustment.
-document.addEventListener("click", function() {});
-
-test(function(t) {
-  assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
-
-  //if (window.internals)
-  //    internals.settings.setMockScrollbarsEnabled(true);
-
+promise_test (async () => {
   assert_greater_than(window.innerWidth - document.body.clientWidth, 5);
   var scrollbarX = document.body.clientWidth + 5;
   var scrollbarY = window.innerHeight / 2;  // Trying to grab the thumb
-
-  // Ensure we use a touch with an area to test under touch adjustment
-  var touchWidth = 25;
-  var touchHeight = 25;
-
   // Scroll down to ensure we test the difference between content and client
   // co-ordinate spaces.
   window.scrollTo(0, 800);
   assert_equals(window.scrollY, 800);
 
-  eventSender.gestureTapDown(scrollbarX, scrollbarY, touchWidth, touchHeight);
-  eventSender.gestureShowPress(scrollbarX, scrollbarY, touchWidth, touchHeight);
-  eventSender.gestureScrollBegin(scrollbarX - 20, scrollbarY);
-  eventSender.gestureTapCancel(scrollbarX, scrollbarY);
-  assert_equals(window.scrollY, 800);
-  eventSender.gestureScrollUpdate(0, 20);
+  await smoothScroll(20, scrollbarX, scrollbarY, GestureSourceType.TOUCH_INPUT,
+                     "up", SPEED_INSTANT);
   // We don't know exactly how far draging the thumb will scroll.  If we end
-  // up scrolling in the wrong direction it probably means hit tests are passing
-  // through the scrollbar and hitting the content.
-  assert_greater_than(window.scrollY, 820);
-  eventSender.gestureScrollUpdate(0, 60);
-  assert_greater_than(window.scrollY, 885);
-  eventSender.gestureScrollEnd(0, 0);
-  assert_greater_than(window.scrollY, 885);
-}, 'This tests scroll gesture events on main frame scroll bars The document should be slightly scrolled down if successful.');
-
+  // up scrolling in the wrong direction it probably means hit tests are
+  // passing through the scrollbar and hitting the content.
+  await waitFor( () => { return window.scrollY >= 820;});
+  await smoothScroll(60, scrollbarX, scrollbarY, GestureSourceType.TOUCH_INPUT,
+                     "up", SPEED_INSTANT);
+  await waitFor( () => { return window.scrollY >= 885;});
+}, 'This tests scroll gesture events on main frame scroll bars The document ' +
+   'should be slightly scrolled down if successful.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-textarea-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-textarea-expected.txt
deleted file mode 100644
index b3613580..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-textarea-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-This tests scroll gesture events on a textarea scrollbar. The textarea below should be slightly scrolled down if successful.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS textArea.scrollTop is 0
-PASS textArea.scrollTop is >= 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-textarea.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-textarea.html
index fcb8cef..88793547 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-textarea.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar-textarea.html
@@ -1,7 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 ::-webkit-scrollbar {
   background-color: #ccc;
@@ -20,12 +20,10 @@
 }
 
 #text {
-    height: 300px;
-    width: 200px;
+  height: 300px;
+  width: 200px;
 }
 </style>
-</head>
-<body onload="runTest();">
 <textarea id="text">
 Attack your ankles catnip leap hiss chase the red dot, zzz run run I don't like
 that food puking eat feed me.  Attack shed everywhere sleep on your face
@@ -36,62 +34,20 @@
 mousie tail flick stuck in a tree, judging you toss the mousie stretching
 hairball biting attack.  Give me fish eat judging you claw, eat biting I don't
 like that food toss the mousie catnip sleep in the sink toss the mousie purr.
-Sniff sleep on your keyboard eat the grass lick, rip the couch give me fish leap
-bat shed everywhere knock over the lamp jump on the table toss the mousie.
+Sniff sleep on your keyboard eat the grass lick, rip the couch give me fish
+leap bat shed everywhere knock over the lamp jump on the table toss the mousie.
 </textarea>
 
 <script type="text/javascript">
-
-var textArea;
-
-function verticalScrollTest() {
-    textArea = document.getElementById('text');
-    var scrollbarX = textArea.offsetLeft + textArea.offsetWidth - 5;
-    var scrollThumbSafeOffset = 20;
-    var scrollbarY = textArea.offsetTop + scrollThumbSafeOffset;
-
-    // Ensure we use a touch with an area to test under touch adjustment
-    var touchWidth = 25;
-    var touchHeight = 25;
-
-    eventSender.gestureTapDown(
-        scrollbarX, scrollbarY, touchWidth, touchHeight);
-    shouldBe('textArea.scrollTop', '0');
-    eventSender.gestureShowPress(
-        scrollbarX, scrollbarY, touchWidth, touchHeight);
-    eventSender.gestureScrollBegin(
-        scrollbarX, scrollbarY);
-    eventSender.gestureScrollUpdate(0, 20);
-    eventSender.gestureScrollEnd(0, 0);
-    shouldBeGreaterThanOrEqual('textArea.scrollTop', '1');
-
-    // If we've got here, we've passed.
-    isSuccessfullyParsed();
-}
-
-function exitIfNecessary()
-{
-    debug('Gesture events not implemented on this platform or broken');
-    isSuccessfullyParsed();
-}
-
-function runTest()
-{
-    internals.settings.setMockScrollbarsEnabled(true);
-    if (window.eventSender) {
-        description(
-            'This tests scroll gesture events on a textarea scrollbar. ' +
-            'The textarea below should be slightly scrolled down ' +
-            'if successful.');
-
-        if (eventSender.clearTouchPoints)
-            verticalScrollTest();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.");
-    }
-}
+var textArea = document.getElementById('text');
+promise_test (async () => {
+  var scrollbarX = textArea.offsetLeft + textArea.offsetWidth - 5;
+  var scrollThumbSafeOffset = 20;
+  var scrollbarY = textArea.offsetTop + scrollThumbSafeOffset;
+  assert_equals(textArea.scrollTop, 0);
+  await smoothScroll(20, scrollbarX, scrollbarY, GestureSourceType.TOUCH_INPUT,
+                     "up", SPEED_INSTANT);
+  await waitFor( () => { return textArea.scrollTop > 0; });
+}, 'This tests scroll gesture events on a textarea scrollbar. The textarea ' +
+   'below should be slightly scrolled down if successful.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar.html
index cba9bf47..5eda1cc 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/gesture-scrollbar.html
@@ -1,7 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 ::-webkit-scrollbar {
   background-color: #ccc;
@@ -20,101 +20,50 @@
 }
 
 #scrollable {
-    height: 300px;
-    width: 300px;
-    overflow: scroll;
+  height: 300px;
+  width: 300px;
+  overflow: scroll;
 }
 
 .large {
-    height: 600px;
-    width: 600px;
+  height: 600px;
+  width: 600px;
 }
 </style>
-</head>
-<body onload="runTest();">
 <div id="scrollable">
   <div class="large">
   </div>
 </div>
-<div id="console"></div>
 
 <script type="text/javascript">
+internals.settings.setMockScrollbarsEnabled(true);
 
-var movingDiv;
+var movingDiv = document.getElementById('scrollable');
+promise_test (async () => {
+  var scrollbarX = movingDiv.offsetLeft + movingDiv.offsetWidth - 5;
+  var scrollThumbSafeOffset = 80;
+  var scrollbarY = movingDiv.offsetTop + scrollThumbSafeOffset;
+  assert_equals(movingDiv.scrollTop, 0);
+  await smoothScroll(20, scrollbarX, scrollbarY, GestureSourceType.TOUCH_INPUT,
+                     "up", SPEED_INSTANT);
+  await waitFor( () => { return movingDiv.scrollTop >= 20;});
+  await smoothScroll(60, scrollbarX, scrollbarY, GestureSourceType.TOUCH_INPUT,
+                     "up", SPEED_INSTANT);
+  await waitFor( () => { return movingDiv.scrollTop >= 85;});
+}, 'This tests scroll gesture events on a vertical scroll bar. The ' +
+   'scrollable div below should be slightly scrolled down if successful.');
 
-function scrollTest() {
-    movingDiv = document.getElementById('scrollable');
-
-    // Ensure the div is a target for touch adjustment.
-    movingDiv.addEventListener('click', function() {});
-
-    var scrollbarX = movingDiv.offsetLeft + movingDiv.offsetWidth - 5;
-    var scrollThumbSafeOffset = 80;
-    var scrollbarY = movingDiv.offsetTop + scrollThumbSafeOffset;
-
-    // Ensure we use a touch with an area to test under touch adjustment
-    var touchWidth = 25;
-    var touchHeight = 25;
-
-    shouldBe('movingDiv.scrollTop', '0');
-    eventSender.gestureTapDown(scrollbarX, scrollbarY, touchWidth, touchHeight);
-    eventSender.gestureShowPress(scrollbarX, scrollbarY, touchWidth, touchHeight);
-    eventSender.gestureScrollBegin(scrollbarX + 20, scrollbarY);
-    eventSender.gestureTapCancel(scrollbarX, scrollbarY);
-    shouldBe('movingDiv.scrollTop', '0');
-    eventSender.gestureScrollUpdate(0, 20);
-    shouldBeGreaterThanOrEqual('movingDiv.scrollTop', '20');
-    eventSender.gestureScrollUpdate(0, 60);
-    shouldBeGreaterThanOrEqual('movingDiv.scrollTop', '85');
-    eventSender.gestureScrollEnd(0, 0);
-    shouldBeGreaterThanOrEqual('movingDiv.scrollTop', '85');
-
-    // Test horizontal scrollbar too
-    scrollbarY = movingDiv.offsetTop + movingDiv.offsetHeight - 5;
-    scrollbarX = movingDiv.offsetLeft + scrollThumbSafeOffset;
-    shouldBe('movingDiv.scrollLeft', '0');
-    eventSender.gestureTapDown(scrollbarX, scrollbarY, touchWidth, touchHeight);
-    eventSender.gestureShowPress(scrollbarX, scrollbarY, touchWidth, touchHeight);
-    eventSender.gestureScrollBegin(scrollbarX, scrollbarY + 20);
-    eventSender.gestureTapCancel(scrollbarX, scrollbarY);
-    shouldBe('movingDiv.scrollLeft', '0');
-    eventSender.gestureScrollUpdate(20, 0);
-    shouldBeGreaterThanOrEqual('movingDiv.scrollLeft', '20');
-    eventSender.gestureScrollUpdate(60, 0);
-    shouldBeGreaterThanOrEqual('movingDiv.scrollLeft', '85');
-    eventSender.gestureScrollEnd(0, 0);
-    shouldBeGreaterThanOrEqual('movingDiv.scrollLeft', '85');
-
-    // If we've got here, we've passed.
-    isSuccessfullyParsed();
-}
-
-function exitIfNecessary()
-{
-    debug('Gesture events not implemented on this platform or broken');
-    isSuccessfullyParsed();
-}
-
-function runTest()
-{
-    internals.settings.setMockScrollbarsEnabled(true);
-
-    if (window.testRunner)
-        testRunner.dumpAsText();
-
-    if (window.eventSender) {
-        description('This tests scroll gesture events on a scroll bar. ' +
-            'The scrollable div below should be slightly scrolled down ' +
-            'if successful.');
-
-        if (eventSender.clearTouchPoints)
-            scrollTest();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.");
-    }
-}
+promise_test (async () => {
+  var scrollbarY = movingDiv.offsetTop + movingDiv.offsetHeight - 5;
+  var scrollThumbSafeOffset = 80;
+  var scrollbarX = movingDiv.offsetLeft + scrollThumbSafeOffset;
+  assert_equals(movingDiv.scrollLeft, 0);
+  await smoothScroll(20, scrollbarX, scrollbarY, GestureSourceType.TOUCH_INPUT,
+                     "left", SPEED_INSTANT);
+  await waitFor( () => { return movingDiv.scrollLeft >= 20;});
+  await smoothScroll(60, scrollbarX, scrollbarY, GestureSourceType.TOUCH_INPUT,
+                     "left", SPEED_INSTANT);
+  await waitFor( () => { return movingDiv.scrollLeft >= 85; });
+}, 'This tests scroll gesture events on a horizontal scroll bar. The ' +
+   'scrollable div below should be slightly scrolled right if successful.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/resources/scroll-inside-editable-iframe-promise-resolve-on-load.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/resources/scroll-inside-editable-iframe-promise-resolve-on-load.html
new file mode 100644
index 0000000..ad592a9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/resources/scroll-inside-editable-iframe-promise-resolve-on-load.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+#greenbox {
+  width: 100px;
+  height: 100px;
+  background: green;
+  padding: 0px;
+  margin: 0px;
+}
+#redbox {
+  width: 100px;
+  height: 100px;
+  background: red;
+  padding: 0px;
+  margin: 0px;
+}
+
+td {
+  padding: 0px;
+}
+</style>
+</head>
+<body style="margin:0" onload="parent.iframeLoadResolve()" contenteditable>
+<table id="table_to_fill">
+    <tr><td><div id="greenbox"></div></td></tr>
+    <tr><td><div id="redbox"></div></td></tr>
+    <tr><td><div id="greenbox"></div></td></tr>
+    <tr><td><div id="redbox"></div></td></tr>
+    <tr><td><div id="greenbox"></div></td></tr>
+    <tr><td><div id="redbox"></div></td></tr>
+    <tr><td><div id="greenbox"></div></td></tr>
+    <tr><td><div id="redbox"></div></td></tr>
+</table>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/resources/scroll-inside-iframe.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/resources/scroll-inside-iframe.html
index 94f7887..4fa5199e 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/resources/scroll-inside-iframe.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/resources/scroll-inside-iframe.html
@@ -21,7 +21,7 @@
 }
 </style>
 </head>
-<body style="margin:0" onload="parent.runTest()">
+<body style="margin:0" onload="parent.iframeLoadResolve()">
 
 <table id="table_to_fill">
     <tr><td><div id="greenbox"></div></td></tr>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-fully-scrolled-iframe-propagates-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-fully-scrolled-iframe-propagates-expected.txt
deleted file mode 100644
index f732b93..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-fully-scrolled-iframe-propagates-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-
-This tests that a fling gesture sent to an iframe with no remaining scroll offset is correctly targeting the parent container.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS iframeScrollingElement.scrollHeight - iframeScrollingElement.scrollTop is iframe.clientHeight
-PASS movedbox.scrollTop is 50
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-fully-scrolled-iframe-propagates.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-fully-scrolled-iframe-propagates.html
index 8abee07..e51e6853 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-fully-scrolled-iframe-propagates.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-fully-scrolled-iframe-propagates.html
@@ -1,13 +1,11 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
-
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #bluebox {
@@ -19,10 +17,10 @@
 }
 
 #outerdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 200px;
+  height: 200px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 td {
@@ -30,92 +28,54 @@
 }
 
 </style>
-</head>
 <body style="margin:0" >
-
 <div id="outerdiv">
   <table border="0" cellspacing="0px" >
     <tr><td>
-        <iframe style="display: block;" frameBorder="0" id="touchtargetiframe" src="resources/scroll-inside-editable-iframe.html"></iframe>
+        <iframe style="display: block;" frameBorder="0" id="touchtargetiframe"
+            src="resources/scroll-inside-editable-iframe-promise-resolve-on-load.html"></iframe>
     </td></tr>
     <tr><td>
       <div id="bluebox"></div>
     </td></tr>
   </table>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movedbox;
-var touchtarget;
+var movedbox = document.getElementById("outerdiv");
+var touchtarget = document.getElementById("touchtargetiframe");
 var iframe;
 var iframeScrollingElement;
-var iframeDocumentElement;
 var expectedGesturesTotal = 1;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0'];
-var scrollAmountY = ['50'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0'];
+var scrollAmountX = [0];
+var scrollAmountY = [50];
+var scrolledElement = movedbox;
 var scrollEventsOccurred = 0;
-var scrolledElement = 'movedbox';
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
+var expectedScrollEventsOccurred = 1;
 
-function firstGestureScroll()
-{
-    iframe = touchtarget;
-    iframeScrollingElement = iframe.contentDocument.scrollingElement;
-    
-    var amountToScroll = iframeScrollingElement.scrollHeight - iframe.clientHeight;
+promise_test (async () => {
+  await iframeOnLoadPromise;
+  touchtarget.contentDocument.addEventListener("scroll", recordScroll);
+  iframe = touchtarget;
+  iframeScrollingElement = iframe.contentDocument.scrollingElement;
+  var amountToScroll = iframeScrollingElement.scrollHeight - iframe.clientHeight;
+  iframeScrollingElement.scrollTop = amountToScroll;
 
-    iframeScrollingElement.scrollTop = amountToScroll;
+  // Sanity - make sure the iframe is actually fully scrolled
+  assert_equals(iframeScrollingElement.scrollHeight -
+      iframeScrollingElement.scrollTop, iframe.clientHeight,
+      "iframe must be fully scrolled");
+  var x = 10;
+  var y = 72;
+  // scroll for 63 pixels or till the outer div is fully scrolled.
+  scrollAmountY[0] = Math.min(movedbox.scrollHeight - movedbox.clientHeight, 63);
+  await smoothScroll(63, x, y, GestureSourceType.TOUCH_INPUT, "down");
 
-    // Sanity - make sure the iframe is actually fully scrolled
-    shouldBe('iframeScrollingElement.scrollHeight - iframeScrollingElement.scrollTop', 'iframe.clientHeight');
-
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -20);  
-    eventSender.gestureScrollUpdate(0, -18);
-    eventSender.gestureScrollUpdate(0, -15);
-    eventSender.gestureScrollUpdate(0, -10);
-    eventSender.gestureScrollEnd(0, 0);
-
-    amountToScroll = movedbox.scrollHeight - movedbox.clientHeight;
-    scrollAmountY[0] = amountToScroll.toString();
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    movedbox = document.getElementById("outerdiv");
-    touchtarget = document.getElementById("touchtargetiframe");
-
-    touchtarget.contentDocument.addEventListener("scroll", recordScroll);
-    touchtarget.contentDocument.documentElement.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a fling gesture sent to an iframe with no remaining scroll offset ' +
-                    'is correctly targeting the parent container.');
-        
-        if (checkTestDependencies() && eventSender.gestureScrollUpdate)
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
+  // Wait for layout.
+  await waitFor(checkScrollOffset);
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+}, 'This tests that a scroll gesture sent to an iframe with no remaining ' +
+    'scroll offset is correctly targeting the parent container.');
 </script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-expected.txt
deleted file mode 100644
index c4aed1d..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This tests that a page cannot be scrolled with touch if its body has style overflow:hidden.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS document.documentElement.scrollTop is 0
-PASS document.documentElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.documentElement.scrollTop is 0
-PASS document.documentElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-PASS scrollEventsOccurred is 0
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-propagated-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-propagated-expected.txt
deleted file mode 100644
index c5e49a2..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-propagated-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-This tests that a page cannot be scrolled vertically with touch if its body has style overflow-y:hidden and the scroll event is propogated from a scrollable child div.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-first gesture
-PASS document.documentElement.scrollTop is 0
-PASS document.documentElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.documentElement.scrollTop is 0
-PASS document.documentElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-PASS scrollEventsOccurred is 0
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-propagated.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-propagated.html
index 7ad6e2b..3cb2e6f 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-propagated.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-propagated.html
@@ -1,8 +1,7 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -36,9 +35,7 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0; overflow-y:hidden;" onload="runTest();">
-
+<body style="margin:0; overflow-y:hidden;" onload="buildPage();">
 <div id="touchtarget">
   <table border="0" cellspacing="0px" id="tablefoo">
     <tr>
@@ -56,102 +53,68 @@
     <tr><td><div id="greenbox"></div></td></tr>
     <tr><td><div id="redbox"></div></td></tr>
 </table>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [0, 0];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '0';
-var scrolledElement = 'document.documentElement';
+var expectedScrollEventsOccurred = 0;
+var scrolledElement = document.scrollingElement;
 
 // Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    var table = document.getElementById('table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 2;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
+function buildPage() {
+  var table = document.getElementById('table_to_fill');
+  var targetHeight = document.body.offsetHeight;
+  var cellPairHeight = table.offsetHeight;
+  var numberOfReps = targetHeight / cellPairHeight * 2;
+  var i;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="greenbox"></div></td>';
+    table.appendChild(p);
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="redbox"></div></td>';
+    table.appendChild(p);
+  }
 
-    window.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
+  window.addEventListener("scroll", recordScroll);
 }
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(95, 12);
-    eventSender.gestureScrollUpdate(0, -100);
-    eventSender.gestureScrollUpdate(0, -10);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 95;
+var y = 12;
+function firstGestureScroll() {
+  return smoothScroll(110, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 97);
-    eventSender.gestureScrollUpdate(0, -95);
-    eventSender.gestureScrollUpdate(0, -200);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-
-    // In this test we do not expect any scroll events to
-    // be received by the event listener, so we signal
-    // the end of the test by calling this function instead
-    // of relying on recordScroll() to do it.
-    finishTest();
+function secondGestureScroll() {
+  x = 12;
+  y = 97;
+  return smoothScroll(295, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-window.jsTestIsAsync = true;
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    if (window.eventSender) {
-        description('This tests that a page cannot be scrolled vertically with touch if its body has style overflow-y:hidden ' +
-                    'and the scroll event is propogated from a scrollable child div.');
-
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
+function touchTargetScrollTop() {
+  return document.getElementById('touchtarget').scrollTop;
 }
 
-function finishTest()
-{
-    if (window.eventSender) {
-        if (gesturesOccurred == expectedGesturesTotal) {
-            shouldBe('scrollEventsOccurred', expectedScrollEventsOccurred);
-            successfullyParsed = true;
-            finishJSTest();
-        }
-    }
-}
+promise_test (async () => {
+  await firstGestureScroll();
+  // wait for the iframe to fully scroll, then wait for 100 rafs to make sure
+  // that the rest of the delta does not propagate to the outer div.
+  await waitForAnimationEnd(touchTargetScrollTop, 500, 20);
+  await conditionHolds(() => { return notScrolled(); });
+  
+  // Since the touchTarget div is already at its extent the scrolling
+  // propagates to the document. However the page does not scroll since the
+  // body element has overflow-y:hidden.
+  await secondGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+}, 'This tests that a page cannot be scrolled vertically with touch if its ' +
+   'body has style overflow-y:hidden and the scroll event is propogated ' +
+   'from a scrollable child div.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden-expected.txt
deleted file mode 100644
index 0f5ef11..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests that a page cannot be scrolled horizontally with touch (but can still be scrolled vertically) if its body has style overflow-x:hidden. The scroll events in this test have both an x and y component.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS document.scrollingElement.scrollTop is 110
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.scrollingElement.scrollTop is 205
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html
index a7b89a2..ad8739bed 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html
@@ -1,8 +1,7 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -36,9 +35,8 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0; overflow-x:hidden;" onload="runTest();">
 
+<body style="margin:0; overflow-x:hidden;" onload="buildPage();">
 <table id="horizontal_table_to_fill">
   <tr id="firstrow">
     <td><div id="redbox"></div></td>
@@ -50,99 +48,73 @@
     <tr><td><div id="greenbox"></div></td></tr>
     <tr><td><div id="redbox"></div></td></tr>
 </table>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['110', '205'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [110, 205];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'document.scrollingElement'
+var expectedScrollEventsOccurred = 2;
+var scrolledElement = document.scrollingElement;
 
 // Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    // build vertical table
-    var table = document.getElementById('vertical_table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 2;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
+function buildPage() {
+  // build vertical table
+  var table = document.getElementById('vertical_table_to_fill');
+  var targetHeight = document.body.offsetHeight;
+  var cellPairHeight = table.offsetHeight;
+  var numberOfReps = targetHeight / cellPairHeight * 2;
+  var i;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="greenbox"></div></td>';
+    table.appendChild(p);
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="redbox"></div></td>';
+    table.appendChild(p);
+  }
 
-    // build horizontal table
-    var horizontalTable = document.getElementById('horizontal_table_to_fill');
-    var tableRow = document.getElementById('firstrow');
-    var targetWidth = document.body.offsetWidth;
-    var cellPairWidth = horizontalTable.offsetWidth;
-    numberOfReps = targetWidth / cellPairWidth * 2;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('td');
-        p.innerHTML = '<div id="redbox"></div>';
-        tableRow.appendChild(p);
-        var p = document.createElement('td');
-        p.innerHTML = '<div id="greenbox"></div>';
-        tableRow.appendChild(p);
-    }
+  // build horizontal table
+  var horizontalTable = document.getElementById('horizontal_table_to_fill');
+  var tableRow = document.getElementById('firstrow');
+  var targetWidth = document.body.offsetWidth;
+  var cellPairWidth = horizontalTable.offsetWidth;
+  numberOfReps = targetWidth / cellPairWidth * 2;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('td');
+    p.innerHTML = '<div id="redbox"></div>';
+    tableRow.appendChild(p);
+    var p = document.createElement('td');
+    p.innerHTML = '<div id="greenbox"></div>';
+    tableRow.appendChild(p);
+  }
 
-    window.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
+  window.addEventListener("scroll", recordScroll);
 }
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(95, 12);
-    eventSender.gestureScrollUpdate(-55, -110);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 95;
+var y = 12;
+function firstGestureScroll() {
+  return smoothScroll(110, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 97);
-    eventSender.gestureScrollUpdate(-42, -95);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 12;
+  y = 97;
+  return smoothScroll(95, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    if (window.eventSender) {
-        description('This tests that a page cannot be scrolled horizontally with touch ' +
-                    '(but can still be scrolled vertically) if its body has style overflow-x:hidden. ' +
-                    'The scroll events in this test have both an x and y component.');
-
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
-}
+promise_test (async () => {
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests that a page cannot be scrolled horizontally with touch ' +
+   '(but can still be scrolled vertically) if its body has style ' +
+   'overflow-x:hidden. The scroll events in this test have both an x and y ' +
+   'component.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden-expected.txt
deleted file mode 100644
index c565ecb..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This tests that a page cannot be scrolled vertically with touch (but can still be scrolled horizontally) if its body has style overflow-y:hidden. The scroll events in this test have both an x and y component.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-first gesture
-PASS document.scrollingElement.scrollTop is 0
-PASS document.scrollingElement.scrollLeft is 55
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.scrollingElement.scrollTop is 0
-PASS document.scrollingElement.scrollLeft is 97
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html
index 5e9dd09..3ee79f50 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html
@@ -1,8 +1,7 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -36,9 +35,8 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0; overflow-y:hidden;" onload="runTest();">
 
+<body style="margin:0; overflow-y:hidden;" onload="buildPage();">
 <table id="horizontal_table_to_fill">
   <tr id="firstrow">
     <td><div id="redbox"></div></td>
@@ -50,100 +48,73 @@
     <tr><td><div id="greenbox"></div></td></tr>
     <tr><td><div id="redbox"></div></td></tr>
 </table>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['55', '97'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [55, 97];
+var scrollAmountY = [0, 0];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'document.scrollingElement'
+var expectedScrollEventsOccurred = 2;
+var scrolledElement = document.scrollingElement;
 
 // Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    // build vertical table
-    var table = document.getElementById('vertical_table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 2;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
+function buildPage() {
+  // build vertical table
+  var table = document.getElementById('vertical_table_to_fill');
+  var targetHeight = document.body.offsetHeight;
+  var cellPairHeight = table.offsetHeight;
+  var numberOfReps = targetHeight / cellPairHeight * 2;
+  var i;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="greenbox"></div></td>';
+    table.appendChild(p);
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="redbox"></div></td>';
+    table.appendChild(p);
+  }
 
-    // build horizontal table
-    var horizontalTable = document.getElementById('horizontal_table_to_fill');
-    var tableRow = document.getElementById('firstrow');
-    var targetWidth = document.body.offsetWidth;
-    var cellPairWidth = horizontalTable.offsetWidth;
-    numberOfReps = targetWidth / cellPairWidth * 2;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('td');
-        p.innerHTML = '<div id="redbox"></div>';
-        tableRow.appendChild(p);
-        var p = document.createElement('td');
-        p.innerHTML = '<div id="greenbox"></div>';
-        tableRow.appendChild(p);
-    }
+  // build horizontal table
+  var horizontalTable = document.getElementById('horizontal_table_to_fill');
+  var tableRow = document.getElementById('firstrow');
+  var targetWidth = document.body.offsetWidth;
+  var cellPairWidth = horizontalTable.offsetWidth;
+  numberOfReps = targetWidth / cellPairWidth * 2;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('td');
+    p.innerHTML = '<div id="redbox"></div>';
+    tableRow.appendChild(p);
+    var p = document.createElement('td');
+    p.innerHTML = '<div id="greenbox"></div>';
+    tableRow.appendChild(p);
+  }
 
-    window.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
+  window.addEventListener("scroll", recordScroll);
 }
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(195, 12);
-    eventSender.gestureScrollUpdate(-55, -110);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 195;
+var y = 12;
+function firstGestureScroll() {
+  return smoothScroll(55, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(120, 255);
-    eventSender.gestureScrollUpdate(-42, -95);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 120;
+  y = 255;
+  return smoothScroll(42, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
 }
 
-window.jsTestIsAsync = true;
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    if (window.eventSender) {
-        description('This tests that a page cannot be scrolled vertically with touch ' +
-                    '(but can still be scrolled horizontally) if its body has style overflow-y:hidden. ' +
-                    'The scroll events in this test have both an x and y component.');
-
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
-}
+promise_test (async () => {
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+},'This tests that a page cannot be scrolled vertically with touch ' +
+  '(but can still be scrolled horizontally) if its body has style ' +
+  'overflow-y:hidden. The scroll events in this test have both an x and y ' +
+  'component.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body.html
index 54a500f0..05808d7 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-body.html
@@ -1,8 +1,7 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -36,9 +35,8 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0; overflow:hidden;" onload="runTest();">
 
+<body style="margin:0; overflow:hidden;" onload="buildPage();">
 <table id="horizontal_table_to_fill">
   <tr id="firstrow">
     <td><div id="redbox"></div></td>
@@ -50,116 +48,68 @@
     <tr><td><div id="greenbox"></div></td></tr>
     <tr><td><div id="redbox"></div></td></tr>
 </table>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var expectedGesturesTotal = 2;
-var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '0';
-var scrolledElement = 'document.documentElement';
+var expectedScrollEventsOccurred = 0;
+var scrolledElement = document.documentElement;
 
 // Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    // build vertical table
-    var table = document.getElementById('vertical_table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 2;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
+function buildPage() {
+  // build vertical table
+  var table = document.getElementById('vertical_table_to_fill');
+  var targetHeight = document.body.offsetHeight;
+  var cellPairHeight = table.offsetHeight;
+  var numberOfReps = targetHeight / cellPairHeight * 2;
+  var i;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="greenbox"></div></td>';
+    table.appendChild(p);
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="redbox"></div></td>';
+    table.appendChild(p);
+  }
 
-    // build horizontal table
-    var horizontalTable = document.getElementById('horizontal_table_to_fill');
-    var tableRow = document.getElementById('firstrow');
-    var targetWidth = document.body.offsetWidth;
-    var cellPairWidth = horizontalTable.offsetWidth;
-    numberOfReps = targetWidth / cellPairWidth * 2;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('td');
-        p.innerHTML = '<div id="redbox"></div>';
-        tableRow.appendChild(p);
-        var p = document.createElement('td');
-        p.innerHTML = '<div id="greenbox"></div>';
-        tableRow.appendChild(p);
-    }
+  // build horizontal table
+  var horizontalTable = document.getElementById('horizontal_table_to_fill');
+  var tableRow = document.getElementById('firstrow');
+  var targetWidth = document.body.offsetWidth;
+  var cellPairWidth = horizontalTable.offsetWidth;
+  numberOfReps = targetWidth / cellPairWidth * 2;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('td');
+    p.innerHTML = '<div id="redbox"></div>';
+    tableRow.appendChild(p);
+    var p = document.createElement('td');
+    p.innerHTML = '<div id="greenbox"></div>';
+    tableRow.appendChild(p);
+  }
 
-    window.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
+  window.addEventListener("scroll", recordScroll);
 }
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(95, 12);
-    eventSender.gestureScrollUpdate(-55, -110);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 95;
+var y = 12;
+function firstGestureScroll() {
+  return smoothScroll(55, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 97);
-    eventSender.gestureScrollUpdate(-42, -95);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-
-    // In this test we do not expect any scroll events to
-    // be received by the event listener, so we signal
-    // the end of the test by calling this function instead
-    // of relying on recordScroll() to do it.
-    finishTest();
+function secondGestureScroll() {
+  x = 12;
+  y = 97;
+  return smoothScroll(42, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    if (window.eventSender) {
-        description('This tests that a page cannot be scrolled with touch if its body has style overflow:hidden.');
-
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
-}
-
-function finishTest()
-{
-    if (window.eventSender) {
-        if (gesturesOccurred == expectedGesturesTotal) {
-            shouldBe('scrollEventsOccurred', expectedScrollEventsOccurred);
-            successfullyParsed = true;
-            isSuccessfullyParsed();
-            if (window.testRunner)
-                testRunner.notifyDone();
-        }
-    }
-}
+promise_test (async () => {
+  await firstGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  await secondGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+}, 'This tests that a page cannot be scrolled with touch if its body has ' +
+   'style overflow:hidden.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-div-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-div-expected.txt
deleted file mode 100644
index c712478..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-div-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This tests that a non-scrollable div cannot be scrolled with touch.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movingdiv.scrollTop is 0
-PASS movingdiv.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movingdiv.scrollTop is 0
-PASS movingdiv.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-PASS scrollEventsOccurred is 0
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-div.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-div.html
index 071ba05..8d7c8e7 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-div.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-div.html
@@ -1,8 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -36,8 +35,7 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
+<body style="margin:0">
 <div id="touchtarget">
   <table border="0" cellspacing="0px" id="tablefoo">
     <tr>
@@ -50,83 +48,38 @@
     </tr>
   </table>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movingdiv;
+var movingdiv = document.getElementById('touchtarget');
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [0, 0];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '0';
-var scrolledElement = 'movingdiv'
+var expectedScrollEventsOccurred = 0;
+var scrolledElement = movingdiv;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(95, 12);
-    eventSender.gestureScrollUpdate(-90, 0);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 95;
+var y = 12;
+function firstGestureScroll() {
+  return smoothScroll(90, x, y, GestureSourceType.TOUCH_INPUT, "right",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 97);
-    eventSender.gestureScrollUpdate(0, -95);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-
-    // In this test we do not expect any scroll events to
-    // be received by the event listener, so we signal
-    // the end of the test by calling this function instead
-    // of relying on recordScroll() to do it.
-    finishTest();
+function secondGestureScroll() {
+  y = 97;
+  return smoothScroll(95, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
+promise_test (async () => {
+  movingdiv.addEventListener("scroll", recordScroll);
 
-function runTest()
-{
-    movingdiv = document.getElementById('touchtarget');
-    movingdiv.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a non-scrollable div cannot be scrolled with touch.');
-
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
-}
-
-function finishTest()
-{
-    if (window.eventSender) {
-        if (gesturesOccurred == expectedGesturesTotal) {
-            shouldBe('scrollEventsOccurred', expectedScrollEventsOccurred);
-            successfullyParsed = true;
-            isSuccessfullyParsed();
-            if (window.testRunner)
-                testRunner.notifyDone();
-        }
-    }
-}
+  await firstGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  await secondGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+}, 'This tests that a non-scrollable div cannot be scrolled with touch.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-iframe-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-iframe-expected.txt
deleted file mode 100644
index ce71c4c..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-iframe-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-
-This tests that non-scrollable iframes cannot be scrolled with touch.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS touchtarget.contentDocument.scrollingElement.scrollTop is 0
-PASS touchtarget.contentDocument.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS touchtarget.contentDocument.scrollingElement.scrollTop is 0
-PASS touchtarget.contentDocument.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-PASS scrollEventsOccurred is 0
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-iframe.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-iframe.html
index 5d4ed3c..a7f5726 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-iframe.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-noscroll-iframe.html
@@ -1,8 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 200px;
@@ -12,89 +11,48 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
-
 </style>
-</head>
-<body>
+
 <iframe id="touchtarget" src="resources/scroll-inside-iframe.html" scrolling="no"></iframe>
     <p id="description"></p>
 <div id="console"></div>
-<script type="text/javascript">
 
-var touchtarget;
+<script type="text/javascript">
+var touchtarget = document.getElementById('touchtarget');
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [0, 0];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '0';
-var scrolledElement = 'touchtarget.contentDocument.scrollingElement'
+var expectedScrollEventsOccurred = 0;
+var scrolledElement;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(12, 150);
-    eventSender.gestureScrollUpdate(0, -140);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 12;
+var y = 150;
+function firstGestureScroll() {
+  return smoothScroll(140, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 110);
-    eventSender.gestureScrollUpdate(0, -60);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-
-    // In this test we do not expect any scroll events to
-    // be received by the event listener, so we signal
-    // the end of the test by calling this function instead
-    // of relying on recordScroll() to do it.
-    finishTest();
+function secondGestureScroll() {
+  y = 110;
+  return smoothScroll(60, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
+promise_test (async () => {
+  // Wait for the iframe to be ready before starting the test.
+  await iframeOnLoadPromise;
+  touchtarget.contentDocument.addEventListener("scroll", recordScroll);
+  scrolledElement = touchtarget.contentDocument.scrollingElement;
 
-function runTest()
-{
-    touchtarget = document.getElementById('touchtarget');
-    touchtarget.contentDocument.addEventListener("scroll", recordScroll);
-    touchtarget.contentDocument.body.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that non-scrollable iframes cannot be scrolled with touch.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree .  Touch-scroll the red/green strip.");
-    }
-}
-
-function finishTest()
-{
-    if (window.eventSender) {
-        if (gesturesOccurred == expectedGesturesTotal) {
-            shouldBe('scrollEventsOccurred', expectedScrollEventsOccurred);
-            successfullyParsed = true;
-            isSuccessfullyParsed();
-            if (window.testRunner)
-                testRunner.notifyDone();
-        }
-    }
-}
+  await firstGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  await secondGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+}, 'This tests that non-scrollable iframes cannot be scrolled with touch.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-expected.txt
deleted file mode 100644
index cb6ab12..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests gesture event scrolling of an overflow div.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movingdiv.scrollTop is 0
-PASS movingdiv.scrollLeft is 90
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movingdiv.scrollTop is 95
-PASS movingdiv.scrollLeft is 90
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-not-propagated-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-not-propagated-expected.txt
deleted file mode 100644
index 08cf136..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-not-propagated-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests that a gesture scroll is not propagated from an inner div to an outer div when the inner div has no remaining scroll offset when the preventPropagation flag is set for the GestureScrollUpdate event type.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-not-propagated.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-not-propagated.html
deleted file mode 100644
index 2c799f1..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-not-propagated.html
+++ /dev/null
@@ -1,145 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
-<style type="text/css">
-
-::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
-}
-
-#greenbox {
-  width: 100px;
-  height: 100px;
-  background: green;
-  padding: 0px;
-  margin: 0px;
-}
-
-#redbox {
-  width: 100px;
-  height: 100px;
-  background: red;
-  padding: 0px;
-  margin: 0px;
-}
-
-#bluebox {
-  width: 100px;
-  height: 100px;
-  background: blue;
-  padding: 0px;
-  margin: 0px;
-}
-
-#innerdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
-}
-
-#outerdiv {
-    width: 250px;
-    height: 250px;
-    overflow-y: scroll;
-    overflow-x: scroll;
-}
-
-td {
-  padding: 0px;
-}
-
-</style>
-</head>
-<body style="margin:0" onload="runTest();">
-
-<div id="outerdiv">
-  <table border="0" cellspacing="0px" >
-    <tr><td>
-      <div id="innerdiv">
-        <table border="0" cellspacing="0px" >
-          <tr><td><div id="greenbox"></div></td></tr>
-          <tr><td><div id="redbox"></div></td></tr>
-          <tr><td><div id="greenbox"></div></td></tr>
-          <tr><td><div id="redbox"></div></td></tr>
-        </table>
-      </div>
-    </td></tr>
-    <tr><td>
-      <div id="bluebox"></div>
-    </td></tr>
-  </table>
-</div>
-
-<p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
-
-var movedbox;
-var touchtarget;
-var expectedGesturesTotal = 2;
-var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
-var scrollEventsOccurred = 0;
-var scrolledElement = 'movedbox';
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -150);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 40);
-    eventSender.gestureScrollUpdate(0, -50);
-    eventSender.gestureScrollUpdate(0, -10);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    movedbox = document.getElementById("outerdiv");
-    touchtarget = document.getElementById("innerdiv");
-    touchtarget.addEventListener("scroll", recordScroll);
-    touchtarget.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a gesture scroll is not propagated from an ' +
-                'inner div to an outer div when the inner div has no ' +
-                'remaining scroll offset when the preventPropagation flag is set for ' +
-                'the GestureScrollUpdate event type.');
-        if (checkTestDependencies() && eventSender.gestureScrollUpdate)
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
-</script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally-expected.txt
deleted file mode 100644
index 232541b8..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This tests that a gesture scroll isn't propagated from an inner div to an outer div when the inner div has remaining scroll offset on one axis but not on the other, unless the outer div starts at its scroll extent
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS horizontal.scrollLeft is 15
-PASS vertical.scrollTop is 0
-PASS horizontal.scrollLeft is 600
-PASS vertical.scrollTop is 0
-PASS horizontal.scrollLeft is 600
-PASS vertical.scrollTop is 20
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html
index bfb48836..dc22def7 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html
@@ -1,13 +1,12 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #contents {
@@ -30,59 +29,46 @@
 }
 
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
 
+<body style="margin:0">
 <div id="vertical">
   <div id="horizontal">
     <div id="contents"></div>
   </div>
 </div>
-
-<p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    if (window.eventSender) {
-        description('This tests that a gesture scroll isn\'t propagated from an ' +
-            'inner div to an outer div when the inner div has ' +
-            'remaining scroll offset on one axis but not on the other, unless ' +
-            'the outer div starts at its scroll extent');
-        if (checkTestDependencies()) {
-            eventSender.gestureScrollBegin(10, 10);
-            eventSender.gestureScrollUpdate(-15, -20);
-            eventSender.gestureScrollEnd(0, 0);
-            shouldBe("horizontal.scrollLeft", "15");
-            shouldBe("vertical.scrollTop", "0");
-
-            // Scroll to extents.
-            eventSender.gestureScrollBegin(10, 10);
-            eventSender.gestureScrollUpdate(-1000, -1000);
-            eventSender.gestureScrollEnd(0, 0);
-            shouldBe("horizontal.scrollLeft", "600");
-            shouldBe("vertical.scrollTop", "0");
-
-            eventSender.gestureScrollBegin(10, 10);
-            eventSender.gestureScrollUpdate(-15, -20);
-            eventSender.gestureScrollEnd(0, 0);
-            shouldBe("horizontal.scrollLeft", "600");
-            shouldBe("vertical.scrollTop", "20");
-
-            if (window.testRunner)
-              testRunner.notifyDone();
-        } else {
-            exitIfNecessary();
-        }
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page diagonally to validate the implementation.");
-    }
-}
-</script>
-
 </body>
-</html>
+
+<script type="text/javascript">
+promise_test (async () => {
+  var x = 10;
+  var y = 10;
+  // Scroll diagonally, since the child div scrolls horizontally the unused
+  // delta y does not propagate to the parent div.
+  await smoothScroll(15, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
+  await waitFor( () => {
+      return approx_equals(horizontal.scrollLeft, 15, 2);
+  });
+  assert_equals(vertical.scrollTop, 0);
+
+  // Scroll the horizontal div to extents.
+  await smoothScroll(1000, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
+  await waitFor( () => { return horizontal.scrollLeft == 600; });
+  // Wait for 100 RAFs to make sure the scroll does not propagate to the
+  // vertical div.
+  await conditionHolds(() => { return vertical.scrollTop == 0; });
+
+  // Since the horizontal div cannot consume any scroll delta, scrolling
+  // propagates to its parent div.
+  await smoothScroll(15, x, y, GestureSourceType.TOUCH_INPUT, "downright",
+      SPEED_INSTANT);
+  await waitFor( () => {
+      return approx_equals(vertical.scrollTop, 15, 2);
+  });
+  assert_equals(horizontal.scrollLeft, 600);
+}, 'This tests that a gesture scroll isn\'t propagated from an ' +
+   'inner div to an outer div when the inner div has ' +
+   'remaining scroll offset on one axis but not on the other, unless ' +
+   'the inner div starts at its scroll extent');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-expected.txt
deleted file mode 100644
index 5df885b..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests that a gesture scroll isn't propagated from an inner div to an outer div when the inner div has no remaining scroll offset.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent.html
index 7d1be9b0..13639ec 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent.html
@@ -1,13 +1,12 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -35,17 +34,17 @@
 }
 
 #innerdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 200px;
+  height: 200px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 #outerdiv {
-    width: 250px;
-    height: 250px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 250px;
+  height: 250px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 td {
@@ -53,9 +52,8 @@
 }
 
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
 
+<body style="margin:0">
 <div id="outerdiv">
   <table border="0" cellspacing="0px" >
     <tr><td>
@@ -73,72 +71,48 @@
     </td></tr>
   </table>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movedbox;
 var touchtarget;
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [0, 0];
+var scrolledElement = document.getElementById("outerdiv");
 var scrollEventsOccurred = 0;
-var scrolledElement = 'movedbox'
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
+var expectedScrollEventsOccurred = 2;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -150);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 10;
+var y = 72;
+function firstGestureScroll() {
+  return smoothScroll(150, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 40);
-    eventSender.gestureScrollUpdate(0, -50);
-    eventSender.gestureScrollUpdate(0, -10);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 12;
+  y = 40;
+  return smoothScroll(60, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    movedbox = document.getElementById("outerdiv");
-    touchtarget = document.getElementById("innerdiv");
-    touchtarget.addEventListener("scroll", recordScroll);
-    touchtarget.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a gesture scroll isn\'t propagated from an ' +
-                'inner div to an outer div when the inner div has no ' +
-                'remaining scroll offset.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
+function innerDivScrollTop() {
+  return document.getElementById('innerdiv').scrollTop;
 }
+
+promise_test (async () => {
+  touchtarget = document.getElementById("innerdiv");
+  touchtarget.addEventListener("scroll", recordScroll);
+
+  await firstGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  await secondGestureScroll();
+  // wait for the inner to fully scroll, then wait for 100 rafs to make sure
+  // that the rest of the delta does not propagate to the outer div.
+  await waitForAnimationEnd(innerDivScrollTop, 500, 20);
+  await conditionHolds(() => { return notScrolled(); });
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+}, 'This tests that a gesture scroll isn\'t propagated from an inner div to ' +
+   'an outer div when the inner div has no remaining scroll offset.');
 </script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-removed-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-removed-expected.txt
deleted file mode 100644
index 065e9e2..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-removed-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Verifies that deleting the element being gesture-scrolled immediately before sending the scroll-end does not cause a crash. The test passes if it does not crash.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-starting gesture scroll
-deleting the targeted element
-dispatching the GestureScrollEnd
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-removed.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-removed.html
deleted file mode 100644
index 43ba59a..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-removed.html
+++ /dev/null
@@ -1,77 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<style>
-#container {
-  width: 100px;
-  height: 100px;
-  position: relative;
-  overflow-y: scroll;
-  background: white;
-}
-
-::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
-}
-
-.box {
-  width: 100px;
-  height: 100px;
-  background: red;
-  padding: 0px;
-  margin: 0px;
-}
-
-td {
-  padding: 0px;
-}
-</style>
-</head>
-<body style="margin:0">
-<div id="container">
-  <table border="0" cellspacing="0px" id="tablefoo">
-    <tr>
-      <td><div id="redbox" class="box" style="background-color:red;"></div></td>
-    </tr>
-    <tr>
-      <td><div id="greenbox" class="box" style="background-color:green;"></div></td>
-    </tr>
-  </table>
-</div>
-
-<p id="description"></p>
-<div id="console"></div>
-<script>
-
-if (window.testRunner)
-    testRunner.dumpAsText();
-
-function runTest()
-{
-    if (!window.eventSender) {
-        debug("This test requires DumpRenderTree.");
-        return;
-    }
-
-    description('Verifies that deleting the element being gesture-scrolled immediately before sending the scroll-end does not cause a crash. The test passes if it does not crash.');
-
-    debug("starting gesture scroll");
-    eventSender.gestureScrollBegin(10, 10);
-    eventSender.gestureScrollUpdate(0, -5);
-
-    debug("deleting the targeted element");
-    var target = document.getElementById('redbox');
-    target.parentNode.removeChild(target);
-    target = null;
-    gc();
-
-    debug("dispatching the GestureScrollEnd");
-    eventSender.gestureScrollEnd(0, 0);
-}
-
-runTest();
-</script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled-expected.txt
deleted file mode 100644
index b53f85f..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests gesture event scrolling of an overflow div with page scale.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movingdiv.scrollTop is 0
-PASS movingdiv.scrollLeft is 45
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movingdiv.scrollTop is 47
-PASS movingdiv.scrollLeft is 45
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html
index ad3733c8..5fd035da 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html
@@ -1,8 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #movingbox {
@@ -46,8 +45,7 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
+<body style="margin:0">
 <div id="touchtarget">
   <div id="movingbox">
     <table border="0" cellspacing="0px" id="tablefoo">
@@ -62,78 +60,51 @@
     </table>
   </div>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movingdiv;
+var movingdiv = document.getElementById('movingbox');
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [0, 0];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'movingdiv'
+var expectedScrollEventsOccurred = 2;
+var scrolledElement = movingdiv;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(95, 12);
-    eventSender.gestureScrollUpdate(-90, 0);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 95;
+var y = 12;
+function firstGestureScroll() {
+  return smoothScroll(90, x, y, GestureSourceType.TOUCH_INPUT, "right",
+      SPEED_INSTANT);
 }
 
- function secondGestureScroll()
- {
-     debug("second gesture");
-     eventSender.gestureScrollBegin(12, 97);
-     eventSender.gestureScrollUpdate(0, -95);
-     eventSender.gestureScrollEnd(0, 0);
-
-     // Wait for layout.
-     checkScrollOffset();
- }
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    var scaleFactor = 2.0;
-    if (window.internals)
-      internals.setPageScaleFactor(scaleFactor);
-
-    movingdiv = document.getElementById('movingbox');
-    movingdiv.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender && window.internals) {
-        description('This tests gesture event scrolling of an overflow div with page scale.');
-
-        if (checkTestDependencies()) {
-            if (internals.runtimeFlags.fractionalScrollOffsetsEnabled) {
-                debug("fractional scroll mode");
-                internals.settings.setPreferCompositingToLCDTextEnabled(true);
-                scrollAmountX = ['90', '90'];
-                scrollAmountY = ['47.5', '95'];
-            } else {
-                scrollAmountX = ['45', '45'];
-                scrollAmountY = ['0', '47'];
-            }
-            firstGestureScroll();
-        } else
-            exitIfNecessary();
-
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
+function secondGestureScroll() {
+  x = 12;
+  y = 97;
+  return smoothScroll(95, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
+
+promise_test (async () => {
+  var scaleFactor = 2.0;
+  if (window.internals)
+    internals.setPageScaleFactor(scaleFactor);
+
+  movingdiv.addEventListener("scroll", recordScroll);
+
+  if (internals.runtimeFlags.fractionalScrollOffsetsEnabled) {
+    internals.settings.setPreferCompositingToLCDTextEnabled(true);
+    scrollAmountX = [90, 90];
+    scrollAmountY = [47.5, 95];
+  } else {
+    scrollAmountX = [45, 45];
+    scrollAmountY = [0, 47];
+  }
+
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests gesture event scrolling of an overflow div with page scale.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-twice-past-extent-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-twice-past-extent-expected.txt
deleted file mode 100644
index 407b16aa..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-twice-past-extent-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests that a gesture scroll isn't propagated from an inner div to an outer div when the scrolled divs have no remaining scroll offset.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-twice-past-extent.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-twice-past-extent.html
index ab5137bf..008c438 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-twice-past-extent.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-twice-past-extent.html
@@ -1,13 +1,11 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
-
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -43,24 +41,24 @@
 }
 
 #innerdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 200px;
+  height: 200px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 #outerdiv {
-    width: 250px;
-    height: 250px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 250px;
+  height: 250px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 #outermostdiv {
-    width: 300px;
-    height: 300px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 300px;
+  height: 300px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 td {
@@ -68,9 +66,8 @@
 }
 
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
 
+<body style="margin:0">
 <div id="outermostdiv">
   <table border="0" cellspacing="0px" >
     <tr><td>
@@ -97,73 +94,49 @@
     </td></tr>
   </table>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movedbox;
 var touchtarget;
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [0, 0];
+var scrolledElement = document.getElementById("outermostdiv");
 var scrollEventsOccurred = 0;
-var scrolledElement = 'movedbox'
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
+var expectedScrollEventsOccurred = 1;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -200);
-    eventSender.gestureScrollUpdate(0, -30);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x =10;
+var y = 72;
+function firstGestureScroll() {
+  return smoothScroll(230, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 40);
-    eventSender.gestureScrollUpdate(0, -20);
-    eventSender.gestureScrollUpdate(0, -25);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 12;
+  y = 40;
+  return smoothScroll(45, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    movedbox = document.getElementById("outermostdiv");
-    touchtarget = document.getElementById("innerdiv");
-    touchtarget.addEventListener("scroll", recordScroll);
-    touchtarget.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a gesture scroll isn\'t propagated from an ' +
-                'inner div to an outer div when the scrolled divs have no ' +
-                'remaining scroll offset.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
+function innerDivScrollTop() {
+  return document.getElementById('innerdiv').scrollTop;
 }
+
+promise_test (async () => {
+  touchtarget = document.getElementById("innerdiv");
+  touchtarget.addEventListener("scroll", recordScroll);
+
+  await firstGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  await secondGestureScroll();
+  // wait for the inner to fully scroll, then wait for 100 rafs to make sure
+  // that the rest of the delta does not propagate to the outer most div.
+  await waitForAnimationEnd(innerDivScrollTop, 200, 20);
+  await conditionHolds(() => { return notScrolled(); });
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+
+}, 'This tests that a gesture scroll isn\'t propagated from an inner div to ' +
+   'an outer div when the scrolled divs have no remaining scroll offset.');
 </script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed-expected.txt
deleted file mode 100644
index 70f519c9..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests gesture event scrolling of an overflow div with browser zoom.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movingdiv.scrollTop is 0
-PASS movingdiv.scrollLeft is 80
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movingdiv.scrollTop is 32
-PASS movingdiv.scrollLeft is 80
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html
index 1d9a3a4..b68d3359 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html
@@ -1,8 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #movingbox {
@@ -46,8 +45,7 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
+<body style="margin:0">
 <div id="touchtarget">
   <div id="movingbox">
     <table border="0" cellspacing="0px" id="tablefoo">
@@ -62,68 +60,40 @@
     </table>
   </div>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movingdiv;
+var movingdiv = document.getElementById('movingbox');
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['80', '80'];
-var scrollAmountY = ['0', '32'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [80, 80];
+var scrollAmountY = [0, 32];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'movingdiv'
+var expectedScrollEventsOccurred = 2;
+var scrolledElement = movingdiv;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-
-    internals.setZoomFactor(0.5);
-    eventSender.gestureScrollBegin(45, 12);
-    eventSender.gestureScrollUpdate(-40, 0);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 45;
+var y = 12;
+function firstGestureScroll() {
+  internals.setZoomFactor(0.5);
+  return smoothScroll(40, x, y, GestureSourceType.TOUCH_INPUT, "right",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-
-    internals.setZoomFactor(1.25);
-    eventSender.gestureScrollBegin(12, 47);
-    eventSender.gestureScrollUpdate(0, -40);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 12;
+  y = 47;
+  internals.setZoomFactor(1.25);
+  return smoothScroll(40, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
+promise_test (async () => {
+  movingdiv.addEventListener("scroll", recordScroll);
 
-function runTest()
-{
-    movingdiv = document.getElementById('movingbox');
-    movingdiv.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests gesture event scrolling of an overflow div with browser zoom.');
-
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
-}
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests gesture event scrolling of an overflow div with browser zoom.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div.html
index f5750fc..a2af188 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-div.html
@@ -1,8 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 100px;
@@ -12,8 +11,8 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #movingbox {
@@ -46,8 +45,8 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
+
+<body style="margin:0">
 <div id="touchtarget">
   <div id="movingbox">
     <table border="0" cellspacing="0px" id="tablefoo">
@@ -62,64 +61,38 @@
     </table>
   </div>
 </div>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var movingdiv;
+var movingdiv = document.getElementById('movingbox');
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['90', '90'];
-var scrollAmountY = ['0', '95'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [90, 90];
+var scrollAmountY = [0, 95];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'movingdiv'
+var expectedScrollEventsOccurred = 2;
+var scrolledElement = movingdiv;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(95, 12);
-    eventSender.gestureScrollUpdate(-90, 0);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 95;
+var y = 12;
+function firstGestureScroll() {
+  return smoothScroll(90, x, y, GestureSourceType.TOUCH_INPUT, "right",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 97);
-    eventSender.gestureScrollUpdate(0, -95);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 12;
+  y = 97;
+  return smoothScroll(95, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
+promise_test (async () => {
+  movingdiv.addEventListener("scroll", recordScroll);
 
-function runTest()
-{
-    movingdiv = document.getElementById('movingbox');
-    movingdiv.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests gesture event scrolling of an overflow div.');
-
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch scroll the red rect to log.");
-    }
-}
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests gesture event scrolling of an overflow div.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable-expected.txt
deleted file mode 100644
index 11deaca8..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-
-This tests gesture event scrolling of an iframe in an overflow div. Red-green strip is scrolled on pass.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS touchtarget.contentDocument.scrollingElement.scrollTop is 140
-PASS touchtarget.contentDocument.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS touchtarget.contentDocument.scrollingElement.scrollTop is 200
-PASS touchtarget.contentDocument.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html
index 7e20efc..bdc932a 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html
@@ -1,89 +1,63 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   background: white;
 }
-    
+
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #container {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
-    display: block;
+  width: 200px;
+  height: 200px;
+  overflow-y: scroll;
+  overflow-x: scroll;
+  display: block;
 }
-
 </style>
-</head>
-<body>
-<div id="container">
-<iframe id="touchtarget" src="resources/scroll-inside-editable-iframe.html"></iframe>
-</div>
-    <p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
 
-var touchtarget;
+<div id="container">
+<iframe id="touchtarget" src="resources/scroll-inside-editable-iframe-promise-resolve-on-load.html"></iframe>
+</div>
+
+<script type="text/javascript">
+var touchtarget = document.getElementById('touchtarget');
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['140', '200'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [140, 200];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'touchtarget.contentDocument.scrollingElement'
+var expectedScrollEventsOccurred = 2;
+var scrolledElement;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(12, 150);
-    eventSender.gestureScrollUpdate(0, -140);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 12;
+var y = 150;
+function firstGestureScroll() {
+  return smoothScroll(140, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 110);
-    eventSender.gestureScrollUpdate(0, -60);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  y = 110;
+  return smoothScroll(60, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
-        
-if (window.testRunner)
-    testRunner.waitUntilDone();
     
-function runTest()
-{
-    touchtarget = document.getElementById('touchtarget');
-    touchtarget.contentDocument.addEventListener("scroll", recordScroll);
-    touchtarget.contentDocument.body.addEventListener("mousewheel", recordWheel);
+promise_test (async () => {
+  // Wait for the iframe to be ready before starting the test.
+  await iframeOnLoadPromise;
+  touchtarget.contentDocument.addEventListener("scroll", recordScroll);
+  scrolledElement = touchtarget.contentDocument.scrollingElement;
 
-    if (window.eventSender) {
-        description('This tests gesture event scrolling of an iframe in an overflow div. ' +
-            'Red-green strip is scrolled on pass.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch-scroll the red/green strip.");
-    }
-}
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests gesture event scrolling of an iframe in an overflow div. ' +
+    'Red-green strip is scrolled on pass.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-expected.txt
deleted file mode 100644
index 1307bbb..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-
-This tests gesture event scrolling in iframes. Red-green strip is scrolled on pass.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS touchtarget.contentDocument.scrollingElement.scrollTop is 140
-PASS touchtarget.contentDocument.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS touchtarget.contentDocument.scrollingElement.scrollTop is 200
-PASS touchtarget.contentDocument.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-not-propagated-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-not-propagated-expected.txt
deleted file mode 100644
index 39ae3bef..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-not-propagated-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-
-This tests that a gesture scroll is not propagated from an iframe to an outer div when the iframe has no remaining scroll offset when the preventPropagation flag is set for the gesture type GestureScrollUpdate.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS iframeScrollingElement.scrollHeight - iframeScrollingElement.scrollTop is iframe.clientHeight
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-not-propagated.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-not-propagated.html
deleted file mode 100644
index 8cd71f4..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-not-propagated.html
+++ /dev/null
@@ -1,115 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
-<style type="text/css">
-
-::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
-}
-
-#bluebox {
-  width: 100px;
-  height: 100px;
-  background: blue;
-  padding: 0px;
-  margin: 0px;
-}
-
-#outerdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
-}
-
-td {
-  padding: 0px;
-}
-
-</style>
-</head>
-<body style="margin:0" >
-
-<div id="outerdiv">
-  <table border="0" cellspacing="0px" >
-    <tr><td>
-        <iframe frameBorder="0" style="display: block;" id="touchtargetiframe" src="resources/scroll-inside-editable-iframe.html"></iframe>
-    </td></tr>
-    <tr><td>
-      <div id="bluebox"></div>
-    </td></tr>
-  </table>
-</div>
-
-<p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
-
-var movedbox;
-var touchtarget;
-var iframe;
-var iframeBody;
-var iframeDocumentElement;
-var expectedGesturesTotal = 1;
-var gesturesOccurred = 0;
-var scrollAmountX = ['0'];
-var scrollAmountY = ['0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0'];
-var scrollEventsOccurred = 0;
-var scrolledElement = 'movedbox';
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-
-function firstGestureScroll()
-{
-    iframe = touchtarget;
-    iframeScrollingElement = iframe.contentDocument.scrollingElement;
-
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -700);
-    eventSender.gestureScrollUpdate(0, -100);
-    eventSender.gestureScrollUpdate(0, -70);
-    eventSender.gestureScrollUpdate(0, -10);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Make sure the actual iframe got fully scrolled
-    shouldBe('iframeScrollingElement.scrollHeight - iframeScrollingElement.scrollTop', 'iframe.clientHeight');
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    movedbox = document.getElementById("outerdiv");
-    touchtarget = document.getElementById("touchtargetiframe");
-    touchtarget.contentDocument.addEventListener("scroll", recordScroll);
-    touchtarget.contentDocument.body.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a gesture scroll is not propagated from an ' +
-                'iframe to an outer div when the iframe has no remaining ' +
-                'scroll offset when the preventPropagation flag is set for the gesture '+
-                'type GestureScrollUpdate.');
-        if (checkTestDependencies() && eventSender.gestureScrollUpdate)
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
-</script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-past-extent-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-past-extent-expected.txt
deleted file mode 100644
index e644588b..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-past-extent-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-
-This tests that a gesture scroll isn't propagated from an iframe to an outer div when the iframe has no remaining scroll offset.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS movedbox.scrollTop is 0
-PASS movedbox.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-past-extent.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-past-extent.html
index 345ad0bc..4dac0f79 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-past-extent.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe-past-extent.html
@@ -1,13 +1,11 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
-
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #bluebox {
@@ -19,32 +17,32 @@
 }
 
 #container {
-    width: 150px;
-    height: 150px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 150px;
+  height: 150px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 #outerdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 200px;
+  height: 200px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 td {
   padding: 0px;
 }
-
 </style>
-</head>
-<body style="margin:0" >
 
+<body style="margin:0" >
 <div id="outerdiv">
   <table border="0" cellspacing="0px" >
     <tr><td>
       <div id="container">
-        <iframe id="touchtargetiframe" src="resources/scroll-inside-editable-iframe.html"></iframe>
+        <iframe id="touchtargetiframe"
+          src="resources/scroll-inside-editable-iframe-promise-resolve-on-load.html">
+        </iframe>
       </div>
     </td></tr>
     <tr><td>
@@ -52,72 +50,53 @@
     </td></tr>
   </table>
 </div>
+</body>
 
 <p id="description"></p>
 <div id="console"></div>
 <script type="text/javascript">
-
-var movedbox;
 var touchtarget;
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [0, 0];
 var scrollEventsOccurred = 0;
-var scrolledElement = 'movedbox'
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
+var scrolledElement = document.getElementById("outerdiv");
+var expectedScrollEventsOccurred = 1;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -700);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 10;
+var y = 72;
+function firstGestureScroll() {
+  return smoothScroll(700, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 40);
-    eventSender.gestureScrollUpdate(0, -10);
-    eventSender.gestureScrollUpdate(0, -50);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 12;
+  y = 40;
+  return smoothScroll(60, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    movedbox = document.getElementById("outerdiv");
-    touchtarget = document.getElementById("touchtargetiframe");
-    touchtarget.contentDocument.addEventListener("scroll", recordScroll);
-    touchtarget.contentDocument.body.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a gesture scroll isn\'t propagated from an ' +
-                'iframe to an outer div when the iframe has no remaining ' +
-                'scroll offset.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
+function iframeScrollTop() {
+  return touchtarget.contentDocument.scrollingElement.scrollTop;
 }
+
+promise_test (async () => {
+  // Wait for the iframe to be ready before starting the test.
+  await iframeOnLoadPromise;
+  touchtarget = document.getElementById("touchtargetiframe");
+  touchtarget.contentDocument.addEventListener("scroll", recordScroll);
+
+  await firstGestureScroll();
+  await conditionHolds(() => { return notScrolled(); });
+  await secondGestureScroll();
+  // wait for the iframe to fully scroll, then wait for 100 to make
+  // sure that the scrolling does not propagate to the outer div.
+  await waitForAnimationEnd(iframeScrollTop, 500, 20);
+  await conditionHolds(() => { return notScrolled(); });
+  assert_equals(scrollEventsOccurred, expectedScrollEventsOccurred);
+}, 'This tests that a gesture scroll isn\'t propagated from an ' +
+   'iframe to an outer div when the iframe has no remaining ' +
+   'scroll offset.');
 </script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe.html
index 90dfc5f6..3ea6a410 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-iframe.html
@@ -1,8 +1,7 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 #touchtarget {
   width: 200px;
@@ -12,71 +11,46 @@
 }
 
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
-
 </style>
-</head>
-<body>
-<iframe id="touchtarget" src="resources/scroll-inside-iframe.html"></iframe>
-    <p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
 
-var touchtarget;
+<iframe id="touchtarget" src="resources/scroll-inside-iframe.html"></iframe>
+
+<script type="text/javascript">
+var touchtarget = document.getElementById('touchtarget');;
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['140', '200'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [140, 200];
 var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'touchtarget.contentDocument.scrollingElement'
+var expectedScrollEventsOccurred = 2;
+var scrolledElement;
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(12, 150);
-    eventSender.gestureScrollUpdate(0, -140);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 12;
+var y = 150;
+function firstGestureScroll() {
+  return smoothScroll(140, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 110);
-    eventSender.gestureScrollUpdate(0, -60);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  y = 110;
+  return smoothScroll(60, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
+promise_test (async () => {
+  // Wait for the iframe to be ready before starting the test.
+  await iframeOnLoadPromise;
+  touchtarget.contentDocument.addEventListener("scroll", recordScroll);
+  scrolledElement = touchtarget.contentDocument.scrollingElement;
 
-function runTest()
-{
-    touchtarget = document.getElementById('touchtarget');
-    touchtarget.contentDocument.addEventListener("scroll", recordScroll);
-    touchtarget.contentDocument.body.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests gesture event scrolling in iframes. ' +
-            'Red-green strip is scrolled on pass.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree .  Touch-scroll the red/green strip.");
-    }
-}
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests gesture event scrolling in iframes. ' +
+   'Red-green strip is scrolled on pass.');
 </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-input-field-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-input-field-expected.txt
deleted file mode 100644
index dd5aa912..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-input-field-expected.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-
-This tests that an input text field can be properly scrolled with touch gestures
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-===Testing fling behavior===
-PASS box.scrollLeft is 0
-PASS container.scrollLeft is 0
-Flinging input text should scroll text by the specified amount
-PASS box.scrollLeft is 40
-PASS container.scrollLeft is 0
-Flinging input text past the scrollable width shouldn't scroll containing div
-PASS box.scrollLeft is fullyScrolled
-PASS container.scrollLeft is 0
-Flinging fully scrolled input text should fling containing div
-PASS box.scrollLeft is fullyScrolled
-PASS container.scrollLeft is 60
-===Testing scroll behavior===
-PASS box.scrollLeft is 0
-PASS container.scrollLeft is 0
-Gesture scrolling input text should scroll text the specified amount
-PASS box.scrollLeft is 60
-PASS container.scrollLeft is 0
-Gesture scrolling input text past scroll width shouldn't scroll container div
-PASS box.scrollLeft is fullyScrolled
-PASS container.scrollLeft is 0
-===Testing vertical scroll behavior===
-PASS box.scrollTop is 0
-PASS container.scrollTop is 0
-Vertically gesture scrolling input text should scroll containing div the specified amount
-PASS box.scrollTop is 0
-PASS container.scrollTop is 60
-PASS box.scrollTop is 0
-PASS container.scrollTop is 0
-Vertically flinging input text should scroll the containing div the specified amount
-PASS box.scrollTop is 0
-PASS container.scrollTop is 60
-===Testing non-overflow scroll behavior===
-Input box without overflow should not scroll
-PASS box.scrollLeft is 0
-PASS container.scrollLeft is 0
-PASS box.clientWidth is >= box.scrollWidth
-PASS box.scrollLeft is 0
-PASS container.scrollLeft is 60
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-input-field.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-input-field.html
index b122ff84..8e91822 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-input-field.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-input-field.html
@@ -1,198 +1,130 @@
 <!DOCTYPE html>
-<html>
-    <head>
-        <script src="../../../../resources/js-test.js"></script>
-        <script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
+<body style="margin:0">
+  <div id="container" style="width: 500px; height: 200px; overflow-y: scroll; overflow-x: scroll">
+      <form>
+        <input id="box" size="10" style="height:100px; font-size:xx-large" type="text" value="asasd;flkajsd;flkasjdf;alksdjf;alskdfja;lksdja;sdlfjkas;ldkf"></input>
+      </form>
+      <div style="background: green; height: 1000px; width: 1000px"></div>
+  </div>
+</body>
 
-    </head>
+<script type="text/javascript">
+var gestureX = 100;
+var gestureY = 50;
+var box = document.getElementById("box");
+var container = document.getElementById("container");
+var fullyScrolled;
 
-    <body style="margin:0" onload="runTest()">
-      <div id="container" style="width: 500px; height: 200px; overflow-y: scroll; overflow-x: scroll">
-          <form>
-              <input id="box" size="10" style="height:100px; font-size:xx-large" type="text" value="asasd;flkajsd;flkasjdf;alksdjf;alskdfja;lksdja;sdlfjkas;ldkf"></input>
-          </form>
-          <div style="background: green; height: 1000px; width: 1000px"></div>
-      </div>
+function calculateFullScroll() {
+  fullyScrolled = box.scrollWidth - box.clientWidth;
 
-      <p id="description"></p>
-      <div id="console"></div>
+  // FIXME: Mac has a quirk where the text box text can actually be scrolled a little bit 
+  // past the end. That is, scrollLeft = (scrollWidth - clientWidth) + 2 on Mac. Once
+  // this is fixed we can remove this adjustment.
+  box.scrollLeft = 100000;
+  fullyScrolled += box.scrollLeft - fullyScrolled;
 
-      <script type="text/javascript">
-        var gestureX = 100;
-        var gestureY = 50;
-        var box;
-        var container;
-        var fullyScrolled;
+  resetScroll();
+}
 
-        function calculateFullScroll()
-        {
-            fullyScrolled = box.scrollWidth - box.clientWidth;
+calculateFullScroll();
 
-            // FIXME: Mac has a quirk where the text box text can actually be scrolled a little bit 
-            // past the end. That is, scrollLeft = (scrollWidth - clientWidth) + 2 on Mac. Once
-            // this is fixed we can remove this adjustment.
-            box.scrollLeft = 100000;
-            fullyScrolled += box.scrollLeft - fullyScrolled;
+function resetScroll() {
+  container.scrollLeft = 0;
+  box.scrollLeft = 0;
+  container.scrollTop = 0;
+  box.scrollTop = 0;
+}
 
-            resetScroll();
-        }
+promise_test (async () => {
+  resetScroll();
+  assert_equals(box.scrollLeft, 0);
+  assert_equals(container.scrollLeft, 0);
 
-        function resetScroll()
-        {
-          container.scrollLeft = 0;
-          box.scrollLeft = 0;
-          container.scrollTop = 0;
-          box.scrollTop = 0;
-        }
+  await swipe(40, gestureX, gestureY, "left", SPEED_INSTANT);
+  await waitFor(() => { return box.scrollLeft > 40} );
+  assert_equals(container.scrollLeft, 0);
 
-        function testFlingGestureScroll()
-        {
-            debug("===Testing fling behavior===");
-            resetScroll();
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
 
-            shouldBe('box.scrollLeft', '0');
-            shouldBe('container.scrollLeft', '0');
+  // Flinging input text past the scrollable width shouldn't scroll containing
+  // div.
+  await swipe(fullyScrolled + 500, gestureX, gestureY, "left", SPEED_INSTANT);
+  await waitFor(() => { return box.scrollLeft == fullyScrolled; });
+  // Wait for 100 RAFs to make sure the scroll does not propagate to the
+  // container.
+  await conditionHolds(() => { return container.scrollLeft == 0; });
 
-            eventSender.gestureScrollBegin(gestureX, gestureY);
+  // Flinging fully scrolled input text should fling containing div.
+  await swipe(60, gestureX, gestureY, "left", SPEED_INSTANT);
+  await waitForAnimationEnd(() => { return container.scrollLeft; }, 700, 20);
+  assert_greater_than(container.scrollLeft, 60);
+  assert_equals(box.scrollLeft, fullyScrolled);
+}, "gesture fling on input field");
 
-            eventSender.gestureScrollUpdate(-10, 0);
-            eventSender.gestureScrollUpdate(-10, 0);
-            eventSender.gestureScrollUpdate(-10, 0);
-            eventSender.gestureScrollUpdate(-10, 0);
-            eventSender.gestureScrollEnd(0, 0);
+promise_test (async () => {
+  resetScroll();
+  assert_equals(box.scrollLeft, 0);
+  assert_equals(container.scrollLeft, 0);
 
-            debug("Flinging input text should scroll text by the specified amount");
-            shouldBe('box.scrollLeft', '40');
-            shouldBe('container.scrollLeft', '0');
+  // Gesture scrolling input text should scroll text the specified amount.
+  await smoothScroll(60, gestureX, gestureY, GestureSourceType.TOUCH_INPUT,
+      "right");
+  await waitFor(() => {
+      return approx_equals(60, box.scrollLeft, 2);
+  });
+  assert_equals(container.scrollLeft, 0);
 
-            resetScroll();
+  resetScroll();
 
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(-fullyScrolled, 0);
-            eventSender.gestureScrollUpdate(-100, 0);
-            eventSender.gestureScrollUpdate(-100, 0);
-            eventSender.gestureScrollUpdate(-300, 0);
-            eventSender.gestureScrollEnd(0, 0);
+  // Gesture scrolling input text past scroll width shouldn't scroll container
+  // div.
+  await smoothScroll(fullyScrolled + 50, gestureX, gestureY,
+    GestureSourceType.TOUCH_INPUT, "right");
+  await waitFor(() => { return box.scrollLeft == fullyScrolled; });
+  assert_equals(container.scrollLeft, 0);
+}, "gesture scroll on input field");
 
-            debug("Flinging input text past the scrollable width shouldn't scroll containing div");
+promise_test (async () => {
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
+  assert_equals(container.scrollTop, 0);
 
-            shouldBe('box.scrollLeft', 'fullyScrolled');
-            shouldBe('container.scrollLeft', '0');
+  // Vertically gesture scrolling input text should scroll containing div the
+  // specified amount.
+  await smoothScroll(60, gestureX, gestureY, GestureSourceType.TOUCH_INPUT,
+      "down");
+  await waitFor(() => {
+      return approx_equals(60, container.scrollTop, 2);
+  });
+  assert_equals(box.scrollTop, 0);
 
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollEnd(0, 0);
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
+  assert_equals(container.scrollTop, 0);
+  // Vertically flinging input text should scroll the containing div the
+  // specified amount.
+  await swipe(60, gestureX, gestureY, "up", SPEED_INSTANT);
+  await waitForAnimationEnd(() => { return container.scrollTop; }, 700, 20);
+  assert_greater_than(container.scrollTop, 60);
+  assert_equals(box.scrollTop, 0);
+}, "vertical scroll on input feild");
 
-            debug("Flinging fully scrolled input text should fling containing div");
-            shouldBe('box.scrollLeft', 'fullyScrolled');
-            shouldBe('container.scrollLeft', '60');
-        }
+promise_test (async () => {
+  box.value = "short";
+  assert_equals(box.scrollLeft, 0);
+  assert_equals(container.scrollLeft, 0);
+  assert_greater_than_equal(box.clientWidth, box.scrollWidth);
 
-        function testGestureScroll()
-        {
-            debug("===Testing scroll behavior===");
-            resetScroll();
-
-            shouldBe('box.scrollLeft', '0');
-            shouldBe('container.scrollLeft', '0');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Gesture scrolling input text should scroll text the specified amount");
-            shouldBe('box.scrollLeft', '60');
-            shouldBe('container.scrollLeft', '0');
-
-            resetScroll();
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(-fullyScrolled, 0);
-            eventSender.gestureScrollUpdate(-50, 0);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Gesture scrolling input text past scroll width shouldn't scroll container div");
-            shouldBe('box.scrollLeft', 'fullyScrolled');
-            shouldBe('container.scrollLeft', '0');
-        }
-
-        function testVerticalScroll()
-        {
-            debug("===Testing vertical scroll behavior===");
-            resetScroll();
-
-            shouldBe('box.scrollTop', '0');
-            shouldBe('container.scrollTop', '0');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -30);
-            eventSender.gestureScrollUpdate(0, -30);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Vertically gesture scrolling input text should scroll containing div the specified amount");
-            shouldBe('box.scrollTop', '0');
-            shouldBe('container.scrollTop', '60');
-
-            resetScroll();
-
-            shouldBe('box.scrollTop', '0');
-            shouldBe('container.scrollTop', '0');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -30);
-            eventSender.gestureScrollUpdate(0, -30);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Vertically flinging input text should scroll the containing div the specified amount");
-            shouldBe('box.scrollTop', '0');
-            shouldBe('container.scrollTop', '60');
-        }
-
-        function testNonOverflowingText()
-        {
-            debug("===Testing non-overflow scroll behavior===");
-            box.value = "short";
-
-            debug("Input box without overflow should not scroll");
-            shouldBe('box.scrollLeft', '0');
-            shouldBe('container.scrollLeft', '0');
-            shouldBeGreaterThanOrEqual('box.clientWidth', 'box.scrollWidth');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollEnd(0, 0);
-
-            shouldBe('box.scrollLeft', '0');
-            shouldBe('container.scrollLeft', '60');
-        }
-
-        if (window.testRunner)
-            testRunner.waitUntilDone();
-
-        function runTest()
-        {
-            box = document.getElementById("box");
-            container = document.getElementById("container");
-
-            if (window.eventSender) {
-                description('This tests that an input text field can be properly scrolled with touch gestures');
-
-                if (checkTestDependencies() && eventSender.gestureScrollUpdate) {
-                    calculateFullScroll();
-                    testFlingGestureScroll();
-                    testGestureScroll();
-                    testVerticalScroll();
-                    testNonOverflowingText();
-                    testRunner.notifyDone();
-                } else
-                    exitIfNecessary();
-            } else {
-                debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-            }
-        }
-      </script>
-    </body>
-</html>
+  await smoothScroll(60, gestureX, gestureY, GestureSourceType.TOUCH_INPUT,
+      "right");
+  await waitFor(() => {
+      return approx_equals(60, container.scrollLeft, 2);
+  });
+  assert_equals(box.scrollLeft, 0);
+}, "non-overflow scroll behavior");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-listbox-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-listbox-expected.txt
deleted file mode 100644
index 61fbdd649..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-listbox-expected.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-
-This tests that an input text field can be properly scrolled with touch gestures
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-===Testing fling gestures===
-PASS box.scrollTop is 0
-PASS container.scrollTop is 0
-Flinging the list box should scroll the list, the scrolls should be locked to item boundaries
-PASS box.scrollTop is 2*itemHeight+10
-PASS container.scrollTop is 0
-Flinging the list past the end shouldn't scroll containing div
-PASS box.scrollTop is fullyScrolled
-PASS container.scrollTop is 0
-Flinging fully scrolled list should fling containing div
-PASS box.scrollTop is fullyScrolled
-PASS container.scrollTop is 60
-===Testing gesture scroll===
-PASS box.scrollTop is 0
-PASS container.scrollTop is 0
-Gesture scrolling list should scroll the list, the scrolls should lock to item boundaries
-PASS box.scrollTop is 3*itemHeight+6
-PASS container.scrollTop is 0
-PASS box.scrollTop is 0
-PASS container.scrollTop is 0
-Gesture scrolling list past the end shouldn't scroll container div
-PASS box.scrollTop is fullyScrolled
-PASS container.scrollTop is 0
-Gesture scrolling list past the end should scroll container div when starting at scroll extent
-PASS box.scrollTop is fullyScrolled
-PASS container.scrollTop is fullyScrolled + 50
-===Testing horizontal scroll===
-PASS box.scrollLeft is 0
-PASS container.scrollLeft is 0
-Horizontal scrolls should not affect listbox
-PASS box.scrollLeft is 0
-PASS container.scrollLeft is 60
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-listbox.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-listbox.html
index f308ade..05e5291 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-listbox.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-listbox.html
@@ -1,178 +1,109 @@
 <!DOCTYPE html>
-<html>
-    <head>
-        <script src="../../../../resources/js-test.js"></script>
-        <script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 
-    </head>
+<body style="margin:0">
+  <div id="container" style="height: 500px; overflow-x: scroll; overflow-y: scroll">
+      <select id="box" style="width=200px" size="5">
+          <option>Option 1</option>
+          <option>Option 2</option>
+          <option>Option 3</option>
+          <option>Option 4</option>
+          <option>Option 5</option>
+          <option>Option 6</option>
+          <option>Option 7</option>
+          <option>Option 8</option>
+          <option>Option 9</option>
+          <option>Option 10</option>
+          <option>Option 11</option>
+      </select>
+      <div style="background: green; height: 1000px; width: 1000px"></div>
+  </div>
+</body>
 
-    <body style="margin:0" onload="runTest()">
-      <div id="container" style="height: 500px; overflow-x: scroll; overflow-y: scroll">
-          <select id="box" style="width=200px" size="5">
-              <option>Option 1</option>
-              <option>Option 2</option>
-              <option>Option 3</option>
-              <option>Option 4</option>
-              <option>Option 5</option>
-              <option>Option 6</option>
-              <option>Option 7</option>
-              <option>Option 8</option>
-              <option>Option 9</option>
-              <option>Option 10</option>
-              <option>Option 11</option>
-          </select>
-          <div style="background: green; height: 1000px; width: 1000px"></div>
-      </div>
+<script type="text/javascript">
+var gestureX = 30;
+var gestureY = 30;
+var box = document.getElementById("box");
+var container = document.getElementById("container");
+var itemHeight = box.clientHeight / box.size;
+var fullyScrolled = box.scrollHeight - box.clientHeight;
 
-      <p id="description"></p>
-      <div id="console"></div>
+function resetScroll() {
+  container.scrollTop = 0;
+  box.scrollTop = 0;
+  container.scrollLeft = 0;
+  box.scrollLeft = 0;
+}
 
-      <script type="text/javascript">
-        var gestureX = 30;
-        var gestureY = 30;
-        var itemHeight;
-        var box;
-        var container;
-        var fullyScrolled;
+promise_test (async () => {
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
+  assert_equals(container.scrollTop, 0);
 
-        function resetScroll()
-        {
-          container.scrollTop = 0;
-          box.scrollTop = 0;
-          container.scrollLeft = 0;
-          box.scrollLeft = 0;
-        }
+  await swipe(2 * itemHeight + 10, gestureX, gestureY, "up", SPEED_INSTANT);
+  await waitForAnimationEnd(() => { return box.scrollTop; }, 700, 20);
+  assert_greater_than(box.scrollTop, 2 * itemHeight + 10);
+  assert_equals(container.scrollTop, 0);
 
-        // Fling to scroll the inner text a little but not all the way
-        function testFlingGestures()
-        {
-            debug("===Testing fling gestures===");
-            resetScroll();
-            shouldBe('box.scrollTop', '0');
-            shouldBe('container.scrollTop', '0');
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
 
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -itemHeight);
-            eventSender.gestureScrollUpdate(0, -itemHeight);
-            eventSender.gestureScrollUpdate(0, -10);
-            eventSender.gestureScrollEnd(0, 0);
+  await swipe(fullyScrolled + 500, gestureX, gestureY, "up", SPEED_INSTANT);
+  await waitFor(() => { return box.scrollTop == fullyScrolled; });
+  // Wait for 100 RAFs to make sure the scroll does not propagate to the
+  // container.
+  await conditionHolds(() => { return container.scrollTop == 0; });
 
-            debug("Flinging the list box should scroll the list, the scrolls should be locked to item boundaries");
-            shouldBe('box.scrollTop', '2*itemHeight+10');
-            shouldBe('container.scrollTop', '0');
+  // Flinging list past the end should scroll container div when starting at
+  // scroll extent.
+  await swipe(60, gestureX, gestureY, "up", SPEED_INSTANT);
+  await waitForAnimationEnd(() => { return container.scrollTop; }, 700, 20);
+  assert_greater_than(container.scrollTop, 60);
+  assert_equals(box.scrollTop, fullyScrolled);
+}, "fling gestures on a list box");
 
-            resetScroll();
+promise_test (async () => {
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
+  assert_equals(container.scrollTop, 0);
 
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -fullyScrolled);
-            eventSender.gestureScrollUpdate(0, -300);
-            eventSender.gestureScrollUpdate(0, -100);
-            eventSender.gestureScrollUpdate(0, -100);
-            eventSender.gestureScrollEnd(0, 0);
+  await smoothScroll(3 * itemHeight + 6, gestureX, gestureY,
+      GestureSourceType.TOUCH_INPUT, "down");
+  await waitFor(() => {
+      return approx_equals(3 * itemHeight + 6, box.scrollTop, 2);
+  });
+  assert_equals(container.scrollTop, 0);
 
-            debug("Flinging the list past the end shouldn't scroll containing div");
-            shouldBe('box.scrollTop', 'fullyScrolled');
-            shouldBe('container.scrollTop', '0');
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
 
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -30);
-            eventSender.gestureScrollUpdate(0, -30);
-            eventSender.gestureScrollEnd(0, 0);
+  await smoothScroll(fullyScrolled + 50, gestureX, gestureY,
+      GestureSourceType.TOUCH_INPUT, "down");
+  await waitFor(() => { return box.scrollTop == fullyScrolled; });
+  // Wait for 100 RAFs to make sure the scroll does not propagate to the main
+  // frame.
+  await conditionHolds(() => { return container.scrollTop == 0; });
 
-            debug("Flinging fully scrolled list should fling containing div");
-            shouldBe('box.scrollTop', 'fullyScrolled');
-            shouldBe('container.scrollTop', '60');
-        }
+  // Gesture scrolling list past the end should scroll container div when
+  // starting at scroll extent.
+  await smoothScroll(fullyScrolled + 50, gestureX, gestureY,
+      GestureSourceType.TOUCH_INPUT, "down");
+  await waitFor(() => {
+      return approx_equals(fullyScrolled + 50, container.scrollTop, 2);
+  });
+}, "gesture scroll on a list box");
 
-        function testGestureScroll()
-        {
-            debug("===Testing gesture scroll===");
+promise_test (async () => {
+  resetScroll();
+  assert_equals(box.scrollTop, 0);
+  assert_equals(container.scrollTop, 0);
 
-            resetScroll();
-            shouldBe('box.scrollTop', '0');
-            shouldBe('container.scrollTop', '0');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -(itemHeight-3));
-            eventSender.gestureScrollUpdate(0, -3);
-            eventSender.gestureScrollUpdate(0, -(itemHeight-3));
-            eventSender.gestureScrollUpdate(0, -3);
-            eventSender.gestureScrollUpdate(0, -(itemHeight-3));
-            eventSender.gestureScrollUpdate(0, -3);
-            eventSender.gestureScrollUpdate(0, -6);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Gesture scrolling list should scroll the list, the scrolls should lock to item boundaries");
-            shouldBe('box.scrollTop', '3*itemHeight+6');
-            shouldBe('container.scrollTop', '0');
-
-            resetScroll();
-            shouldBe('box.scrollTop', '0');
-            shouldBe('container.scrollTop', '0');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -fullyScrolled);
-            eventSender.gestureScrollUpdate(0, -50);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Gesture scrolling list past the end shouldn't scroll container div");
-            shouldBe('box.scrollTop', 'fullyScrolled');
-            shouldBe('container.scrollTop', '0');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(0, -fullyScrolled);
-            eventSender.gestureScrollUpdate(0, -50);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Gesture scrolling list past the end should scroll container div when starting at scroll extent");
-            shouldBe('box.scrollTop', 'fullyScrolled');
-            shouldBe('container.scrollTop', 'fullyScrolled + 50');
-
-        }
-
-        function testHorizontalScroll()
-        {
-            debug("===Testing horizontal scroll===");
-
-            resetScroll();
-            shouldBe('box.scrollLeft', '0');
-            shouldBe('container.scrollLeft', '0');
-
-            eventSender.gestureScrollBegin(gestureX, gestureY);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollUpdate(-30, 0);
-            eventSender.gestureScrollEnd(0, 0);
-
-            debug("Horizontal scrolls should not affect listbox");
-            shouldBe('box.scrollLeft', '0');
-            shouldBe('container.scrollLeft', '60');
-        }
-
-        if (window.testRunner)
-            testRunner.waitUntilDone();
-
-        function runTest()
-        {
-            box = document.getElementById("box");
-            container = document.getElementById("container");
-            itemHeight = box.clientHeight / box.size;
-
-            fullyScrolled = box.scrollHeight - box.clientHeight;
-
-            if (window.eventSender) {
-                description('This tests that an input text field can be properly scrolled with touch gestures');
-
-                if (checkTestDependencies() && eventSender.gestureScrollUpdate) {
-                    testFlingGestures();
-                    testGestureScroll();
-                    testHorizontalScroll();
-                    testRunner.notifyDone();
-                } else
-                    exitIfNecessary();
-            } else {
-                debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-            }
-        }
-      </script>
-    </body>
-</html>
+  await smoothScroll(60, gestureX, gestureY,
+      GestureSourceType.TOUCH_INPUT, "right");
+  await waitFor(() => { return approx_equals(60, container.scrollLeft, 2); });
+  assert_equals(box.scrollLeft, 0,
+      "Horizontal scrolls should not affect listbox");
+}, "Horizontal scroll on a list box");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-expected.txt
deleted file mode 100644
index 7f93d003..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests scroll gesture event scrolling on a whole page.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS document.scrollingElement.scrollTop is 70
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.scrollingElement.scrollTop is 130
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-not-propagated-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-not-propagated-expected.txt
deleted file mode 100644
index 177ddf4..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-not-propagated-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests that a gesture scroll is not propagated from a div to the page when the div has no remaining scroll offset when the preventPropagation flag is set for the GestureScrollUpdate event.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS document.documentElement.scrollTop is 0
-PASS document.documentElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.documentElement.scrollTop is 0
-PASS document.documentElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-not-propagated.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-not-propagated.html
deleted file mode 100644
index 03da226..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-not-propagated.html
+++ /dev/null
@@ -1,156 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
-<style type="text/css">
-::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
-}
-
-#greenbox {
-  width: 100px;
-  height: 100px;
-  background: green;
-  padding: 0px;
-  margin: 0px;
-}
-
-#redbox {
-  width: 100px;
-  height: 100px;
-  background: red;
-  padding: 0px;
-  margin: 0px;
-}
-
-#bluebox {
-  width: 100px;
-  height: 100px;
-  background: blue;
-  padding: 0px;
-  margin: 0px;
-}
-
-#yellowbox {
-  width: 100px;
-  height: 100px;
-  background: yellow;
-  padding: 0px;
-  margin: 0px;
-}
-
-#touchtargetdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
-}
-
-td {
-  padding: 0px;
-}
-</style>
-</head>
-<body style="margin:0" onload="runTest();">
-
-<div id="touchtargetdiv">
-  <table border="0" cellspacing="0px" >
-    <tr><td><div id="yellowbox"></div></td></tr>
-    <tr><td><div id="bluebox"></div></td></tr>
-    <tr><td><div id="yellowbox"></div></td></tr>
-    <tr><td><div id="bluebox"></div></td></tr>
-  </table>
-</div>
-
-<table id="table_to_fill" border="0" cellspacing="0px">
-  <tr><td><div id="greenbox"></div></td></tr>
-  <tr><td><div id="redbox"></div></td></tr>
-</table>
-
-<p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
-
-var touchtarget;
-var expectedGesturesTotal = 2;
-var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
-var scrollEventsOccurred = 0;
-var scrolledElement = 'document.documentElement';
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-
-// Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    var table = document.getElementById('table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 2;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
-}
-
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -110);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 40);
-    eventSender.gestureScrollUpdate(0, -200);
-    eventSender.gestureScrollUpdate(0, -160);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    touchtarget = document.getElementById('touchtargetdiv');
-    touchtarget.addEventListener("scroll", recordScroll);
-    touchtarget.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a gesture scroll is not propagated from a div ' + 
-                'to the page when the div has no remaining scroll offset when ' +
-                'the preventPropagation flag is set for the GestureScrollUpdate event.');
-        if (checkTestDependencies() && eventSender.gestureScrollUpdate)
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
-</script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent-expected.txt
deleted file mode 100644
index e1b1525a..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests that a gesture scroll isn't propagated from a div to the page when the div has no remaining scroll offset.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS document.scrollingElement.scrollTop is 0
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.scrollingElement.scrollTop is 0
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html
index d5e895c..f82577ff 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-past-extent.html
@@ -1,12 +1,11 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -42,19 +41,18 @@
 }
 
 #touchtargetdiv {
-    width: 200px;
-    height: 200px;
-    overflow-y: scroll;
-    overflow-x: scroll;
+  width: 200px;
+  height: 200px;
+  overflow-y: scroll;
+  overflow-x: scroll;
 }
 
 td {
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
 
+<body style="margin:0" onload="buildPage();">
 <div id="touchtargetdiv">
   <table border="0" cellspacing="0px" >
     <tr><td><div id="yellowbox"></div></td></tr>
@@ -68,88 +66,57 @@
   <tr><td><div id="greenbox"></div></td></tr>
   <tr><td><div id="redbox"></div></td></tr>
 </table>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
-var touchtarget;
-var expectedGesturesTotal = 2;
-var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['0', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
-var scrollEventsOccurred = 0;
-var scrolledElement = 'document.scrollingElement'
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
+var movingDiv = document.getElementById('touchtargetdiv');
 
 // Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    var table = document.getElementById('table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 2;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
+function buildPage() {
+  var table = document.getElementById('table_to_fill');
+  var targetHeight = document.body.offsetHeight;
+  var cellPairHeight = table.offsetHeight;
+  var numberOfReps = targetHeight / cellPairHeight * 2;
+  var i;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="greenbox"></div></td>';
+    table.appendChild(p);
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="redbox"></div></td>';
+    table.appendChild(p);
+  }
 }
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -110);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function firstGestureScroll() {
+  var x = 10;
+  var y = 72;
+  return smoothScroll(110, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(12, 40);
-    eventSender.gestureScrollUpdate(0, -200);
-    eventSender.gestureScrollUpdate(0, -160);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  var x = 12;
+  var y = 40;
+  return smoothScroll(360, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    touchtarget = document.getElementById('touchtargetdiv');
-    touchtarget.addEventListener("scroll", recordScroll);
-    touchtarget.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests that a gesture scroll isn\'t propagated from a div ' + 
-                'to the page when the div has no remaining scroll offset.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
+promise_test (async () => {
+  await firstGestureScroll();
+  await waitFor(() => {
+      return approx_equals(110, movingDiv.scrollTop, 2);
+  });
+  await secondGestureScroll();
+  var expectedScrollTop = movingDiv.scrollHeight - movingDiv.clientHeight;
+  await waitFor(() => {
+      return approx_equals(expectedScrollTop, movingDiv.scrollTop, 2);
+  });
+  // Wait for 100 RAFs to make sure the scroll does not propagate to the main
+  // frame.
+  await conditionHolds(() => {
+      return document.scrollingElement.scrollTop == 0;
+  });
+}, 'This tests that a gesture scroll isn\'t propagated from a div ' + 
+   'to the page when the div has no remaining scroll offset.');
 </script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed-expected.txt
deleted file mode 100644
index 61a46395..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This tests scroll gesture event scrolling on a whole page with browser zoom.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS document.scrollingElement.scrollTop is 140
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-second gesture
-PASS document.scrollingElement.scrollTop is 40
-PASS document.scrollingElement.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDocument]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html
index a74d336..c07191a 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page-zoomed.html
@@ -1,12 +1,11 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 ::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
+  width: 0px;
+  height: 0px;
 }
 
 #greenbox {
@@ -28,118 +27,69 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
 
+<body style="margin:0" onload="buildPage();">
 <table id="table_to_fill">
-    <tr><td><div id="greenbox"></div></td></tr>
-    <tr><td><div id="redbox"></div></td></tr>
+  <tr><td><div id="greenbox"></div></td></tr>
+  <tr><td><div id="redbox"></div></td></tr>
 </table>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['140', '40'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [140, 40];
+var scrolledElement = document.scrollingElement;
 var scrollEventsOccurred = 0;
-var scrolledElement = 'document.scrollingElement'
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-
-function recordScroll(event) {
-    debug('scroll event ' + scrollEventsOccurred + '+> ' + event.target);
-    scrollEventsOccurred++;
-
-    if (window.eventSender) {
-        // Because scroll events arrive asynchronously, only one will arrive.
-        if (gesturesOccurred == expectedGesturesTotal) {
-            shouldBe('scrollEventsOccurred', expectedScrollEventsOccurred);
-            // If we've got here, we've passed.
-            successfullyParsed = true;
-            isSuccessfullyParsed();
-            if (window.testRunner)
-                testRunner.notifyDone();
-        }
-    }
-}
+var expectedScrollEventsOccurred = 2;
 
 // Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    var table = document.getElementById('table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 4;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
+function buildPage() {
+  var table = document.getElementById('table_to_fill');
+  var targetHeight = window.innerHeight;
+  var cellPairHeight = table.offsetHeight;
+  var numberOfReps = targetHeight / cellPairHeight * 4;
+  var i;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="greenbox"></div></td>';
+    table.appendChild(p);
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="redbox"></div></td>';
+    table.appendChild(p);
+  }
 
-    window.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
+  window.addEventListener("scroll", recordScroll);
 }
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    
-    internals.setZoomFactor(0.5);
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -30);
-    eventSender.gestureScrollUpdate(0, -40);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 10;
+var y = 72;
+function firstGestureScroll() {
+  internals.setZoomFactor(0.5);
+  return smoothScroll(70, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, 200);
-    eventSender.gestureScrollEnd(0, 0);
-
-    internals.setZoomFactor(2.0);
-    eventSender.gestureScrollBegin(799, 40);
-    eventSender.gestureScrollUpdate(0, -40);
-    eventSender.gestureScrollUpdate(0, -40);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  return smoothScroll(200, x, y, GestureSourceType.TOUCH_INPUT, "up",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    if (window.eventSender) {
-        description('This tests scroll gesture event scrolling on a whole page with browser zoom.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
+function thirdGestureScroll() {
+  internals.setZoomFactor(2.0);
+  x = 799;
+  y = 40;
+  return smoothScroll(80, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
+
+promise_test (async () => {
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await thirdGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests scroll gesture event scrolling on a whole page with browser' +
+   'zoom.');
 </script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page.html
index 686543c9..2f9c3d3d 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page.html
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-page.html
@@ -1,8 +1,7 @@
 <!DOCTYPE html>
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../../resources/gesture-util.js"></script>
 <style type="text/css">
 ::-webkit-scrollbar {
     width: 0px;
@@ -28,110 +27,61 @@
   padding: 0px;
 }
 </style>
-</head>
-<body style="margin:0" onload="runTest();">
 
+<body style="margin:0" onload="buildPage();">
 <table id="table_to_fill">
     <tr><td><div id="greenbox"></div></td></tr>
     <tr><td><div id="redbox"></div></td></tr>
 </table>
+</body>
 
-<p id="description"></p>
-<div id="console"></div>
 <script type="text/javascript">
-
 var expectedGesturesTotal = 2;
 var gesturesOccurred = 0;
-var scrollAmountX = ['0', '0'];
-var scrollAmountY = ['70', '130'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
+var scrollAmountX = [0, 0];
+var scrollAmountY = [70, 130];
+var scrolledElement = document.scrollingElement;
 var scrollEventsOccurred = 0;
-var scrolledElement = 'document.scrollingElement'
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
+var expectedScrollEventsOccurred = 1;
 
-function recordScroll(event) {
-	debug('scroll event ' + scrollEventsOccurred + '+> ' + event.target);
-	scrollEventsOccurred++;
-
-	if (window.eventSender) {
-		// Because scroll events arrive asynchronously, only one will arrive.
-	    if (gesturesOccurred == expectedGesturesTotal) {
-			shouldBe('scrollEventsOccurred', expectedScrollEventsOccurred);
-	        // If we've got here, we've passed.
-	        successfullyParsed = true;
-	        isSuccessfullyParsed();
-	        if (window.testRunner)
-	            testRunner.notifyDone();
-		}
-    }
-}
 
 // Always construct a page larger than the vertical height of the window.
-function buildPage()
-{
-    var table = document.getElementById('table_to_fill');
-    var targetHeight = document.body.offsetHeight;
-    var cellPairHeight = table.offsetHeight;
-    var numberOfReps = targetHeight / cellPairHeight * 2;
-    var i;
-    for (i = 0; i < numberOfReps; i++) {
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="greenbox"></div></td>';
-        table.appendChild(p);
-        var p = document.createElement('tr');
-        p.innerHTML = '<td><div id="redbox"></div></td>';
-        table.appendChild(p);
-    }
+function buildPage() {
+  var table = document.getElementById('table_to_fill');
+  var targetHeight = window.innerHeight;
+  var cellPairHeight = table.offsetHeight;
+  var numberOfReps = targetHeight / cellPairHeight * 2;
+  var i;
+  for (i = 0; i < numberOfReps; i++) {
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="greenbox"></div></td>';
+    table.appendChild(p);
+    var p = document.createElement('tr');
+    p.innerHTML = '<td><div id="redbox"></div></td>';
+    table.appendChild(p);
+  }
 
-    window.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
+  window.addEventListener("scroll", recordScroll);
 }
 
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(10, 72);
-    eventSender.gestureScrollUpdate(0, -30);
-    eventSender.gestureScrollUpdate(0, -40);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+var x = 10;
+var y = 72;
+function firstGestureScroll() {
+  return smoothScroll(70, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-function secondGestureScroll()
-{
-    debug("second gesture");
-    eventSender.gestureScrollBegin(799, 40);
-    eventSender.gestureScrollUpdate(0, -30);
-    eventSender.gestureScrollUpdate(0, -30);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
+function secondGestureScroll() {
+  x = 799;
+  y = 40;
+  return smoothScroll(60, x, y, GestureSourceType.TOUCH_INPUT, "down",
+      SPEED_INSTANT);
 }
 
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    buildPage();
-    if (window.eventSender) {
-        description('This tests scroll gesture event scrolling on a whole page.');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Gesture-scroll the page to validate the implementation.");
-    }
-}
+promise_test (async () => {
+  await firstGestureScroll();
+  await waitFor(checkScrollOffset);
+  await secondGestureScroll();
+  await waitFor(checkScrollOffset);
+}, 'This tests scroll gesture event scrolling on a whole page.');
 </script>
-
-
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-shy-target-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-shy-target-expected.txt
deleted file mode 100644
index 5952e39a..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-shy-target-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This tests gesture scrolling div which is deleted while latched, Square is (mostly) green on pass
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-first gesture
-PASS movingdiv.scrollTop is 62
-PASS movingdiv.scrollLeft is 63
-PASS wheelEventsOccurred is 0
-second gesture
-received element removing keypress
-PASS movingdiv.scrollTop is 0
-PASS movingdiv.scrollLeft is 0
-PASS wheelEventsOccurred is 0
-scroll event 0+> [object HTMLDivElement]
-PASS scrollEventsOccurred is 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-shy-target.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-shy-target.html
deleted file mode 100644
index 9f36c6e..0000000
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/touch-gesture-scroll-shy-target.html
+++ /dev/null
@@ -1,148 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../../../resources/js-test.js"></script>
-<script src="resources/gesture-helpers.js"></script>
-<style type="text/css">
-::-webkit-scrollbar {
-    width: 0px;
-    height: 0px;
-}
-
-#touchtarget {
-  width: 100px;
-  height: 100px;
-  position: absolute;
-  background: white;
-  top: 100px;
-  left: 100px;
-}
-
-#movingbox {
-  width: 100%;
-  height: 100%;
-  position: absolute;
-  word-wrap: break-word;
-  overflow-y: scroll;
-  overflow-x: scroll;
-  display: block;
-}
-
-.greenbox {
-  width: 100px;
-  height: 100px;
-  background: green;
-  padding: 0px;
-  margin: 0px;
-}
-
-.redbox {
-  width: 100px;
-  height: 100px;
-  background: red;
-  padding: 0px;
-  margin: 0px;
-}
-
-td {
-  padding: 0px;
-}
-</style>
-</head>
-<body style="margin:0" onload="runTest();">
-<div id="touchtarget">
-  <div id="movingbox">
-    <table border="0" cellspacing="0px" id="table">
-      <tr>
-        <td><div class="redbox" id="redbox"></div></td>
-        <td><div class="greenbox" id="greenbox_1"></div></td>
-      </tr>
-      <tr>
-        <td><div class="greenbox" id="greenbox_2"></div></td>
-        <td><div class="greenbox" id="greenbox_3"></div></td>
-      </tr>
-    </table>
-  </div>
-</div>
-
-<p id="description"></p>
-<div id="console"></div>
-<script type="text/javascript">
-
-var movingdiv;
-var expectedGesturesTotal = 2;
-var gesturesOccurred = 0;
-var scrollAmountX = ['63', '0'];
-var scrollAmountY = ['62', '0'];
-var wheelEventsOccurred = 0;
-var expectedWheelEventsOccurred = ['0', '0'];
-var scrollEventsOccurred = 0;
-var expectedScrollEventsOccurred = '1';
-var scrolledElement = 'movingdiv'
-
-document.onkeypress = function()
-{
-    debug('received element removing keypress');
-    var parent = document.getElementById('touchtarget');
-    movingdiv = document.getElementById('movingbox');
-    parent.removeChild(movingdiv);
-    return false;
-}
-
-function firstGestureScroll()
-{
-    debug("first gesture");
-    eventSender.gestureScrollBegin(195, 195);
-    eventSender.gestureScrollUpdate(-20, -28);
-    eventSender.gestureScrollUpdate(-14, -4);
-    eventSender.gestureScrollUpdate(-29, -30);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // Wait for layout.
-    checkScrollOffset();
-}
-
-function secondGestureScroll()
-{
-    movingdiv.scrollTop = 0;
-    movingdiv.scrollLeft = 0;
-
-    debug("second gesture");
-    eventSender.gestureScrollBegin(195, 195);
-    eventSender.gestureScrollUpdate(-20, -28);
-    eventSender.gestureScrollUpdate(-14, -4);
-
-    // Send a key to delete the target Element.
-    eventSender.keyDown("d", []);
-
-    eventSender.gestureScrollUpdate(-29, -30);
-    eventSender.gestureScrollEnd(0, 0);
-
-    // By getting here successfully, the test demonstrates that the implementation
-    // does not crash when a latched Element is removed from the DOM.
-    checkScrollOffset();
-}
-
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function runTest()
-{
-    movingdiv = document.getElementById('movingbox');
-    movingdiv.addEventListener("scroll", recordScroll);
-    window.addEventListener("mousewheel", recordWheel);
-
-    if (window.eventSender) {
-        description('This tests gesture scrolling div which is deleted while latched, ' +
-            'Square is (mostly) green on pass');
-        if (checkTestDependencies())
-            firstGestureScroll();
-        else
-            exitIfNecessary();
-    } else {
-        debug("This test requires DumpRenderTree.  Touch-scroll the red rect to log.");
-    }
-}
-</script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/flipped-blocks-hit-test-line-edges-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/flipped-blocks-hit-test-line-edges-expected.txt
new file mode 100644
index 0000000..2a4360a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/writing-mode/flipped-blocks-hit-test-line-edges-expected.txt
@@ -0,0 +1,12 @@
+Lorem ipsum dolor sit amet
+PASS: offset at (100,105) was 4.
+PASS: offset at (160,105) was 5.
+PASS: offset at (100,104) was 10.
+PASS: offset at (160,104) was 11.
+PASS: offset at (60,26) was 24.
+PASS: offset at (160,26) was 26.
+PASS: offset at (60,25) was 24.
+PASS: offset at (160,25) was 26.
+PASS: offset at (60,24) was 24.
+PASS: offset at (160,24) was 26.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/images/imagemap-focus-ring-with-paint-root-offset-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/images/imagemap-focus-ring-with-paint-root-offset-expected.png
deleted file mode 100644
index 28fb45c..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/images/imagemap-focus-ring-with-paint-root-offset-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-layout-inline-children-replaced-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-layout-inline-children-replaced-expected.txt
index bc481cf..92df863 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-layout-inline-children-replaced-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/block-layout-inline-children-replaced-expected.txt
@@ -15,11 +15,6 @@
           "object": "LayoutImage IMG",
           "rect": [151, 117, 100, 100],
           "reason": "appeared"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [151, 117, 100, 100],
-          "reason": "chunk appeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/box/box-inline-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/box/box-inline-resize-expected.txt
index 124db37..5b6271d 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/box/box-inline-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/box/box-inline-resize-expected.txt
@@ -9,22 +9,17 @@
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [40, 107, 113, 27],
-          "reason": "chunk appeared"
+          "reason": "geometry"
         },
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [8, 107, 113, 27],
-          "reason": "disappeared"
+          "reason": "geometry"
         },
         {
           "object": "LayoutImage IMG id='foo'",
           "rect": [8, 88, 32, 32],
           "reason": "appeared"
-        },
-        {
-          "object": "LayoutImage IMG id='foo'",
-          "rect": [8, 88, 32, 32],
-          "reason": "chunk appeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-expected.txt
index 27355c9..6b8cf635 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-expected.txt
@@ -8,13 +8,8 @@
       "paintInvalidations": [
         {
           "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
-          "rect": [50, 50, 500, 500],
+          "rect": [50, 50, 600, 500],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
-          "rect": [550, 50, 100, 500],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt
index 50388f6..55868f5 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt
@@ -10,11 +10,6 @@
           "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
           "rect": [550, 50, 100, 500],
           "reason": "incremental"
-        },
-        {
-          "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
-          "rect": [550, 50, 100, 500],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/clipped-relative-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/clipped-relative-expected.txt
index 6212af9e..50831112 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/clipped-relative-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/clipped-relative-expected.txt
@@ -13,11 +13,6 @@
         },
         {
           "object": "LayoutImage IMG",
-          "rect": [102, 74, 110, 232],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage IMG",
           "rect": [8, 74, 94, 232],
           "reason": "geometry"
         }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
index 07485121..d06d0d71 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
@@ -15,11 +15,6 @@
           "object": "LayoutImage IMG",
           "rect": [8, 8, 114, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [122, 8, 80, 232],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-block-to-display-none-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-block-to-display-none-expected.txt
index b37ed834..ad626dd2 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-block-to-display-none-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-block-to-display-none-expected.txt
@@ -7,14 +7,9 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutBlockFlow HTML",
-          "rect": [8, 16, 751, 150],
-          "reason": "chunk disappeared"
-        },
-        {
-          "object": "LayoutBlockFlow HTML",
-          "rect": [8, 16, 751, 39],
-          "reason": "chunk appeared"
+          "object": "LayoutIFrame IFRAME id='iframe'",
+          "rect": [8, 72, 732, 94],
+          "reason": "disappeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-none-to-display-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-none-to-display-block-expected.txt
index 16e77e0..b03ea5f7 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-none-to-display-block-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/iframe-display-none-to-display-block-expected.txt
@@ -7,14 +7,9 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutBlockFlow HTML",
-          "rect": [8, 16, 751, 150],
-          "reason": "chunk appeared"
-        },
-        {
-          "object": "LayoutBlockFlow HTML",
-          "rect": [8, 16, 751, 39],
-          "reason": "chunk disappeared"
+          "object": "LayoutIFrame IFRAME id='iframe'",
+          "rect": [8, 72, 732, 94],
+          "reason": "appeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt
index fbc6962..d9caeee 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt
@@ -7,11 +7,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutBlockFlow HTML",
-          "rect": [50, 50, 259, 194],
-          "reason": "chunk disappeared"
-        },
-        {
           "object": "LayoutImage IMG",
           "rect": [50, 50, 259, 194],
           "reason": "style change"
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/image-resize-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/image-resize-expected.txt
index 33d3e2fb..0f315334 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/image-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/image-resize-expected.txt
@@ -8,23 +8,13 @@
       "paintInvalidations": [
         {
           "object": "LayoutImage (positioned) IMG",
+          "rect": [0, 50, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (positioned) IMG",
           "rect": [0, 50, 100, 200],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 50, 100, 150],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 200, 100, 50],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [100, 50, 50, 150],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/percent-size-image-resize-container-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/percent-size-image-resize-container-expected.txt
index 33d3e2fb..0f315334 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/percent-size-image-resize-container-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/image/percent-size-image-resize-container-expected.txt
@@ -8,23 +8,13 @@
       "paintInvalidations": [
         {
           "object": "LayoutImage (positioned) IMG",
+          "rect": [0, 50, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (positioned) IMG",
           "rect": [0, 50, 100, 200],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 50, 100, 150],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 200, 100, 50],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [100, 50, 50, 150],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/list-marker-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/list-marker-2-expected.txt
index eb3b86a..2deb2638 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/list-marker-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/list-marker-2-expected.txt
@@ -12,11 +12,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutImage IMG id='target'",
-          "rect": [48, 150, 100, 50],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutListMarker (anonymous)",
           "rect": [30, 185, 7, 19],
           "reason": "geometry"
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
index 4e12464..ab906992 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
@@ -15,11 +15,6 @@
           "object": "LayoutImage IMG",
           "rect": [8, 64, 114, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [122, 64, 100, 232],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selected-replaced-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selected-replaced-expected.txt
index 93683da..d4b3f6d6 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selected-replaced-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selected-replaced-expected.txt
@@ -8,23 +8,13 @@
       "paintInvalidations": [
         {
           "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
+          "rect": [8, 132, 214, 232],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
           "rect": [8, 52, 214, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 132, 214, 152],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 284, 214, 80],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 52, 214, 80],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-clear-after-move-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-clear-after-move-expected.txt
index 3df8e87..efa22d9 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-clear-after-move-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/selection/selection-clear-after-move-expected.txt
@@ -10,11 +10,6 @@
           "object": "LayoutImage IMG",
           "rect": [100, 300, 50, 50],
           "reason": "selection"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [100, 300, 50, 50],
-          "reason": "chunk disappeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform/transform-replaced-shadows-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform/transform-replaced-shadows-expected.txt
index 440289b..701b881 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform/transform-replaced-shadows-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/transform/transform-replaced-shadows-expected.txt
@@ -13,18 +13,8 @@
         },
         {
           "object": "LayoutImage IMG id='box' class='smaller'",
-          "rect": [28, 28, 200, 200],
-          "reason": "paint property change"
-        },
-        {
-          "object": "LayoutImage IMG id='box' class='smaller'",
           "rect": [9, 28, 138, 139],
           "reason": "paint property change"
-        },
-        {
-          "object": "LayoutImage IMG id='box' class='smaller'",
-          "rect": [28, 28, 100, 100],
-          "reason": "paint property change"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt
index 121af42..3200056 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/layers/layer-canvas-log-expected.txt
@@ -58,32 +58,8 @@
             }
         }
     }
-    10 : {
-        commandIndex : 10
-        method : "restore"
-        params : undefined
-    }
     2 : {
         commandIndex : 2
-        method : "save"
-        params : undefined
-    }
-    3 : {
-        commandIndex : 3
-        method : "clipRect"
-        params : {
-            SkRegion::Op : "kIntersect_Op"
-            rect : {
-                bottom : 71
-                left : 0
-                right : 19
-                top : 50
-            }
-            softClipEdgeStyle : true
-        }
-    }
-    4 : {
-        commandIndex : 4
         method : "drawImageRect"
         params : {
             dst : {
@@ -121,26 +97,21 @@
             }
         }
     }
-    5 : {
-        commandIndex : 5
-        method : "restore"
-        params : undefined
-    }
-    6 : {
-        commandIndex : 6
+    3 : {
+        commandIndex : 3
         method : "save"
         params : undefined
     }
-    7 : {
-        commandIndex : 7
+    4 : {
+        commandIndex : 4
         method : "translate"
         params : {
             dx : 0
             dy : 71
         }
     }
-    8 : {
-        commandIndex : 8
+    5 : {
+        commandIndex : 5
         method : "clipRect"
         params : {
             SkRegion::Op : "kIntersect_Op"
@@ -153,8 +124,8 @@
             softClipEdgeStyle : true
         }
     }
-    9 : {
-        commandIndex : 9
+    6 : {
+        commandIndex : 6
         method : "drawRect"
         params : {
             paint : {
@@ -181,5 +152,10 @@
             }
         }
     }
+    7 : {
+        commandIndex : 7
+        method : "restore"
+        params : undefined
+    }
 }
 
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/block-layout-inline-children-replaced-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/block-layout-inline-children-replaced-expected.txt
index af9d49e7..9341e6235 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/block-layout-inline-children-replaced-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/block-layout-inline-children-replaced-expected.txt
@@ -26,11 +26,6 @@
           "object": "LayoutImage IMG",
           "rect": [151, 117, 100, 100],
           "reason": "appeared"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [151, 117, 100, 100],
-          "reason": "chunk appeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-expected.txt
index 5f18316d..4433062 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-expected.txt
@@ -19,13 +19,8 @@
       "paintInvalidations": [
         {
           "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
-          "rect": [50, 50, 500, 500],
+          "rect": [50, 50, 600, 500],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
-          "rect": [550, 50, 100, 500],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt
index d4cba97..a3132b9 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/canvas-resize-no-full-invalidation-expected.txt
@@ -21,11 +21,6 @@
           "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
           "rect": [550, 50, 100, 500],
           "reason": "incremental"
-        },
-        {
-          "object": "LayoutHTMLCanvas (positioned) CANVAS id='canvas'",
-          "rect": [550, 50, 100, 500],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
index 58396c3..2203bb1 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/clip/replaced-clipped-positioned-not-wrong-incremental-repainting-expected.txt
@@ -26,11 +26,6 @@
           "object": "LayoutImage IMG",
           "rect": [8, 8, 114, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [122, 8, 80, 232],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt
index 7ab9cda..84a2bcf 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt
@@ -37,11 +37,6 @@
           "object": "LayoutImage (positioned) IMG id='repaintdiv' class='repaintdiv'",
           "rect": [80, 80, 182, 29],
           "reason": "style change"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG id='repaintdiv' class='repaintdiv'",
-          "rect": [80, 80, 182, 29],
-          "reason": "style change"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt
index 89d4aba..978d42d2 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/image/do-not-paint-below-image-baseline-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutBlockFlow HTML",
-          "rect": [50, 50, 259, 194],
-          "reason": "chunk disappeared"
-        },
-        {
           "object": "LayoutImage IMG",
           "rect": [50, 50, 259, 194],
           "reason": "style change"
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/image-resize-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/image-resize-expected.txt
index 8a2fbab9d..fd61bfe 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/image-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/image/image-resize-expected.txt
@@ -19,23 +19,13 @@
       "paintInvalidations": [
         {
           "object": "LayoutImage (positioned) IMG",
+          "rect": [0, 50, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (positioned) IMG",
           "rect": [0, 50, 100, 200],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 50, 100, 150],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 200, 100, 50],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [100, 50, 50, 150],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/image/percent-size-image-resize-container-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/image/percent-size-image-resize-container-expected.txt
index 8a2fbab9d..fd61bfe 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/image/percent-size-image-resize-container-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/image/percent-size-image-resize-container-expected.txt
@@ -19,23 +19,13 @@
       "paintInvalidations": [
         {
           "object": "LayoutImage (positioned) IMG",
+          "rect": [0, 50, 150, 150],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (positioned) IMG",
           "rect": [0, 50, 100, 200],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 50, 100, 150],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [0, 200, 100, 50],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage (positioned) IMG",
-          "rect": [100, 50, 50, 150],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-clear-after-move-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-clear-after-move-expected.txt
index 5788d96..1544eaa9 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-clear-after-move-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-clear-after-move-expected.txt
@@ -21,11 +21,6 @@
           "object": "LayoutImage IMG",
           "rect": [100, 300, 50, 50],
           "reason": "selection"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [100, 300, 50, 50],
-          "reason": "chunk disappeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/transform/transform-replaced-shadows-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/transform/transform-replaced-shadows-expected.txt
index 491b6d3..8361ae35 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/transform/transform-replaced-shadows-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/transform/transform-replaced-shadows-expected.txt
@@ -24,18 +24,8 @@
         },
         {
           "object": "LayoutImage IMG id='box' class='smaller'",
-          "rect": [28, 28, 200, 200],
-          "reason": "paint property change"
-        },
-        {
-          "object": "LayoutImage IMG id='box' class='smaller'",
           "rect": [9, 28, 138, 139],
           "reason": "paint property change"
-        },
-        {
-          "object": "LayoutImage IMG id='box' class='smaller'",
-          "rect": [28, 28, 100, 100],
-          "reason": "paint property change"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/fuchsia/paint/invalidation/selection/selected-replaced-expected.txt b/third_party/WebKit/LayoutTests/platform/fuchsia/paint/invalidation/selection/selected-replaced-expected.txt
deleted file mode 100644
index 377adb5..0000000
--- a/third_party/WebKit/LayoutTests/platform/fuchsia/paint/invalidation/selection/selected-replaced-expected.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  "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": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 132, 214, 232],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 52, 214, 232],
-          "reason": "geometry"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow (anonymous)",
-      "reason": "geometry"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "geometry"
-    },
-    {
-      "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-      "reason": "geometry"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/selection-rect-transform-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/selection-rect-transform-expected.png
index 8d781f2..d768bd3 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/selection-rect-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/selection-rect-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/box/box-inline-resize-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/box/box-inline-resize-expected.txt
index ab1843a1b..cb6a48c 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/box/box-inline-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/box/box-inline-resize-expected.txt
@@ -20,22 +20,17 @@
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [40, 107, 113, 27],
-          "reason": "chunk appeared"
+          "reason": "geometry"
         },
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [8, 107, 113, 27],
-          "reason": "disappeared"
+          "reason": "geometry"
         },
         {
           "object": "LayoutImage IMG id='foo'",
           "rect": [8, 88, 32, 32],
           "reason": "appeared"
-        },
-        {
-          "object": "LayoutImage IMG id='foo'",
-          "rect": [8, 88, 32, 32],
-          "reason": "chunk appeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
index d433731..e1089d7f 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -23,26 +23,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copyvideo'",
-          "rect": [571, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopvideo'",
           "rect": [571, 415, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopvideo'",
-          "rect": [571, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-invideo'",
-          "rect": [571, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-invideo'",
           "rect": [571, 315, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -53,26 +38,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outvideo'",
-          "rect": [571, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-invideo'",
           "rect": [571, 115, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-invideo'",
-          "rect": [571, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='copycanvas'",
-          "rect": [429, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='copycanvas'",
           "rect": [429, 515, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -83,26 +53,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopcanvas'",
-          "rect": [429, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-incanvas'",
           "rect": [429, 315, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-incanvas'",
-          "rect": [429, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outcanvas'",
-          "rect": [429, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-outcanvas'",
           "rect": [429, 165, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -113,26 +68,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-incanvas'",
-          "rect": [429, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='copyimage'",
           "rect": [287, 515, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copyimage'",
-          "rect": [287, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopimage'",
-          "rect": [287, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopimage'",
           "rect": [287, 415, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -143,26 +83,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-inimage'",
-          "rect": [287, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-outimage'",
           "rect": [287, 165, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outimage'",
-          "rect": [287, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-inimage'",
-          "rect": [287, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-inimage'",
           "rect": [287, 115, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -173,26 +98,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copysolid color'",
-          "rect": [145, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopsolid color'",
           "rect": [145, 415, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopsolid color'",
-          "rect": [145, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-insolid color'",
-          "rect": [145, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-insolid color'",
           "rect": [145, 315, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -203,26 +113,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outsolid color'",
-          "rect": [145, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-insolid color'",
           "rect": [145, 115, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-insolid color'",
-          "rect": [145, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorvideo'",
-          "rect": [601, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorvideo'",
           "rect": [601, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -233,26 +128,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightervideo'",
-          "rect": [601, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outvideo'",
           "rect": [601, 365, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outvideo'",
-          "rect": [601, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overvideo'",
-          "rect": [601, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overvideo'",
           "rect": [601, 265, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -263,26 +143,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopvideo'",
-          "rect": [601, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overvideo'",
           "rect": [601, 65, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overvideo'",
-          "rect": [601, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorcanvas'",
-          "rect": [459, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorcanvas'",
           "rect": [459, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -293,26 +158,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightercanvas'",
-          "rect": [459, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outcanvas'",
           "rect": [459, 365, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outcanvas'",
-          "rect": [459, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overcanvas'",
-          "rect": [459, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overcanvas'",
           "rect": [459, 265, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -323,26 +173,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopcanvas'",
-          "rect": [459, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overcanvas'",
           "rect": [459, 65, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overcanvas'",
-          "rect": [459, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorimage'",
-          "rect": [317, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorimage'",
           "rect": [317, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -353,26 +188,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lighterimage'",
-          "rect": [317, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outimage'",
           "rect": [317, 365, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outimage'",
-          "rect": [317, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overimage'",
-          "rect": [317, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overimage'",
           "rect": [317, 265, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -383,26 +203,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopimage'",
-          "rect": [317, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overimage'",
           "rect": [317, 65, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overimage'",
-          "rect": [317, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorsolid color'",
-          "rect": [175, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorsolid color'",
           "rect": [175, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -413,16 +218,6 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightersolid color'",
-          "rect": [175, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outsolid color'",
-          "rect": [175, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outsolid color'",
           "rect": [175, 365, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -433,26 +228,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-oversolid color'",
-          "rect": [175, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-atopsolid color'",
           "rect": [175, 215, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopsolid color'",
-          "rect": [175, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-oversolid color'",
-          "rect": [175, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-oversolid color'",
           "rect": [175, 65, 50, 40],
           "reason": "invalidate paint rectangle"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-update-transform-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-update-transform-expected.png
index 09d2264..023d8117 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-update-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/overflow/overflow-update-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/selection-rect-transform-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/selection-rect-transform-expected.png
index df00a17..537ad07 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/selection-rect-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/selection-rect-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/box/box-inline-resize-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/box/box-inline-resize-expected.txt
index da53d2e2..e8f4906a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/box/box-inline-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/box/box-inline-resize-expected.txt
@@ -20,22 +20,17 @@
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [40, 103, 113, 29],
-          "reason": "chunk appeared"
+          "reason": "geometry"
         },
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [8, 103, 113, 29],
-          "reason": "disappeared"
+          "reason": "geometry"
         },
         {
           "object": "LayoutImage IMG id='foo'",
           "rect": [8, 84, 32, 32],
           "reason": "appeared"
-        },
-        {
-          "object": "LayoutImage IMG id='foo'",
-          "rect": [8, 84, 32, 32],
-          "reason": "chunk appeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/clip/clipped-relative-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/clip/clipped-relative-expected.txt
index 176c7ca..02c8c5e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/clip/clipped-relative-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/clip/clipped-relative-expected.txt
@@ -24,11 +24,6 @@
         },
         {
           "object": "LayoutImage IMG",
-          "rect": [102, 70, 110, 232],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage IMG",
           "rect": [8, 70, 94, 232],
           "reason": "geometry"
         }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
index 9144227..2cd5739 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -23,26 +23,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copyvideo'",
-          "rect": [571, 513, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopvideo'",
           "rect": [571, 413, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopvideo'",
-          "rect": [571, 413, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-invideo'",
-          "rect": [571, 313, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-invideo'",
           "rect": [571, 313, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -53,26 +38,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outvideo'",
-          "rect": [571, 163, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-invideo'",
           "rect": [571, 113, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-invideo'",
-          "rect": [571, 113, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='copycanvas'",
-          "rect": [429, 513, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='copycanvas'",
           "rect": [429, 513, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -83,26 +53,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopcanvas'",
-          "rect": [429, 413, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-incanvas'",
           "rect": [429, 313, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-incanvas'",
-          "rect": [429, 313, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outcanvas'",
-          "rect": [429, 163, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-outcanvas'",
           "rect": [429, 163, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -113,26 +68,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-incanvas'",
-          "rect": [429, 113, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='copyimage'",
           "rect": [287, 513, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copyimage'",
-          "rect": [287, 513, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopimage'",
-          "rect": [287, 413, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopimage'",
           "rect": [287, 413, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -143,26 +83,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-inimage'",
-          "rect": [287, 313, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-outimage'",
           "rect": [287, 163, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outimage'",
-          "rect": [287, 163, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-inimage'",
-          "rect": [287, 113, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-inimage'",
           "rect": [287, 113, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -173,26 +98,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copysolid color'",
-          "rect": [145, 513, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopsolid color'",
           "rect": [145, 413, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopsolid color'",
-          "rect": [145, 413, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-insolid color'",
-          "rect": [145, 313, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-insolid color'",
           "rect": [145, 313, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -203,26 +113,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outsolid color'",
-          "rect": [145, 163, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-insolid color'",
           "rect": [145, 113, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-insolid color'",
-          "rect": [145, 113, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorvideo'",
-          "rect": [601, 563, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorvideo'",
           "rect": [601, 563, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -233,26 +128,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightervideo'",
-          "rect": [601, 463, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outvideo'",
           "rect": [601, 363, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outvideo'",
-          "rect": [601, 363, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overvideo'",
-          "rect": [601, 263, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overvideo'",
           "rect": [601, 263, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -263,26 +143,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopvideo'",
-          "rect": [601, 213, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overvideo'",
           "rect": [601, 63, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overvideo'",
-          "rect": [601, 63, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorcanvas'",
-          "rect": [459, 563, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorcanvas'",
           "rect": [459, 563, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -293,26 +158,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightercanvas'",
-          "rect": [459, 463, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outcanvas'",
           "rect": [459, 363, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outcanvas'",
-          "rect": [459, 363, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overcanvas'",
-          "rect": [459, 263, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overcanvas'",
           "rect": [459, 263, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -323,26 +173,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopcanvas'",
-          "rect": [459, 213, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overcanvas'",
           "rect": [459, 63, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overcanvas'",
-          "rect": [459, 63, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorimage'",
-          "rect": [317, 563, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorimage'",
           "rect": [317, 563, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -353,26 +188,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lighterimage'",
-          "rect": [317, 463, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outimage'",
           "rect": [317, 363, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outimage'",
-          "rect": [317, 363, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overimage'",
-          "rect": [317, 263, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overimage'",
           "rect": [317, 263, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -383,26 +203,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopimage'",
-          "rect": [317, 213, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overimage'",
           "rect": [317, 63, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overimage'",
-          "rect": [317, 63, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorsolid color'",
-          "rect": [175, 563, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorsolid color'",
           "rect": [175, 563, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -413,16 +218,6 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightersolid color'",
-          "rect": [175, 463, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outsolid color'",
-          "rect": [175, 363, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outsolid color'",
           "rect": [175, 363, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -433,26 +228,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-oversolid color'",
-          "rect": [175, 263, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-atopsolid color'",
           "rect": [175, 213, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopsolid color'",
-          "rect": [175, 213, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-oversolid color'",
-          "rect": [175, 63, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-oversolid color'",
           "rect": [175, 63, 50, 40],
           "reason": "invalidate paint rectangle"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/list-marker-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/list-marker-2-expected.txt
index 45e6d41..51c4890 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/list-marker-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/list-marker-2-expected.txt
@@ -23,11 +23,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutImage IMG id='target'",
-          "rect": [48, 144, 100, 50],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutListMarker (anonymous)",
           "rect": [31, 180, 7, 18],
           "reason": "geometry"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
index 97f25682..c09f6049 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
@@ -26,11 +26,6 @@
           "object": "LayoutImage IMG",
           "rect": [8, 60, 114, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [122, 60, 100, 232],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selected-replaced-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selected-replaced-expected.txt
index ba4e53fd..fce57f5 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selected-replaced-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selected-replaced-expected.txt
@@ -19,23 +19,13 @@
       "paintInvalidations": [
         {
           "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
+          "rect": [8, 148, 214, 232],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
           "rect": [8, 68, 214, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 148, 214, 152],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 300, 214, 80],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 68, 214, 80],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-update-transform-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-update-transform-expected.png
index 0b91d43f..9c58860e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-update-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/overflow/overflow-update-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/selection-rect-transform-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/selection-rect-transform-expected.png
index 43ccfc4..7bdc69e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/selection-rect-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/selection-rect-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/box/box-inline-resize-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/box/box-inline-resize-expected.txt
index 898a81b..43f833bb 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/box/box-inline-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/box/box-inline-resize-expected.txt
@@ -20,22 +20,17 @@
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [40, 107, 111, 28],
-          "reason": "chunk appeared"
+          "reason": "geometry"
         },
         {
           "object": "InlineTextBox 'Chromium'",
           "rect": [8, 107, 111, 28],
-          "reason": "disappeared"
+          "reason": "geometry"
         },
         {
           "object": "LayoutImage IMG id='foo'",
           "rect": [8, 88, 32, 32],
           "reason": "appeared"
-        },
-        {
-          "object": "LayoutImage IMG id='foo'",
-          "rect": [8, 88, 32, 32],
-          "reason": "chunk appeared"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/clip/clipped-relative-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/clip/clipped-relative-expected.txt
index f1221fa..4ba8da1 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/clip/clipped-relative-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/clip/clipped-relative-expected.txt
@@ -24,11 +24,6 @@
         },
         {
           "object": "LayoutImage IMG",
-          "rect": [102, 74, 110, 232],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage IMG",
           "rect": [8, 74, 94, 232],
           "reason": "geometry"
         }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
index be9955c..e62a20cc 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/image/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -23,26 +23,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copyvideo'",
-          "rect": [569, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopvideo'",
           "rect": [569, 415, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopvideo'",
-          "rect": [569, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-invideo'",
-          "rect": [569, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-invideo'",
           "rect": [569, 315, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -53,26 +38,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outvideo'",
-          "rect": [569, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-invideo'",
           "rect": [569, 115, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-invideo'",
-          "rect": [569, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='copycanvas'",
-          "rect": [427, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='copycanvas'",
           "rect": [427, 515, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -83,26 +53,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopcanvas'",
-          "rect": [427, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-incanvas'",
           "rect": [427, 315, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-incanvas'",
-          "rect": [427, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outcanvas'",
-          "rect": [427, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-outcanvas'",
           "rect": [427, 165, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -113,26 +68,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-incanvas'",
-          "rect": [427, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='copyimage'",
           "rect": [285, 515, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copyimage'",
-          "rect": [285, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopimage'",
-          "rect": [285, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopimage'",
           "rect": [285, 415, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -143,26 +83,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-inimage'",
-          "rect": [285, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-outimage'",
           "rect": [285, 165, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outimage'",
-          "rect": [285, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-inimage'",
-          "rect": [285, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-inimage'",
           "rect": [285, 115, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -173,26 +98,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='copysolid color'",
-          "rect": [143, 515, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-atopsolid color'",
           "rect": [143, 415, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-atopsolid color'",
-          "rect": [143, 415, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-insolid color'",
-          "rect": [143, 315, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-insolid color'",
           "rect": [143, 315, 80, 40],
           "reason": "invalidate paint rectangle"
@@ -203,26 +113,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-outsolid color'",
-          "rect": [143, 165, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-insolid color'",
           "rect": [143, 115, 80, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-insolid color'",
-          "rect": [143, 115, 80, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorvideo'",
-          "rect": [599, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorvideo'",
           "rect": [599, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -233,26 +128,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightervideo'",
-          "rect": [599, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outvideo'",
           "rect": [599, 365, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outvideo'",
-          "rect": [599, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overvideo'",
-          "rect": [599, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overvideo'",
           "rect": [599, 265, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -263,26 +143,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopvideo'",
-          "rect": [599, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overvideo'",
           "rect": [599, 65, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overvideo'",
-          "rect": [599, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorcanvas'",
-          "rect": [457, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorcanvas'",
           "rect": [457, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -293,26 +158,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightercanvas'",
-          "rect": [457, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outcanvas'",
           "rect": [457, 365, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outcanvas'",
-          "rect": [457, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overcanvas'",
-          "rect": [457, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overcanvas'",
           "rect": [457, 265, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -323,26 +173,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopcanvas'",
-          "rect": [457, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overcanvas'",
           "rect": [457, 65, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overcanvas'",
-          "rect": [457, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorimage'",
-          "rect": [315, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorimage'",
           "rect": [315, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -353,26 +188,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lighterimage'",
-          "rect": [315, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outimage'",
           "rect": [315, 365, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outimage'",
-          "rect": [315, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-overimage'",
-          "rect": [315, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-overimage'",
           "rect": [315, 265, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -383,26 +203,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopimage'",
-          "rect": [315, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-overimage'",
           "rect": [315, 65, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-overimage'",
-          "rect": [315, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='xorsolid color'",
-          "rect": [173, 565, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='xorsolid color'",
           "rect": [173, 565, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -413,16 +218,6 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='lightersolid color'",
-          "rect": [173, 465, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-outsolid color'",
-          "rect": [173, 365, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='destination-outsolid color'",
           "rect": [173, 365, 50, 40],
           "reason": "invalidate paint rectangle"
@@ -433,26 +228,11 @@
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='destination-oversolid color'",
-          "rect": [173, 265, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-atopsolid color'",
           "rect": [173, 215, 50, 40],
           "reason": "invalidate paint rectangle"
         },
         {
-          "object": "LayoutHTMLCanvas CANVAS id='source-atopsolid color'",
-          "rect": [173, 215, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
-          "object": "LayoutHTMLCanvas CANVAS id='source-oversolid color'",
-          "rect": [173, 65, 50, 40],
-          "reason": "invalidate paint rectangle"
-        },
-        {
           "object": "LayoutHTMLCanvas CANVAS id='source-oversolid color'",
           "rect": [173, 65, 50, 40],
           "reason": "invalidate paint rectangle"
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/list-marker-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/list-marker-2-expected.txt
index 66306ca..e7355168c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/list-marker-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/list-marker-2-expected.txt
@@ -23,11 +23,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutImage IMG id='target'",
-          "rect": [48, 150, 100, 50],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutListMarker (anonymous)",
           "rect": [30, 185, 7, 19],
           "reason": "geometry"
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
index 829cd369..e319b61 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/position/positioned-list-offset-change-repaint-expected.txt
@@ -26,11 +26,6 @@
           "object": "LayoutImage IMG",
           "rect": [8, 64, 114, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [122, 64, 100, 232],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selected-replaced-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selected-replaced-expected.txt
index 812b604..377adb5 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selected-replaced-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selected-replaced-expected.txt
@@ -19,23 +19,13 @@
       "paintInvalidations": [
         {
           "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
+          "rect": [8, 132, 214, 232],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
           "rect": [8, 52, 214, 232],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 132, 214, 152],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 284, 214, 80],
-          "reason": "incremental"
-        },
-        {
-          "object": "LayoutImage (relative positioned) IMG id='test' class='moved'",
-          "rect": [8, 52, 214, 80],
-          "reason": "incremental"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/resources/gesture-util.js b/third_party/WebKit/LayoutTests/resources/gesture-util.js
index 9fae8c4..917e799 100644
--- a/third_party/WebKit/LayoutTests/resources/gesture-util.js
+++ b/third_party/WebKit/LayoutTests/resources/gesture-util.js
@@ -27,6 +27,26 @@
   });
 }
 
+// Returns a promise that resolves when the given condition holds for 100
+// animation frames or rejects if the condition changes to false within 100
+// animation frames.
+function conditionHolds(condition, error_message = 'Condition is not true anymore.') {
+  const MAX_FRAME = 100;
+  return new Promise((resolve, reject) => {
+    function tick(frames) {
+      // We requestAnimationFrame either for 200 frames or until condition is
+      // met.
+      if (frames >= MAX_FRAME)
+        resolve();
+      else if (!condition())
+        reject(error_message);
+      else
+        requestAnimationFrame(tick.bind(this, frames + 1));
+    }
+    tick(0);
+  });
+}
+
 function waitForAnimationEnd(getValue, max_frame, max_unchanged_frame) {
   const MAX_FRAME = max_frame;
   const MAX_UNCHANGED_FRAME = max_unchanged_frame;
@@ -236,6 +256,30 @@
   });
 }
 
+// Helper functions used in some of the gesture scroll layouttests.
+function recordScroll() {
+  scrollEventsOccurred++;
+}
+function notScrolled() {
+  return scrolledElement.scrollTop == 0 && scrolledElement.scrollLeft == 0;
+}
+function checkScrollOffset() {
+  // To avoid flakiness up to two pixels off per gesture is allowed.
+  var pixels = 2 * (gesturesOccurred + 1);
+  var result = approx_equals(scrolledElement.scrollTop, scrollAmountY[gesturesOccurred], pixels) &&
+      approx_equals(scrolledElement.scrollLeft, scrollAmountX[gesturesOccurred], pixels);
+  if (result)
+    gesturesOccurred++;
+
+  return result;
+}
+
+// This promise gets resolved in iframe onload.
+var iframeLoadResolve;
+iframeOnLoadPromise = new Promise(function(resolve) {
+  iframeLoadResolve = resolve;
+});
+
 function touchTapOn(xPosition, yPosition) {
   return new Promise(function(resolve, reject) {
     if (window.chrome && chrome.gpuBenchmarking) {
@@ -253,4 +297,4 @@
 
 function approx_equals(actual, expected, epsilon) {
   return actual >= expected - epsilon && actual <= expected + epsilon;
-}
\ No newline at end of file
+}
diff --git a/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt b/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt
index 4ee93d8d..a8c8042c 100644
--- a/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-scroll-expected.txt
@@ -1,20 +1,20 @@
 This test verifies the hit test regions given to the compositor specifically around composited overflow scroll elements.
 
-scrollContent: DIV#scroll1 scrolling (1, 14, 273, 12)
+scrollContent: DIV#scroll1 scrolling (0, 13, 273, 12)
 
-scrollContent5: DIV#scroll5 scrolling (1, 14, 273, 12)
+scrollContent5: DIV#scroll5 scrolling (0, 13, 273, 12)
 
 scrollContent6: DIV#scroll6 scrolling[-1,-14] (0, 0, 273, 12)
 
-nestedContent: DIV#scroll2b scrolling (1, 31, 256, 12)
+nestedContent: DIV#scroll2b scrolling (0, 30, 256, 12)
 
+overflowwithhandler: DIV#overflowwithhandler scrolling (0, 0, 256, 116)
 overflowwithhandler: DIV#overflowwithhandler (0, 0, 273, 52)
-overflowwithhandler: DIV#overflowwithhandler scrolling (1, 1, 256, 116)
 
 overflowwithborder: DIV#overflowwithborder (0, 0, 290, 70)
-overflowwithborder: DIV#overflowwithborder scrolling (10, 10, 255, 116)
+overflowwithborder: DIV#overflowwithborder scrolling (4, 4, 255, 116)
 
 withTransform: DIV#transformed (0, 0, 271, 12)
-withTransform: DIV#scroll3 scrolling (1, 14, 273, 14)
+withTransform: DIV#scroll3 scrolling (0, 13, 273, 14)
 
 
diff --git a/third_party/WebKit/LayoutTests/virtual/video-surface-layer/external/wpt/picture-in-picture/idlharness.window-expected.txt b/third_party/WebKit/LayoutTests/virtual/video-surface-layer/external/wpt/picture-in-picture/idlharness.window-expected.txt
index 43a7519..0c0c0659 100644
--- a/third_party/WebKit/LayoutTests/virtual/video-surface-layer/external/wpt/picture-in-picture/idlharness.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/video-surface-layer/external/wpt/picture-in-picture/idlharness.window-expected.txt
@@ -1,5 +1,4 @@
 This is a testharness.js-based test.
-PASS idlharness
 PASS picture-in-picture interfaces.
 PASS Partial interface HTMLVideoElement: original interface defined
 PASS Partial interface Document: original interface defined
diff --git a/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html b/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html
index dba4f8b..7712f2e8 100644
--- a/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html
+++ b/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html
@@ -3,13 +3,13 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mock_service) => {
+vr_test( (t, device_controller) => {
   return navigator.getVRDisplays().then( (displays) => {
     let display = displays[0];
     let counter = 0;
@@ -64,7 +64,7 @@
     // Ensure that there's a non-null pose available for the mock framework to
     // return, and wait for one rAF callback to let it propagate. This is a
     // workaround for delayed promise execution in the mocking framework.
-    mock_service.mockVRDisplays_[0].setPose(VALID_POSE);
+    device_controller.setPose(VALID_POSE);
     display.requestAnimationFrame(onFirstAnimationFrame);
   }, (err) => {
     t.step( () => {
diff --git a/third_party/WebKit/LayoutTests/vr/events_vrdisplayactivate.html b/third_party/WebKit/LayoutTests/vr/events_vrdisplayactivate.html
index 3fba30a..6f30f53 100644
--- a/third_party/WebKit/LayoutTests/vr/events_vrdisplayactivate.html
+++ b/third_party/WebKit/LayoutTests/vr/events_vrdisplayactivate.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
@@ -11,7 +11,7 @@
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mockService) => {
+vr_test( (t, deviceController) => {
   let watcherDone = new Event("watcherdone");
   let eventWatcher = new EventWatcher(t, window, ["vrdisplayactivate",
                                                   "watcherdone"]);
@@ -35,7 +35,7 @@
       });
     }
     window.addEventListener("vrdisplayactivate", onDisplayActivate, false);
-    mockService.mockVRDisplays_[0].forceActivate(2 /*Display mounted, although any int 0-3 is valid*/);
+    deviceController.forceActivate(2 /*Display mounted, although any int 0-3 is valid*/);
 
   }, (err) => {
     t.step( () => {
diff --git a/third_party/WebKit/LayoutTests/vr/events_vrdisplayconnect.html b/third_party/WebKit/LayoutTests/vr/events_vrdisplayconnect.html
index 7753bc6..e3435dd 100644
--- a/third_party/WebKit/LayoutTests/vr/events_vrdisplayconnect.html
+++ b/third_party/WebKit/LayoutTests/vr/events_vrdisplayconnect.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
@@ -11,7 +11,7 @@
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mockService) => {
+vr_test( (t) => {
   let watcherDone = new Event("watcherdone");
   let eventWatcher = new EventWatcher(t, window, ["vrdisplayconnect",
                                                   "watcherdone"]);
@@ -27,13 +27,18 @@
     t.step( () => {
       assert_equals(displays.length, 1);
     }, "Starting with a single display");
-    mockService.addVRDisplay(fakeDisplays["FakeMagicWindowOnly"]);
+
+    XRTest.simulateDeviceConnection({ supportsImmersive: false });
 
     setTimeout( () => {
       navigator.getVRDisplays().then( (displays) => {
         t.step( () => {
           assert_equals(displays.length, 2);
-          assert_equals(displays[1].displayName, "FakeVRDisplay");
+          // The name FakeDevice is a default name given by the XR backend
+          // mocking and sent to the on connect method. As displayName is
+          // eventually going away, doesn't seem much point in making this
+          // setable.
+          assert_equals(displays[1].displayName, "FakeDevice");
         }, "Check added display");
         window.dispatchEvent(watcherDone);
       }, (err) => {
diff --git a/third_party/WebKit/LayoutTests/vr/events_vrdisplaypresentchange.html b/third_party/WebKit/LayoutTests/vr/events_vrdisplaypresentchange.html
index 6859daf4..6d9629e 100644
--- a/third_party/WebKit/LayoutTests/vr/events_vrdisplaypresentchange.html
+++ b/third_party/WebKit/LayoutTests/vr/events_vrdisplaypresentchange.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/exitPresent_reject_notpresenting.html b/third_party/WebKit/LayoutTests/vr/exitPresent_reject_notpresenting.html
index bc22adc..41cb060 100644
--- a/third_party/WebKit/LayoutTests/vr/exitPresent_reject_notpresenting.html
+++ b/third_party/WebKit/LayoutTests/vr/exitPresent_reject_notpresenting.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/exitPresent_resolve.html b/third_party/WebKit/LayoutTests/vr/exitPresent_resolve.html
index 02a1e51..233cee01 100644
--- a/third_party/WebKit/LayoutTests/vr/exitPresent_resolve.html
+++ b/third_party/WebKit/LayoutTests/vr/exitPresent_resolve.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/getEyeParameters_match.html b/third_party/WebKit/LayoutTests/vr/getEyeParameters_match.html
index b97219f7..b04ceeaf 100644
--- a/third_party/WebKit/LayoutTests/vr/getEyeParameters_match.html
+++ b/third_party/WebKit/LayoutTests/vr/getEyeParameters_match.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 
diff --git a/third_party/WebKit/LayoutTests/vr/getEyeParameters_null.html b/third_party/WebKit/LayoutTests/vr/getEyeParameters_null.html
index e664a132..67d8a3a1 100644
--- a/third_party/WebKit/LayoutTests/vr/getEyeParameters_null.html
+++ b/third_party/WebKit/LayoutTests/vr/getEyeParameters_null.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 
diff --git a/third_party/WebKit/LayoutTests/vr/getFrameData_noupdate.html b/third_party/WebKit/LayoutTests/vr/getFrameData_noupdate.html
index 7f01ad243..0567fa8 100644
--- a/third_party/WebKit/LayoutTests/vr/getFrameData_noupdate.html
+++ b/third_party/WebKit/LayoutTests/vr/getFrameData_noupdate.html
@@ -3,16 +3,16 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mock_service) => {
+vr_test( (t, device_controller) => {
   return navigator.getVRDisplays().then( (displays) => {
     var display = displays[0];
-    mock_service.mockVRDisplays_[0].setPose(VALID_POSE);
+    device_controller.setPose(VALID_POSE);
     var fd = new VRFrameData();
     t.step( () => {
       assert_false(display.getFrameData(fd));
diff --git a/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html b/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html
index f2a870d..acc371c 100644
--- a/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html
+++ b/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html
@@ -3,13 +3,13 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mock_service) => {
+vr_test( (t, device_controller) => {
   return navigator.getVRDisplays().then( (displays) => {
     var display = displays[0];
     var expected_pose = VALID_POSE;
@@ -21,7 +21,7 @@
         t.step( () => {
           assert_false(display.getFrameData(fd));
         }, "Expecting to not get framedata since there is no pose");
-        mock_service.mockVRDisplays_[0].setPose(expected_pose);
+        device_controller.setPose(expected_pose);
         t.step( () => {
           assert_false(display.getFrameData(fd));
         }, "Does not update within the same frame");
diff --git a/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html b/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html
index 26f7ec21..568c0cf4 100644
--- a/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html
+++ b/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html
@@ -3,19 +3,19 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mock_service) => {
+vr_test( (t, device_controller) => {
   return navigator.getVRDisplays().then( (displays) => {
     var display = displays[0];
     var expected_pose = VALID_POSE;
     var fd1 = new VRFrameData();
     var fd2 = new VRFrameData();
-    mock_service.mockVRDisplays_[0].setPose(expected_pose);
+    device_controller.setPose(expected_pose);
 
     function onFrame() {
       display.requestAnimationFrame(onFrame);
diff --git a/third_party/WebKit/LayoutTests/vr/getFrameData_windowRAF.html b/third_party/WebKit/LayoutTests/vr/getFrameData_windowRAF.html
index b37dfa5..96e16f6 100644
--- a/third_party/WebKit/LayoutTests/vr/getFrameData_windowRAF.html
+++ b/third_party/WebKit/LayoutTests/vr/getFrameData_windowRAF.html
@@ -3,13 +3,13 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mock_service) => {
+vr_test( (t, device_controller) => {
   return navigator.getVRDisplays().then( (displays) => {
     var display = displays[0];
     var expected_pose = VALID_POSE;
@@ -22,7 +22,7 @@
         t.step( () => {
           assert_false(display.getFrameData(fd));
         }, "Expecting to not get framedata since there is no pose");
-        mock_service.mockVRDisplays_[0].setPose(expected_pose);
+        device_controller.setPose(expected_pose);
         t.step( () => {
           assert_false(display.getFrameData(fd));
         }, "Does not update within the same frame");
diff --git a/third_party/WebKit/LayoutTests/vr/getLayers_notpresenting.html b/third_party/WebKit/LayoutTests/vr/getLayers_notpresenting.html
index 66ab87b..633d2309 100644
--- a/third_party/WebKit/LayoutTests/vr/getLayers_notpresenting.html
+++ b/third_party/WebKit/LayoutTests/vr/getLayers_notpresenting.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/getLayers_presenting.html b/third_party/WebKit/LayoutTests/vr/getLayers_presenting.html
index 7d802e5..8eefa20 100644
--- a/third_party/WebKit/LayoutTests/vr/getLayers_presenting.html
+++ b/third_party/WebKit/LayoutTests/vr/getLayers_presenting.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 
diff --git a/third_party/WebKit/LayoutTests/vr/getLayers_presenting_nondefaultbounds.html b/third_party/WebKit/LayoutTests/vr/getLayers_presenting_nondefaultbounds.html
index 571e7d3..64f745e 100644
--- a/third_party/WebKit/LayoutTests/vr/getLayers_presenting_nondefaultbounds.html
+++ b/third_party/WebKit/LayoutTests/vr/getLayers_presenting_nondefaultbounds.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 
diff --git a/third_party/WebKit/LayoutTests/vr/getLayers_update.html b/third_party/WebKit/LayoutTests/vr/getLayers_update.html
index 063864b..9c5a7ee 100644
--- a/third_party/WebKit/LayoutTests/vr/getLayers_update.html
+++ b/third_party/WebKit/LayoutTests/vr/getLayers_update.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 
diff --git a/third_party/WebKit/LayoutTests/vr/getVRDisplays_one_display.html b/third_party/WebKit/LayoutTests/vr/getVRDisplays_one_display.html
index d51db24a..d109976 100644
--- a/third_party/WebKit/LayoutTests/vr/getVRDisplays_one_display.html
+++ b/third_party/WebKit/LayoutTests/vr/getVRDisplays_one_display.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 <script>
diff --git a/third_party/WebKit/LayoutTests/vr/getVRDisplays_two_display.html b/third_party/WebKit/LayoutTests/vr/getVRDisplays_two_display.html
index 14fe014c..9b257f5 100644
--- a/third_party/WebKit/LayoutTests/vr/getVRDisplays_two_display.html
+++ b/third_party/WebKit/LayoutTests/vr/getVRDisplays_two_display.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 <script>
diff --git a/third_party/WebKit/LayoutTests/vr/getVRDisplays_zero_display.html b/third_party/WebKit/LayoutTests/vr/getVRDisplays_zero_display.html
index d816b073..ba31c9d 100644
--- a/third_party/WebKit/LayoutTests/vr/getVRDisplays_zero_display.html
+++ b/third_party/WebKit/LayoutTests/vr/getVRDisplays_zero_display.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <script>
diff --git a/third_party/WebKit/LayoutTests/vr/multiple_requestAnimationFrame_called.html b/third_party/WebKit/LayoutTests/vr/multiple_requestAnimationFrame_called.html
index 1ef4804..ea62950 100644
--- a/third_party/WebKit/LayoutTests/vr/multiple_requestAnimationFrame_called.html
+++ b/third_party/WebKit/LayoutTests/vr/multiple_requestAnimationFrame_called.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <script>
diff --git a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_called.html b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_called.html
index e090200..e858a2d 100644
--- a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_called.html
+++ b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_called.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <script>
diff --git a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_consistentTimestamps.html b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_consistentTimestamps.html
index 46239e1..c3653606 100644
--- a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_consistentTimestamps.html
+++ b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_consistentTimestamps.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_handoff.html b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_handoff.html
index d4e9a3d..0c0b1ac 100644
--- a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_handoff.html
+++ b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_handoff.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_invalidhandle.html b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_invalidhandle.html
index 69f050f2..496ca9ea 100644
--- a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_invalidhandle.html
+++ b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_invalidhandle.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <script>
diff --git a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_submitFrame_combinations.html b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_submitFrame_combinations.html
index 73150707..177a711 100644
--- a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_submitFrame_combinations.html
+++ b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_submitFrame_combinations.html
@@ -3,7 +3,8 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
@@ -11,7 +12,7 @@
 <script>
 let fakeDisplays = fakeVRDisplays();
 
-vr_test( (t, mock_service) => {
+vr_test( (t, device_controller) => {
   return navigator.getVRDisplays().then( (displays) => {
     let display = displays[0];
 
@@ -35,11 +36,11 @@
     }
 
     function getSubmitFrameCount() {
-      return mock_service.mockVRDisplays_[0].getSubmitFrameCount();
+      return device_controller.getSubmitFrameCount();
     }
 
     function getMissingFrameCount() {
-      return mock_service.mockVRDisplays_[0].getMissingFrameCount();
+      return device_controller.getMissingFrameCount();
     }
 
     function onFrame1() {
diff --git a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_unregister.html b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_unregister.html
index 42af0a36..263d4966 100644
--- a/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_unregister.html
+++ b/third_party/WebKit/LayoutTests/vr/requestAnimationFrame_unregister.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <script>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_NaN_bounds.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_NaN_bounds.html
index 0ad0146..19582b6 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_NaN_bounds.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_NaN_bounds.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badleftbounds.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badleftbounds.html
index 501cd1a..dbbc9034 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badleftbounds.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badleftbounds.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badrightbounds.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badrightbounds.html
index 83584005..1fcbd42 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badrightbounds.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_badrightbounds.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nogesture.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nogesture.html
index b9fa396..36ed691f 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nogesture.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nogesture.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nolayers.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nolayers.html
index dd8fbf2..c34db803 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nolayers.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nolayers.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nosource.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nosource.html
index 7000c75..3e5534b 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nosource.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nosource.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_notsupported.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_notsupported.html
index 2639be1..f225d77 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_notsupported.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_notsupported.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nowebgl.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nowebgl.html
index 2bde1c0..210dc29a 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nowebgl.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nowebgl.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nullsource.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nullsource.html
index 3ce1d66..2f7ff1b 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nullsource.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_nullsource.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_toomanylayers.html b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_toomanylayers.html
index 45231b7..532f42d1 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_reject_toomanylayers.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_reject_toomanylayers.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve.html b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve.html
index 067517be..b054da02 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithgesture.html b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithgesture.html
index 7e34fa1..9a92b1a 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithgesture.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithgesture.html
@@ -4,7 +4,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithoutgesture.html b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithoutgesture.html
index 985ef630..90f90273 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithoutgesture.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_repeatwithoutgesture.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_then_reject.html b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_then_reject.html
index 4dae0aa..2389f21 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_then_reject.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_then_reject.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_webgl2.html b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_webgl2.html
index ae9ab3e0..9a96991f 100644
--- a/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_webgl2.html
+++ b/third_party/WebKit/LayoutTests/vr/requestPresent_resolve_webgl2.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 
 <canvas id="webgl2-canvas"></canvas>
diff --git a/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js b/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js
index c21bbed..b669d11 100644
--- a/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js
+++ b/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js
@@ -1,9 +1,51 @@
 'use strict';
 
+MockDevice.prototype.setPose = function(pose) {
+  if (pose == null) {
+    this.pose_ = null;
+  } else {
+    this.pose_ = {
+      orientation: null,
+      position: null,
+      angularVelocity: null,
+      linearVelocity: null,
+      angularAcceleration: null,
+      linearAcceleration: null,
+      inputState: null,
+      poseReset: false,
+      poseIndex: 0
+    };
+    for (var field in pose) {
+      if (this.pose_.hasOwnProperty(field)) {
+        this.pose_[field] = pose[field];
+      }
+    }
+  }
+};
+
+MockDevice.prototype.forceActivate = function(reason) {
+  this.displayClient_.onActivate(reason);
+};
+
 function vr_test(func, vrDisplays, name, properties) {
-  setFakeDevices(vrDisplays);
-  let t = async_test(name, properties);
-  func(t, mockVRService);
+  let chain = Promise.resolve();
+  let firstDeviceController;
+  vrDisplays.forEach(display => {
+    return chain.then(
+        XRTest
+            .simulateDeviceConnection(
+                {supportsImmersive: display.capabilities.canPresent})
+            .then((deviceController) => {
+              deviceController.displayInfo_ = display;
+              if (!firstDeviceController) {
+                firstDeviceController = deviceController;
+              }
+            }));
+  });
+  chain.then(() => {
+    let t = async_test(name, properties);
+    func(t, firstDeviceController);
+  })
 }
 
 function fakeVRDisplays(){
diff --git a/third_party/WebKit/LayoutTests/vr/stageParameters_match.html b/third_party/WebKit/LayoutTests/vr/stageParameters_match.html
index 162cea8..e6bfdae7 100644
--- a/third_party/WebKit/LayoutTests/vr/stageParameters_match.html
+++ b/third_party/WebKit/LayoutTests/vr/stageParameters_match.html
@@ -3,7 +3,7 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="resources/vr-test-utils.js "></script>
 <script src="resources/test-constants.js"></script>
 
diff --git a/third_party/WebKit/LayoutTests/xr/ar_hittest.html b/third_party/WebKit/LayoutTests/xr/ar_hittest.html
index a0734aa..c7506bd 100644
--- a/third_party/WebKit/LayoutTests/xr/ar_hittest.html
+++ b/third_party/WebKit/LayoutTests/xr/ar_hittest.html
@@ -3,44 +3,54 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test((session, t) => {
+let testName = "Ensures hit-test returns expected mock results";
+
+let fakeDeviceInitParams = { supportsImmersive: false };
+
+let requestSessionOptions = [ {
+    immersive: false,
+    outputContext: getOutputContext()
+  } ];
+
+let expectedHitMatrix = [1, 0, 0, 1,
+                         0, 1, 0, 2,
+                         0, 0, 1, 3,
+                         0, 0, 0, 1];
+
+let testFunction = function(session, t, fakeDeviceController) {
   assert_false(session.immersive);
   return session.requestFrameOfReference("eye-level").then((frameOfReference) => {
     let direction = new Float32Array([1.0, 0.0, 0.0]);
     let origin = new Float32Array([0.0, 0.0, 0.0]);
 
     let hit = new device.mojom.XRHitResult();
-    hit.hitMatrix = [1, 0, 0, 1,
-                0, 1, 0, 2,
-                0, 0, 1, 3,
-                0, 0, 0, 1];
-    setHitTestResults({ results: [hit] });
+    hit.hitMatrix = expectedHitMatrix;
+
+    fakeDeviceController.setHitTestResults({ results: [hit] });
 
     return session.requestHitTest(origin, direction, frameOfReference).then(
         (hitResults) => {
           // Test that hit results are what we expected.
           assert_equals(hitResults.length, 1);
-          let expectedHitMatrix = [1, 0, 0, 1,
-                                   0, 1, 0, 2,
-                                   0, 0, 1, 3,
-                                   0, 0, 0, 1];
           assert_equals(hitResults[0].hitMatrix.length, 16);
-          for (let i = 0; i < expectedHitMatrix.length; ++i) {
-            assert_approx_equals(hitResults[0].hitMatrix[i],
-                expectedHitMatrix[i],
-                FLOAT_EPSILON);
-          }
+          assert_matrices_approx_equal(
+              hitResults[0].hitMatrix,
+              expectedHitMatrix,
+              FLOAT_EPSILON,
+              "Hit test matrix does not equals input test matrix");
         });
     });
-  }, fakeDevices["FakeARPhone"], [ { immersive: false, outputContext: getOutputContext() } ],
-"Ensures hit-test returns expected mock results");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/events_deviceconnect.html b/third_party/WebKit/LayoutTests/xr/events_deviceconnect.html
index 3258c7c3..0038331 100644
--- a/third_party/WebKit/LayoutTests/xr/events_deviceconnect.html
+++ b/third_party/WebKit/LayoutTests/xr/events_deviceconnect.html
@@ -3,13 +3,11 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas id="webgl-canvas"></canvas>
 <script>
 promise_test((t) => {
-  setFakeDevices([fakeXRDevices()["FakeGooglePixelPhone"]]);
-
   let watcherDone = new Event("watcherdone");
   let eventWatcher = new EventWatcher(t, navigator.xr, ["devicechange",
                                                         "watcherdone"]);
@@ -22,6 +20,8 @@
 
   navigator.xr.addEventListener("devicechange", onDeviceChange, false);
 
+  XRTest.simulateDeviceConnection({ supportsImmersive:true });
+
   return eventWatcher.wait_for(["devicechange", "watcherdone"]);
 
 }, "Test devicechange fires when devices are connected.");
diff --git a/third_party/WebKit/LayoutTests/xr/events_session_end.html b/third_party/WebKit/LayoutTests/xr/events_session_end.html
index 853196a8..a1092f4 100644
--- a/third_party/WebKit/LayoutTests/xr/events_session_end.html
+++ b/third_party/WebKit/LayoutTests/xr/events_session_end.html
@@ -3,16 +3,24 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
 
-let fakeDevices = fakeXRDevices();
+let testName = "Test end fires when session ends";
+
 let watcherDone = new Event("watcherdone");
 
-xr_session_promise_test( (session, t) => {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { immersive: true },
+  { outputContext: getOutputContext() }
+];
+
+let testFunction = function(session, t) {
   let eventWatcher = new EventWatcher(t, session, ["end", "watcherdone"]);
   let eventPromise = eventWatcher.wait_for(["end", "watcherdone"]);
 
@@ -26,10 +34,9 @@
   session.end();
 
   return eventPromise;
-}, fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"Test end fires when session ends");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/events_session_resetpose.html b/third_party/WebKit/LayoutTests/xr/events_session_resetpose.html
index bb62e6e3..11d18956 100644
--- a/third_party/WebKit/LayoutTests/xr/events_session_resetpose.html
+++ b/third_party/WebKit/LayoutTests/xr/events_session_resetpose.html
@@ -3,44 +3,61 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName =
+  "XRSession resetpose from a device properly fires off the right events"
+
 let watcherDone = new Event("watcherdone");
 
-xr_session_promise_test( (session, t) => {
-    // Session must have a baseLayer or frame requests will be ignored.
-    session.baseLayer = new XRWebGLLayer(session, gl);
+let fakeDeviceInitParams = { supportsImmersive:true };
 
-    let eventWatcher = new EventWatcher(t, session, ["resetpose", "watcherdone"]);
-    let eventPromise = eventWatcher.wait_for(["resetpose", "watcherdone"]);
+let requestSessionOptions = [
+  { outputContext: getOutputContext() },
+  { immersive: true },
+];
 
-    // Need to have a valid pose or input event's don't process.
-    var pose = Object.assign({}, VALID_POSE);
-    pose.poseReset = true;
-    setPose(pose);
+let testFunction = function(session, t, fakeDeviceController) {
+  // Session must have a baseLayer or frame requests will be ignored.
+  session.baseLayer = new XRWebGLLayer(session, gl);
 
-    function onPoseReset(event) {
-      t.step( () => {
-        assert_equals(event.session, session);
-        session.dispatchEvent(watcherDone);
-      });
-    }
+  let eventWatcher = new EventWatcher(
+    t, session, ["resetpose", "watcherdone"]);
+  let eventPromise = eventWatcher.wait_for(["resetpose", "watcherdone"]);
 
-    session.addEventListener("resetpose", onPoseReset, false);
+  // Need to have a valid pose or input events don't process.
+  fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
+      eye:"left",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }, {
+      eye:"right",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }]);
+  fakeDeviceController.setResetPose(true);
 
-    // Trigger the reset pose.
-    session.requestAnimationFrame((time, xrFrame) => {});
+  function onPoseReset(event) {
+    t.step( () => {
+      assert_equals(event.session, session);
+      session.dispatchEvent(watcherDone);
+    });
+  }
 
-    return eventPromise;
-  }, fakeDevices["FakeGooglePixelPhone"], [
-    { outputContext: getOutputContext() },
-    { immersive: true },
-  ],
-"XRSession resetpose from a device properly fires off the right events");
+  session.addEventListener("resetpose", onPoseReset, false);
+
+  // Trigger the reset pose.
+  session.requestAnimationFrame((time, xrFrame) => {});
+
+  return eventPromise;
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/events_session_select.html b/third_party/WebKit/LayoutTests/xr/events_session_select.html
index b6fe9c70..d7f9f9b 100644
--- a/third_party/WebKit/LayoutTests/xr/events_session_select.html
+++ b/third_party/WebKit/LayoutTests/xr/events_session_select.html
@@ -3,72 +3,91 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "XRInputSources primary input presses properly fires off the "
+  + "right events";
+
 let watcherDone = new Event("watcherdone");
 
-xr_session_promise_test( (session, t) => {
-    let eventWatcher = new EventWatcher(t, session, ["selectstart", "selectend", "select", "watcherdone"]);
-    let eventPromise = eventWatcher.wait_for(["selectstart", "selectend", "select", "watcherdone"]);
+let fakeDeviceInitParams = { supportsImmersive:true };
 
-    // Need to have a valid pose or input event's don't process.
-    setPose(VALID_POSE);
+let requestSessionOptions = [{ immersive: true }];
 
-    function onSessionSelectStart(event) {
-      t.step( () => {
-        let input_sources = session.getInputSources();
-        assert_equals(event.frame.session, session);
-        assert_equals(event.inputSource, input_sources[0]);
-      });
-    }
+let testFunction = function(session, t, fakeDeviceController) {
+  let eventWatcher = new EventWatcher(
+    t, session, ["selectstart", "selectend", "select", "watcherdone"]);
+  let eventPromise = eventWatcher.wait_for(
+    ["selectstart", "selectend", "select", "watcherdone"]);
 
-    function onSessionSelectEnd(event) {
-      t.step( () => {
-        let input_sources = session.getInputSources();
-        assert_equals(event.frame.session, session);
-        assert_equals(event.inputSource, input_sources[0]);
-      });
-    }
+  // Need to have a valid pose or input event's don't process.
+  fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
+      eye:"left",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }, {
+      eye:"right",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }]);
 
-    function onSessionSelect(event) {
-      t.step( () => {
-        let input_sources = session.getInputSources();
-        assert_equals(event.frame.session, session);
-        assert_equals(event.inputSource, input_sources[0]);
-      });
-      session.dispatchEvent(watcherDone);
-    }
-    session.addEventListener("selectstart", onSessionSelectStart, false);
-    session.addEventListener("selectend", onSessionSelectEnd, false);
-    session.addEventListener("select", onSessionSelect, false);
-
-    // Session must have a baseLayer or frame requests will be ignored.
-    session.baseLayer = new XRWebGLLayer(session, gl);
-
-    let input_source = new MockXRInputSource();
-
-    addInputSource(input_source);
-
-    // Press the primary input button and then release it a short time later.
-    session.requestAnimationFrame((time, xrFrame) => {
-      input_source.primaryInputPressed = true;
-
-       session.requestAnimationFrame((time, xrFrame) => {
-          input_source.primaryInputPressed = false;
-
-          session.requestAnimationFrame((time, xrFrame) => {
-            // Need to process one more frame to allow select to propegate.
-          });
-       });
+  function onSessionSelectStart(event) {
+    t.step( () => {
+      let input_sources = session.getInputSources();
+      assert_equals(event.frame.session, session);
+      assert_equals(event.inputSource, input_sources[0]);
     });
+  }
 
-    return eventPromise;
-  }, fakeDevices["FakeGooglePixelPhone"], [ { immersive: true } ],
-"XRInputSources primary input presses properly fires off the right events");
+  function onSessionSelectEnd(event) {
+    t.step( () => {
+      let input_sources = session.getInputSources();
+      assert_equals(event.frame.session, session);
+      assert_equals(event.inputSource, input_sources[0]);
+    });
+  }
+
+  function onSessionSelect(event) {
+    t.step( () => {
+      let input_sources = session.getInputSources();
+      assert_equals(event.frame.session, session);
+      assert_equals(event.inputSource, input_sources[0]);
+    });
+    session.dispatchEvent(watcherDone);
+  }
+  session.addEventListener("selectstart", onSessionSelectStart, false);
+  session.addEventListener("selectend", onSessionSelectEnd, false);
+  session.addEventListener("select", onSessionSelect, false);
+
+  // Session must have a baseLayer or frame requests will be ignored.
+  session.baseLayer = new XRWebGLLayer(session, gl);
+
+  let input_source = new MockXRInputSource();
+
+  fakeDeviceController.addInputSource(input_source);
+
+  // Press the primary input button and then release it a short time later.
+  session.requestAnimationFrame((time, xrFrame) => {
+    input_source.primaryInputPressed = true;
+
+      session.requestAnimationFrame((time, xrFrame) => {
+        input_source.primaryInputPressed = false;
+
+        session.requestAnimationFrame((time, xrFrame) => {
+          // Need to process one more frame to allow select to propegate.
+        });
+      });
+  });
+
+  return eventPromise;
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/events_session_select_subframe.html b/third_party/WebKit/LayoutTests/xr/events_session_select_subframe.html
index 1afa558..864dbb36 100644
--- a/third_party/WebKit/LayoutTests/xr/events_session_select_subframe.html
+++ b/third_party/WebKit/LayoutTests/xr/events_session_select_subframe.html
@@ -3,69 +3,87 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "Ensures that an XRInputSources primary input being pressed and "
+  + "released in the space of a single frame properly fires off the right "
+  + "events";
+
 let watcherDone = new Event("watcherdone");
 
-xr_session_promise_test( (session, t) => {
-    let eventWatcher = new EventWatcher(t, session, ["selectstart", "selectend", "select", "watcherdone"]);
-    let eventPromise = eventWatcher.wait_for(["selectstart", "selectend", "select", "watcherdone"]);
+let fakeDeviceInitParams = { supportsImmersive:true };
 
-    // Need to have a valid pose or input event's don't process.
-    setPose(VALID_POSE);
+let requestSessionOptions = [{ immersive: true }];
 
-    function onSessionSelectStart(event) {
-      t.step( () => {
-        let input_sources = session.getInputSources();
-        assert_equals(event.frame.session, session);
-        assert_equals(event.inputSource, input_sources[0]);
-      });
-    }
+let testFunction = function(session, t, fakeDeviceController) {
+  let eventWatcher = new EventWatcher(
+    t, session, ["selectstart", "selectend", "select", "watcherdone"]);
+  let eventPromise = eventWatcher.wait_for(
+    ["selectstart", "selectend", "select", "watcherdone"]);
 
-    function onSessionSelectEnd(event) {
-      t.step( () => {
-        let input_sources = session.getInputSources();
-        assert_equals(event.frame.session, session);
-        assert_equals(event.inputSource, input_sources[0]);
-      });
-    }
+  // Need to have a valid pose or input event's don't process.
+  fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
+      eye:"left",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }, {
+      eye:"right",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }]);
 
-    function onSessionSelect(event) {
-      t.step( () => {
-        let input_sources = session.getInputSources();
-        assert_equals(event.frame.session, session);
-        assert_equals(event.inputSource, input_sources[0]);
-      });
-      session.dispatchEvent(watcherDone);
-    }
-    session.addEventListener("selectstart", onSessionSelectStart, false);
-    session.addEventListener("selectend", onSessionSelectEnd, false);
-    session.addEventListener("select", onSessionSelect, false);
-
-    // Session must have a baseLayer or frame requests will be ignored.
-    session.baseLayer = new XRWebGLLayer(session, gl);
-
-    let input_source = new MockXRInputSource();
-
-    addInputSource(input_source);
-
-    // Press the primary input button and then release it a short time later.
-    session.requestAnimationFrame((time, xrFrame) => {
-      input_source.primaryInputPressed = true;
-      input_source.primaryInputPressed = false;
-
-      session.requestAnimationFrame((time, xrFrame) => {
-        // Need to process one more frame to allow select to propegate.
-      });
+  function onSessionSelectStart(event) {
+    t.step( () => {
+      let input_sources = session.getInputSources();
+      assert_equals(event.frame.session, session);
+      assert_equals(event.inputSource, input_sources[0]);
     });
+  }
 
-    return eventPromise;
-  }, fakeDevices["FakeGooglePixelPhone"], [ { immersive: true } ],
-"Ensures that an XRInputSources primary input being pressed and released in the space of a single frame properly fires off the right events");
+  function onSessionSelectEnd(event) {
+    t.step( () => {
+      let input_sources = session.getInputSources();
+      assert_equals(event.frame.session, session);
+      assert_equals(event.inputSource, input_sources[0]);
+    });
+  }
+
+  function onSessionSelect(event) {
+    t.step( () => {
+      let input_sources = session.getInputSources();
+      assert_equals(event.frame.session, session);
+      assert_equals(event.inputSource, input_sources[0]);
+    });
+    session.dispatchEvent(watcherDone);
+  }
+  session.addEventListener("selectstart", onSessionSelectStart, false);
+  session.addEventListener("selectend", onSessionSelectEnd, false);
+  session.addEventListener("select", onSessionSelect, false);
+
+  // Session must have a baseLayer or frame requests will be ignored.
+  session.baseLayer = new XRWebGLLayer(session, gl);
+
+  let input_source = new MockXRInputSource();
+
+  fakeDeviceController.addInputSource(input_source);
+
+  // Press the primary input button and then release it a short time later.
+  session.requestAnimationFrame((time, xrFrame) => {
+    input_source.primaryInputPressed = true;
+    input_source.primaryInputPressed = false;
+
+    session.requestAnimationFrame((time, xrFrame) => {
+      // Need to process one more frame to allow select to propegate.
+    });
+  });
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_called.html b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_called.html
index 341a0318..cdebdb8b 100644
--- a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_called.html
+++ b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_called.html
@@ -3,28 +3,35 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "XRSession requestAnimationFrame properly calls the provided "
+  + "callback";
 
-xr_session_promise_test( (session) => new Promise((resolve) => {
-    // Session must have a baseLayer or frame requests will be ignored.
-    session.baseLayer = new XRWebGLLayer(session, gl);
+let fakeDeviceInitParams = { supportsImmersive:true };
 
-    function onFrame(time, xrFrame) {
-      assert_true(xrFrame instanceof XRFrame);
-      // Test does not complete until the returned promise resolves.
-      resolve();
-    }
+let requestSessionOptions = [
+  { immersive: true },
+  { outputContext: getOutputContext() }
+];
 
-    session.requestAnimationFrame(onFrame);
-  }), fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"XRSession requestAnimationFrame properly calls the provided callback");
+let testFunction = (session) => new Promise((resolve) => {
+  // Session must have a baseLayer or frame requests will be ignored.
+  session.baseLayer = new XRWebGLLayer(session, gl);
+
+  function onFrame(time, xrFrame) {
+    assert_true(xrFrame instanceof XRFrame);
+    // Test does not complete until the returned promise resolves.
+    resolve();
+  }
+
+  session.requestAnimationFrame(onFrame);
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_invalidhandle.html b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_invalidhandle.html
index 720e87f..7e9b66eb 100644
--- a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_invalidhandle.html
+++ b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_invalidhandle.html
@@ -4,14 +4,22 @@
 
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "XRSession cancelAnimationFrame does not have unexpected "
+  + "behavior when given invalid handles";
 
-xr_session_promise_test( (session) => new Promise((resolve) => {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { immersive: true },
+  { outputContext: getOutputContext() }
+];
+
+let testFunction = (session) => new Promise((resolve) => {
   // Session must have a baseLayer or frame requests will be ignored.
   session.baseLayer = new XRWebGLLayer(session, gl);
 
@@ -36,10 +44,9 @@
   session.cancelAnimationFrame(handle - 1);
   session.cancelAnimationFrame(0.5);
   session.cancelAnimationFrame(null);
-}), fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles");
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_nolayer.html b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_nolayer.html
index 752985e..4affa62 100644
--- a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_nolayer.html
+++ b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_nolayer.html
@@ -3,14 +3,22 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "XRSession requestAnimationFrame must fail if the session has "
+  + "no baseLayer";
 
-xr_session_promise_test( (session) => new Promise((resolve, reject) => {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { immersive: true },
+  { outputContext: getOutputContext() }
+];
+
+let testFunction = (session) => new Promise((resolve, reject) => {
   // Session must have a baseLayer or frame requests will be ignored.
   let webglLayer = new XRWebGLLayer(session, gl);
 
@@ -32,11 +40,9 @@
 
   let goodHandle = session.requestAnimationFrame(onGoodFrame);
   assert_not_equals(goodHandle, 0);
+});
 
-}), fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"XRSession requestAnimationFrame must fail if the session has no baseLayer");
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_unregister.html b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_unregister.html
index f46afb1..5cdbfce 100644
--- a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_unregister.html
+++ b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_unregister.html
@@ -3,14 +3,22 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "XRSession requestAnimationFrame callbacks can be unregistered "
+  + "with cancelAnimationFrame";
 
-xr_session_promise_test( (session) => new Promise((resolve, reject) => {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { immersive: true },
+  { outputContext: getOutputContext() }
+];
+
+let testFunction = (session) => new Promise((resolve, reject) => {
   // Session must have a baseLayer or frame requests will be ignored.
   session.baseLayer = new XRWebGLLayer(session, gl);
 
@@ -44,10 +52,9 @@
   session.cancelAnimationFrame(handle);
   session.requestAnimationFrame(onFrameGood);
   handle2 = session.requestAnimationFrame(onFrameBad);
-}), fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame");
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html b/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
index e394a76..43885bd 100644
--- a/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
+++ b/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
@@ -3,23 +3,28 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session, t) =>  {
+let testName = "XRFrame getDevicePose updates on the next frame";
+
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions =  [
+  { immersive: true },
+  { outputContext: getOutputContext() }
+];
+
+let testFunction = function(session, t, fakeDeviceController) {
   // Session must have a baseLayer or else frame requests will be ignored.
   session.baseLayer = new XRWebGLLayer(session, gl);
 
-  setPose(null);
-
   return session.requestFrameOfReference("eye-level")
     .then((frameOfRef) => new Promise((resolve, reject) => {
-      let expected_pose = VALID_POSE;
       let counter = 0;
       function onFrame(time, vrFrame) {
         session.requestAnimationFrame(onFrame);
@@ -28,7 +33,17 @@
             // Expecting to not get a pose since none has been supplied
             assert_equals(vrFrame.getDevicePose(frameOfRef), null);
 
-            setPose(expected_pose);
+            // Need to have a valid pose or input event's don't process.
+            fakeDeviceController.setXRPresentationFrameData(
+              VALID_POSE_MATRIX, [{
+                eye:"left",
+                projectionMatrix: VALID_PROJECTION_MATRIX,
+                viewMatrix: VALID_VIEW_MATRIX
+              }, {
+                eye:"right",
+                projectionMatrix: VALID_PROJECTION_MATRIX,
+                viewMatrix: VALID_VIEW_MATRIX
+              }]);
 
             // Check that pose does not update pose within the same frame.
             assert_equals(vrFrame.getDevicePose(frameOfRef), null);
@@ -42,8 +57,7 @@
             let poseMatrix = pose.poseModelMatrix;
             assert_not_equals(poseMatrix, null);
 
-            let expectedMatrix = matrixFrom11Pose(expected_pose);
-            assert_matrices_approx_equal(poseMatrix, expectedMatrix);
+            assert_matrices_approx_equal(poseMatrix, VALID_POSE_MATRIX);
           });
 
           // Finished.
@@ -54,10 +68,9 @@
 
       session.requestAnimationFrame(onFrame);
     }));
-}, fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"XRFrame getDevicePose updates on the next frame");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/getInputPose_hand.html b/third_party/WebKit/LayoutTests/xr/getInputPose_hand.html
index e700620..8444877 100644
--- a/third_party/WebKit/LayoutTests/xr/getInputPose_hand.html
+++ b/third_party/WebKit/LayoutTests/xr/getInputPose_hand.html
@@ -3,20 +3,36 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session, t) => new Promise((resolve) => {
+let testName = "XRInputSources with a pointer origin of 'hand' properly "
+  + "communicate their poses";
+
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions =  [{ immersive: true }];
+
+let testFunction =
+  (session, t, fakeDeviceController) => new Promise((resolve) => {
     // Session must have a baseLayer or frame requests will be ignored.
     session.baseLayer = new XRWebGLLayer(session, gl);
 
-    // Need to have a valid pose or input updates don't process.
-    setPose(VALID_POSE);
+    // Need to have a valid pose or input events don't process.
+    fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
+        eye:"left",
+        projectionMatrix: VALID_PROJECTION_MATRIX,
+        viewMatrix: VALID_VIEW_MATRIX
+      }, {
+        eye:"right",
+        projectionMatrix: VALID_PROJECTION_MATRIX,
+        viewMatrix: VALID_VIEW_MATRIX
+      }]);
 
     let input_source = new MockXRInputSource();
     input_source.targetRayMode = "tracked-pointer";
@@ -24,7 +40,7 @@
 
     // Don't set a grip matrix yet
 
-    addInputSource(input_source);
+    fakeDeviceController.addInputSource(input_source);
 
     // Must have a frameOfReference to get input poses. eye-level doesn't apply
     // any transforms to the given matrix.
@@ -54,8 +70,11 @@
         t.step( () => {
           // When a grip matrix is present but no pointer offset is specified,
           // the grip and pointer matrices should be the same.
-          assert_matrices_approx_equal(input_pose.gripMatrix, VALID_GRIP, FLOAT_EPSILON, "A");
-          assert_matrices_approx_equal(input_pose.pointerMatrix, input_pose.gripMatrix, FLOAT_EPSILON, "B");
+          assert_matrices_approx_equal(input_pose.gripMatrix, VALID_GRIP,
+            FLOAT_EPSILON, "Grip matrix is not equal to input.");
+          assert_matrices_approx_equal(input_pose.pointerMatrix,
+            input_pose.gripMatrix, FLOAT_EPSILON,
+            "Grip matrix is not equal to pointer matrix.");
         });
 
         input_source.pointerOffset = VALID_POINTER_OFFSET;
@@ -72,8 +91,11 @@
           // When a grip matrix and pointer offset are specified,
           // pointer matrix should be grip matrix multiplied with the pointer
           // offset.
-          assert_matrices_approx_equal(input_pose.gripMatrix, VALID_GRIP, FLOAT_EPSILON, "C");
-          assert_matrices_approx_equal(input_pose.pointerMatrix, VALID_GRIP_WITH_POINTER_OFFSET, FLOAT_EPSILON, "D");
+          assert_matrices_approx_equal(input_pose.gripMatrix, VALID_GRIP,
+            FLOAT_EPSILON, "Grip matrix is not equal to input valid grip.");
+          assert_matrices_approx_equal(input_pose.pointerMatrix,
+            VALID_GRIP_WITH_POINTER_OFFSET, FLOAT_EPSILON,
+            "Grip matrix not multipled properly.");
         });
 
         resolve();
@@ -82,7 +104,9 @@
       // Can only request input poses in an xr frame.
       session.requestAnimationFrame(CheckInvalidGrip);
     });
-  }), fakeDevices["FakeGooglePixelPhone"], [ { immersive: true } ],
-"XRInputSources with a pointer origin of 'hand' properly communicate their poses");
+  });
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/requestDevice_one_device.html b/third_party/WebKit/LayoutTests/xr/requestDevice_one_device.html
deleted file mode 100644
index f25429e7..0000000
--- a/third_party/WebKit/LayoutTests/xr/requestDevice_one_device.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<script>
-let fakeDevices = fakeXRDevices();
-
-promise_test((t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) => {
-    t.step(() => {
-      assert_true(device != null);
-      assert_true(device instanceof XRDevice);
-    });
-  });
-}, "navigator.xr.requestDevice properly returns a single device");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/requestDevice_two_devices.html b/third_party/WebKit/LayoutTests/xr/requestDevice_two_devices.html
deleted file mode 100644
index 279d554d..0000000
--- a/third_party/WebKit/LayoutTests/xr/requestDevice_two_devices.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<script>
-let fakeDevices = fakeXRDevices();
-promise_test( (t) => {
-  // TODO(offenwanger) When we know how to we want to select which device to
-  // return, update this test.
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"], fakeDevices["FakeMagicWindowOnly"]]);
-  return navigator.xr.requestDevice().then( (device) => {
-    t.step(() => {
-      assert_true(device != null);
-      assert_true(device instanceof XRDevice);
-    });
-
-    // Repeated calls to requestDevice should yeild the same device each time.
-    navigator.xr.requestDevice().then( (device2) => {
-      t.step(() => {
-        assert_true(device2 != null);
-        assert_true(device2 instanceof XRDevice);
-        assert_equals(device, device2);
-      });
-    });
-  });
-}, "navigator.xr.requestDevice properly returns one device when there are two");
-</script>
-
diff --git a/third_party/WebKit/LayoutTests/xr/requestDevice_with_device.html b/third_party/WebKit/LayoutTests/xr/requestDevice_with_device.html
new file mode 100644
index 0000000..6cf7d5f7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/xr/requestDevice_with_device.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-test-utils.js"></script>
+<script src="../xr/resources/test-constants.js"></script>
+<script>
+
+promise_test((t) => {
+  return XRTest.simulateDeviceConnection({ supportsImmersive: true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => {
+      t.step(() => {
+        assert_true(device != null);
+        assert_true(device instanceof XRDevice);
+      });
+    });
+}, "navigator.xr.requestDevice properly returns a single device");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/xr/requestDevice_zero_devices.html b/third_party/WebKit/LayoutTests/xr/requestDevice_without_device.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/xr/requestDevice_zero_devices.html
rename to third_party/WebKit/LayoutTests/xr/requestDevice_without_device.html
index a9c2622..b273a87 100644
--- a/third_party/WebKit/LayoutTests/xr/requestDevice_zero_devices.html
+++ b/third_party/WebKit/LayoutTests/xr/requestDevice_without_device.html
@@ -3,11 +3,10 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <script>
-let fakeDevices = fakeXRDevices();
 
 promise_test( (t) => {
   return promise_rejects(t, 'NotFoundError', navigator.xr.requestDevice());
diff --git a/third_party/WebKit/LayoutTests/xr/resources/test-constants.js b/third_party/WebKit/LayoutTests/xr/resources/test-constants.js
index a936d26..7295a93 100644
--- a/third_party/WebKit/LayoutTests/xr/resources/test-constants.js
+++ b/third_party/WebKit/LayoutTests/xr/resources/test-constants.js
@@ -1,46 +1,29 @@
 // assert_equals can fail when comparing floats due to precision errors, so
 // use assert_approx_equals with this constant instead
-var FLOAT_EPSILON = 0.000001;
+const FLOAT_EPSILON = 0.001;
 
-// A valid VRPose for when we don't care about specific values
-var VALID_POSE = {
-  position: [1.1, 2.2, 3.3],
-  linearVelocity: [0.1, 0.2, 0.3],
-  linearAcceleration: [0.0, 0.1, 0.2],
-  orientation: [0.1, 0.2, 0.3, 0.4],
-  angularVelocity: [1.1, 2.1, 3.1],
-  angularAcceleration: [1.0, 2.0, 3.0]
-}
+// A valid pose matrix for  when we don't care about specific values
+const IDENTITY_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
+
+// A valid pose matrix for  when we don't care about specific values
+const VALID_POSE_MATRIX = [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1];
+
+const VALID_PROJECTION_MATRIX =
+    [1, 0, 0, 0, 0, 1, 0, 0, 3, 2, -1, -1, 0, 0, -0.2, 0];
+
+const VALID_VIEW_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 3, 2, 1];
 
 // A valid VRPose for when we want the HMD to report being at the origin
-var ORIGIN_POSE = {
-  position: [0.0, 0.0, 0.0],
-  linearVelocity: [0.0, 0.0, 0.0],
-  linearAcceleration: [0.0, -9.8, 0.0],
-  orientation: [0.0, 0.0, 0.0, 1.0],
-  angularVelocity: [0.0, 0.0, 0.0],
-  angularAcceleration: [0.0, 0.0, 0.0]
-}
-
+const ORIGIN_POSE = IDENTITY_MATRIX;
 
 // A valid input grip matrix for  when we don't care about specific values
-var VALID_GRIP = [1, 0, 0, 0,
-                  0, 1, 0, 0,
-                  0, 0, 1, 0,
-                  4, 3, 2, 1];
+const VALID_GRIP = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 3, 2, 1];
 
 // A valid input pointer offset for  when we don't care about specific values
-var VALID_POINTER_OFFSET = [1, 0, 0, 0,
-                            0, 1, 0, 0,
-                            0, 0, 1, 0,
-                            0, 0, 1, 1];
+const VALID_POINTER_OFFSET = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1];
 
-var VALID_GRIP_WITH_POINTER_OFFSET = [1, 0, 0, 0,
-                                      0, 1, 0, 0,
-                                      0, 0, 1, 0,
-                                      4, 3, 3, 1];
+const VALID_GRIP_WITH_POINTER_OFFSET =
+    [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 3, 3, 1];
 
-var VALID_STAGE_TRANSFORM = [1, 0, 0, 0,
-                             0, 1, 0, 0,
-                             0, 0, 1, 0,
-                             1.0, 1.65, -1.0, 1];
+const VALID_STAGE_TRANSFORM =
+    [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.0, 1.65, -1.0, 1];
diff --git a/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js b/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
deleted file mode 100644
index 22058646..0000000
--- a/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
+++ /dev/null
@@ -1,722 +0,0 @@
-'use strict';
-
-/* This class contains everything required to trick the system into thinking it
- * has a fully functional XRDevices. When moving test to WPT, this file will
- * need to be modified to be consistent with what is decided for the cross-
- * platform APIs. */
-
-let mockVRService;
-function addFakeDevice(device) {
-  // TODO(offenwanger): Switch out this code with test API code when it's
-  // available.
-  if (!mockVRService) {
-    mockVRService = new MockVRService(mojo.frameInterfaces);
-  }
-
-  mockVRService.addVRDisplay(device);
-}
-
-function setFakeDevices(devices) {
-  // TODO(offenwanger): Switch out this code with test API code when it's
-  // available.
-  if (!mockVRService) {
-    mockVRService = new MockVRService(mojo.frameInterfaces);
-  }
-
-  mockVRService.setVRDisplays(devices);
-}
-
-// Sets the pose for the first device
-function setPose(pose) {
-  mockVRService.mockVRDisplays_[0].setPose(pose);
-}
-
-function setHitTestResults(results)  {
-  mockVRService.mockVRDisplays_[0].setHitTestResults(results);
-}
-
-function setStageTransform(transform) {
-  mockVRService.mockVRDisplays_[0].setStageTransform(transform);
-}
-
-// Returns the submitted frame count for the first display
-function getSubmitFrameCount() {
-  return mockVRService.mockVRDisplays_[0].getSubmitFrameCount();
-}
-
-// Returns the missing (not submitted) frame count for the first display
-function getMissingFrameCount() {
-  return mockVRService.mockVRDisplays_[0].getMissingFrameCount();
-}
-
-function addInputSource(input_source) {
-  return mockVRService.mockVRDisplays_[0].addInputSource(input_source);
-}
-
-function removeInputSource(input_source) {
-  return mockVRService.mockVRDisplays_[0].removeInputSource(input_source);
-}
-
-function fakeXRDevices() {
-  let generic_left_fov = {
-    upDegrees: 45,
-    downDegrees: 45,
-    leftDegrees: 50,
-    rightDegrees: 40,
-  };
-
-  let generic_right_fov = {
-    upDegrees: 45,
-    downDegrees: 45,
-    leftDegrees: 40,
-    rightDegrees: 50,
-  };
-
-  let generic_left_eye = {
-    fieldOfView: generic_left_fov,
-    offset: [-0.03, 0, 0],
-    renderWidth: 1024,
-    renderHeight: 1024
-  };
-
-  let generic_right_eye = {
-    fieldOfView: generic_right_fov,
-    offset: [0.03, 0, 0],
-    renderWidth: 1024,
-    renderHeight: 1024
-  };
-
-  return {
-    FakeMagicWindowOnly: {
-      displayName: 'FakeVRDisplay',
-      capabilities:
-          {hasPosition: false, hasExternalDisplay: false, canPresent: false},
-      stageParameters: null,
-      leftEye: null,
-      rightEye: null,
-      webxrDefaultFramebufferScale: 1.0,
-    },
-
-    FakeRoomScale: {
-      displayName: 'FakeVRDisplayRoom',
-      capabilities: {
-        hasPosition: true,
-        hasExternalDisplay: true,
-        canPresent: true,
-        maxLayers: 1
-      },
-      stageParameters: {
-        standingTransform: [
-          0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 0.1, 0.2, 0.3,
-          0.4, 0.5
-        ],
-        sizeX: 5.0,
-        sizeZ: 3.0,
-      },
-      leftEye: generic_left_eye,
-      rightEye: generic_right_eye,
-      webxrDefaultFramebufferScale: 1.0,
-    },
-
-    FakeGooglePixelPhone: {
-      // Pixel info as of Dec. 22 2016
-      displayName: 'Google, Inc. Daydream View',
-      capabilities: {
-        hasPosition: false,
-        hasExternalDisplay: false,
-        canPresent: true,
-        maxLayers: 1
-      },
-      stageParameters: null,
-      leftEye: {
-        fieldOfView: {
-          upDegrees: 48.316,
-          downDegrees: 50.099,
-          leftDegrees: 35.197,
-          rightDegrees: 50.899,
-        },
-        offset: [-0.032, 0, 0],
-        renderWidth: 1920,
-        renderHeight: 2160
-      },
-      rightEye: {
-        fieldOfView: {
-          upDegrees: 48.316,
-          downDegrees: 50.099,
-          leftDegrees: 50.899,
-          rightDegrees: 35.197
-        },
-        offset: [0.032, 0, 0],
-        renderWidth: 1920,
-        renderHeight: 2160
-      },
-      webxrDefaultFramebufferScale: 0.7,
-    },
-
-    FakeARPhone: {
-      displayName: 'AR device',
-      capabilities: {
-        hasPosition: true,
-        hasExternalDisplay: false,
-        canPresent: false,
-        maxLayers: 1,
-        canProvidePassThroughImages: true,
-      },
-      stageParameters: null,
-      leftEye: {
-        fieldOfView: {
-          upDegrees: 48.316,
-          downDegrees: 50.099,
-          leftDegrees: 35.197,
-          rightDegrees: 50.899,
-        },
-        offset: [-0.032, 0, 0],
-        renderWidth: 1920,
-        renderHeight: 2160,
-      },
-      rightEye: null,
-      webxrDefaultFramebufferScale: 0.7,
-    }
-  };
-}
-
-// TODO(offenwanger): Update this to remove references to 1.1
-// Gets the corresponding transform matrix for a WebVR 1.1 pose
-function matrixFrom11Pose(pose) {
-  let x = pose.orientation[0];
-  let y = pose.orientation[1];
-  let z = pose.orientation[2];
-  let w = pose.orientation[3];
-  let x2 = x + x;
-  let y2 = y + y;
-  let z2 = z + z;
-  let xx = x * x2;
-  let xy = x * y2;
-  let xz = x * z2;
-  let yy = y * y2;
-  let yz = y * z2;
-  let zz = z * z2;
-  let wx = w * x2;
-  let wy = w * y2;
-  let wz = w * z2;
-
-  let out = new Float32Array(16);
-  out[0] = 1 - (yy + zz);
-  out[1] = xy + wz;
-  out[2] = xz - wy;
-  out[3] = 0;
-  out[4] = xy - wz;
-  out[5] = 1 - (xx + zz);
-  out[6] = yz + wx;
-  out[7] = 0;
-  out[8] = xz + wy;
-  out[9] = yz - wx;
-  out[10] = 1 - (xx + yy);
-  out[11] = 0;
-  out[12] = pose.position[0];
-  out[13] = pose.position[1];
-  out[14] = pose.position[2];
-  out[15] = 1;
-
-  return out;
-}
-
-function perspectiveFromFieldOfView(fov, near, far) {
-  let upTan = Math.tan(fov.upDegrees * Math.PI / 180.0);
-  let downTan = Math.tan(fov.downDegrees * Math.PI / 180.0);
-  let leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0);
-  let rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0);
-  let xScale = 2.0 / (leftTan + rightTan);
-  let yScale = 2.0 / (upTan + downTan);
-  let nf = 1.0 / (near - far);
-
-  let out = new Float32Array(16);
-  out[0] = xScale;
-  out[1] = 0.0;
-  out[2] = 0.0;
-  out[3] = 0.0;
-  out[4] = 0.0;
-  out[5] = yScale;
-  out[6] = 0.0;
-  out[7] = 0.0;
-  out[8] = -((leftTan - rightTan) * xScale * 0.5);
-  out[9] = ((upTan - downTan) * yScale * 0.5);
-  out[10] = (near + far) * nf;
-  out[11] = -1.0;
-  out[12] = 0.0;
-  out[13] = 0.0;
-  out[14] = (2.0 * far * near) * nf;
-  out[15] = 0.0;
-
-  return out;
-}
-
-function assert_matrices_approx_equal(matA, matB, epsilon = FLOAT_EPSILON, message = "") {
-  if (matA == null && matB == null) {
-    return;
-  }
-
-  assert_not_equals(matA, null);
-  assert_not_equals(matB, null);
-
-  assert_equals(matA.length, 16);
-  assert_equals(matB.length, 16);
-
-  let mismatched_element = -1;
-  for (let i = 0; i < 16; ++i) {
-    if (Math.abs(matA[i] - matB[i]) > epsilon) {
-      mismatched_element = i;
-      break;
-    }
-  }
-
-  if (mismatched_element > -1) {
-    let matA_str = "[";
-    let matB_str = "[";
-    for (let i = 0; i < 16; ++i) {
-      matA_str += matA[i] + (i < 15 ? ", " : "");
-      matB_str += matB[i] + (i < 15 ? ", " : "");
-    }
-    matA_str += "]";
-    matB_str += "]";
-
-    let error_message = message ? message + "\n" : "Matrix comparison failed.\n";
-    error_message += " Difference in element " + mismatched_element + " exceeded the given epsilon.\n";
-
-    error_message += " Matrix A: " + matA_str + "\n";
-    error_message += " Matrix B: " + matB_str + "\n";
-
-    assert_approx_equals(matA[mismatched_element], matB[mismatched_element], epsilon, error_message);
-  }
-}
-
-// TODO(offenwanger): Delete everything below when test API code is available.
-class MockDevice {
-  constructor(displayInfo, service) {
-    this.displayClient_ = new device.mojom.VRDisplayClientPtr();
-    this.displayInfo_ = displayInfo;
-    this.service_ = service;
-    this.presentation_provider_ = new MockXRPresentationProvider();
-
-    this.pose_ = null;
-    this.next_frame_id_ = 0;
-
-    this.input_sources_ = [];
-    this.next_input_source_index_ = 1;
-
-    if (service.client_) {
-      this.notifyClientOfDisplay();
-    }
-  }
-
-  requestSession(sessionOptions, was_activation) {
-    return this.supportsSession(sessionOptions).then((result) => {
-      // The JavaScript bindings convert c_style_names to camelCase names.
-      var options = new device.mojom.XRPresentationTransportOptions();
-      options.transportMethod =
-          device.mojom.XRPresentationTransportMethod.SUBMIT_AS_MAILBOX_HOLDER;
-      options.waitForTransferNotification = true;
-      options.waitForRenderNotification = true;
-
-      let submit_frame_sink;
-      if (result.supportsSession) {
-        submit_frame_sink = {
-          clientRequest: this.presentation_provider_.getClientRequest(),
-          provider: this.presentation_provider_.bindProvider(sessionOptions),
-          transportOptions: options
-        };
-
-
-        let dataProviderPtr = new device.mojom.XRFrameDataProviderPtr();
-        let dataProviderRequest = mojo.makeRequest(dataProviderPtr);
-        let dataProviderBinding = new mojo.Binding(
-            device.mojom.XRFrameDataProvider, this, dataProviderRequest);
-
-        let enviromentProviderPtr =
-            new device.mojom.XREnviromentIntegrationProviderPtr();
-        let enviromentProviderRequest = mojo.makeRequest(enviromentProviderPtr);
-        let enviromentProviderBinding = new mojo.Binding(
-            device.mojom.XREnviromentIntegrationProvider, this,
-            enviromentProviderRequest);
-
-        return Promise.resolve({
-          session: {
-            submitFrameSink: submit_frame_sink,
-            dataProvider: dataProviderPtr,
-            enviromentProvider: enviromentProviderPtr
-          }
-        });
-      } else {
-        return Promise.resolve({session: null});
-      }
-    });
-  }
-
-  supportsSession(options) {
-    return Promise.resolve({
-      supportsSession:
-          !options.immersive || this.displayInfo_.capabilities.canPresent
-    });
-  };
-
-  setPose(pose) {
-    if (pose == null) {
-      this.pose_ = null;
-    } else {
-      this.initPose();
-      this.fillPose(pose);
-    }
-  }
-
-  setStageTransform(value) {
-    if (value) {
-      if (!this.displayInfo_.stageParameters) {
-        this.displayInfo_.stageParameters = {
-          standingTransform: value,
-          sizeX: 1.5,
-          sizeZ: 1.5,
-        };
-      } else {
-        this.displayInfo_.stageParameters.standingTransform = value;
-      }
-    } else if (this.displayInfo_.stageParameters) {
-      this.displayInfo_.stageParameters = null;
-    }
-
-    this.displayClient_.onChanged(this.displayInfo_);
-  }
-
-  getFrameData() {
-    if (this.pose_) {
-      this.pose_.poseIndex++;
-
-      let input_states = [];
-      for (let i = 0; i < this.input_sources_.length; ++i) {
-        input_states.push(this.input_sources_[i].getInputSourceState());
-      }
-
-      this.pose_.inputState = input_states;
-    }
-
-    // Convert current document time to monotonic time.
-    var now = window.performance.now() / 1000.0;
-    var diff = now - internals.monotonicTimeToZeroBasedDocumentTime(now);
-    now += diff;
-    now *= 1000000;
-
-    return Promise.resolve({
-      frameData: {
-        pose: this.pose_,
-        bufferHolder: null,
-        bufferSize: {},
-        timeDelta: {
-          microseconds: now,
-        },
-        frameId: this.next_frame_id_++,
-        projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
-      }
-    });
-  }
-
-  setHitTestResults(results) {
-    this.hittest_results_ = results;
-  }
-
-  requestHitTest(ray) {
-    var hit_results = this.hittest_results_;
-    if (!hit_results) {
-      var hit = new device.mojom.XRHitResult();
-      hit.hitMatrix = [1, 0, 0, 0,
-                       0, 1, 0, 0,
-                       0, 0, 1, 0,
-                       0, 0, 0, 1];
-      hit_results = { results: [hit] };
-    }
-    return Promise.resolve(hit_results);
-  }
-
-  getSubmitFrameCount() {
-    return this.presentation_provider_.submit_frame_count_;
-  }
-
-  getMissingFrameCount() {
-    return this.presentation_provider_.missing_frame_count_;
-  }
-
-  forceActivate(reason) {
-    this.displayClient_.onActivate(reason);
-  }
-
-  // This function calls to the backend to add this device to the list.
-  notifyClientOfDisplay() {
-    let displayPtr = new device.mojom.VRDisplayHostPtr();
-    let displayRequest = mojo.makeRequest(displayPtr);
-    let displayBinding =
-        new mojo.Binding(device.mojom.VRDisplayHost, this, displayRequest);
-
-    let clientRequest = mojo.makeRequest(this.displayClient_);
-    this.service_.client_.onDisplayConnected(
-        displayPtr, clientRequest, this.displayInfo_);
-  }
-
-  initPose() {
-    this.pose_ = {
-      orientation: null,
-      position: null,
-      angularVelocity: null,
-      linearVelocity: null,
-      angularAcceleration: null,
-      linearAcceleration: null,
-      inputState: null,
-      poseReset: false,
-      poseIndex: 0
-    };
-  }
-
-  fillPose(pose) {
-    for (var field in pose) {
-      if (this.pose_.hasOwnProperty(field)) {
-        this.pose_[field] = pose[field];
-      }
-    }
-  }
-
-  addInputSource(input_source) {
-    let index = this.next_input_source_index_;
-    input_source.source_id_ = index;
-    this.next_input_source_index_++;
-    this.input_sources_.push(input_source);
-  }
-
-  removeInputSource(input_source) {
-    for (let i = 0; i < this.input_sources_.length; ++i) {
-      if (input_source.source_id_ == this.input_sources_[i].source_id_) {
-        this.input_sources_.splice(i, 1);
-        break;
-      }
-    }
-  }
-}
-
-class MockXRPresentationProvider {
-  constructor() {
-    this.binding_ =
-        new mojo.Binding(device.mojom.XRPresentationProviderPtr, this);
-    this.submit_frame_count_ = 0;
-    this.missing_frame_count_ = 0;
-  }
-
-  bindProvider(request) {
-    let providerPtr = new device.mojom.XRPresentationProviderPtr();
-    let providerRequest = mojo.makeRequest(providerPtr);
-
-    this.binding_.close();
-
-    this.binding_ = new mojo.Binding(
-        device.mojom.XRPresentationProvider, this, providerRequest);
-
-    return providerPtr;
-  }
-
-  getClientRequest() {
-    this.submitFrameClient_ = new device.mojom.XRPresentationClientPtr();
-    return mojo.makeRequest(this.submitFrameClient_);
-  }
-
-
-  submitFrameMissing(frameId, mailboxHolder, timeWaited) {
-    this.missing_frame_count_++;
-  }
-
-  submitFrame(frameId, mailboxHolder, timeWaited) {
-    this.submit_frame_count_++;
-
-    // Trigger the submit completion callbacks here. WARNING: The
-    // Javascript-based mojo mocks are *not* re-entrant. It's OK to
-    // wait for these notifications on the next frame, but waiting
-    // within the current frame would never finish since the incoming
-    // calls would be queued until the current execution context finishes.
-    this.submitFrameClient_.onSubmitFrameTransferred(true);
-    this.submitFrameClient_.onSubmitFrameRendered();
-  }
-}
-
-class MockVRService {
-  constructor() {
-    this.bindingSet_ = new mojo.BindingSet(device.mojom.VRService);
-    this.mockVRDisplays_ = [];
-
-    this.interceptor_ =
-        new MojoInterfaceInterceptor(device.mojom.VRService.name);
-    this.interceptor_.oninterfacerequest = e =>
-        this.bindingSet_.addBinding(this, e.handle);
-    this.interceptor_.start();
-  }
-
-  setVRDisplays(displays) {
-    this.mockVRDisplays_ = [];
-    for (let i = 0; i < displays.length; i++) {
-      displays[i].index = i;
-      this.mockVRDisplays_.push(new MockDevice(displays[i], this));
-    }
-  }
-
-  addVRDisplay(display) {
-    if (this.mockVRDisplays_.length) {
-      display.index = this.mockVRDisplays_[this.mockVRDisplays_.length - 1] + 1;
-    } else {
-      display.index = 0;
-    }
-    this.mockVRDisplays_.push(new MockDevice(display, this));
-  }
-
-  // This function gets called from mojo, and is what triggers all displays to
-  // be added.
-  setClient(client) {
-    this.client_ = client;
-    for (let i = 0; i < this.mockVRDisplays_.length; i++) {
-      this.mockVRDisplays_[i].notifyClientOfDisplay();
-    }
-
-    let device_number = this.mockVRDisplays_.length;
-    return Promise.resolve({numberOfConnectedDevices: device_number});
-  }
-}
-
-class MockXRInputSource {
-  constructor() {
-    this.source_id_ = 0;
-    this.primary_input_pressed_ = false;
-    this.primary_input_clicked_ = false;
-    this.grip_ = null;
-
-    this.target_ray_mode_ = "gaze";
-    this.pointer_offset_ = null;
-    this.emulated_position_ = false;
-    this.handedness_ = "";
-    this.desc_dirty_ = true;
-  }
-
-  get primaryInputPressed() {
-    return this.primary_input_pressed_;
-  }
-
-  set primaryInputPressed(value) {
-    if (this.primary_input_pressed_ && !value) {
-      this.primary_input_clicked_ = true;
-    }
-    this.primary_input_pressed_ = value;
-  }
-
-  get grip() {
-    if (this.grip_) {
-      return this.grip_.matrix;
-    }
-    return null;
-  }
-
-  set grip(value) {
-    if (!value) {
-      this.grip_ = null;
-      return;
-    }
-    this.grip_ = new gfx.mojom.Transform();
-    this.grip_.matrix = new Float32Array(value);
-  }
-
-  get targetRayMode() {
-    return this.target_ray_mode_;
-  }
-
-  set targetRayMode(value) {
-    if (this.target_ray_mode_ != value) {
-      this.desc_dirty_ = true;
-      this.target_ray_mode_ = value;
-    }
-  }
-
-  get pointerOffset() {
-    if (this.pointer_offset_) {
-      return this.pointer_offset_.matrix;
-    }
-    return null;
-  }
-
-  set pointerOffset(value) {
-    this.desc_dirty_ = true;
-    if (!value) {
-      this.pointer_offset_ = null;
-      return;
-    }
-    this.pointer_offset_ = new gfx.mojom.Transform();
-    this.pointer_offset_.matrix = new Float32Array(value);
-  }
-
-  get emulatedPosition() {
-    return this.emulated_position_;
-  }
-
-  set emulatedPosition(value) {
-    if (this.emulated_position_ != value) {
-      this.desc_dirty_ = true;
-      this.emulated_position_ = value;
-    }
-  }
-
-  get handedness() {
-    return this.handedness_;
-  }
-
-  set handedness(value) {
-    if (this.handedness_ != value) {
-      this.desc_dirty_ = true;
-      this.handedness_ = value;
-    }
-  }
-
-  getInputSourceState() {
-    let input_state = new device.mojom.XRInputSourceState();
-
-    input_state.sourceId = this.source_id_;
-
-    input_state.primaryInputPressed = this.primary_input_pressed_;
-    input_state.primaryInputClicked = this.primary_input_clicked_;
-
-    input_state.grip = this.grip_;
-
-    if (this.desc_dirty_) {
-      let input_desc = new device.mojom.XRInputSourceDescription();
-
-      input_desc.emulatedPosition = this.emulated_position_;
-
-      switch (this.target_ray_mode_) {
-        case "gaze":
-          input_desc.targetRayMode = device.mojom.XRTargetRayMode.GAZING;
-          break;
-        case "tracked-pointer":
-          input_desc.targetRayMode = device.mojom.XRTargetRayMode.POINTING;
-          break;
-      }
-
-      switch (this.handedness_) {
-        case "left":
-          input_desc.handedness = device.mojom.XRHandedness.LEFT;
-          break;
-        case "right":
-          input_desc.handedness = device.mojom.XRHandedness.RIGHT;
-          break;
-        default:
-          input_desc.handedness = device.mojom.XRHandedness.NONE;
-          break;
-      }
-
-      input_desc.pointerOffset = this.pointer_offset_;
-
-      input_state.description = input_desc;
-
-      this.desc_dirty_ = false;
-    }
-
-    return input_state;
-  }
-}
diff --git a/third_party/WebKit/LayoutTests/xr/resources/xr-internal-device-mocking.js b/third_party/WebKit/LayoutTests/xr/resources/xr-internal-device-mocking.js
new file mode 100644
index 0000000..ba4b678
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/xr/resources/xr-internal-device-mocking.js
@@ -0,0 +1,229 @@
+'use strict';
+
+/* This file contains extensions to the base mocking from the WebPlatform tests
+ * for interal tests. The main mocked objects are found in
+ * ../external/wpt/resources/chromium/webxr-test.js. */
+
+MockDevice.prototype.base_getFrameData = MockDevice.prototype.getFrameData;
+
+MockDevice.prototype.getFrameData = function() {
+  return this.base_getFrameData().then((result) => {
+    if (result.frameData && result.frameData.pose && this.input_sources_) {
+      let input_states = [];
+      for (let i = 0; i < this.input_sources_.length; ++i) {
+        input_states.push(this.input_sources_[i].getInputSourceState());
+      }
+
+      result.frameData.pose.inputState = input_states;
+    }
+
+    return result;
+  });
+};
+
+MockDevice.prototype.addInputSource = function(source) {
+  if (!this.input_sources_) {
+    this.input_sources_ = [];
+    this.next_input_source_index_ = 1;
+  }
+  let index = this.next_input_source_index_;
+  source.source_id_ = index;
+  this.next_input_source_index_++;
+  this.input_sources_.push(source);
+};
+
+MockDevice.prototype.removeInputSource = function(source) {
+  if (!this.input_sources_)
+    return;
+
+  for (let i = 0; i < this.input_sources_.length; ++i) {
+    if (source.source_id_ == this.input_sources_[i].source_id_) {
+      this.input_sources_.splice(i, 1);
+      break;
+    }
+  }
+};
+
+MockDevice.prototype.setHitTestResults = function(results) {
+  this.hittest_results_ = results;
+};
+
+MockDevice.prototype.requestHitTest = function(ray) {
+  var hit_results = this.hittest_results_;
+  if (!hit_results) {
+    var hit = new device.mojom.XRHitResult();
+    hit.hitMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
+    hit_results = {results: [hit]};
+  }
+  return Promise.resolve(hit_results);
+};
+
+MockDevice.prototype.setResetPose = function(to) {
+  if (this.pose_) {
+    this.pose_.poseReset = to;
+  }
+};
+
+MockDevice.prototype.setStageTransform = function(value) {
+  if (value) {
+    if (!this.displayInfo_.stageParameters) {
+      this.displayInfo_.stageParameters = {
+        standingTransform: value,
+        sizeX: 1.5,
+        sizeZ: 1.5,
+      };
+    } else {
+      this.displayInfo_.stageParameters.standingTransform = value;
+    }
+  } else if (this.displayInfo_.stageParameters) {
+    this.displayInfo_.stageParameters = null;
+  }
+
+  this.displayClient_.onChanged(this.displayInfo_);
+};
+
+MockDevice.prototype.getSubmitFrameCount = function() {
+  return this.presentation_provider_.submit_frame_count_;
+};
+
+MockDevice.prototype.getMissingFrameCount = function() {
+  return this.presentation_provider_.missing_frame_count_;
+};
+
+class MockXRInputSource {
+  constructor() {
+    this.source_id_ = 0;
+    this.primary_input_pressed_ = false;
+    this.primary_input_clicked_ = false;
+    this.grip_ = null;
+
+    this.target_ray_mode_ = 'gaze';
+    this.pointer_offset_ = null;
+    this.emulated_position_ = false;
+    this.handedness_ = '';
+    this.desc_dirty_ = true;
+  }
+
+  get primaryInputPressed() {
+    return this.primary_input_pressed_;
+  }
+
+  set primaryInputPressed(value) {
+    if (this.primary_input_pressed_ && !value) {
+      this.primary_input_clicked_ = true;
+    }
+    this.primary_input_pressed_ = value;
+  }
+
+  get grip() {
+    if (this.grip_) {
+      return this.grip_.matrix;
+    }
+    return null;
+  }
+
+  set grip(value) {
+    if (!value) {
+      this.grip_ = null;
+      return;
+    }
+    this.grip_ = new gfx.mojom.Transform();
+    this.grip_.matrix = new Float32Array(value);
+  }
+
+  get targetRayMode() {
+    return this.target_ray_mode_;
+  }
+
+  set targetRayMode(value) {
+    if (this.target_ray_mode_ != value) {
+      this.desc_dirty_ = true;
+      this.target_ray_mode_ = value;
+    }
+  }
+
+  get pointerOffset() {
+    if (this.pointer_offset_) {
+      return this.pointer_offset_.matrix;
+    }
+    return null;
+  }
+
+  set pointerOffset(value) {
+    this.desc_dirty_ = true;
+    if (!value) {
+      this.pointer_offset_ = null;
+      return;
+    }
+    this.pointer_offset_ = new gfx.mojom.Transform();
+    this.pointer_offset_.matrix = new Float32Array(value);
+  }
+
+  get emulatedPosition() {
+    return this.emulated_position_;
+  }
+
+  set emulatedPosition(value) {
+    if (this.emulated_position_ != value) {
+      this.desc_dirty_ = true;
+      this.emulated_position_ = value;
+    }
+  }
+
+  get handedness() {
+    return this.handedness_;
+  }
+
+  set handedness(value) {
+    if (this.handedness_ != value) {
+      this.desc_dirty_ = true;
+      this.handedness_ = value;
+    }
+  }
+
+  getInputSourceState() {
+    let input_state = new device.mojom.XRInputSourceState();
+
+    input_state.sourceId = this.source_id_;
+
+    input_state.primaryInputPressed = this.primary_input_pressed_;
+    input_state.primaryInputClicked = this.primary_input_clicked_;
+
+    input_state.grip = this.grip_;
+
+    if (this.desc_dirty_) {
+      let input_desc = new device.mojom.XRInputSourceDescription();
+
+      input_desc.emulatedPosition = this.emulated_position_;
+
+      switch (this.target_ray_mode_) {
+        case 'gaze':
+          input_desc.targetRayMode = device.mojom.XRTargetRayMode.GAZING;
+          break;
+        case 'tracked-pointer':
+          input_desc.targetRayMode = device.mojom.XRTargetRayMode.POINTING;
+          break;
+      }
+
+      switch (this.handedness_) {
+        case 'left':
+          input_desc.handedness = device.mojom.XRHandedness.LEFT;
+          break;
+        case 'right':
+          input_desc.handedness = device.mojom.XRHandedness.RIGHT;
+          break;
+        default:
+          input_desc.handedness = device.mojom.XRHandedness.NONE;
+          break;
+      }
+
+      input_desc.pointerOffset = this.pointer_offset_;
+
+      input_state.description = input_desc;
+
+      this.desc_dirty_ = false;
+    }
+
+    return input_state;
+  }
+}
diff --git a/third_party/WebKit/LayoutTests/xr/resources/xr-test-utils.js b/third_party/WebKit/LayoutTests/xr/resources/xr-test-utils.js
index 1d90a72a..94e338d 100644
--- a/third_party/WebKit/LayoutTests/xr/resources/xr-test-utils.js
+++ b/third_party/WebKit/LayoutTests/xr/resources/xr-test-utils.js
@@ -2,16 +2,19 @@
 // performs tests. If func returns a promise, test will only pass if the promise
 // resolves.
 function xr_session_promise_test(
-    func, device, sessionOptions, name, properties) {
+    func, deviceOptions, sessionOptions, name, properties) {
   if (document.getElementById('webgl-canvas') ||
       document.getElementById('webgl2-canvas')) {
     webglCanvasSetup();
   }
 
-  addFakeDevice(device);
-
   promise_test((t) => {
-    return navigator.xr.requestDevice()
+    let fakeDeviceController;
+    return XRTest.simulateDeviceConnection(deviceOptions)
+        .then((controller) => {
+          fakeDeviceController = controller;
+          return navigator.xr.requestDevice();
+        })
         .then((device) => {
           if (gl) {
             return gl.setCompatibleXRDevice(device).then(
@@ -42,15 +45,17 @@
                   device.requestSession(nextOptions)
                       .then((session) => {
                         testSession = session;
-                        return func(session, t);
+                        return func(session, t, fakeDeviceController);
                       })
-                      .then(
-                          () => {// End the session. Silence any errors
-                                 // generated if the session was already ended.
-                                 // TODO(offenwanger): This throw error when a
-                                 // session is already ended is not defined by
-                                 // the spec.
-                                 testSession.end().catch(() => {})})
+                      .then(() => {
+                        // End the session. Silence any errors
+                        // generated if the session was already ended.
+                        // TODO(offenwanger): This throw error when a
+                        // session is already ended is not defined by
+                        // the spec.
+                        testSession.end().catch(() => {});
+                        fakeDeviceController.setXRPresentationFrameData(null);
+                      })
                       .then(() => nextSessionTest(++i))
                       .catch((err) => {
                         let optionsString = '{';
@@ -96,6 +101,115 @@
   return outputCanvas.getContext('xrpresent');
 }
 
+function perspectiveFromFieldOfView(fov, near, far) {
+  let upTan = Math.tan(fov.upDegrees * Math.PI / 180.0);
+  let downTan = Math.tan(fov.downDegrees * Math.PI / 180.0);
+  let leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0);
+  let rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0);
+  let xScale = 2.0 / (leftTan + rightTan);
+  let yScale = 2.0 / (upTan + downTan);
+  let nf = 1.0 / (near - far);
+
+  let out = new Float32Array(16);
+  out[0] = xScale;
+  out[1] = 0.0;
+  out[2] = 0.0;
+  out[3] = 0.0;
+  out[4] = 0.0;
+  out[5] = yScale;
+  out[6] = 0.0;
+  out[7] = 0.0;
+  out[8] = -((leftTan - rightTan) * xScale * 0.5);
+  out[9] = ((upTan - downTan) * yScale * 0.5);
+  out[10] = (near + far) * nf;
+  out[11] = -1.0;
+  out[12] = 0.0;
+  out[13] = 0.0;
+  out[14] = (2.0 * far * near) * nf;
+  out[15] = 0.0;
+
+  return out;
+}
+
+
+function assert_matrices_approx_equal(
+    matA, matB, epsilon = FLOAT_EPSILON, message = '') {
+  if (matA == null && matB == null) {
+    return;
+  }
+
+  assert_not_equals(matA, null);
+  assert_not_equals(matB, null);
+
+  assert_equals(matA.length, 16);
+  assert_equals(matB.length, 16);
+
+  let mismatched_element = -1;
+  for (let i = 0; i < 16; ++i) {
+    if (Math.abs(matA[i] - matB[i]) > epsilon) {
+      mismatched_element = i;
+      break;
+    }
+  }
+
+  if (mismatched_element > -1) {
+    let error_message =
+        message ? message + '\n' : 'Matrix comparison failed.\n';
+    error_message += ' Difference in element ' + mismatched_element +
+        ' exceeded the given epsilon.\n';
+
+    error_message += ' Matrix A: [' + matA.join(',') + ']\n';
+    error_message += ' Matrix B: [' + matB.join(',') + ']\n';
+
+    assert_approx_equals(
+        matA[mismatched_element], matB[mismatched_element], epsilon,
+        error_message);
+  }
+}
+
+
+function assert_matrices_significantly_not_equal(
+    matA, matB, epsilon = FLOAT_EPSILON, message = '') {
+  if (matA == null && matB == null) {
+    return;
+  }
+
+  assert_not_equals(matA, null);
+  assert_not_equals(matB, null);
+
+  assert_equals(matA.length, 16);
+  assert_equals(matB.length, 16);
+
+  let mismatch = false;
+  for (let i = 0; i < 16; ++i) {
+    if (Math.abs(matA[i] - matB[i]) > epsilon) {
+      mismatch = true;
+      break;
+    }
+  }
+
+  if (!mismatch) {
+    let matA_str = '[';
+    let matB_str = '[';
+    for (let i = 0; i < 16; ++i) {
+      matA_str += matA[i] + (i < 15 ? ', ' : '');
+      matB_str += matB[i] + (i < 15 ? ', ' : '');
+    }
+    matA_str += ']';
+    matB_str += ']';
+
+    let error_message =
+        message ? message + '\n' : 'Matrix comparison failed.\n';
+    error_message +=
+        ' No element exceeded the given epsilon ' + epsilon + '.\n';
+
+    error_message += ' Matrix A: ' + matA_str + '\n';
+    error_message += ' Matrix B: ' + matB_str + '\n';
+
+    assert_unreached(error_message);
+  }
+}
+
 // TODO(offenwanger): eventSender cannot be used with WPTs. Find another way to
 // fake use gestures.
 // https://chromium.googlesource.com/chromium/src/+/lkgr/docs/testing/web_platform_tests.md#tests-that-require-testing-apis
diff --git a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_create_with_xr_device.html b/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_create_with_xr_device.html
index 7b6c75a..9edcc75 100644
--- a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_create_with_xr_device.html
+++ b/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_create_with_xr_device.html
@@ -3,26 +3,27 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
 
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) => {
-    webglCanvas = document.getElementById('webgl-canvas');
-    gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-    assert_equals(gl.getContextAttributes().compatibleXRDevice, device);
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => {
+      webglCanvas = document.getElementById('webgl-canvas');
+      gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
+      assert_equals(gl.getContextAttributes().compatibleXRDevice, device);
 
-    // Check that an offscreen context behaves no different.
-    let offscreenCanvas = document.createElement('canvas');
-    let offscreenGl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-    assert_equals(offscreenGl.getContextAttributes().compatibleXRDevice, device);
-  });
+      // Check that an offscreen context behaves no different.
+      let offscreenCanvas = document.createElement('canvas');
+      let offscreenGl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
+      assert_equals(offscreenGl.getContextAttributes().compatibleXRDevice, device);
+    });
 }, "A webglCanvasContext created with an XRDevice has that device set");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_set_device_lost_context.html b/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_set_device_lost_context.html
index e435a2e0..47c7c99 100644
--- a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_set_device_lost_context.html
+++ b/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_set_device_lost_context.html
@@ -3,22 +3,23 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) => {
-    webglCanvas = document.getElementById('webgl-canvas');
-    gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-    gl.getExtension('WEBGL_lose_context').loseContext();
-    return promise_rejects(t, 'InvalidStateError',
-        gl.setCompatibleXRDevice(device));
-  });
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => {
+      webglCanvas = document.getElementById('webgl-canvas');
+      gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
+      gl.getExtension('WEBGL_lose_context').loseContext();
+      return promise_rejects(t, 'InvalidStateError',
+          gl.setCompatibleXRDevice(device));
+    });
 }, "A lost webglCanvasContext should not be able to set device");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_no_gesture.html b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_no_gesture.html
index 7d717b11..2f1d5ad5 100644
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_no_gesture.html
+++ b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_no_gesture.html
@@ -3,16 +3,17 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) =>
-    promise_rejects(t, 'SecurityError', device.requestSession({ immersive: true })));
-}, "requestSession with an immersive session outside of a user gesture rejects");
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => promise_rejects(
+      t, 'SecurityError', device.requestSession({ immersive: true })));
+}, "request immersive session outside of a user gesture rejects");
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_supported.html b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_supported.html
index 4aeaeae3..e3a3a99 100644
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_supported.html
+++ b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_supported.html
@@ -3,16 +3,16 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
 
 xr_session_promise_test( (session) => {
   assert_not_equals(session, null);
-}, fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
+}, { supportsImmersive:true }, [{ immersive: true }],
 "requestSession for an immersive session with a user gesture resolves");
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_unsupported.html b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_unsupported.html
index 593ab50d..f41ce4c 100644
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_unsupported.html
+++ b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_unsupported.html
@@ -3,17 +3,15 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeMagicWindowOnly"]]);
-
-  return navigator.xr.requestDevice()
+  return XRTest.simulateDeviceConnection({ supportsImmersive:false })
+    .then( (controller) => { return navigator.xr.requestDevice() })
     .then( (magicWindowOnlyDevice) => new Promise((resolve) => {
       runWithUserGesture( () => {
         resolve(promise_rejects(
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_rejects.html b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_rejects.html
index 9e9ef8d..d3f016d 100644
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_rejects.html
+++ b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_rejects.html
@@ -3,21 +3,22 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <script>
-let fakeDevices = fakeXRDevices();
+
 
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeMagicWindowOnly"]]);
-  return navigator.xr.requestDevice().then( (magicWindowOnlyDevice) => {
-    return promise_rejects(
-      t,
-      "NotSupportedError",
-      magicWindowOnlyDevice.supportsSession({ immersive: true })
-    );
-  });
+  return XRTest.simulateDeviceConnection({ supportsImmersive:false })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (magicWindowOnlyDevice) => {
+      return promise_rejects(
+        t,
+        "NotSupportedError",
+        magicWindowOnlyDevice.supportsSession({ immersive: true })
+      );
+    });
 }, "supportsSession rejects when immersive session is not supported on device");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_resolves.html b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_resolves.html
index 1f5b4587..8b398ba 100644
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_resolves.html
+++ b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_resolves.html
@@ -3,16 +3,15 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <script>
-let fakeDevices = fakeXRDevices();
 
 promise_test( () => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) =>
-    device.supportsSession({ immersive: true }));
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => device.supportsSession({ immersive: true }));
 }, "supportsSession resolves when support immersive session is supported on device");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_non_exclusive.html b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_non_exclusive.html
index a3e806b..7082623e 100644
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_non_exclusive.html
+++ b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_non_exclusive.html
@@ -3,23 +3,23 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <script>
-let fakeDevices = fakeXRDevices();
 
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) => {
-    // Non-immersive sessions without a outputContext should not be supported.
-    promise_rejects(t, 'NotSupportedError', device.supportsSession());
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => {
+      // Non-immersive sessions without a outputContext should not be supported.
+      promise_rejects(t, 'NotSupportedError', device.supportsSession());
 
-    // Non-immersive sessions with an outputContexted should be supported.
-    return device.supportsSession({
-        outputContext: getOutputContext()
+      // Non-immersive sessions with an outputContext should be supported.
+      return device.supportsSession({
+          outputContext: getOutputContext()
+      });
     });
-  });
 }, "supportsSession properly identifies supported non-immersive sessions");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrFrameOfReference_stage_updates.html b/third_party/WebKit/LayoutTests/xr/xrFrameOfReference_stage_updates.html
index 86de570..31c1a55 100644
--- a/third_party/WebKit/LayoutTests/xr/xrFrameOfReference_stage_updates.html
+++ b/third_party/WebKit/LayoutTests/xr/xrFrameOfReference_stage_updates.html
@@ -3,20 +3,37 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName =
+  "'stage' XRFrameOfReference updates properly when the transform changes";
 
-xr_session_promise_test( (session, t) =>  {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { outputContext: getOutputContext() },
+  { immersive: true },
+];
+
+let testFunction = function(session, t, fakeDeviceController) {
   // Session must have a baseLayer or else frame requests will be ignored.
   session.baseLayer = new XRWebGLLayer(session, gl);
 
-  setPose(ORIGIN_POSE);
-  setStageTransform(null);
+  fakeDeviceController.setStageTransform(null);
+  fakeDeviceController.setXRPresentationFrameData(ORIGIN_POSE, [{
+      eye:"left",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }, {
+      eye:"right",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }]);
 
   return session.requestFrameOfReference("stage")
     .then((frameOfRef) => new Promise((resolve, reject) => {
@@ -33,7 +50,7 @@
           assert_greater_than(poseMatrix[13], 1.0);
           assert_approx_equals(poseMatrix[14], 0.0, FLOAT_EPSILON);
 
-          setStageTransform(VALID_STAGE_TRANSFORM);
+          fakeDeviceController.setStageTransform(VALID_STAGE_TRANSFORM);
         });
 
         session.requestAnimationFrame(onFrame);
@@ -55,10 +72,9 @@
 
       session.requestAnimationFrame(onFirstFrame);
     }));
-}, fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"'stage' XRFrameOfReference updates properly when the transform changes");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrInputSource_add_remove.html b/third_party/WebKit/LayoutTests/xr/xrInputSource_add_remove.html
index 92720cc..40bbc7f 100644
--- a/third_party/WebKit/LayoutTests/xr/xrInputSource_add_remove.html
+++ b/third_party/WebKit/LayoutTests/xr/xrInputSource_add_remove.html
@@ -3,20 +3,35 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session, t) => new Promise((resolve) => {
+let testName = "XRInputSources can be properly added and removed from the "
+  + "session";
+
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [{ immersive: true }];
+
+let testFunction = (session, t, fakeDeviceController) => new Promise((resolve) => {
     // Session must have a baseLayer or frame requests will be ignored.
     session.baseLayer = new XRWebGLLayer(session, gl);
 
-    // Need to have a valid pose or input updates don't process.
-    setPose(VALID_POSE);
+    // Need to have a valid pose or input events don't process.
+    fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
+        eye:"left",
+        projectionMatrix: VALID_PROJECTION_MATRIX,
+        viewMatrix: VALID_VIEW_MATRIX
+      }, {
+        eye:"right",
+        projectionMatrix: VALID_PROJECTION_MATRIX,
+        viewMatrix: VALID_VIEW_MATRIX
+      }]);
 
     let input_sources = session.getInputSources();
 
@@ -28,7 +43,7 @@
     input_source_1.targetRayMode = "tracked-pointer";
     input_source_1.handedness = "right";
 
-    addInputSource(input_source_1);
+    fakeDeviceController.addInputSource(input_source_1);
 
     session.requestAnimationFrame((time, xrFrame) => {
       let input_sources = session.getInputSources();
@@ -42,7 +57,7 @@
       let input_source_2 = new MockXRInputSource();
       input_source_2.targetRayMode = "gaze";
       input_source_2.emulatedPosition = "true";
-      addInputSource(input_source_2);
+      fakeDeviceController.addInputSource(input_source_2);
 
       session.requestAnimationFrame((time, xrFrame) => {
         let input_sources = session.getInputSources();
@@ -53,7 +68,7 @@
           assert_equals(input_sources[1].handedness, "");
         });
 
-        removeInputSource(input_source_1);
+        fakeDeviceController.removeInputSource(input_source_1);
 
         session.requestAnimationFrame((time, xrFrame) => {
           let input_sources = session.getInputSources();
@@ -68,7 +83,9 @@
         });
       });
     });
-  }), fakeDevices["FakeGooglePixelPhone"], [ { immersive: true } ],
-"XRInputSources can be properly added and removed from the session");
+  });
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_environmentBlendMode.html b/third_party/WebKit/LayoutTests/xr/xrSession_environmentBlendMode.html
index 217cfb9..6f3029d 100644
--- a/third_party/WebKit/LayoutTests/xr/xrSession_environmentBlendMode.html
+++ b/third_party/WebKit/LayoutTests/xr/xrSession_environmentBlendMode.html
@@ -3,23 +3,30 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session, t) => {
+let testName = "environmentBlendMode is correct for a VR device";
+
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { outputContext: getOutputContext() },
+  { immersive: true },
+];
+
+let testFunction = function(session, t) {
   t.step(() => {
     assert_equals(session.environmentBlendMode, 'opaque');
   });
-}, fakeDevices["FakeGooglePixelPhone"], [
-    { immersive: true },
-    { outputContext: getOutputContext() }
-],
-"environmentBlendMode is correct for a VR device");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 // TODO(https://crbug.com/828321): Enable once session options for AR are in place.
 /*xr_session_promise_test( (session, t) => {
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_exclusive.html b/third_party/WebKit/LayoutTests/xr/xrSession_exclusive.html
index bbd3ad00..2ac44504 100644
--- a/third_party/WebKit/LayoutTests/xr/xrSession_exclusive.html
+++ b/third_party/WebKit/LayoutTests/xr/xrSession_exclusive.html
@@ -3,26 +3,27 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
 
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) => new Promise((resolve) => {
-    runWithUserGesture( () => {
-      resolve(device.requestSession({ immersive: true }).then( (session) => {
-        assert_true(session.immersive);
-        assert_equals(session.device, device);
-        assert_approx_equals(session.depthNear, 0.1, FLOAT_EPSILON);
-        assert_approx_equals(session.depthFar, 1000.0, FLOAT_EPSILON);
-      }));
-    });
-  }));
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => new Promise((resolve) => {
+      runWithUserGesture( () => {
+        resolve(device.requestSession({ immersive: true }).then( (session) => {
+          assert_true(session.immersive);
+          assert_equals(session.device, device);
+          assert_approx_equals(session.depthNear, 0.1, FLOAT_EPSILON);
+          assert_approx_equals(session.depthFar, 1000.0, FLOAT_EPSILON);
+        }));
+      });
+    }));
 }, "supportsSession returns expected immersive session");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_prevent_multiple_exclusive.html b/third_party/WebKit/LayoutTests/xr/xrSession_prevent_multiple_exclusive.html
index 95ee719..a9295eb1 100644
--- a/third_party/WebKit/LayoutTests/xr/xrSession_prevent_multiple_exclusive.html
+++ b/third_party/WebKit/LayoutTests/xr/xrSession_prevent_multiple_exclusive.html
@@ -3,41 +3,43 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
 
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
-  return navigator.xr.requestDevice().then( (device) => new Promise((resolve) => {
-    runWithUserGesture( () => {
-      resolve(device.requestSession({ immersive: true })
-        .then( (session) => new Promise((resolve) => {
-          runWithUserGesture( () => {
-            // Requesting a second immersive session from a device that already
-            // has an active immersive session should fail. Immersive sessions
-            // are, well... immersive!
-            resolve(promise_rejects(
-              t,
-              "InvalidStateError",
-              device.requestSession({ immersive: true })
-            ).then( () => {
-                // End the immersive session and try again. Now the immersive
-                // session creation should succeed.
-                return session.end().then( () => new Promise((resolve) => {
-                  runWithUserGesture( () => {
-                    resolve(device.requestSession({ immersive: true }));
-                  });
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => new Promise((resolve) => {
+      runWithUserGesture( () => {
+        resolve(device.requestSession({ immersive: true })
+          .then( (session) => new Promise((resolve) => {
+            runWithUserGesture( () => {
+              // Requesting a second immersive session from a device that already
+              // has an active immersive session should fail. Immersive sessions
+              // should take up the users entire view, and therefore it should
+              // be impossible for a user to be engaged with more than one.
+              resolve(promise_rejects(
+                t,
+                "InvalidStateError",
+                device.requestSession({ immersive: true })
+              ).then( () => {
+                  // End the immersive session and try again. Now the immersive
+                  // session creation should succeed.
+                  return session.end().then( () => new Promise((resolve) => {
+                    runWithUserGesture( () => {
+                      resolve(device.requestSession({ immersive: true }));
+                    });
+                  }));
                 }));
-              }));
-          });
-      })));
-    });
-  }));
+            });
+        })));
+      });
+    }));
 }, "requestSession prevents creation of multiple simultaneous immersive sessions");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_requestAnimationFrame_timestamp.html b/third_party/WebKit/LayoutTests/xr/xrSession_requestAnimationFrame_timestamp.html
index d0bd2a4..be3c2ca 100644
--- a/third_party/WebKit/LayoutTests/xr/xrSession_requestAnimationFrame_timestamp.html
+++ b/third_party/WebKit/LayoutTests/xr/xrSession_requestAnimationFrame_timestamp.html
@@ -3,21 +3,39 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 const TEN_SECONDS = 10000; // 10k ms in ten seconds
 const ONE_MINUTE = 60000; // 60k ms in one minute
 
-xr_session_promise_test( (session, t) =>  {
+let testName = "XRFrame getDevicePose updates on the next frame";
+
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { outputContext: getOutputContext() },
+  { immersive: true },
+];
+
+let testFunction = function(session, t, fakeDeviceController) {
   // Session must have a baseLayer or else frame requests will be ignored.
   session.baseLayer = new XRWebGLLayer(session, gl);
 
-  setPose(null);
+  // Need to have a valid pose or input event's don't process.
+  fakeDeviceController.setXRPresentationFrameData(
+  VALID_POSE_MATRIX, [{
+    eye:"left",
+    projectionMatrix: VALID_PROJECTION_MATRIX,
+    viewMatrix: VALID_VIEW_MATRIX
+  }, {
+    eye:"right",
+    projectionMatrix: VALID_PROJECTION_MATRIX,
+    viewMatrix: VALID_VIEW_MATRIX
+  }]);
 
   return session.requestFrameOfReference("eye-level")
     .then((frameOfRef) => new Promise((resolve, reject) => {
@@ -86,10 +104,9 @@
       });
 
     }));
-}, fakeDevices["FakeGooglePixelPhone"], [
-      { immersive: true },
-      { outputContext: getOutputContext() }
-  ],
-"XRFrame getDevicePose updates on the next frame");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_requestFrameOfReference.html b/third_party/WebKit/LayoutTests/xr/xrSession_requestFrameOfReference.html
index 33627b4..dcc5996 100644
--- a/third_party/WebKit/LayoutTests/xr/xrSession_requestFrameOfReference.html
+++ b/third_party/WebKit/LayoutTests/xr/xrSession_requestFrameOfReference.html
@@ -3,15 +3,22 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "requestFrameOfReference returns the expected objects";
 
-xr_session_promise_test( (session, t) => {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [
+  { outputContext: getOutputContext() },
+  { immersive: true },
+];
+
+let testFunction = function(session, t) {
   return promise_rejects(t, new TypeError(), session.requestFrameOfReference("foo"))
     .then(() => Promise.all([
       session.requestFrameOfReference("head-model").then( (frameOfRef) => {
@@ -33,10 +40,9 @@
           "stage frameOfRef is not correct type.");
       })
   ]));
-}, fakeDevices["FakeGooglePixelPhone"], [
-    { immersive: true },
-    { outputContext: getOutputContext() }
-],
-"requestFrameOfReference returns the expected objects");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrView_match.html b/third_party/WebKit/LayoutTests/xr/xrView_match.html
index dd459fb..4b61d777 100644
--- a/third_party/WebKit/LayoutTests/xr/xrView_match.html
+++ b/third_party/WebKit/LayoutTests/xr/xrView_match.html
@@ -3,19 +3,35 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session) => {
+let testName = "XRFrame contains the expected views";
+
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [{ immersive: true }];
+
+let testFunction = function(session, t, fakeDeviceController) {
   // Session must have a baseLayer or frame requests will be ignored.
   let webglLayer = new XRWebGLLayer(session, gl);
   session.baseLayer = webglLayer;
 
+  // Need to have a valid pose or input event's don't process.
+  fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
+      eye:"left",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }, {
+      eye:"right",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }]);
+
   return session.requestFrameOfReference("eye-level").then((frameOfRef) => new Promise((resolve) =>{
     function onFrame(time, xrFrame) {
       // Ensure that two views are provided.
@@ -37,16 +53,8 @@
       assert_not_equals(leftView.projectionMatrix, null);
       assert_not_equals(rightView.projectionMatrix, null);
 
-      let displayLeftEye = fakeDevices["FakeGooglePixelPhone"].leftEye;
-      let displayRightEye = fakeDevices["FakeGooglePixelPhone"].rightEye;
-
-      let expectedLeftProjection = perspectiveFromFieldOfView(
-          displayLeftEye.fieldOfView, session.depthNear, session.depthFar);
-      let expectedRightProjection = perspectiveFromFieldOfView(
-          displayRightEye.fieldOfView, session.depthNear, session.depthFar);
-
-      assert_matrices_approx_equal(leftView.projectionMatrix, expectedLeftProjection);
-      assert_matrices_approx_equal(rightView.projectionMatrix, expectedRightProjection);
+      assert_matrices_approx_equal(leftView.projectionMatrix, VALID_PROJECTION_MATRIX);
+      assert_matrices_approx_equal(rightView.projectionMatrix, VALID_PROJECTION_MATRIX);
 
       // Finished test.
       resolve();
@@ -54,7 +62,9 @@
 
     session.requestAnimationFrame(onFrame);
   }));
-}, fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"XRFrame contains the expected views");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrView_oneframeupdate.html b/third_party/WebKit/LayoutTests/xr/xrView_oneframeupdate.html
index 130de587..6b1985e 100644
--- a/third_party/WebKit/LayoutTests/xr/xrView_oneframeupdate.html
+++ b/third_party/WebKit/LayoutTests/xr/xrView_oneframeupdate.html
@@ -3,20 +3,33 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-let displayLeftEye = fakeDevices["FakeGooglePixelPhone"].leftEye;
-let displayRightEye = fakeDevices["FakeGooglePixelPhone"].rightEye;
+let testName = "XRView projection matrices update near and far depths on the "
+  + "next frame";
 
-xr_session_promise_test( (session) => {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [{ immersive: true }];
+
+let testFunction = function(session, t, fakeDeviceController) {
   // Session must have a baseLayer or frame requests will be ignored.
   session.baseLayer = new XRWebGLLayer(session, gl);
+  // Need to have a valid pose or input event's don't process.
+  fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
+      eye:"left",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }, {
+      eye:"right",
+      projectionMatrix: VALID_PROJECTION_MATRIX,
+      viewMatrix: VALID_VIEW_MATRIX
+    }]);
 
   return session.requestFrameOfReference("eye-level")
     .then((frameOfRef) => new Promise((resolve) =>{
@@ -26,13 +39,8 @@
         if (counter == 0) {
           session.requestAnimationFrame(onFrame);
 
-          let expectedLeftProjection = perspectiveFromFieldOfView(
-              displayLeftEye.fieldOfView, session.depthNear, session.depthFar);
-          let expectedRightProjection = perspectiveFromFieldOfView(
-              displayRightEye.fieldOfView, session.depthNear, session.depthFar);
-
-          assert_matrices_approx_equal(xrFrame.views[0].projectionMatrix, expectedLeftProjection);
-          assert_matrices_approx_equal(xrFrame.views[1].projectionMatrix, expectedRightProjection);
+          assert_matrices_approx_equal(xrFrame.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrices_approx_equal(xrFrame.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
 
           // Update the near and far depths for the session.
           session.depthNear = 1.0;
@@ -40,21 +48,17 @@
 
           // The projection matrices the views report should not take into
           // account the new session depth values this frame.
-          assert_matrices_approx_equal(xrFrame.views[0].projectionMatrix, expectedLeftProjection);
-          assert_matrices_approx_equal(xrFrame.views[1].projectionMatrix, expectedRightProjection);
+          assert_matrices_approx_equal(xrFrame.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrices_approx_equal(xrFrame.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
         } else {
           // New depth values should be retained between frames.
           assert_equals(session.depthNear, 1.0);
           assert_equals(session.depthFar, 10.0);
 
-          let expectedLeftProjection = perspectiveFromFieldOfView(
-              displayLeftEye.fieldOfView, session.depthNear, session.depthFar);
-          let expectedRightProjection = perspectiveFromFieldOfView(
-              displayRightEye.fieldOfView, session.depthNear, session.depthFar);
-
-          // Projection matricies should now reflect the new depth values
-          assert_matrices_approx_equal(xrFrame.views[0].projectionMatrix, expectedLeftProjection);
-          assert_matrices_approx_equal(xrFrame.views[1].projectionMatrix, expectedRightProjection);
+          // Projection matricies should now reflect the new depth values, i.e.
+          // have changed.
+          assert_matrices_significantly_not_equal(xrFrame.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrices_significantly_not_equal(xrFrame.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
           resolve();
         }
         counter++;
@@ -62,7 +66,9 @@
 
       session.requestAnimationFrame(onFrame);
   }));
-}, fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"XRView projection matrices update near and far depths on the next frame");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrViewport_valid.html b/third_party/WebKit/LayoutTests/xr/xrViewport_valid.html
index d88f65f..b649250 100644
--- a/third_party/WebKit/LayoutTests/xr/xrViewport_valid.html
+++ b/third_party/WebKit/LayoutTests/xr/xrViewport_valid.html
@@ -3,15 +3,19 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "XRViewport attributes are valid";
 
-xr_session_promise_test( (session, t) => {
+let fakeDeviceInitParams = { supportsImmersive:true };
+
+let requestSessionOptions = [{ immersive: true }];
+
+let testFunction = function(session, t) {
   // Session must have a baseLayer or frame requests will be ignored.
   let webglLayer = new XRWebGLLayer(session, gl);
   session.baseLayer = webglLayer;
@@ -65,7 +69,9 @@
       }
       session.requestAnimationFrame(onFrame);
   }));
-}, fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"XRViewport attributes are valid");
+};
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_constructor.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_constructor.html
index 907c8c4..3defcbdf 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_constructor.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_constructor.html
@@ -3,15 +3,21 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName =
+  "Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame";
 
-xr_session_promise_test( (session) => new Promise((resolve, reject) => {
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions =  [{ immersive: true }];
+
+let testFunction =
+  (session, t, fakeDeviceController) => new Promise((resolve, reject) => {
   try {
     let webglLayerGood = new XRWebGLLayer(session, gl);
   } catch (err) {
@@ -45,7 +51,9 @@
 
   lose_context_ext.loseContext();
 
-}), fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame");
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_dirty_framebuffer.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_dirty_framebuffer.html
index 843f0c5f..292d30d 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_dirty_framebuffer.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_dirty_framebuffer.html
@@ -3,30 +3,38 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
+<script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session) => new Promise((resolve, reject) => {
+let testName = "A frame should be submitted if the base layer was written to "
+  + "during requestAnimationFrame";
+
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions =  [{ immersive: true }];
+
+let testFunction =
+  (session, t, fakeDeviceController) => new Promise((resolve, reject) => {
   // Session must have a baseLayer or else frame requests will be ignored.
   let webglLayer = new XRWebGLLayer(session, gl);
   session.baseLayer = webglLayer;
 
   function onSkipFrame(time, xrFrame) {
-    assert_equals(getSubmitFrameCount(), 0);
-    assert_equals(getMissingFrameCount(), 0);
+    assert_equals(fakeDeviceController.getSubmitFrameCount(), 0);
+    assert_equals(fakeDeviceController.getMissingFrameCount(), 0);
     // No GL commands issued.
     session.requestAnimationFrame(onDrawToCanvas);
   }
 
   function onDrawToCanvas(time, xrFrame) {
     // Ensure the previous step did not submit a frame.
-    assert_equals(getSubmitFrameCount(), 0);
-    assert_equals(getMissingFrameCount(), 1);
+    assert_equals(fakeDeviceController.getSubmitFrameCount(), 0);
+    assert_equals(fakeDeviceController.getMissingFrameCount(), 1);
 
     // Clear the canvas, but don't touch the framebuffer.
     gl.clear(gl.COLOR_BUFFER_BIT);
@@ -35,8 +43,8 @@
 
   function onDrawToFramebuffer(time, xrFrame) {
     // Ensure both previous steps did not submit frames.
-    assert_equals(getSubmitFrameCount(), 0);
-    assert_equals(getMissingFrameCount(), 2);
+    assert_equals(fakeDeviceController.getSubmitFrameCount(), 0);
+    assert_equals(fakeDeviceController.getMissingFrameCount(), 2);
 
     // Clear the VRWebGLLayer framebuffer.
     gl.bindFramebuffer(gl.FRAMEBUFFER, webglLayer.framebuffer);
@@ -44,14 +52,17 @@
 
     // After the function returns ensure the frame was submitted.
     window.setTimeout(() => {
-      assert_equals(getSubmitFrameCount(), 1);
-      assert_equals(getMissingFrameCount(), 2);
+      assert_equals(fakeDeviceController.getSubmitFrameCount(), 1);
+      assert_equals(fakeDeviceController.getMissingFrameCount(), 2);
       // Finished test.
       resolve();
     }, 100);
   }
 
   session.requestAnimationFrame(onSkipFrame);
-}), fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"A frame should be submitted if the base layer was written to during requestAnimationFrame");
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_draw.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_draw.html
index 709e8dd..d1b85e8 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_draw.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_draw.html
@@ -3,13 +3,19 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
+let testName =
+  "Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame";
+
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions =  [{ immersive: true }];
 
 // Very simple program setup with no error checking.
 function setupProgram(gl, vertexSrc, fragmentSrc) {
@@ -31,7 +37,8 @@
   return program;
 }
 
-xr_session_promise_test( (session) => new Promise((resolve, reject) => {
+let testFunction =
+  (session, t, fakeDeviceController) => new Promise((resolve, reject) => {
   // Setup simple WebGL geometry to draw with.
   let program = setupProgram(gl,
     "attribute vec4 vPosition; void main() { gl_Position = vPosition; }",
@@ -85,8 +92,9 @@
     runDrawTests("NO_ERROR");
     resolve();
   });
+});
 
-}), fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"Ensure a WebGL layer's framebuffer can only be drawn to inside a XR frame");
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_scale.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_scale.html
index 6800f9f7..43ff8f58 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_scale.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_framebuffer_scale.html
@@ -3,15 +3,21 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session, t) => new Promise((resolve, reject) => {
+let testName = "Ensure framebuffer scaling works as expected.";
+
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions =  [{ immersive: true }];
+
+let testFunction =
+  (session, t, fakeDeviceController) => new Promise((resolve, reject) => {
   // Session must have a baseLayer or else frame requests will be ignored.
   let webglLayer = new XRWebGLLayer(session, gl);
   let defaultFramebufferWidth = webglLayer.framebufferWidth;
@@ -25,7 +31,7 @@
     assert_greater_than(nativeScale, 0);
 
     // The native scale should be the inverse for the default framebuffer scale.
-    assert_approx_equals(nativeScale, 1/fakeDevices["FakeGooglePixelPhone"].webxrDefaultFramebufferScale, FLOAT_EPSILON);
+    assert_approx_equals(nativeScale, 1/fakeDeviceController.displayInfo_.webxrDefaultFramebufferScale, FLOAT_EPSILON);
   });
 
   webglLayer = new XRWebGLLayer(session, gl, { framebufferScaleFactor: nativeScale });
@@ -50,6 +56,9 @@
   });
 
   resolve();
-}), fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"Ensure framebuffer scaling works as expected.");
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_incompatible_device.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_incompatible_device.html
index e529071..ea2d3d95 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_incompatible_device.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_incompatible_device.html
@@ -3,31 +3,32 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+
 
 promise_test( (t) => {
-  setFakeDevices([fakeDevices["FakeGooglePixelPhone"]]);
   webglCanvasSetup();
-  return navigator.xr.requestDevice().then( (device) => new Promise((resolve) => {
-    runWithUserGesture( () => {
-      resolve(device.requestSession({ immersive: true })
-        .then( (session) => new Promise((resolve) => {
-          try {
-            let webglLayer = new XRWebGLLayer(session, gl);
-            reject("XRWebGLLayer should fail with invalid arguments");
-          } catch (err) {
-            assert_equals(err.name, "InvalidStateError");
-            resolve();
-          }
-      })));
-    });
-  }));
+  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+    .then( (controller) => { return navigator.xr.requestDevice() })
+    .then( (device) => new Promise((resolve) => {
+      runWithUserGesture( () => {
+        resolve(device.requestSession({ immersive: true })
+          .then( (session) => new Promise((resolve) => {
+            try {
+              let webglLayer = new XRWebGLLayer(session, gl);
+              reject("XRWebGLLayer should fail with invalid arguments");
+            } catch (err) {
+              assert_equals(err.name, "InvalidStateError");
+              resolve();
+            }
+        })));
+      });
+    }));
 }, "XRWebGLLayer throws error when the session device is not the set device");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_non_exclusive_adjust_size.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_non_exclusive_adjust_size.html
index 05a0c74..c993040 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_non_exclusive_adjust_size.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_non_exclusive_adjust_size.html
@@ -3,13 +3,15 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "Ensure a WebGL layer's framebuffer size is adjusted "
+  + "appropriately when a large canvas is requested";
+
 let outputContext = getOutputContext();
 let outputCanvas = outputContext.canvas;
 
@@ -19,7 +21,11 @@
 
 let outputCanvasRatio = outputCanvas.width / outputCanvas.height;
 
-xr_session_promise_test( (session, t) => new Promise((resolve, reject) => {
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions = [{ outputContext: outputContext }];
+
+let testFunction = (session, t) => new Promise((resolve, reject) => {
   let webglLayer = new XRWebGLLayer(session, gl);
   session.baseLayer = webglLayer;
 
@@ -47,7 +53,9 @@
 
     resolve();
   }, 100);
-}), fakeDevices["FakeGooglePixelPhone"], [{ outputContext: outputContext }],
-"Ensure a WebGL layer's framebuffer size is adjusted appropriately when a large canvas is requested");
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_opaque_framebuffer.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_opaque_framebuffer.html
index 8f77b02..4237a543 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_opaque_framebuffer.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_opaque_framebuffer.html
@@ -3,15 +3,23 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
+let testName = "Ensure that the framebuffer given by the WebGL layer is opaque";
 
-xr_session_promise_test( (session) => new Promise((resolve, reject) => {
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions = [
+  { immersive: true },
+  { outputContext: getOutputContext() }
+];
+
+let testFunction =
+  (session, t, fakeDeviceController) => new Promise((resolve, reject) => {
   // Session must have a baseLayer or frame requests will be ignored.
   let webglLayer = new XRWebGLLayer(session, gl);
   session.baseLayer = webglLayer;
@@ -97,11 +105,9 @@
     // Finished.
     resolve();
   });
+});
 
-}), fakeDevices["FakeGooglePixelPhone"], [
-    { immersive: true },
-    { outputContext: getOutputContext() }
-],
-"Ensure that the framebuffer given by the WebGL layer is opaque");
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_viewport_scale.html b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_viewport_scale.html
index 923690de..bcda0095 100644
--- a/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_viewport_scale.html
+++ b/third_party/WebKit/LayoutTests/xr/xrWebGLLayer_viewport_scale.html
@@ -3,15 +3,22 @@
 <script src="../resources/testharnessreport.js"></script>
 <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../xr/resources/xr-device-mocking.js"></script>
+<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <script src="../xr/resources/test-constants.js"></script>
 <canvas id="webgl-canvas"></canvas>
 
 <script>
-let fakeDevices = fakeXRDevices();
 
-xr_session_promise_test( (session, t) => new Promise((resolve, reject) => {
+let testName = "Ensure viewport scale changes only take effect on the next "
+  + "frame and are properly clamped.";
+
+let fakeDeviceInitParams = { supportsImmersive: true };
+
+let requestSessionOptions =  [{ immersive: true }];
+
+let testFunction =
+  (session, t, fakeDeviceController) => new Promise((resolve, reject) => {
   // Session must have a baseLayer or else frame requests will be ignored.
   let webglLayer = new XRWebGLLayer(session, gl);
   session.baseLayer = webglLayer;
@@ -69,6 +76,9 @@
   }
 
   session.requestAnimationFrame(onFirstFrame);
-}), fakeDevices["FakeGooglePixelPhone"], [{ immersive: true }],
-"Ensure viewport scale changes only take effect on the next frame and are properly clamped.");
+});
+
+xr_session_promise_test(
+  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
+
 </script>
diff --git a/third_party/blink/perf_tests/bindings/resources/structured-clone-perf-test.js b/third_party/blink/perf_tests/bindings/resources/structured-clone-perf-test.js
index f44f460e..0efc05701 100644
--- a/third_party/blink/perf_tests/bindings/resources/structured-clone-perf-test.js
+++ b/third_party/blink/perf_tests/bindings/resources/structured-clone-perf-test.js
@@ -1,17 +1,32 @@
 const StructuredClonePerfTestRunner = (function() {
-  function pingPong(data) {
+  function pingPong(data, useWorker) {
     return new Promise((resolve, reject) => {
       let beginSerialize, endSerialize, beginDeserialize;
-      window.addEventListener('message', function listener(e) {
-        try {
-          e.data;  // Force deserialization.
-          const endDeserialize = PerfTestRunner.now();
-          window.removeEventListener('message', listener);
-          resolve([endSerialize - beginSerialize, endDeserialize - beginDeserialize]);
-        } catch (err) { reject(err); }
-      });
+      if (useWorker) {
+        worker.addEventListener('message', function listener(e) {
+          try {
+            e.data;  // Force deserialization.
+            const endDeserialize = PerfTestRunner.now();
+            worker.removeEventListener('message', listener);
+            resolve([endSerialize - beginSerialize, endDeserialize - beginDeserialize]);
+          } catch (err) { reject(err); }
+        });
+      } else {
+        window.addEventListener('message', function listener(e) {
+          try {
+            e.data;  // Force deserialization.
+            const endDeserialize = PerfTestRunner.now();
+            window.removeEventListener('message', listener);
+            resolve([endSerialize - beginSerialize, endDeserialize - beginDeserialize]);
+          } catch (err) { reject(err); }
+        });
+      }
       beginSerialize = PerfTestRunner.now();
-      window.postMessage(data, '*');
+      if (useWorker) {
+        worker.postMessage(data);
+      } else {
+        window.postMessage(data, '*');
+      }
       beginDeserialize = endSerialize = PerfTestRunner.now();
       // While Chrome does the deserialize lazily when e.data is read, this
       // isn't portable, so it's more fair to measure from when the message is
@@ -22,6 +37,9 @@
   return {
     measureTimeAsync(test) {
       let isDone = false;
+      if (test.worker) {
+        worker = new Worker('resources/worker-structured-clone.js');
+      }
       PerfTestRunner.startMeasureValuesAsync({
         description: test.description,
         unit: 'ms',
@@ -32,7 +50,7 @@
       });
 
       function pingPongUntilDone() {
-        pingPong(test.data).then(([serializeTime, deserializeTime]) => {
+        pingPong(test.data, test.worker).then(([serializeTime, deserializeTime]) => {
           console.log([serializeTime, deserializeTime]);
           if (test.measure === 'serialize')
             PerfTestRunner.measureValueAsync(serializeTime);
diff --git a/third_party/blink/perf_tests/bindings/resources/worker-structured-clone.js b/third_party/blink/perf_tests/bindings/resources/worker-structured-clone.js
new file mode 100644
index 0000000..8b23883
--- /dev/null
+++ b/third_party/blink/perf_tests/bindings/resources/worker-structured-clone.js
@@ -0,0 +1,3 @@
+self.onmessage = function(e) {
+    self.postMessage(e.data);
+};
diff --git a/third_party/blink/perf_tests/bindings/worker-structured-clone-json-serialize.html b/third_party/blink/perf_tests/bindings/worker-structured-clone-json-serialize.html
new file mode 100644
index 0000000..42d95c5
--- /dev/null
+++ b/third_party/blink/perf_tests/bindings/worker-structured-clone-json-serialize.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<body>
+<script src="../resources/runner.js"></script>
+<script src="resources/structured-clone-perf-test.js"></script>
+<script>
+StructuredClonePerfTestRunner.measureTimeAsync({
+  description: "Measures performance of serializing JSON-like data.",
+  data: JSON.parse(PerfTestRunner.loadFile("resources/blink-dev.json")),
+    measure: "serialize",
+    worker: true,
+});
+</script>
+</body>
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 98953f2..590ae4e 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -1799,11 +1799,9 @@
   kScrollToFragmentSucceedWithASCII = 2337,
   kScrollToFragmentSucceedWithUTF8 = 2338,
   kScrollToFragmentSucceedWithIsomorphic = 2339,
-  kScrollToFragmentSucceedWithMixed = 2340,
   kScrollToFragmentFailWithASCII = 2341,
   kScrollToFragmentFailWithUTF8 = 2342,
   kScrollToFragmentFailWithIsomorphic = 2343,
-  kScrollToFragmentFailWithMixed = 2344,
   kScrollToFragmentFailWithInvalidEncoding = 2345,
   kRTCPeerConnectionWithActiveCsp = 2346,
   // The above items are available as of the M65 branch.
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index faf6af63..5365b6d 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -173,31 +173,10 @@
     // waiting).
     DCHECK(resource);
 
-    if (!resource->GetResponse().CacheStorageCacheName().IsNull()) {
-      // The resource has a cache storage cache and so may have a code cache.
-      // TODO(rmcilroy): We currently disable streaming even if the cache
-      // storage doesn't contain a code-cache (and thus we might be planning to
-      // produce a code-cache in this storage). We no longer need to suppress
-      // streaming when producing a code-cache so we should avoid suppressing
-      // in this case.
-      streamer->SuppressStreaming(ScriptStreamer::kHasCodeCache);
-      Cancel();
-      return;
-    }
-
-    SingleCachedMetadataHandler* cache_handler = resource->CacheHandler();
-    scoped_refptr<CachedMetadata> code_cache(
-        cache_handler ? cache_handler->GetCachedMetadata(
-                            V8CodeCache::TagForCodeCache(cache_handler))
-                      : nullptr);
-    if (code_cache.get()) {
+    if (V8CodeCache::HasCodeCache(resource->CacheHandler())) {
       // The resource has a code cache entry, so it's unnecessary to stream
       // and parse the code. Cancel the streaming and resume the non-streaming
-      // code path.
-      // TODO(rmcilroy): We currently disable streaming even if the code-cache
-      // only contains a time-stamp (and thus we might be planning to produce a
-      // code-cache). We no longer need to suppress streaming when producing a
-      // code-cache so we should avoid suppressing in this case.
+      // code path which will consume the code cache.
       streamer->SuppressStreaming(ScriptStreamer::kHasCodeCache);
       Cancel();
       return;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc b/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc
index 548294f..727c3ee 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc
@@ -62,6 +62,14 @@
 
 }  // namespace
 
+bool V8CodeCache::HasCodeCache(SingleCachedMetadataHandler* cache_handler) {
+  if (!cache_handler)
+    return false;
+
+  uint32_t code_cache_tag = V8CodeCache::TagForCodeCache(cache_handler);
+  return cache_handler->GetCachedMetadata(code_cache_tag).get();
+}
+
 v8::ScriptCompiler::CachedData* V8CodeCache::CreateCachedData(
     SingleCachedMetadataHandler* cache_handler) {
   DCHECK(cache_handler);
@@ -124,10 +132,7 @@
                            no_cache_reason);
   }
 
-  uint32_t code_cache_tag = V8CodeCache::TagForCodeCache(cache_handler);
-  scoped_refptr<CachedMetadata> code_cache =
-      cache_handler->GetCachedMetadata(code_cache_tag);
-  if (code_cache) {
+  if (HasCodeCache(cache_handler)) {
     return std::make_tuple(v8::ScriptCompiler::kConsumeCodeCache,
                            ProduceCacheOptions::kNoProduceCache,
                            no_cache_reason);
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_code_cache.h b/third_party/blink/renderer/bindings/core/v8/v8_code_cache.h
index 8f85911f..ddeaef8 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_code_cache.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_code_cache.h
@@ -45,6 +45,10 @@
   static uint32_t TagForTimeStamp(SingleCachedMetadataHandler*);
   static void SetCacheTimeStamp(SingleCachedMetadataHandler*);
 
+  // Returns true iff the SingleCachedMetadataHandler contains a code cache
+  // that can be consumed by V8.
+  static bool HasCodeCache(SingleCachedMetadataHandler*);
+
   static std::tuple<v8::ScriptCompiler::CompileOptions,
                     ProduceCacheOptions,
                     v8::ScriptCompiler::NoCacheReason>
diff --git a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
index 210149d9b..df2dc2a 100644
--- a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl.cc
@@ -409,7 +409,6 @@
       request.GetFetchCredentialsMode(), std::move(task_runner));
 
   if (allow_load) {
-    ThreadableLoaderOptions options;
     ResourceLoaderOptions resource_loader_options;
     resource_loader_options.data_buffering_policy = kDoNotBufferData;
 
@@ -441,8 +440,8 @@
 
     Document* document = ToDocument(observer_->LifecycleContext());
     DCHECK(document);
-    loader_ = ThreadableLoader::Create(*document, client_adapter_.get(),
-                                       options, resource_loader_options);
+    loader_ = new ThreadableLoader(*document, client_adapter_.get(),
+                                   resource_loader_options, base::nullopt);
     loader_->Start(webcore_request);
   }
 
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index ae91026..f8949303 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -854,12 +854,10 @@
         std::move(factory_clone));
   }
 
-  ThreadableLoaderOptions threadable_loader_options;
-
   probe::willStartFetch(execution_context_, this);
-  threadable_loader_ = ThreadableLoader::Create(*execution_context_, this,
-                                                threadable_loader_options,
-                                                resource_loader_options);
+  threadable_loader_ = new ThreadableLoader(*execution_context_, this,
+                                            resource_loader_options,
+                                            base::nullopt);
   threadable_loader_->Start(request);
 }
 
@@ -884,12 +882,10 @@
   resource_loader_options.data_buffering_policy = kDoNotBufferData;
   resource_loader_options.security_origin = fetch_request_data_->Origin().get();
 
-  ThreadableLoaderOptions threadable_loader_options;
-
   probe::willStartFetch(execution_context_, this);
-  threadable_loader_ = ThreadableLoader::Create(*execution_context_, this,
-                                                threadable_loader_options,
-                                                resource_loader_options);
+  threadable_loader_ = new ThreadableLoader(*execution_context_, this,
+                                            resource_loader_options,
+                                            base::nullopt);
   threadable_loader_->Start(request);
 }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 709ce20..86f5987 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1467,10 +1467,6 @@
           UseCounter::Count(&GetFrame(),
                             WebFeature::kScrollToFragmentSucceedWithIsomorphic);
           break;
-        case DecodeURLResult::kMixed:
-          UseCounter::Count(&GetFrame(),
-                            WebFeature::kScrollToFragmentSucceedWithMixed);
-          break;
       }
     } else {
       switch (decode_result) {
@@ -1486,10 +1482,6 @@
           UseCounter::Count(&GetFrame(),
                             WebFeature::kScrollToFragmentFailWithIsomorphic);
           break;
-        case DecodeURLResult::kMixed:
-          UseCounter::Count(&GetFrame(),
-                            WebFeature::kScrollToFragmentFailWithMixed);
-          break;
       }
     }
   } else {
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index 76934b84..dd3fee71 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -67,29 +67,31 @@
               ? GetOrCreateResourceDispatcher()->GetWeakPtr()
               : nullptr;
       if (Is3d()) {
+        const CanvasResourceProvider::ResourceUsage usage =
+            SharedGpuContext::IsGpuCompositingEnabled()
+                ? CanvasResourceProvider::kAcceleratedCompositedResourceUsage
+                : CanvasResourceProvider::kSoftwareCompositedResourceUsage;
+
         CanvasResourceProvider::PresentationMode presentation_mode =
             RuntimeEnabledFeatures::WebGLImageChromiumEnabled()
                 ? CanvasResourceProvider::kAllowImageChromiumPresentationMode
                 : CanvasResourceProvider::kDefaultPresentationMode;
 
         ReplaceResourceProvider(CanvasResourceProvider::Create(
-            Size(),
-            SharedGpuContext::IsGpuCompositingEnabled()
-                ? CanvasResourceProvider::kAcceleratedCompositedResourceUsage
-                : CanvasResourceProvider::kSoftwareCompositedResourceUsage,
-            SharedGpuContext::ContextProviderWrapper(),
-            0,  // msaa_sample_count
-            ColorParams(), presentation_mode, std::move(dispatcher)));
+            Size(), usage, SharedGpuContext::ContextProviderWrapper(),
+            0 /* msaa_sample_count */, ColorParams(), presentation_mode,
+            std::move(dispatcher)));
       } else {
-        bool want_acceleration =
+        DCHECK(Is2d());
+        const bool want_acceleration =
             hint == kPreferAcceleration && ShouldAccelerate2dContext();
 
-        CanvasResourceProvider::ResourceUsage usage =
+        const CanvasResourceProvider::ResourceUsage usage =
             want_acceleration
                 ? CanvasResourceProvider::kAcceleratedCompositedResourceUsage
                 : CanvasResourceProvider::kSoftwareCompositedResourceUsage;
 
-        CanvasResourceProvider::PresentationMode presentation_mode =
+        const CanvasResourceProvider::PresentationMode presentation_mode =
             RuntimeEnabledFeatures::Canvas2dImageChromiumEnabled()
                 ? CanvasResourceProvider::kAllowImageChromiumPresentationMode
                 : CanvasResourceProvider::kDefaultPresentationMode;
@@ -108,9 +110,8 @@
         }
       }
     }
-    if (!ResourceProvider()) {
+    if (!ResourceProvider())
       did_fail_to_create_resource_provider_ = true;
-    }
   }
   return ResourceProvider();
 }
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index a788952..0b843ba 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -144,9 +144,8 @@
 unsigned HTMLCanvasElement::global_accelerated_context_count_ = 0;
 
 HTMLCanvasElement::~HTMLCanvasElement() {
-  if (surface_layer_bridge_ && surface_layer_bridge_->GetCcLayer()) {
+  if (surface_layer_bridge_ && surface_layer_bridge_->GetCcLayer())
     GraphicsLayer::UnregisterContentsLayer(surface_layer_bridge_->GetCcLayer());
-  }
   v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
       -externally_allocated_memory_);
 }
@@ -257,8 +256,9 @@
   // Unknown type.
   if (context_type == CanvasRenderingContext::kContextTypeCount ||
       (context_type == CanvasRenderingContext::kContextXRPresent &&
-       !OriginTrials::WebXREnabled(&GetDocument())))
+       !OriginTrials::WebXREnabled(&GetDocument()))) {
     return nullptr;
+  }
 
   // Log the aliased context type used.
   if (!context_) {
@@ -296,9 +296,8 @@
 
   probe::didCreateCanvasContext(&GetDocument());
 
-  if (Is3d()) {
+  if (Is3d())
     UpdateMemoryUsage();
-  }
 
   LayoutObject* layout_object = GetLayoutObject();
   if (layout_object && Is2d() && !context_->CreationAttributes().alpha) {
@@ -336,31 +335,25 @@
 bool HTMLCanvasElement::IsWebGL1Enabled() const {
   Document& document = GetDocument();
   LocalFrame* frame = document.GetFrame();
-  if (frame) {
-    Settings* settings = frame->GetSettings();
-    if (settings && settings->GetWebGL1Enabled())
-      return true;
-  }
-  return false;
+  if (!frame)
+    return false;
+  Settings* settings = frame->GetSettings();
+  return settings && settings->GetWebGL1Enabled();
 }
 
 bool HTMLCanvasElement::IsWebGL2Enabled() const {
   Document& document = GetDocument();
   LocalFrame* frame = document.GetFrame();
-  if (frame) {
-    Settings* settings = frame->GetSettings();
-    if (settings && settings->GetWebGL2Enabled())
-      return true;
-  }
-  return false;
+  if (!frame)
+    return false;
+  Settings* settings = frame->GetSettings();
+  return settings && settings->GetWebGL2Enabled();
 }
 
 bool HTMLCanvasElement::IsWebGLBlocked() const {
   Document& document = GetDocument();
   LocalFrame* frame = document.GetFrame();
-  if (frame && frame->Client()->ShouldBlockWebGL())
-    return true;
-  return false;
+  return frame && frame->Client()->ShouldBlockWebGL();
 }
 
 void HTMLCanvasElement::DidDraw(const FloatRect& rect) {
@@ -453,15 +446,13 @@
         unaccelerated_bridge_used_for_testing) {
   // Create and configure an unaccelerated Canvas2DLayerBridge.
   std::unique_ptr<Canvas2DLayerBridge> bridge;
-  if (unaccelerated_bridge_used_for_testing) {
+  if (unaccelerated_bridge_used_for_testing)
     bridge = std::move(unaccelerated_bridge_used_for_testing);
-  } else {
+  else
     bridge = CreateUnaccelerated2dBuffer();
-  }
 
-  if (bridge && canvas2d_bridge_) {
+  if (bridge && canvas2d_bridge_)
     ReplaceExisting2dLayerBridge(std::move(bridge));
-  }
 
   // We must force a paint invalidation on the canvas even if it's
   // content did not change because it layer was destroyed.
@@ -502,14 +493,12 @@
     if (dirty_rect_.IsEmpty())
       return;
 
-    if (canvas2d_bridge_) {
+    if (canvas2d_bridge_)
       canvas2d_bridge_->DoPaintInvalidation(invalidation_rect);
-    }
   }
 
-  if (context_ && HasImageBitmapContext() && context_->CcLayer()) {
+  if (context_ && HasImageBitmapContext() && context_->CcLayer())
     context_->CcLayer()->SetNeedsDisplay();
-  }
 
   NotifyListenersCanvasChanged();
   did_notify_listeners_for_current_frame_ = true;
@@ -554,14 +543,16 @@
   unsigned w = 0;
   AtomicString value = getAttribute(widthAttr);
   if (value.IsEmpty() || !ParseHTMLNonNegativeInteger(value, w) ||
-      w > 0x7fffffffu)
+      w > 0x7fffffffu) {
     w = kDefaultCanvasWidth;
+  }
 
   unsigned h = 0;
   value = getAttribute(heightAttr);
   if (value.IsEmpty() || !ParseHTMLNonNegativeInteger(value, h) ||
-      h > 0x7fffffffu)
+      h > 0x7fffffffu) {
     h = kDefaultCanvasHeight;
+  }
 
   if (Is2d()) {
     context_->Reset();
@@ -622,9 +613,8 @@
 
   bool listener_needs_new_frame_capture = false;
   for (const CanvasDrawListener* listener : listeners_) {
-    if (listener->NeedsNewFrame()) {
+    if (listener->NeedsNewFrame())
       listener_needs_new_frame_capture = true;
-    }
   }
 
   if (listener_needs_new_frame_capture) {
@@ -636,9 +626,8 @@
     sk_sp<SkImage> image =
         source_image->PaintImageForCurrentFrame().GetSkImage();
     for (CanvasDrawListener* listener : listeners_) {
-      if (listener->NeedsNewFrame()) {
+      if (listener->NeedsNewFrame())
         listener->SendNewFrame(image, source_image->ContextProviderWrapper());
-      }
     }
   }
 }
@@ -693,11 +682,10 @@
   if (!context_ && !PlaceholderFrame())
     return;
 
-  if (Is3d()) {
+  if (Is3d())
     context_->SetFilterQuality(FilterQuality());
-  } else if (canvas2d_bridge_) {
+  else if (canvas2d_bridge_)
     canvas2d_bridge_->UpdateFilterQuality();
-  }
 
   if (HasResourceProvider() && !canvas_is_clear_)
     PaintTiming::From(GetDocument()).MarkFirstContentfulPaint();
@@ -754,9 +742,8 @@
   size_ = size;
   did_fail_to_create_resource_provider_ = false;
   DiscardResourceProvider();
-  if (Is2d() && context_->isContextLost()) {
+  if (Is2d() && context_->isContextLost())
     context_->DidSetSurfaceSize();
-  }
   if (frame_dispatcher_)
     frame_dispatcher_->Reshape(size_);
 }
@@ -861,9 +848,8 @@
   double quality = kUndefinedQualityValue;
   if (!quality_argument.IsEmpty()) {
     v8::Local<v8::Value> v8_value = quality_argument.V8Value();
-    if (v8_value->IsNumber()) {
+    if (v8_value->IsNumber())
       quality = v8_value.As<v8::Number>()->Value();
-    }
   }
   return ToDataURLInternal(mime_type, quality, kBackBuffer);
 }
@@ -894,9 +880,8 @@
   double quality = kUndefinedQualityValue;
   if (!quality_argument.IsEmpty()) {
     v8::Local<v8::Value> v8_value = quality_argument.V8Value();
-    if (v8_value->IsNumber()) {
+    if (v8_value->IsNumber())
       quality = v8_value.As<v8::Number>()->Value();
-    }
   }
 
   String encoding_mime_type = ImageEncoderUtils::ToEncodingMimeType(
@@ -923,7 +908,6 @@
                           V8BlobCallback>::InvokeAndReportException,
                       WrapPersistent(ToV8PersistentCallbackFunction(callback)),
                       nullptr, nullptr));
-    return;
   }
 }
 
@@ -937,8 +921,9 @@
 
 bool HTMLCanvasElement::OriginClean() const {
   if (GetDocument().GetSettings() &&
-      GetDocument().GetSettings()->GetDisableReadingFromCanvas())
+      GetDocument().GetSettings()->GetDisableReadingFromCanvas()) {
     return false;
+  }
   if (PlaceholderFrame())
     return PlaceholderFrame()->OriginClean();
   return origin_clean_;
@@ -984,8 +969,9 @@
   if (criteria != kIgnoreResourceLimitCriteria) {
     Settings* settings = GetDocument().GetSettings();
     if (!settings ||
-        canvas_pixel_count < settings->GetMinimumAccelerated2dCanvasSize())
+        canvas_pixel_count < settings->GetMinimumAccelerated2dCanvasSize()) {
       return false;
+    }
 
     // When GPU allocated memory runs low (due to having created too many
     // accelerated canvases), the compositor starves and browser becomes laggy.
@@ -1015,12 +1001,9 @@
 }
 
 unsigned HTMLCanvasElement::GetMSAASampleCountFor2dContext() const {
-  unsigned msaa_sample_count = 0;
-  if (GetDocument().GetSettings()) {
-    msaa_sample_count =
-        GetDocument().GetSettings()->GetAccelerated2dCanvasMSAASampleCount();
-  }
-  return msaa_sample_count;
+  if (!GetDocument().GetSettings())
+    return 0;
+  return GetDocument().GetSettings()->GetAccelerated2dCanvasMSAASampleCount();
 }
 
 std::unique_ptr<Canvas2DLayerBridge>
@@ -1068,19 +1051,16 @@
     if (external_canvas2d_bridge->IsValid())
       canvas2d_bridge_ = std::move(external_canvas2d_bridge);
   } else {
-    if (ShouldAccelerate(kNormalAccelerationCriteria)) {
+    if (ShouldAccelerate(kNormalAccelerationCriteria))
       canvas2d_bridge_ = CreateAccelerated2dBuffer();
-    }
-    if (!canvas2d_bridge_) {
+    if (!canvas2d_bridge_)
       canvas2d_bridge_ = CreateUnaccelerated2dBuffer();
-    }
   }
 
-  if (canvas2d_bridge_) {
+  if (canvas2d_bridge_)
     canvas2d_bridge_->SetCanvasResourceHost(this);
-  } else {
+  else
     return;
-  }
 
   did_fail_to_create_resource_provider_ = false;
   UpdateMemoryUsage();
@@ -1090,8 +1070,9 @@
   // consistency, we don't want to apply AA in accelerated canvases but not in
   // unaccelerated canvases.
   if (!GetMSAASampleCountFor2dContext() && GetDocument().GetSettings() &&
-      !GetDocument().GetSettings()->GetAntialiased2dCanvasEnabled())
+      !GetDocument().GetSettings()->GetAntialiased2dCanvasEnabled()) {
     context_->SetShouldAntialias(false);
+  }
 
   if (context_)
     SetNeedsCompositingUpdate();
@@ -1119,9 +1100,8 @@
   DCHECK(Is2d());
   if (!canvas2d_bridge_ && !did_fail_to_create_resource_provider_) {
     SetCanvas2DLayerBridgeInternal(nullptr);
-    if (did_fail_to_create_resource_provider_ && !Size().IsEmpty()) {
+    if (did_fail_to_create_resource_provider_ && !Size().IsEmpty())
       context_->LoseContext(CanvasRenderingContext::kSyntheticLostContext);
-    }
   }
   return canvas2d_bridge_.get();
 }
@@ -1149,11 +1129,8 @@
     return;
 
   context_->SetIsHidden(hidden);
-  if (hidden) {
-    if (Is3d()) {
-      DiscardResourceProvider();
-    }
-  }
+  if (hidden && Is3d())
+    DiscardResourceProvider();
 }
 
 void HTMLCanvasElement::ContextDestroyed(ExecutionContext*) {
@@ -1229,11 +1206,10 @@
     // use paintRenderingResultsToCanvas instead of getImage in order to keep a
     // cached copy of the backing in the canvas's resource provider.
     RenderingContext()->PaintRenderingResultsToCanvas(kBackBuffer);
-    if (ResourceProvider()) {
+    if (ResourceProvider())
       image = ResourceProvider()->Snapshot();
-    } else {
+    else
       image = CreateTransparentImage(Size());
-    }
   } else {
     if (CanvasHeuristicParameters::kDisableAccelerationToAvoidReadbacks &&
         !RuntimeEnabledFeatures::Canvas2dFixedRenderingModeEnabled() &&
@@ -1242,16 +1218,14 @@
       DisableAcceleration();
     }
     image = RenderingContext()->GetImage(hint);
-    if (!image) {
+    if (!image)
       image = CreateTransparentImage(Size());
-    }
   }
 
-  if (image) {
+  if (image)
     *status = kNormalSourceImageStatus;
-  } else {
+  else
     *status = kInvalidSourceImageStatus;
-  }
   return image;
 }
 
@@ -1326,8 +1300,9 @@
   if (auto* input_element = ToHTMLInputElementOrNull(element)) {
     if (input_element->type() == InputTypeNames::checkbox ||
         input_element->type() == InputTypeNames::radio ||
-        input_element->IsTextButton())
+        input_element->IsTextButton()) {
       return true;
+    }
   }
 
   // A select element with a "multiple" attribute or with a display size greater
@@ -1428,14 +1403,13 @@
   if (Is3d()) {
     if (ResourceProvider()) {
       non_gpu_buffer_count++;
-      if (ResourceProvider()->IsAccelerated()) {
+      if (ResourceProvider()->IsAccelerated())
         gpu_buffer_count += 2;
-      }
     }
     non_gpu_buffer_count += context_->ExternallyAllocatedBufferCountPerPixel();
   }
 
-  int bytes_per_pixel = ColorParams().BytesPerPixel();
+  const int bytes_per_pixel = ColorParams().BytesPerPixel();
 
   // Re-computation of gpu memory usage is only carried out when there is a
   // a change from acceleration to non-accleration or vice versa.
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
index 665eadc..465c02a2 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -651,11 +651,6 @@
       optional_script_to_evaluate_on_load.fromMaybe("");
   v8_session_->setSkipAllPauses(true);
   reloading_ = true;
-  inspected_frames_->Root()->Reload(
-      optional_bypass_cache.fromMaybe(false)
-          ? WebFrameLoadType::kReloadBypassingCache
-          : WebFrameLoadType::kReload,
-      ClientRedirectPolicy::kNotClientRedirect);
   return Response::OK();
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index b34ce05..9b1a6882 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -271,10 +271,14 @@
   ClearNeedsLayout();
 }
 
-void LayoutEmbeddedContent::PaintReplaced(
+void LayoutEmbeddedContent::Paint(const PaintInfo& paint_info) const {
+  EmbeddedContentPainter(*this).Paint(paint_info);
+}
+
+void LayoutEmbeddedContent::PaintContents(
     const PaintInfo& paint_info,
     const LayoutPoint& paint_offset) const {
-  EmbeddedContentPainter(*this).PaintReplaced(paint_info, paint_offset);
+  EmbeddedContentPainter(*this).PaintContents(paint_info, paint_offset);
 }
 
 CursorDirective LayoutEmbeddedContent::GetCursor(const LayoutPoint& point,
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.h b/third_party/blink/renderer/core/layout/layout_embedded_content.h
index dd85f9b..03a42900 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.h
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.h
@@ -63,6 +63,8 @@
   void UpdateGeometry(EmbeddedContentView&);
 
   bool IsLayoutEmbeddedContent() const final { return true; }
+  virtual void PaintContents(const PaintInfo&,
+                             const LayoutPoint& paint_offset) const;
 
   bool IsThrottledFrameView() const;
 
@@ -71,8 +73,7 @@
 
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) final;
   void UpdateLayout() override;
-  void PaintReplaced(const PaintInfo&,
-                     const LayoutPoint& paint_offset) const override;
+  void Paint(const PaintInfo&) const override;
   CursorDirective GetCursor(const LayoutPoint&, Cursor&) const final;
 
   bool CanBeSelectionLeafInternal() const final { return true; }
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_object.cc b/third_party/blink/renderer/core/layout/layout_embedded_object.cc
index 7baf19b..696c65e0 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_object.cc
@@ -83,6 +83,25 @@
   return plugin_availability_ != kPluginAvailable;
 }
 
+void LayoutEmbeddedObject::PaintContents(
+    const PaintInfo& paint_info,
+    const LayoutPoint& paint_offset) const {
+  Element* element = ToElement(GetNode());
+  if (!IsHTMLPlugInElement(element))
+    return;
+
+  LayoutEmbeddedContent::PaintContents(paint_info, paint_offset);
+}
+
+void LayoutEmbeddedObject::Paint(const PaintInfo& paint_info) const {
+  if (ShowsUnavailablePluginIndicator()) {
+    LayoutReplaced::Paint(paint_info);
+    return;
+  }
+
+  LayoutEmbeddedContent::Paint(paint_info);
+}
+
 void LayoutEmbeddedObject::PaintReplaced(
     const PaintInfo& paint_info,
     const LayoutPoint& paint_offset) const {
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_object.h b/third_party/blink/renderer/core/layout/layout_embedded_object.h
index 8ee32cd..88d4ea1 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_object.h
+++ b/third_party/blink/renderer/core/layout/layout_embedded_object.h
@@ -50,8 +50,11 @@
   }
 
  private:
+  void PaintContents(const PaintInfo&,
+                     const LayoutPoint& paint_offset) const final;
   void PaintReplaced(const PaintInfo&,
                      const LayoutPoint& paint_offset) const final;
+  void Paint(const PaintInfo&) const final;
   PaintInvalidationReason InvalidatePaint(
       const PaintInvalidatorContext&) const final;
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 3e0f3535..f24a7fc 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -2080,8 +2080,10 @@
   // call SetNeedsRepaint to cause re-generation of PaintChunks.
   if (!IsText() && (diff.TransformChanged() || diff.OpacityChanged() ||
                     diff.ZIndexChanged() || diff.FilterChanged() ||
-                    diff.BackdropFilterChanged() || diff.CssClipChanged()))
+                    diff.BackdropFilterChanged() || diff.CssClipChanged() ||
+                    diff.BlendModeChanged())) {
     SetNeedsPaintPropertyUpdate();
+  }
 }
 
 void LayoutObject::StyleWillChange(StyleDifference diff,
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index 9d94425..aeaeab8 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -181,18 +181,6 @@
 static const int kMaxCORSRedirects = 20;
 
 // static
-void ThreadableLoader::LoadResourceSynchronously(
-    ExecutionContext& context,
-    const ResourceRequest& request,
-    ThreadableLoaderClient& client,
-    const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resource_loader_options) {
-  (new ThreadableLoader(context, &client, kLoadSynchronously, options,
-                        resource_loader_options))
-      ->Start(request);
-}
-
-// static
 std::unique_ptr<ResourceRequest>
 ThreadableLoader::CreateAccessControlPreflightRequest(
     const ResourceRequest& request,
@@ -242,31 +230,20 @@
   return CreateAccessControlPreflightRequest(request, nullptr);
 }
 
-// static
-ThreadableLoader* ThreadableLoader::Create(
-    ExecutionContext& context,
-    ThreadableLoaderClient* client,
-    const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resource_loader_options) {
-  return new ThreadableLoader(context, client, kLoadAsynchronously, options,
-                              resource_loader_options);
-}
-
 ThreadableLoader::ThreadableLoader(
     ExecutionContext& context,
     ThreadableLoaderClient* client,
-    BlockingBehavior blocking_behavior,
-    const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resource_loader_options)
+    const ResourceLoaderOptions& resource_loader_options,
+    const base::Optional<TimeDelta>& timeout)
     : client_(client),
       loading_context_(ThreadableLoadingContext::Create(context)),
-      options_(options),
+      timeout_(timeout),
       resource_loader_options_(resource_loader_options),
       out_of_blink_cors_(RuntimeEnabledFeatures::OutOfBlinkCORSEnabled()),
       cors_flag_(false),
       security_origin_(resource_loader_options_.security_origin),
       is_using_data_consumer_handle_(false),
-      async_(blocking_behavior == kLoadAsynchronously),
+      async_(resource_loader_options.synchronous_policy == kRequestAsynchronously),
       request_context_(WebURLRequest::kRequestContextUnspecified),
       fetch_request_mode_(network::mojom::FetchRequestMode::kSameOrigin),
       fetch_credentials_mode_(network::mojom::FetchCredentialsMode::kOmit),
@@ -278,6 +255,8 @@
       redirect_mode_(network::mojom::FetchRedirectMode::kFollow),
       override_referrer_(false) {
   DCHECK(client);
+  // timeout_ should either be base::nullopt to indicate no timeout, or non-zero.
+  DCHECK(!timeout_ || !timeout_->is_zero());
 }
 
 void ThreadableLoader::Start(const ResourceRequest& request) {
@@ -1176,15 +1155,13 @@
   if (!actual_request_.IsNull())
     resource_loader_options.data_buffering_policy = kBufferData;
 
-  TimeDelta timeout =
-      TimeDelta::FromMilliseconds(options_.timeout_milliseconds);
-  if (options_.timeout_milliseconds > 0) {
+  if (timeout_) {
     if (!async_) {
-      request.SetTimeoutInterval(timeout);
+      request.SetTimeoutInterval(*timeout_);
     } else if (!timeout_timer_.IsActive()) {
       // The timer can be active if this is the actual request of a
       // CORS-with-preflight request.
-      timeout_timer_.StartOneShot(timeout, FROM_HERE);
+      timeout_timer_.StartOneShot(*timeout_, FROM_HERE);
     }
   }
 
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.h b/third_party/blink/renderer/core/loader/threadable_loader.h
index e64dee1..b75e734 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.h
+++ b/third_party/blink/renderer/core/loader/threadable_loader.h
@@ -60,59 +60,33 @@
 class ThreadableLoadingContext;
 class ThreadableLoaderClient;
 
-// TODO(japhet): This appears unnecessary. Just take a timout_milliseconds
-// optional param in constructor?
-struct ThreadableLoaderOptions {
-  DISALLOW_NEW();
-  ThreadableLoaderOptions() : timeout_milliseconds(0) {}
-  // When adding members, CrossThreadThreadableLoaderOptionsData should
-  // be updated.
-  unsigned long timeout_milliseconds;
-};
-
 // Useful for doing loader operations from any thread (not threadsafe, just able
 // to run on threads other than the main thread).
 //
-// Arguments common to both loadResourceSynchronously() and create():
-//
-// - ThreadableLoaderOptions argument configures this ThreadableLoader's
-//   behavior.
-//
-// - ResourceLoaderOptions argument will be passed to the FetchParameters
-//   that this ThreadableLoader creates. It can be altered e.g. when
-//   redirect happens.
-class CORE_EXPORT ThreadableLoader
+// Can perform requests either synchronously or asynchronously. Requests are
+// asynchronous by default, and this behavior can be controlled by passing
+// a ResourceLoaderOptions with synchronous_policy == kRequestSynchronously to
+// the constructor.
+// In either case, Start() must be called to actaully begin the request.
+class CORE_EXPORT ThreadableLoader final
     : public GarbageCollectedFinalized<ThreadableLoader>,
       private RawResourceClient {
   USING_GARBAGE_COLLECTED_MIXIN(ThreadableLoader);
 
  public:
-  static void LoadResourceSynchronously(ExecutionContext&,
-                                        const ResourceRequest&,
-                                        ThreadableLoaderClient&,
-                                        const ThreadableLoaderOptions&,
-                                        const ResourceLoaderOptions&);
-
-  // Exposed for testing. Code outside this class should not call this function.
-  static std::unique_ptr<ResourceRequest>
-  CreateAccessControlPreflightRequestForTesting(const ResourceRequest&);
-
-  // This method never returns nullptr.
+  // ThreadableLoaderClient methods are never called before Start() call.
   //
-  // This method must always be followed by start() call.
-  // ThreadableLoaderClient methods are never called before start() call.
-  //
-  // The async loading feature is separated into the create() method and
-  // and the start() method in order to:
+  // Loading is separated into the constructor and the Start() method in order
+  // to:
   // - reduce work done in a constructor
   // - not to ask the users to handle failures in the constructor and other
   //   async failures separately
   //
   // Loading completes when one of the following methods are called:
-  // - didFinishLoading()
-  // - didFail()
-  // - didFailAccessControlCheck()
-  // - didFailRedirectCheck()
+  // - DidFinishLoading()
+  // - DidFail()
+  // - DidFailAccessControlCheck()
+  // - DidFailRedirectCheck()
   // After any of these methods is called, the loader won't call any of the
   // ThreadableLoaderClient methods.
   //
@@ -120,21 +94,25 @@
   // client gets invalid. Also, a user must guarantee that the loading
   // completes before the ThreadableLoader is destructed.
   //
-  // When ThreadableLoader::cancel() is called,
-  // ThreadableLoaderClient::didFail() is called with a ResourceError
-  // with isCancellation() returning true, if any of didFinishLoading()
-  // or didFail.*() methods have not been called yet. (didFail() may be
-  // called with a ResourceError with isCancellation() returning true
+  // When ThreadableLoader::Cancel() is called,
+  // ThreadableLoaderClient::DidFail() is called with a ResourceError
+  // with IsCancellation() returning true, if any of DidFinishLoading()
+  // or DidFail.*() methods have not been called yet. (DidFail() may be
+  // called with a ResourceError with IsCancellation() returning true
   // also for cancellation happened inside the loader.)
   //
   // ThreadableLoaderClient methods may call cancel().
-  static ThreadableLoader* Create(ExecutionContext&,
-                                  ThreadableLoaderClient*,
-                                  const ThreadableLoaderOptions&,
-                                  const ResourceLoaderOptions&);
-
+  ThreadableLoader(ExecutionContext&,
+                   ThreadableLoaderClient*,
+                   const ResourceLoaderOptions&,
+                   const base::Optional<TimeDelta>& timeout);
   ~ThreadableLoader() override;
 
+  // Exposed for testing. Code outside this class should not call this function.
+  static std::unique_ptr<ResourceRequest>
+  CreateAccessControlPreflightRequestForTesting(const ResourceRequest&);
+
+  // Must be called to actually begin the request.
   void Start(const ResourceRequest&);
 
   // A ThreadableLoader may have a timeout specified. It is possible, in some
@@ -158,13 +136,6 @@
 
  private:
   class DetachedClient;
-  enum BlockingBehavior { kLoadSynchronously, kLoadAsynchronously };
-
-  ThreadableLoader(ExecutionContext&,
-                   ThreadableLoaderClient*,
-                   BlockingBehavior,
-                   const ThreadableLoaderOptions&,
-                   const ResourceLoaderOptions&);
 
   static std::unique_ptr<ResourceRequest> CreateAccessControlPreflightRequest(
       const ResourceRequest&,
@@ -245,7 +216,7 @@
   ThreadableLoaderClient* client_;
   Member<ThreadableLoadingContext> loading_context_;
 
-  const ThreadableLoaderOptions options_;
+  const base::Optional<TimeDelta> timeout_;
   // Some items may be overridden by m_forceDoNotAllowStoredCredentials and
   // m_securityOrigin. In such a case, build a ResourceLoaderOptions with
   // up-to-date values from them and this variable, and use it.
diff --git a/third_party/blink/renderer/core/loader/threadable_loader_test.cc b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
index 07fc4f1..805d2cb 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
@@ -195,10 +195,9 @@
   }
 
   void CreateLoader(ThreadableLoaderClient* client) override {
-    ThreadableLoaderOptions options;
     ResourceLoaderOptions resource_loader_options;
-    loader_ = ThreadableLoader::Create(GetDocument(), client, options,
-                                       resource_loader_options);
+    loader_ = new ThreadableLoader(GetDocument(), client,
+                                   resource_loader_options, base::nullopt);
   }
 
   void StartLoader(const ResourceRequest& request) override {
@@ -389,14 +388,13 @@
     DCHECK(worker_thread_);
     DCHECK(worker_thread_->IsCurrentThread());
 
-    ThreadableLoaderOptions options;
     ResourceLoaderOptions resource_loader_options;
 
     // Ensure that ThreadableLoader is created.
     DCHECK(worker_thread_->GlobalScope()->IsWorkerGlobalScope());
 
-    loader_ = ThreadableLoader::Create(*worker_thread_->GlobalScope(), client,
-                                       options, resource_loader_options);
+    loader_ = new ThreadableLoader(*worker_thread_->GlobalScope(), client,
+                                   resource_loader_options, base::nullopt);
     DCHECK(loader_);
     event->Signal();
   }
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
index 191e2407..c3f040d 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
@@ -278,8 +278,10 @@
                                                      rect)) {
         continue;
       }
+      LayoutRect layout_rect = LayoutRect(rect.Rect());
+      layout_rect.Move(-layer.OffsetFromLayoutObject());
       touch_action_rects_in_layer_space.emplace_back(TouchActionRect(
-          LayoutRect(rect.Rect()), touch_action_rect.whitelisted_touch_action));
+          layout_rect, touch_action_rect.whitelisted_touch_action));
     }
   }
   layer.CcLayer()->SetTouchActionRegion(
diff --git a/third_party/blink/renderer/core/paint/embedded_content_painter.cc b/third_party/blink/renderer/core/paint/embedded_content_painter.cc
index e75323dc..ca814c2d 100644
--- a/third_party/blink/renderer/core/paint/embedded_content_painter.cc
+++ b/third_party/blink/renderer/core/paint/embedded_content_painter.cc
@@ -20,12 +20,104 @@
 
 namespace blink {
 
-void EmbeddedContentPainter::PaintReplaced(const PaintInfo& paint_info,
+bool EmbeddedContentPainter::IsSelected() const {
+  SelectionState s = layout_embedded_content_.GetSelectionState();
+  if (s == SelectionState::kNone)
+    return false;
+
+  return true;
+}
+
+void EmbeddedContentPainter::Paint(const PaintInfo& paint_info) {
+  // TODO(crbug.com/797779): For now embedded contents don't know whether
+  // they are painted in a fragmented context and may do something bad in a
+  // fragmented context, e.g. creating subsequences. Skip cache to avoid that.
+  // This will be unnecessary when the contents are fragment aware.
+  base::Optional<DisplayItemCacheSkipper> cache_skipper;
+  DCHECK(layout_embedded_content_.HasLayer());
+  if (layout_embedded_content_.Layer()->EnclosingPaginationLayer())
+    cache_skipper.emplace(paint_info.context);
+
+  AdjustPaintOffsetScope adjustment(layout_embedded_content_, paint_info);
+  const auto& local_paint_info = adjustment.GetPaintInfo();
+  auto paint_offset = adjustment.PaintOffset();
+  if (!ReplacedPainter(layout_embedded_content_)
+           .ShouldPaint(local_paint_info, paint_offset))
+    return;
+
+  if (layout_embedded_content_.HasBoxDecorationBackground() &&
+      (local_paint_info.phase == PaintPhase::kForeground ||
+       local_paint_info.phase == PaintPhase::kSelection)) {
+    BoxPainter(layout_embedded_content_)
+        .PaintBoxDecorationBackground(local_paint_info, paint_offset);
+  }
+
+  if (local_paint_info.phase == PaintPhase::kMask) {
+    BoxPainter(layout_embedded_content_)
+        .PaintMask(local_paint_info, paint_offset);
+    return;
+  }
+
+  if (ShouldPaintSelfOutline(local_paint_info.phase)) {
+    ObjectPainter(layout_embedded_content_)
+        .PaintOutline(local_paint_info, paint_offset);
+  }
+
+  if (local_paint_info.phase != PaintPhase::kForeground)
+    return;
+
+  if (layout_embedded_content_.GetEmbeddedContentView()) {
+    LayoutRect border_rect(paint_offset, layout_embedded_content_.Size());
+    if (border_rect.IsEmpty())
+      return;
+
+    base::Optional<ScopedPaintChunkProperties> scoped_paint_chunk_properties;
+    const auto* fragment =
+        local_paint_info.FragmentToPaint(layout_embedded_content_);
+    if (!fragment)
+      return;
+    const auto* properties = fragment->PaintProperties();
+
+    if (properties && properties->OverflowClip()) {
+      scoped_paint_chunk_properties.emplace(
+          local_paint_info.context.GetPaintController(),
+          properties->OverflowClip(), layout_embedded_content_,
+          DisplayItem::PaintPhaseToDrawingType(local_paint_info.phase));
+    }
+
+    layout_embedded_content_.PaintContents(local_paint_info, paint_offset);
+  }
+
+  // Paint a partially transparent wash over selected EmbeddedContentViews.
+  if (IsSelected() && !local_paint_info.IsPrinting() &&
+      !DrawingRecorder::UseCachedDrawingIfPossible(local_paint_info.context,
+                                                   layout_embedded_content_,
+                                                   local_paint_info.phase)) {
+    LayoutRect rect = layout_embedded_content_.LocalSelectionRect();
+    rect.MoveBy(paint_offset);
+    IntRect selection_rect = PixelSnappedIntRect(rect);
+    DrawingRecorder recorder(local_paint_info.context, layout_embedded_content_,
+                             local_paint_info.phase);
+    Color selection_bg = SelectionPaintingUtils::SelectionBackgroundColor(
+        layout_embedded_content_.GetDocument(),
+        layout_embedded_content_.StyleRef(),
+        layout_embedded_content_.GetNode());
+    local_paint_info.context.FillRect(selection_rect, selection_bg);
+  }
+
+  if (layout_embedded_content_.CanResize()) {
+    ScrollableAreaPainter(
+        *layout_embedded_content_.Layer()->GetScrollableArea())
+        .PaintResizer(local_paint_info.context, RoundedIntPoint(paint_offset),
+                      local_paint_info.GetCullRect());
+  }
+}
+
+void EmbeddedContentPainter::PaintContents(const PaintInfo& paint_info,
                                            const LayoutPoint& paint_offset) {
   EmbeddedContentView* embedded_content_view =
       layout_embedded_content_.GetEmbeddedContentView();
-  if (!embedded_content_view)
-    return;
+  CHECK(embedded_content_view);
 
   IntPoint paint_location(RoundedIntPoint(
       paint_offset +
diff --git a/third_party/blink/renderer/core/paint/embedded_content_painter.h b/third_party/blink/renderer/core/paint/embedded_content_painter.h
index b72a09d..ea101cd 100644
--- a/third_party/blink/renderer/core/paint/embedded_content_painter.h
+++ b/third_party/blink/renderer/core/paint/embedded_content_painter.h
@@ -20,9 +20,12 @@
   EmbeddedContentPainter(const LayoutEmbeddedContent& layout_embedded_content)
       : layout_embedded_content_(layout_embedded_content) {}
 
-  void PaintReplaced(const PaintInfo&, const LayoutPoint& paint_offset);
+  void Paint(const PaintInfo&);
+  void PaintContents(const PaintInfo&, const LayoutPoint& paint_offset);
 
  private:
+  bool IsSelected() const;
+
   const LayoutEmbeddedContent& layout_embedded_content_;
 };
 
diff --git a/third_party/blink/renderer/core/paint/embedded_object_painter.cc b/third_party/blink/renderer/core/paint/embedded_object_painter.cc
index 99c1b77a..2b2790e7 100644
--- a/third_party/blink/renderer/core/paint/embedded_object_painter.cc
+++ b/third_party/blink/renderer/core/paint/embedded_object_painter.cc
@@ -7,7 +7,6 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_object.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
-#include "third_party/blink/renderer/core/paint/embedded_content_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
@@ -38,11 +37,8 @@
 
 void EmbeddedObjectPainter::PaintReplaced(const PaintInfo& paint_info,
                                           const LayoutPoint& paint_offset) {
-  if (!layout_embedded_object_.ShowsUnavailablePluginIndicator()) {
-    EmbeddedContentPainter(layout_embedded_object_)
-        .PaintReplaced(paint_info, paint_offset);
+  if (!layout_embedded_object_.ShowsUnavailablePluginIndicator())
     return;
-  }
 
   if (paint_info.phase == PaintPhase::kSelection)
     return;
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter.cc b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
index a569292..be38ebb 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
@@ -33,6 +33,8 @@
                                       const LayoutPoint& paint_offset) {
   GraphicsContext& context = paint_info.context;
 
+  LayoutRect content_rect = layout_html_canvas_.ContentBoxRect();
+  content_rect.MoveBy(paint_offset);
   LayoutRect paint_rect = layout_html_canvas_.ReplacedContentRect();
   paint_rect.MoveBy(paint_offset);
 
@@ -43,7 +45,7 @@
       canvas->RenderingContext() &&
       canvas->RenderingContext()->IsComposited()) {
     if (cc::Layer* layer = canvas->RenderingContext()->CcLayer()) {
-      IntRect pixel_snapped_rect = PixelSnappedIntRect(paint_rect);
+      IntRect pixel_snapped_rect = PixelSnappedIntRect(content_rect);
       layer->SetBounds(static_cast<gfx::Size>(pixel_snapped_rect.Size()));
       layer->SetIsDrawable(true);
       RecordForeignLayer(
@@ -58,9 +60,22 @@
     return;
 
   DrawingRecorder recorder(context, layout_html_canvas_, paint_info.phase);
-  ScopedInterpolationQuality interpolation_quality_scope(
-      context, InterpolationQualityForCanvas(layout_html_canvas_.StyleRef()));
-  canvas->Paint(context, paint_rect);
+
+  bool clip = !content_rect.Contains(paint_rect);
+  if (clip) {
+    context.Save();
+    // TODO(chrishtr): this should be pixel-snapped.
+    context.Clip(FloatRect(content_rect));
+  }
+
+  {
+    ScopedInterpolationQuality interpolation_quality_scope(
+        context, InterpolationQualityForCanvas(layout_html_canvas_.StyleRef()));
+    canvas->Paint(context, paint_rect);
+  }
+
+  if (clip)
+    context.Restore();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index 3426189e..17dfa59 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -478,7 +478,9 @@
   DCHECK(!PhysicalFragment().IsBlockFlow());
 
   const NGLogicalOffset logical_point = point.ConvertToLogical(
-      Style().GetWritingMode(), Style().Direction(), Size(), NGPhysicalSize());
+      Style().GetWritingMode(), Style().Direction(), Size(),
+      // |point| is actually a pixel with size 1x1.
+      NGPhysicalSize(LayoutUnit(1), LayoutUnit(1)));
   const LayoutUnit inline_point = logical_point.inline_offset;
 
   // Stores the closest child before |point| in the inline direction. Used if we
@@ -541,7 +543,9 @@
   DCHECK(ToNGPhysicalBoxFragment(PhysicalFragment()).ChildrenInline());
 
   const NGLogicalOffset logical_point = point.ConvertToLogical(
-      Style().GetWritingMode(), Style().Direction(), Size(), NGPhysicalSize());
+      Style().GetWritingMode(), Style().Direction(), Size(),
+      // |point| is actually a pixel with size 1x1.
+      NGPhysicalSize(LayoutUnit(1), LayoutUnit(1)));
   const LayoutUnit block_point = logical_point.block_offset;
 
   // Stores the closest line box child above |point| in the block direction.
diff --git a/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc b/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc
index 7b7a39e..30701f66 100644
--- a/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc
+++ b/third_party/blink/renderer/core/paint/object_paint_invalidator_test.cc
@@ -337,11 +337,9 @@
   const auto* graphics_layer = GetLayoutView().Layer()->GraphicsLayerBacking();
   const auto* invalidations =
       &graphics_layer->GetRasterInvalidationTracking()->Invalidations();
-  ASSERT_EQ(2u, invalidations->size());
+  ASSERT_EQ(1u, invalidations->size());
   EXPECT_EQ(IntRect(8, 8, 100, 100), (*invalidations)[0].rect);
   EXPECT_EQ(PaintInvalidationReason::kSelection, (*invalidations)[0].reason);
-  EXPECT_EQ(IntRect(8, 8, 100, 100), (*invalidations)[1].rect);
-  EXPECT_EQ(PaintInvalidationReason::kChunkAppeared, (*invalidations)[1].reason);
   EXPECT_EQ(LayoutRect(8, 8, 100, 100), target->SelectionVisualRect());
   GetDocument().View()->SetTracksPaintInvalidations(false);
 
@@ -361,11 +359,9 @@
   GetDocument().View()->UpdateAllLifecyclePhases();
   invalidations =
       &graphics_layer->GetRasterInvalidationTracking()->Invalidations();
-  ASSERT_EQ(2u, invalidations->size());
+  ASSERT_EQ(1u, invalidations->size());
   EXPECT_EQ(IntRect(8, 8, 100, 100), (*invalidations)[0].rect);
   EXPECT_EQ(PaintInvalidationReason::kSelection, (*invalidations)[0].reason);
-  EXPECT_EQ(IntRect(8, 8, 100, 100), (*invalidations)[1].rect);
-  EXPECT_EQ(PaintInvalidationReason::kChunkDisappeared, (*invalidations)[1].reason);
   EXPECT_EQ(LayoutRect(), target->SelectionVisualRect());
   GetDocument().View()->SetTracksPaintInvalidations(false);
 }
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 5026f51..52f90e7 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -1221,4 +1221,34 @@
                      .X());
 }
 
+TEST_P(PaintPropertyTreeUpdateTest,
+       PropertyTreesRebuiltAfterSVGBlendModeChange) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #blended {
+        mix-blend-mode: darken;
+        fill: red;
+      }
+    </style>
+    <svg width="100" height="100">
+      <rect id="blended" x="0" y="0" width="100" height="100"></rect>
+    </svg>
+  )HTML");
+
+  auto* blended_element = GetDocument().getElementById("blended");
+  ASSERT_TRUE(blended_element);
+  const auto* props =
+      blended_element->GetLayoutObject()->FirstFragment().PaintProperties();
+  ASSERT_TRUE(props->Effect());
+  EXPECT_EQ(props->Effect()->BlendMode(), SkBlendMode::kDarken);
+
+  blended_element->setAttribute(HTMLNames::styleAttr,
+                                "mix-blend-mode: lighten;");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  props = blended_element->GetLayoutObject()->FirstFragment().PaintProperties();
+  ASSERT_TRUE(props->Effect());
+  EXPECT_EQ(props->Effect()->BlendMode(), SkBlendMode::kLighten);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.cc b/third_party/blink/renderer/core/paint/replaced_painter.cc
index 923bb72..820d3ce 100644
--- a/third_party/blink/renderer/core/paint/replaced_painter.cc
+++ b/third_party/blink/renderer/core/paint/replaced_painter.cc
@@ -15,26 +15,13 @@
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/rounded_inner_rect_clipper.h"
-#include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
 #include "third_party/blink/renderer/core/paint/selection_painting_utils.h"
-#include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
 
 namespace blink {
 
 void ReplacedPainter::Paint(const PaintInfo& paint_info) {
-  // TODO(crbug.com/797779): For now embedded contents don't know whether
-  // they are painted in a fragmented context and may do something bad in a
-  // fragmented context, e.g. creating subsequences. Skip cache to avoid that.
-  // This will be unnecessary when the contents are fragment aware.
-  base::Optional<DisplayItemCacheSkipper> cache_skipper;
-  if (layout_replaced_.IsLayoutEmbeddedContent()) {
-    DCHECK(layout_replaced_.HasLayer());
-    if (layout_replaced_.Layer()->EnclosingPaginationLayer())
-      cache_skipper.emplace(paint_info.context);
-  }
-
   AdjustPaintOffsetScope adjustment(layout_replaced_, paint_info);
   const auto& local_paint_info = adjustment.GetPaintInfo();
   auto paint_offset = adjustment.PaintOffset();
@@ -45,7 +32,7 @@
   LayoutRect border_rect(paint_offset, layout_replaced_.Size());
 
   if (ShouldPaintSelfBlockBackground(local_paint_info.phase)) {
-    if (layout_replaced_.StyleRef().Visibility() == EVisibility::kVisible &&
+    if (layout_replaced_.Style()->Visibility() == EVisibility::kVisible &&
         layout_replaced_.HasBoxDecorationBackground()) {
       if (layout_replaced_.HasLayer() &&
           layout_replaced_.Layer()->GetCompositingState() ==
@@ -55,8 +42,8 @@
               ->DrawsBackgroundOntoContentLayer())
         return;
 
-      BoxPainter(layout_replaced_)
-          .PaintBoxDecorationBackground(local_paint_info, paint_offset);
+      layout_replaced_.PaintBoxDecorationBackground(local_paint_info,
+                                                    paint_offset);
     }
     // We're done. We don't bother painting any children.
     if (local_paint_info.phase == PaintPhase::kSelfBlockBackgroundOnly)
@@ -64,7 +51,7 @@
   }
 
   if (local_paint_info.phase == PaintPhase::kMask) {
-    BoxPainter(layout_replaced_).PaintMask(local_paint_info, paint_offset);
+    layout_replaced_.PaintMask(local_paint_info, paint_offset);
     return;
   }
 
@@ -83,37 +70,38 @@
       layout_replaced_.GetSelectionState() == SelectionState::kNone)
     return;
 
-  bool skip_clip = layout_replaced_.IsSVGRoot() &&
-                   !ToLayoutSVGRoot(layout_replaced_).ShouldApplyViewportClip();
-  if (skip_clip || !layout_replaced_.ContentBoxRect().IsEmpty()) {
+  {
     base::Optional<ScopedPaintChunkProperties> chunk_properties;
-    if (const auto* fragment = paint_info.FragmentToPaint(layout_replaced_)) {
-      if (const auto* paint_properties = fragment->PaintProperties()) {
-        // Check filter for optimized image policy violation highlights, which
-        // may be applied locally.
-        if (paint_properties->Filter() &&
-            (!layout_replaced_.HasLayer() ||
-             !layout_replaced_.Layer()->IsSelfPaintingLayer())) {
-          chunk_properties.emplace(
-              local_paint_info.context.GetPaintController(),
-              fragment->ContentsProperties(), layout_replaced_,
-              paint_info.DisplayItemTypeForClipping());
-        } else if (paint_properties->OverflowClip()) {
-          chunk_properties.emplace(
-              local_paint_info.context.GetPaintController(),
-              paint_properties->OverflowClip(), layout_replaced_,
-              paint_info.DisplayItemTypeForClipping());
+    bool completely_clipped_out = false;
+
+    if (layout_replaced_.Style()->HasBorderRadius() && border_rect.IsEmpty())
+      completely_clipped_out = true;
+
+    if (!layout_replaced_.IsSVGRoot()) {
+      if (const auto* fragment = paint_info.FragmentToPaint(layout_replaced_)) {
+        if (const auto* paint_properties = fragment->PaintProperties()) {
+          // Check filter for optimized image policy violation highlights, which
+          // may be applied locally.
+          if (paint_properties->Filter() &&
+              (!layout_replaced_.HasLayer() ||
+               !layout_replaced_.Layer()->IsSelfPaintingLayer())) {
+            chunk_properties.emplace(
+                local_paint_info.context.GetPaintController(),
+                fragment->ContentsProperties(), layout_replaced_,
+                DisplayItem::PaintPhaseToDrawingType(local_paint_info.phase));
+          } else if (layout_replaced_.Style()->HasBorderRadius()) {
+            DCHECK(paint_properties->OverflowClip());
+            chunk_properties.emplace(
+                local_paint_info.context.GetPaintController(),
+                paint_properties->OverflowClip(), layout_replaced_,
+                DisplayItem::PaintPhaseToDrawingType(local_paint_info.phase));
+          }
         }
       }
     }
 
-    layout_replaced_.PaintReplaced(local_paint_info, paint_offset);
-  }
-
-  if (layout_replaced_.CanResize()) {
-    ScrollableAreaPainter(*layout_replaced_.Layer()->GetScrollableArea())
-        .PaintResizer(local_paint_info.context, RoundedIntPoint(paint_offset),
-                      local_paint_info.GetCullRect());
+    if (!completely_clipped_out)
+      layout_replaced_.PaintReplaced(local_paint_info, paint_offset);
   }
 
   // The selection tint never gets clipped by border-radius rounding, since we
diff --git a/third_party/blink/renderer/core/paint/svg_root_painter.cc b/third_party/blink/renderer/core/paint/svg_root_painter.cc
index a4976a0a..448e42ae 100644
--- a/third_party/blink/renderer/core/paint/svg_root_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_root_painter.cc
@@ -7,6 +7,7 @@
 #include "base/optional.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
+#include "third_party/blink/renderer/core/paint/box_clipper.h"
 #include "third_party/blink/renderer/core/paint/box_painter.h"
 #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
@@ -50,6 +51,13 @@
   if (svg->HasEmptyViewBox())
     return;
 
+  // Apply initial viewport clip.
+  base::Optional<BoxClipper> box_clipper;
+  if (layout_svg_root_.ShouldApplyViewportClip()) {
+    // TODO(pdr): Clip the paint info cull rect here.
+    box_clipper.emplace(layout_svg_root_, paint_info);
+  }
+
   PaintInfo paint_info_before_filtering(paint_info);
   AffineTransform transform_to_border_box =
       TransformToPixelSnappedBorderBox(paint_offset);
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 16bb9ee2..c6aa9af24 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -820,6 +820,9 @@
       (has_clip && Clip() != other.Clip()))
     diff.SetCSSClipChanged();
 
+  if (GetBlendMode() != other.GetBlendMode())
+    diff.SetBlendModeChanged();
+
   if (HasCurrentTransformAnimation() != other.HasCurrentTransformAnimation() ||
       HasCurrentOpacityAnimation() != other.HasCurrentOpacityAnimation() ||
       HasCurrentFilterAnimation() != other.HasCurrentFilterAnimation() ||
diff --git a/third_party/blink/renderer/core/style/style_difference.cc b/third_party/blink/renderer/core/style/style_difference.cc
index b59d843..a4869fa5 100644
--- a/third_party/blink/renderer/core/style/style_difference.cc
+++ b/third_party/blink/renderer/core/style/style_difference.cc
@@ -72,6 +72,9 @@
         case StyleDifference::kTextDecorationOrColorChanged:
           out << "TextDecorationOrColorChanged";
           break;
+        case StyleDifference::kBlendModeChanged:
+          out << "BlendModeChanged";
+          break;
         default:
           NOTREACHED();
           break;
diff --git a/third_party/blink/renderer/core/style/style_difference.h b/third_party/blink/renderer/core/style/style_difference.h
index a327caef..356d6810 100644
--- a/third_party/blink/renderer/core/style/style_difference.h
+++ b/third_party/blink/renderer/core/style/style_difference.h
@@ -26,6 +26,7 @@
     // The object needs to issue paint invalidations if it is affected by text
     // decorations or properties dependent on color (e.g., border or outline).
     kTextDecorationOrColorChanged = 1 << 6,
+    kBlendModeChanged = 1 << 7,
     // If you add a value here, be sure to update kPropertyDifferenceCount.
   };
 
@@ -131,6 +132,13 @@
     property_specific_differences_ |= kCSSClipChanged;
   }
 
+  bool BlendModeChanged() const {
+    return property_specific_differences_ & kBlendModeChanged;
+  }
+  void SetBlendModeChanged() {
+    property_specific_differences_ |= kBlendModeChanged;
+  }
+
   bool TextDecorationOrColorChanged() const {
     return property_specific_differences_ & kTextDecorationOrColorChanged;
   }
@@ -148,7 +156,7 @@
   void SetCompositingReasonsChanged() { composited_reasons_changed_ = true; }
 
  private:
-  static constexpr int kPropertyDifferenceCount = 7;
+  static constexpr int kPropertyDifferenceCount = 8;
 
   friend CORE_EXPORT std::ostream& operator<<(std::ostream&,
                                               const StyleDifference&);
diff --git a/third_party/blink/renderer/core/style/style_difference_test.cc b/third_party/blink/renderer/core/style/style_difference_test.cc
index 669a39be..349b863 100644
--- a/third_party/blink/renderer/core/style/style_difference_test.cc
+++ b/third_party/blink/renderer/core/style/style_difference_test.cc
@@ -49,6 +49,7 @@
   diff.SetBackdropFilterChanged();
   diff.SetCSSClipChanged();
   diff.SetTextDecorationOrColorChanged();
+  diff.SetBlendModeChanged();
   string_stream << diff;
   EXPECT_EQ(
       "StyleDifference{layoutType=NoLayout, "
@@ -56,7 +57,8 @@
       "visualRectUpdate=0, "
       "propertySpecificDifferences=TransformChanged|OpacityChanged|"
       "ZIndexChanged|FilterChanged|BackdropFilterChanged|CSSClipChanged|"
-      "TextDecorationOrColorChanged, scrollAnchorDisablingPropertyChanged=0}",
+      "TextDecorationOrColorChanged|BlendModeChanged, "
+      "scrollAnchorDisablingPropertyChanged=0}",
       string_stream.str());
 }
 
diff --git a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
index 29686dd..334800af 100644
--- a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
+++ b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
@@ -51,7 +51,7 @@
     : response_address_space_(mojom::IPAddressSpace::kPublic) {}
 
 WorkerClassicScriptLoader::~WorkerClassicScriptLoader() {
-  // If |m_threadableLoader| is still working, we have to cancel it here.
+  // If |threadable_loader_| is still working, we have to cancel it here.
   // Otherwise DidFail() of the deleted |this| will be called from
   // ThreadableLoader::NotifyFinished() when the frame will be
   // destroyed.
@@ -75,14 +75,14 @@
 
   SECURITY_DCHECK(execution_context.IsWorkerGlobalScope());
 
-  ThreadableLoaderOptions options;
-
   ResourceLoaderOptions resource_loader_options;
   resource_loader_options.parser_disposition =
       ParserDisposition::kNotParserInserted;
+  resource_loader_options.synchronous_policy = kRequestSynchronously;
 
-  ThreadableLoader::LoadResourceSynchronously(execution_context, request, *this,
-                                              options, resource_loader_options);
+  threadable_loader_ = new ThreadableLoader(
+      execution_context, this, resource_loader_options, base::nullopt);
+  threadable_loader_->Start(request);
 }
 
 void WorkerClassicScriptLoader::LoadAsynchronously(
@@ -108,8 +108,6 @@
   request.SetFetchRequestMode(fetch_request_mode);
   request.SetFetchCredentialsMode(fetch_credentials_mode);
 
-  ThreadableLoaderOptions options;
-
   ResourceLoaderOptions resource_loader_options;
 
   // During create, callbacks may happen which could remove the last reference
@@ -118,8 +116,8 @@
   // (E.g. see crbug.com/524694 for why we can't easily remove this protect)
   scoped_refptr<WorkerClassicScriptLoader> protect(this);
   need_to_cancel_ = true;
-  threadable_loader_ = ThreadableLoader::Create(
-      execution_context, this, options, resource_loader_options);
+  threadable_loader_ = new ThreadableLoader(
+      execution_context, this, resource_loader_options, base::nullopt);
   threadable_loader_->Start(request);
   if (failed_)
     NotifyFinished();
@@ -219,12 +217,12 @@
 
 void WorkerClassicScriptLoader::NotifyError() {
   failed_ = true;
-  // notifyError() could be called before ThreadableLoader::create() returns
-  // e.g. from didFail(), and in that case m_threadableLoader is not yet set
+  // NotifyError() could be called before ThreadableLoader::Create() returns
+  // e.g. from DidFail(), and in that case threadable_loader_ is not yet set
   // (i.e. still null).
-  // Since the callback invocation in notifyFinished() potentially delete
+  // Since the callback invocation in NotifyFinished() potentially delete
   // |this| object, the callback invocation should be postponed until the
-  // create() call returns. See loadAsynchronously() for the postponed call.
+  // create() call returns. See LoadAsynchronously() for the postponed call.
   if (threadable_loader_)
     NotifyFinished();
 }
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index bebe41442..16e76d30 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -477,7 +477,10 @@
     return;
   }
 
-  timeout_milliseconds_ = timeout;
+  if (timeout)
+    timeout_ = TimeDelta::FromMilliseconds(timeout);
+  else
+    timeout_ = base::nullopt;
 
   // From http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute:
   // Note: This implies that the timeout attribute can be set while fetching is
@@ -686,7 +689,7 @@
     }
 
     // Similarly, timeouts are disabled for synchronous requests as well.
-    if (timeout_milliseconds_ > 0) {
+    if (timeout_) {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kInvalidAccessError,
           "Synchronous requests must not set a timeout.");
@@ -1074,9 +1077,6 @@
   if (request_headers_.size() > 0)
     request.AddHTTPHeaderFields(request_headers_);
 
-  ThreadableLoaderOptions options;
-  options.timeout_milliseconds = timeout_milliseconds_;
-
   ResourceLoaderOptions resource_loader_options;
   resource_loader_options.security_origin = GetSecurityOrigin();
   resource_loader_options.initiator_info.name =
@@ -1124,8 +1124,8 @@
     // TODO(yhirano): Turn this CHECK into DCHECK: see https://crbug.com/570946.
     CHECK(!loader_);
     DCHECK(send_flag_);
-    loader_ = ThreadableLoader::Create(execution_context, this, options,
-                                       resource_loader_options);
+    loader_ = new ThreadableLoader(execution_context, this,
+                                   resource_loader_options, timeout_);
     loader_->Start(request);
 
     return;
@@ -1143,8 +1143,11 @@
       syncxhr_pagedismissal_histogram.Count(pagedismissal);
     }
   }
-  ThreadableLoader::LoadResourceSynchronously(execution_context, request, *this,
-                                              options, resource_loader_options);
+
+  resource_loader_options.synchronous_policy = kRequestSynchronously;
+  loader_ = new ThreadableLoader(execution_context, this,
+                                 resource_loader_options, timeout_);
+  loader_->Start(request);
 
   ThrowForLoadFailureIfNeeded(exception_state, String());
 }
@@ -1223,7 +1226,7 @@
   if (!loader_)
     return true;
 
-  // Cancelling the ThreadableLoader m_loader may result in calling
+  // Cancelling the ThreadableLoader loader_ may result in calling
   // window.onload synchronously. If such an onload handler contains open()
   // call on the same XMLHttpRequest object, reentry happens.
   //
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
index 479cac9..c1dc5a7 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h
@@ -151,7 +151,7 @@
   Document* responseXML(ExceptionState&);
   Blob* ResponseBlob();
   DOMArrayBuffer* ResponseArrayBuffer();
-  unsigned timeout() const { return timeout_milliseconds_; }
+  unsigned timeout() const { return timeout_ ? timeout_->InMilliseconds() : 0; }
   void setTimeout(unsigned timeout, ExceptionState&);
   ResponseTypeCode GetResponseTypeCode() const { return response_type_code_; }
   String responseType();
@@ -311,7 +311,7 @@
   // Not converted to ASCII lowercase. Must be lowered later or compared
   // using case insensitive comparison functions if needed.
   AtomicString mime_type_override_;
-  unsigned long timeout_milliseconds_ = 0;
+  base::Optional<TimeDelta> timeout_;
   TraceWrapperMember<Blob> response_blob_;
 
   Member<ThreadableLoader> loader_;
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
index 9e10d8a..5b3612f 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
@@ -73,8 +73,7 @@
 
   icon_callback_ = std::move(icon_callback);
 
-  ThreadableLoaderOptions threadable_loader_options;
-  threadable_loader_options.timeout_milliseconds = kIconFetchTimeoutInMs;
+  TimeDelta timeout = TimeDelta::FromMilliseconds(kIconFetchTimeoutInMs);
 
   ResourceLoaderOptions resource_loader_options;
   if (execution_context->IsWorkerGlobalScope())
@@ -85,9 +84,8 @@
   resource_request.SetPriority(ResourceLoadPriority::kMedium);
   resource_request.SetRequestorOrigin(execution_context->GetSecurityOrigin());
 
-  threadable_loader_ = ThreadableLoader::Create(*execution_context, this,
-                                                threadable_loader_options,
-                                                resource_loader_options);
+  threadable_loader_ = new ThreadableLoader(*execution_context, this,
+                                            resource_loader_options, timeout);
 
   threadable_loader_->Start(resource_request);
 }
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.cc b/third_party/blink/renderer/modules/eventsource/event_source.cc
index 92ed79a..bfa75488 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.cc
+++ b/third_party/blink/renderer/modules/eventsource/event_source.cc
@@ -151,17 +151,13 @@
 
   const SecurityOrigin* origin = execution_context.GetSecurityOrigin();
 
-  ThreadableLoaderOptions options;
-
   ResourceLoaderOptions resource_loader_options;
   resource_loader_options.data_buffering_policy = kDoNotBufferData;
   resource_loader_options.security_origin = origin;
 
   probe::willSendEventSourceRequest(&execution_context, this);
-  // probe::documentThreadableLoaderStartedLoadingForClient
-  // will be called synchronously.
-  loader_ = ThreadableLoader::Create(execution_context, this, options,
-                                     resource_loader_options);
+  loader_ = new ThreadableLoader(execution_context, this,
+                                 resource_loader_options, base::nullopt);
   loader_->Start(request);
 }
 
diff --git a/third_party/blink/renderer/modules/notifications/notification_image_loader.cc b/third_party/blink/renderer/modules/notifications/notification_image_loader.cc
index 164d95d5..85a4765e 100644
--- a/third_party/blink/renderer/modules/notifications/notification_image_loader.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_image_loader.cc
@@ -106,8 +106,7 @@
   start_time_ = CurrentTimeTicks();
   image_callback_ = std::move(image_callback);
 
-  ThreadableLoaderOptions threadable_loader_options;
-  threadable_loader_options.timeout_milliseconds = kImageFetchTimeoutInMs;
+  TimeDelta timeout = TimeDelta::FromMilliseconds(kImageFetchTimeoutInMs);
 
   // TODO(mvanouwerkerk): Add an entry for notifications to
   // FetchInitiatorTypeNames and use it.
@@ -120,8 +119,8 @@
   resource_request.SetPriority(ResourceLoadPriority::kMedium);
   resource_request.SetRequestorOrigin(context->GetSecurityOrigin());
 
-  threadable_loader_ = ThreadableLoader::Create(
-      *context, this, threadable_loader_options, resource_loader_options);
+  threadable_loader_ = new ThreadableLoader(
+      *context, this, resource_loader_options, timeout);
   threadable_loader_->Start(resource_request);
 }
 
diff --git a/third_party/blink/renderer/platform/ukm_time_aggregator.cc b/third_party/blink/renderer/platform/ukm_time_aggregator.cc
index 9a4dd17..3dab37c 100644
--- a/third_party/blink/renderer/platform/ukm_time_aggregator.cc
+++ b/third_party/blink/renderer/platform/ukm_time_aggregator.cc
@@ -31,7 +31,7 @@
 }
 
 UkmTimeAggregator::ScopedUkmTimer::~ScopedUkmTimer() {
-  if (aggregator_) {
+  if (aggregator_ && base::TimeTicks::IsHighResolution()) {
     aggregator_->RecordSample(metric_index_, start_time_, CurrentTimeTicks(),
                               histogram_counter_);
   }
diff --git a/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc b/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc
index c6f7f45..99ad4f02 100644
--- a/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc
+++ b/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc
@@ -21,6 +21,12 @@
 const char* kMetric2WorstCase = "Paint.WorstCase";
 
 TEST(UkmTimeAggregatorTest, EmptyEventsNotRecorded) {
+  // Although the tests use a mock clock, the UKM aggregator checks if the
+  // system has a high resolution clock before recording results. As a result,
+  // the tests will fail if the system does not have a high resolution clock.
+  if (!base::TimeTicks::IsHighResolution())
+    return;
+
   WTF::ScopedMockClock clock;
   ukm::TestUkmRecorder recorder;
   int64_t source_id = ukm::UkmRecorder::GetNewSourceID();
@@ -35,6 +41,12 @@
 }
 
 TEST(UkmTimeAggregatorTest, EventsRecordedPerSecond) {
+  // Although the tests use a mock clock, the UKM aggregator checks if the
+  // system has a high resolution clock before recording results. As a result,
+  // the tests will fail if the system does not have a high resolution clock.
+  if (!base::TimeTicks::IsHighResolution())
+    return;
+
   WTF::ScopedMockClock clock;
   ukm::TestUkmRecorder recorder;
   int64_t source_id = ukm::UkmRecorder::GetNewSourceID();
@@ -83,6 +95,12 @@
 }
 
 TEST(UkmTimeAggregatorTest, EventsAveragedCorrectly) {
+  // Although the tests use a mock clock, the UKM aggregator checks if the
+  // system has a high resolution clock before recording results. As a result,
+  // the tests will fail if the system does not have a high resolution clock.
+  if (!base::TimeTicks::IsHighResolution())
+    return;
+
   WTF::ScopedMockClock clock;
   ukm::TestUkmRecorder recorder;
   int64_t source_id = ukm::UkmRecorder::GetNewSourceID();
diff --git a/third_party/blink/renderer/platform/weborigin/kurl_test.cc b/third_party/blink/renderer/platform/weborigin/kurl_test.cc
index bafdb25..47df890a 100644
--- a/third_party/blink/renderer/platform/weborigin/kurl_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/kurl_test.cc
@@ -248,10 +248,12 @@
   EXPECT_EQ(String(kDecodedExpected, arraysize(kDecodedExpected)), decoded);
 
   // Test the error behavior for invalid UTF-8 (we differ from WebKit here).
+  // %e4 %a0 are invalid for UTF-8, but %e5%a5%bd is valid.
   String invalid = DecodeURLEscapeSequences("%e4%a0%e5%a5%bd");
-  UChar invalid_expected_helper[4] = {0x00e4, 0x00a0, 0x597d, 0};
+  UChar invalid_expected_helper[6] = {0x00e4, 0x00a0, 0x00e5,
+                                      0x00a5, 0x00bd, 0};
   String invalid_expected(
-      reinterpret_cast<const ::UChar*>(invalid_expected_helper), 3);
+      reinterpret_cast<const ::UChar*>(invalid_expected_helper), 5);
   EXPECT_EQ(invalid_expected, invalid);
 }
 
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index 084e2c6..808cd47 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -27,6 +27,7 @@
 
 2. Generate the config files:
     ./cmake_update.sh
+    # See prerequisites in file comments.
 
   This will also update this file with the new revision.
 
diff --git a/third_party/libaom/cmake_update.sh b/third_party/libaom/cmake_update.sh
index 36dd847..538e39aa 100755
--- a/third_party/libaom/cmake_update.sh
+++ b/third_party/libaom/cmake_update.sh
@@ -25,7 +25,7 @@
 #  -lib32stdc++-7-dev
 # Alternatively: treat 32bit builds like Windows and manually tweak aom_config.h
 
-set -e
+set -eE
 
 # sort() consistently.
 export LC_ALL=C
@@ -64,7 +64,7 @@
 # $1 - Header file directory.
 # $2 - cmake options.
 function gen_config_files {
-  cmake "${SRC}" ${2} &> /dev/null
+  cmake "${SRC}" ${2} &> cmake.txt
 
   case "${1}" in
     *x64*|*ia32*)
@@ -98,9 +98,16 @@
 EOF
 }
 
+# Scope 'trap' error reporting to configuration generation.
+(
 TMP=$(mktemp -d "${BASE}/build.XXXX")
 cd "${TMP}"
 
+trap '{
+  [ -f ${TMP}/cmake.txt ] && cat ${TMP}/cmake.txt
+  echo "Build directory ${TMP} not removed automatically."
+}' ERR
+
 all_platforms="-DCONFIG_SIZE_LIMIT=1"
 all_platforms+=" -DDECODE_HEIGHT_LIMIT=16384 -DDECODE_WIDTH_LIMIT=16384"
 all_platforms+=" -DCONFIG_AV1_ENCODER=0"
@@ -163,6 +170,7 @@
 
 reset_dirs linux/arm64
 gen_config_files linux/arm64 "${toolchain}/arm64-linux-gcc.cmake ${all_platforms}"
+)
 
 cd "${SRC}"
 update_readme
diff --git a/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c b/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c
index 16bef1e3..6338a2a 100644
--- a/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c
+++ b/third_party/tcmalloc/chromium/src/windows/addr2line-pdb.c
@@ -57,8 +57,8 @@
 #define WEBSYM "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols"
 
 void usage() {
-  fprintf(stderr, "usage: "
-          "addr2line-pdb [-f|--functions] [-C|--demangle] [-e filename]\n");
+  fprintf(stderr, "usage: addr2line-pdb "
+          "[-f|--functions] [-C|--demangle] [-e|--exe filename]\n");
   fprintf(stderr, "(Then list the hex addresses on stdin, one per line)\n");
 }
 
@@ -81,7 +81,8 @@
     } else if (strcmp(argv[i], "--demangle") == 0 ||
                strcmp(argv[i], "-C") == 0) {
       symopts |= SYMOPT_UNDNAME;
-    } else if (strcmp(argv[i], "-e") == 0) {
+    } else if (strcmp(argv[i], "--exe") == 0 ||
+               strcmp(argv[i], "-e") == 0) {
       if (i + 1 >= argc) {
         fprintf(stderr, "FATAL ERROR: -e must be followed by a filename\n");
         return 1;
@@ -141,7 +142,7 @@
     /* GNU addr2line seems to just do a strtol and ignore any
      * weird characters it gets, so we will too.
      */
-    unsigned __int64 addr = _strtoui64(buf, NULL, 16);
+    unsigned __int64 reladdr = _strtoui64(buf, NULL, 16);
     ULONG64 buffer[(sizeof(SYMBOL_INFO) +
                     MAX_SYM_NAME*sizeof(TCHAR) +
                     sizeof(ULONG64) - 1)
@@ -149,17 +150,25 @@
     PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
     IMAGEHLP_LINE64 line;
     DWORD dummy;
+
+    // Just ignore overflow. In an overflow scenario, the resulting address
+    // will be lower than module_base which hasn't been mapped by any prior
+    // SymLoadModuleEx() command. This will cause SymFromAddr() and
+    // SymGetLineFromAddr64() both to return failures and print the correct
+    // ?? and ??:0 message variant.
+    ULONG64 absaddr = reladdr + module_base;
+
     pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
     pSymbol->MaxNameLen = MAX_SYM_NAME;
     if (print_function_name) {
-      if (SymFromAddr(process, (DWORD64)addr, NULL, pSymbol)) {
+      if (SymFromAddr(process, (DWORD64)absaddr, NULL, pSymbol)) {
         printf("%s\n", pSymbol->Name);
       } else {
         printf("??\n");
       }
     }
     line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-    if (SymGetLineFromAddr64(process, (DWORD64)addr, &dummy, &line)) {
+    if (SymGetLineFromAddr64(process, (DWORD64)absaddr, &dummy, &line)) {
       printf("%s:%d\n", line.FileName, (int)line.LineNumber);
     } else {
       printf("??:0\n");
diff --git a/tools/grit/grit/format/html_inline.py b/tools/grit/grit/format/html_inline.py
index 3c8e21b..59ff535f 100755
--- a/tools/grit/grit/format/html_inline.py
+++ b/tools/grit/grit/format/html_inline.py
@@ -367,6 +367,7 @@
     if names_only:
       inlined_files.update(GetResourceFilenames(
           filepath,
+          grd_node,
           allow_external_script,
           rewrite_function,
           filename_expansion_function=filename_expansion_function))
@@ -555,6 +556,7 @@
 
 
 def GetResourceFilenames(filename,
+                         grd_node,
                          allow_external_script=False,
                          rewrite_function=None,
                          filename_expansion_function=None):
@@ -562,7 +564,7 @@
   try:
     return DoInline(
         filename,
-        None,
+        grd_node,
         names_only=True,
         preprocess_only=False,
         allow_external_script=allow_external_script,
diff --git a/tools/grit/grit/format/html_inline_unittest.py b/tools/grit/grit/format/html_inline_unittest.py
index 062296c..5040fa3 100755
--- a/tools/grit/grit/format/html_inline_unittest.py
+++ b/tools/grit/grit/format/html_inline_unittest.py
@@ -75,7 +75,8 @@
     for filename in files:
       source_resources.add(tmp_dir.GetPath(filename))
 
-    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'))
+    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
+                                                 None)
     resources.add(tmp_dir.GetPath('index.html'))
     self.failUnlessEqual(resources, source_resources)
     tmp_dir.CleanUp()
@@ -101,7 +102,7 @@
     tmp_dir = util.TempDir(files)
 
     with self.assertRaises(Exception) as cm:
-      html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'))
+      html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'), None)
     self.failUnlessEqual(cm.exception.message, 'Unmatched </if>')
     tmp_dir.CleanUp()
 
@@ -119,7 +120,8 @@
     for filename in files:
       source_resources.add(tmp_dir.GetPath(filename))
 
-    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.js'))
+    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.js'),
+                                                 None)
     resources.add(tmp_dir.GetPath('index.js'))
     self.failUnlessEqual(resources, source_resources)
     tmp_dir.CleanUp()
@@ -266,7 +268,8 @@
     for filename in files:
       source_resources.add(tmp_dir.GetPath(filename))
 
-    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'))
+    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
+                                                 None)
     resources.add(tmp_dir.GetPath('index.html'))
     self.failUnlessEqual(resources, source_resources)
     tmp_dir.CleanUp()
@@ -560,6 +563,82 @@
     self.failUnlessEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
 
+  def testConditionalInclude(self):
+    '''Tests that output and dependency generation includes only files not'''\
+        ''' blocked by  <if> macros.'''
+
+    files = {
+      'index.html': '''
+      <html>
+      <if expr="True">
+        <img src="img1.png" srcset="img2.png 1x, img3.png 2x">
+      </if>
+      <if expr="False">
+        <img src="img4.png" srcset=" img5.png 1x, img6.png 2x ">
+      </if>
+      <if expr="True">
+        <img src="chrome://theme/img11.png" srcset="img7.png 1x, '''\
+            '''chrome://theme/img13.png 2x">
+      </if>
+      <img srcset="img8.png 300w, img9.png 11E-2w,img10.png -1e2w">
+      </html>
+      ''',
+      'img1.png': '''a1''',
+      'img2.png': '''a2''',
+      'img3.png': '''a3''',
+      'img4.png': '''a4''',
+      'img5.png': '''a5''',
+      'img6.png': '''a6''',
+      'img7.png': '''a7''',
+      'img8.png': '''a8''',
+      'img9.png': '''a9''',
+      'img10.png': '''a10''',
+    }
+
+    expected_inlined = '''
+      <html>
+      <img src="data:image/png;base64,YTE=" srcset="data:image/png;base64,'''\
+          '''YTI= 1x,data:image/png;base64,YTM= 2x">
+      <img src="chrome://theme/img11.png" srcset="data:image/png;base64,'''\
+          '''YTc= 1x,chrome://theme/img13.png 2x">
+      <img srcset="data:image/png;base64,YTg= 300w,data:image/png;base64,'''\
+          '''YTk= 11E-2w,data:image/png;base64,YTEw -1e2w">
+      </html>
+      '''
+
+    expected_files = [
+      'index.html',
+      'img1.png',
+      'img2.png',
+      'img3.png',
+      'img7.png',
+      'img8.png',
+      'img9.png',
+      'img10.png'
+    ]
+
+    source_resources = set()
+    tmp_dir = util.TempDir(files)
+    for filename in expected_files:
+      source_resources.add(tmp_dir.GetPath(filename))
+
+    class FakeGrdNode(object):
+      def EvaluateCondition(self, cond):
+        return eval(cond)
+
+    # Test normal inlining.
+    result = html_inline.DoInline(
+        tmp_dir.GetPath('index.html'),
+        FakeGrdNode())
+    resources = result.inlined_files
+    resources.add(tmp_dir.GetPath('index.html'))
+    self.failUnlessEqual(resources, source_resources)
+
+    # ignore whitespace
+    expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
+    actually_inlined = re.sub(r'\s+', ' ',
+                              util.FixLineEnd(result.inlined_data, '\n'))
+    self.failUnlessEqual(expected_inlined, actually_inlined);
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/grit/grit/gather/chrome_html.py b/tools/grit/grit/gather/chrome_html.py
index a215647..a0bd999 100755
--- a/tools/grit/grit/gather/chrome_html.py
+++ b/tools/grit/grit/gather/chrome_html.py
@@ -320,6 +320,7 @@
     if self.flatten_html_:
       return html_inline.GetResourceFilenames(
           self.grd_node.ToRealPath(self.GetInputPath()),
+          self.grd_node,
           allow_external_script=self.allow_external_script_,
           rewrite_function=lambda fp, t, d: ProcessImageSets(
               fp, t, self.scale_factors_, d,
diff --git a/tools/grit/grit/node/include.py b/tools/grit/grit/node/include.py
index 3538cc7..f7a51ed 100755
--- a/tools/grit/grit/node/include.py
+++ b/tools/grit/grit/node/include.py
@@ -116,6 +116,7 @@
     allow_external_script = self.attrs['allowexternalscript'] == 'true'
     return grit.format.html_inline.GetResourceFilenames(
          self.ToRealPath(self.GetInputPath()),
+         self,
          allow_external_script=allow_external_script)
 
   def IsResourceMapSource(self):
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index 0afe4b3..f8eafcd 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -409,7 +409,7 @@
       grit_info_args = [
                          "--inputs",
                          source_path,
-                       ] + grit_flags + grit_defines
+                       ] + grit_flags + grit_defines + define_args
 
       # Only call exec_script when the user has explicitly opted into greater
       # precision at the expense of performance.
diff --git a/tools/json_schema_compiler/json_schema_api.gni b/tools/json_schema_compiler/json_schema_api.gni
index 46cb996f1..1d14f6b 100644
--- a/tools/json_schema_compiler/json_schema_api.gni
+++ b/tools/json_schema_compiler/json_schema_api.gni
@@ -58,11 +58,26 @@
 #   If any deps are specified they will be inherited by the static library
 #   target.
 #
-# generate_static_library [optional, defaults to false]
-#   Produces a static library instead of a source_set.
-#
-# The generated library target also inherits the visibility and output_name
-# of its invoker.
+# visibility [optional]
+#   A specific visibility to apply for the generated static library. If omitted,
+#   visibility will be inherited from the invoker.
+
+# NOTE: Common variables here for when multiple templates use them.
+compiler_root = "//tools/json_schema_compiler"
+compiler_script = "$compiler_root/compiler.py"
+compiler_sources = [
+  "$compiler_root/cc_generator.py",
+  "$compiler_root/code.py",
+  "$compiler_root/compiler.py",
+  "$compiler_root/cpp_bundle_generator.py",
+  "$compiler_root/cpp_generator.py",
+  "$compiler_root/cpp_type_generator.py",
+  "$compiler_root/cpp_util.py",
+  "$compiler_root/h_generator.py",
+  "$compiler_root/idl_schema.py",
+  "$compiler_root/model.py",
+  "$compiler_root/util_cc_helper.py",
+]
 
 template("json_schema_api") {
   assert(defined(invoker.sources),
@@ -80,34 +95,13 @@
     schema_include_rules = invoker.schema_include_rules
   }
 
-  # Keep a copy of the target_name here since it will be trampled
-  # in nested targets.
-  target_visibility = [ ":$target_name" ]
-
   generated_config_name = target_name + "_generated_config"
   config(generated_config_name) {
     include_dirs = [ root_gen_dir ]
-    visibility = target_visibility
   }
 
   root_namespace = invoker.root_namespace
 
-  compiler_root = "//tools/json_schema_compiler"
-  compiler_script = "$compiler_root/compiler.py"
-  compiler_sources = [
-    "$compiler_root/cc_generator.py",
-    "$compiler_root/code.py",
-    "$compiler_root/compiler.py",
-    "$compiler_root/cpp_bundle_generator.py",
-    "$compiler_root/cpp_generator.py",
-    "$compiler_root/cpp_type_generator.py",
-    "$compiler_root/cpp_util.py",
-    "$compiler_root/h_generator.py",
-    "$compiler_root/idl_schema.py",
-    "$compiler_root/model.py",
-    "$compiler_root/util_cc_helper.py",
-  ]
-
   if (schemas) {
     schema_generator_name = target_name + "_schema_generator"
     action_foreach(schema_generator_name) {
@@ -126,11 +120,6 @@
         "--generator=cpp",
         "--include-rules=$schema_include_rules",
       ]
-
-      if (defined(invoker.visibility)) {
-        # If visibility is restricted, add our own target to it.
-        visibility = invoker.visibility + target_visibility
-      }
     }
   }
 
@@ -248,8 +237,5 @@
     if (defined(invoker.visibility)) {
       visibility = invoker.visibility
     }
-    if (defined(invoker.output_name)) {
-      output_name = invoker.output_name
-    }
   }
 }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 464526c..ccbba2f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -16140,6 +16140,7 @@
   <int value="1261" label="INPUTMETHODPRIVATE_GETSURROUNDINGTEXT"/>
   <int value="1262" label="USERSPRIVATE_GETLOGINSTATUS"/>
   <int value="1263" label="FILEMANAGERPRIVATEINTERNAL_INSTALLLINUXPACKAGE"/>
+  <int value="1264" label="VIRTUALKEYBOARDPRIVATE_SETHITTESTBOUNDS"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -19266,11 +19267,11 @@
   <int value="2337" label="ScrollToFragmentSucceedWithASCII"/>
   <int value="2338" label="ScrollToFragmentSucceedWithUTF8"/>
   <int value="2339" label="ScrollToFragmentSucceedWithIsomorphic"/>
-  <int value="2340" label="ScrollToFragmentSucceedWithMixed"/>
+  <int value="2340" label="OBSOLETE_ScrollToFragmentSucceedWithMixed"/>
   <int value="2341" label="ScrollToFragmentFailWithASCII"/>
   <int value="2342" label="ScrollToFragmentFailWithUTF8"/>
   <int value="2343" label="ScrollToFragmentFailWithIsomorphic"/>
-  <int value="2344" label="ScrollToFragmentFailWithMixed"/>
+  <int value="2344" label="OBSOLETE_ScrollToFragmentFailWithMixed"/>
   <int value="2345" label="ScrollToFragmentFailWithInvalidEncoding"/>
   <int value="2346" label="RTCPeerConnectionWithActiveCsp"/>
   <int value="2347" label="ImageDecodingAttribute"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 770adafc..0b9c0e6 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -2265,7 +2265,7 @@
 </histogram>
 
 <histogram name="Android.WebView.VariationsEnableState"
-    enum="AndroidWebViewVariationsEnableState" expires_after="M70">
+    enum="AndroidWebViewVariationsEnableState" expires_after="M69">
   <owner>paulmiller@chromium.org</owner>
   <owner>changwan@chromium.org</owner>
   <summary>
@@ -8138,11 +8138,8 @@
   <summary>
     Time spent updating compositing in the Blink document lifecycle.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
+    Note: As of M70, this histogram has stopped recording metrics on machines
+    with low-resolution clocks.
   </summary>
 </histogram>
 
@@ -8307,11 +8304,8 @@
     Time spent computing IntersectionObserver observations in the Blink document
     lifecycle.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
+    Note: As of M70, this histogram has stopped recording metrics on machines
+    with low-resolution clocks.
   </summary>
 </histogram>
 
@@ -8416,11 +8410,8 @@
   <summary>
     Time spent updating paint in the Blink document lifecycle.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
+    Note: As of M70, this histogram has stopped recording metrics on machines
+    with low-resolution clocks.
   </summary>
 </histogram>
 
@@ -8443,11 +8434,8 @@
     document lifecycle. Available when SlimmingPaintInvalidation or
     SlimmingPaintV2 is enabled.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
+    Note: As of M70, this histogram has stopped recording metrics on machines
+    with low-resolution clocks.
   </summary>
 </histogram>
 
@@ -8695,11 +8683,8 @@
   <summary>
     Time spent updating style and layout in the Blink document lifecycle.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
+    Note: As of M70, this histogram has stopped recording metrics on machines
+    with low-resolution clocks.
   </summary>
 </histogram>
 
diff --git a/tools/perf/core/results_dashboard.py b/tools/perf/core/results_dashboard.py
index 984882e2..c5ad82c 100755
--- a/tools/perf/core/results_dashboard.py
+++ b/tools/perf/core/results_dashboard.py
@@ -86,59 +86,26 @@
     num_retries: Number of times to retry uploading to the perf dashboard upon
       recoverable error.
   """
-  start = time.time()
-
   if send_as_histograms and not service_account_file:
     raise ValueError(
         'Must set a valid service_account_file for uploading histogram set '
         'data')
 
-  # Send all the results from this run and the previous cache to the
-  # dashboard.
-  errors, all_data_uploaded = _SendResultsToDashboard(
-      data, url, is_histogramset=send_as_histograms,
-      service_account_file=service_account_file,
-      token_generator_callback=token_generator_callback,
-      num_retries=num_retries)
-
-  print 'Time spent sending results to %s: %s' % (url, time.time() - start)
-
-  for err in errors:
-    print err
-
-  return all_data_uploaded
-
-
-def _SendResultsToDashboard(
-    dashboard_data, url, is_histogramset, service_account_file,
-    token_generator_callback, num_retries):
-  """Tries to send perf dashboard data to |url|.
-
-  Args:
-    See arguments in SendResults method above.
-
-  Returns:
-    A tuple (errors, all_data_uploaded), whereas:
-      errors is a list of error strings.
-      all_data_uploaded is a boolean indicating whether all perf data was
-        succesfully uploaded.
-  """
-
+  start = time.time()
   errors = []
   all_data_uploaded = False
 
-  data_type = ('histogram' if is_histogramset else 'chartjson')
+  data_type = ('histogram' if send_as_histograms else 'chartjson')
 
-  dashboard_data_str = json.dumps(dashboard_data)
+  dashboard_data_str = json.dumps(data)
 
   for i in xrange(1, num_retries + 1):
     try:
       print 'Sending %s result to dashboard (attempt %i out of %i).' % (
           data_type, i, num_retries)
-      if is_histogramset:
-        oauth_token = token_generator_callback(
-            service_account_file, DEFAULT_TOKEN_TIMEOUT_IN_MINUTES)
-        _SendHistogramJson(url, dashboard_data_str, oauth_token)
+      if send_as_histograms:
+        _SendHistogramJson(url, dashboard_data_str,
+                           service_account_file, token_generator_callback)
       else:
         # TODO(eakuefner): Remove this logic once all bots use histograms.
         _SendResultsJson(url, dashboard_data_str)
@@ -157,7 +124,12 @@
       errors.append(error)
       break
 
-  return errors, all_data_uploaded
+  for err in errors:
+    print err
+
+  print 'Time spent sending results to %s: %s' % (url, time.time() - start)
+
+  return all_data_uploaded
 
 
 def MakeHistogramSetWithDiagnostics(histograms_file,
@@ -470,31 +442,36 @@
       raise SendResultsFatalException('Discarding JSON, error:\n%s' % error)
     raise SendResultsRetryException(error)
 
-def _Httplib2Request(url, data, oauth_token):
-  data = zlib.compress(data)
-  headers = {
-      'Authorization': 'Bearer %s' % oauth_token,
-      'User-Agent': 'perf-uploader/1.0'
-  }
 
-  http = httplib2.Http()
-  return http.request(
-      url + SEND_HISTOGRAMS_PATH, method='POST', body=data, headers=headers)
-
-def _SendHistogramJson(url, histogramset_json, oauth_token):
+def _SendHistogramJson(url, histogramset_json,
+                       service_account_file, token_generator_callback):
   """POST a HistogramSet JSON to the Performance Dashboard.
 
   Args:
     url: URL of Performance Dashboard instance, e.g.
         "https://chromeperf.appspot.com".
     histogramset_json: JSON string that contains a serialized HistogramSet.
-    oauth_token: An oauth token to be used for this upload.
+
+    For |service_account_file| and |token_generator_callback|, see SendResults's
+    documentation.
 
   Returns:
     None if successful, or an error string if there were errors.
   """
   try:
-    response, _ = _Httplib2Request(url, histogramset_json, oauth_token)
+    oauth_token = token_generator_callback(
+        service_account_file, DEFAULT_TOKEN_TIMEOUT_IN_MINUTES)
+
+    data = zlib.compress(histogramset_json)
+    headers = {
+        'Authorization': 'Bearer %s' % oauth_token,
+        'User-Agent': 'perf-uploader/1.0'
+    }
+
+    http = httplib2.Http()
+
+    response, _ = http.request(
+      url + SEND_HISTOGRAMS_PATH, method='POST', body=data, headers=headers)
 
     # A 500 is presented on an exception on the dashboard side, timeout,
     # exception, etc. The dashboard can also send back 400 and 403, we could
diff --git a/tools/perf/core/results_dashboard_unittest.py b/tools/perf/core/results_dashboard_unittest.py
index a242a87..c3299e8 100644
--- a/tools/perf/core/results_dashboard_unittest.py
+++ b/tools/perf/core/results_dashboard_unittest.py
@@ -17,8 +17,10 @@
     self.dashboard_url = 'https://chromeperf.appspot.com'
 
   def testRetryForSendResultRetryException(self):
-    def raise_retry_exception(url, histogramset_json, service_account_file):
+    def raise_retry_exception(
+        url, histogramset_json, service_account_file, token_generator_callback):
       del url, histogramset_json, service_account_file  # unused
+      del token_generator_callback  # unused
       raise results_dashboard.SendResultsRetryException('Should retry')
 
     with mock.patch('core.results_dashboard._SendHistogramJson',
@@ -32,8 +34,10 @@
 
   def testNoRetryForSendResultFatalException(self):
 
-    def raise_retry_exception(url, histogramset_json, service_account_file):
+    def raise_retry_exception(
+        url, histogramset_json, service_account_file, token_generator_callback):
       del url, histogramset_json, service_account_file  # unused
+      del token_generator_callback  # unused
       raise results_dashboard.SendResultsFatalException('Do not retry')
 
     with mock.patch('core.results_dashboard._SendHistogramJson',
@@ -59,8 +63,9 @@
   def testNoRetryAfterSucessfulSendResult(self):
     counter = [0]
     def raise_retry_exception_first_two_times(
-        url, histogramset_json, service_account_file):
+        url, histogramset_json, service_account_file, token_generator_callback):
       del url, histogramset_json, service_account_file  # unused
+      del token_generator_callback  # unused
       counter[0] += 1
       if counter[0] <= 2:
         raise results_dashboard.SendResultsRetryException('Please retry')
diff --git a/ui/aura/mus/in_flight_change.cc b/ui/aura/mus/in_flight_change.cc
index 88111b927..fe5ed31 100644
--- a/ui/aura/mus/in_flight_change.cc
+++ b/ui/aura/mus/in_flight_change.cc
@@ -15,6 +15,51 @@
 
 namespace aura {
 
+std::string ChangeTypeToString(ChangeType change_type) {
+  switch (change_type) {
+    case ChangeType::ADD_CHILD:
+      return "ADD_CHILD";
+    case ChangeType::ADD_TRANSIENT_WINDOW:
+      return "ADD_TRANSIENT_WINDOW";
+    case ChangeType::BOUNDS:
+      return "BOUNDS";
+    case ChangeType::CAPTURE:
+      return "CAPTURE";
+    case ChangeType::CHILD_MODAL_PARENT:
+      return "CHILD_MODAL_PARENT";
+    case ChangeType::DELETE_WINDOW:
+      return "DELETE_WINDOW";
+    case ChangeType::DRAG_LOOP:
+      return "DRAG_LOOP";
+    case ChangeType::FOCUS:
+      return "FOCUS";
+    case ChangeType::MOVE_LOOP:
+      return "MOVE_LOOP";
+    case ChangeType::NEW_TOP_LEVEL_WINDOW:
+      return "NEW_TOP_LEVEL_WINDOW";
+    case ChangeType::NEW_WINDOW:
+      return "NEW_WINDOW";
+    case ChangeType::OPACITY:
+      return "OPACITY";
+    case ChangeType::CURSOR:
+      return "CURSOR";
+    case ChangeType::PROPERTY:
+      return "PROPERTY";
+    case ChangeType::REMOVE_CHILD:
+      return "REMOVE_CHILD";
+    case ChangeType::REMOVE_TRANSIENT_WINDOW_FROM_PARENT:
+      return "REMOVE_TRANSIENT_WINDOW_FROM_PARENT";
+    case ChangeType::REORDER:
+      return "REORDER";
+    case ChangeType::SET_MODAL:
+      return "SET_MODAL";
+    case ChangeType::TRANSFORM:
+      return "TRANSFORM";
+    case ChangeType::VISIBLE:
+      return "VISIBLE";
+  }
+}
+
 // InFlightChange -------------------------------------------------------------
 
 InFlightChange::InFlightChange(WindowMus* window, ChangeType type)
diff --git a/ui/aura/mus/in_flight_change.h b/ui/aura/mus/in_flight_change.h
index 0422d2f..22c994c 100644
--- a/ui/aura/mus/in_flight_change.h
+++ b/ui/aura/mus/in_flight_change.h
@@ -59,6 +59,9 @@
   VISIBLE,
 };
 
+// Print a human-readable string representation of |change_type| for logging.
+std::string ChangeTypeToString(ChangeType change_type);
+
 // InFlightChange is used to track function calls to the server and take the
 // appropriate action when the call fails, or the same property changes while
 // waiting for the response. When a function is called on the server an
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index b2f00dcb..2a287cb 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -1482,8 +1482,11 @@
   for (auto& observer : test_observers_)
     observer.OnChangeCompleted(change_id, change->change_type(), success);
 
-  if (!success)
+  if (!success) {
+    DVLOG(1) << "Change failed id=" << change_id
+             << " type=" << ChangeTypeToString(change->change_type());
     change->ChangeFailed();
+  }
 
   InFlightChange* next_change = GetOldestInFlightChangeMatching(*change);
   if (next_change) {
diff --git a/ui/file_manager/integration_tests/file_manager/zip_files.js b/ui/file_manager/integration_tests/file_manager/zip_files.js
new file mode 100644
index 0000000..248e3fb
--- /dev/null
+++ b/ui/file_manager/integration_tests/file_manager/zip_files.js
@@ -0,0 +1,139 @@
+// Copyright 2018 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.
+
+'use strict';
+
+/**
+ * Returns the file ENTRIES.zipArchive content (2 files) as row entries.
+ */
+function getZipArchiveFileRowEntries() {
+  return [
+    ['image.png', '272 bytes', 'PNG image', 'Sep 2, 2013, 10:01 PM'],
+    ['text.txt', '51 bytes', 'Plain text', 'Sep 2, 2013, 10:01 PM']
+  ];
+}
+
+/**
+ * Tests zip file open (aka unzip) from Downloads.
+ */
+testcase.zipFileOpenDownloads = function() {
+  let appId;
+
+  StepsRunner.run([
+    // Open Files app on Downloads containing a zip file.
+    function() {
+      setupAndWaitUntilReady(
+          null, RootPath.DOWNLOADS, this.next, [ENTRIES.zipArchive], []);
+    },
+    // Select the zip file.
+    function(result) {
+      appId = result.windowId;
+      remoteCall.callRemoteTestUtil('selectFile', appId, ['archive.zip'])
+          .then(this.next);
+    },
+    // Press the Enter key.
+    function(result) {
+      chrome.test.assertTrue(!!result, 'selectFile failed');
+      const key = ['#file-list', 'Enter', 'Enter', false, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key, this.next);
+    },
+    // Check: the zip file content should be shown (unzip).
+    function(result) {
+      chrome.test.assertTrue(!!result, 'fakeKeyDown failed');
+      const files = getZipArchiveFileRowEntries();
+      remoteCall.waitForFiles(appId, files).then(this.next);
+    },
+  ]);
+};
+
+/**
+ * Tests zip file open (aka unzip) from Google Drive.
+ */
+testcase.zipFileOpenDrive = function() {
+  let appId;
+
+  StepsRunner.run([
+    // Open Files app on Drive containing a zip file.
+    function() {
+      setupAndWaitUntilReady(
+          null, RootPath.DRIVE, this.next, [], [ENTRIES.zipArchive]);
+    },
+    // Select the zip file.
+    function(result) {
+      appId = result.windowId;
+      remoteCall.callRemoteTestUtil('selectFile', appId, ['archive.zip'])
+          .then(this.next);
+    },
+    // Press the Enter key.
+    function(result) {
+      chrome.test.assertTrue(!!result, 'selectFile failed');
+      const key = ['#file-list', 'Enter', 'Enter', false, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key, this.next);
+    },
+    // Check: the zip file content should be shown (unzip).
+    function(result) {
+      chrome.test.assertTrue(!!result, 'fakeKeyDown failed');
+      const files = getZipArchiveFileRowEntries();
+      remoteCall.waitForFiles(appId, files).then(this.next);
+    },
+  ]);
+};
+
+/**
+ * Tests zip file open (aka unzip) from a removable USB volume.
+ */
+testcase.zipFileOpenUsb = function() {
+  let appId;
+
+  const USB_VOLUME_QUERY = '#directory-tree [volume-type-icon="removable"]';
+
+  StepsRunner.run([
+    // Open Files app on Drive.
+    function() {
+      setupAndWaitUntilReady(
+          null, RootPath.DRIVE, this.next, [], [ENTRIES.beautiful]);
+    },
+    // Mount empty USB volume in the Drive window.
+    function(results) {
+      appId = results.windowId;
+      chrome.test.sendMessage(
+          JSON.stringify({name: 'mountFakeUsbEmpty'}), this.next);
+    },
+    // Wait for the USB mount.
+    function() {
+      remoteCall.waitForElement(appId, USB_VOLUME_QUERY).then(this.next);
+    },
+    // Click to open the USB volume.
+    function() {
+      remoteCall.callRemoteTestUtil(
+          'fakeMouseClick', appId, [USB_VOLUME_QUERY], this.next);
+    },
+    // Add zip file to the USB volume.
+    function() {
+      addEntries(['usb'], [ENTRIES.zipArchive], this.next);
+    },
+    // Verify the USB file list.
+    function() {
+      const archive = [ENTRIES.zipArchive.getExpectedRow()];
+      remoteCall.waitForFiles(appId, archive).then(this.next);
+    },
+    // Select the zip file.
+    function() {
+      remoteCall.callRemoteTestUtil('selectFile', appId, ['archive.zip'])
+          .then(this.next);
+    },
+    // Press the Enter key.
+    function(result) {
+      chrome.test.assertTrue(!!result, 'selectFile failed');
+      const key = ['#file-list', 'Enter', 'Enter', false, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key, this.next);
+    },
+    // Check: the zip file content should be shown (unzip).
+    function(result) {
+      chrome.test.assertTrue(!!result, 'fakeKeyDown failed');
+      const files = getZipArchiveFileRowEntries();
+      remoteCall.waitForFiles(appId, files).then(this.next);
+    },
+  ]);
+};
diff --git a/ui/file_manager/integration_tests/file_manager_test_manifest.json b/ui/file_manager/integration_tests/file_manager_test_manifest.json
index f096302..55a1c17d 100644
--- a/ui/file_manager/integration_tests/file_manager_test_manifest.json
+++ b/ui/file_manager/integration_tests/file_manager_test_manifest.json
@@ -41,7 +41,8 @@
       "file_manager/tab_index.js",
       "file_manager/tasks.js",
       "file_manager/transfer.js",
-      "file_manager/traverse.js"
+      "file_manager/traverse.js",
+      "file_manager/zip_files.js"
     ]
   },
   "permissions": [
diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc
index 06027f4..7bf3e56 100644
--- a/ui/gl/gl_context_cgl.cc
+++ b/ui/gl/gl_context_cgl.cc
@@ -11,7 +11,6 @@
 #include <sstream>
 #include <vector>
 
-#include "base/debug/crash_logging.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
@@ -232,48 +231,19 @@
 
 uint64_t GLContextCGL::BackpressureFenceCreate() {
   TRACE_EVENT0("gpu", "GLContextCGL::BackpressureFenceCreate");
-  // TODO(ccameron): Remove this CHECK.
-  CHECK(CGLGetCurrentContext() == context_);
-  CHECK(context_);
 
-  // Set a crash key before making any GL calls.
-  static auto* crash_key = base::debug::AllocateCrashKeyString(
-      "gl_context_cgl", base::debug::CrashKeySize::Size256);
-  std::stringstream crash_info;
-  crash_info << "fence:" << next_backpressure_fence_ << " ";
-  crash_info << "canswitch:" << g_support_renderer_switching << " ";
-  crash_info << "didswitch:" << has_switched_gpus_ << " ";
-  crash_info << "dgpu:" << !!discrete_pixelformat_ << " ";
-  base::debug::SetCrashKeyString(crash_key, crash_info.str());
-
-  // Query the CGL retain count of the context.
-  GLuint retain_count =
-      CGLGetContextRetainCount(static_cast<CGLContextObj>(context_));
-  crash_info << "cglret:" << retain_count << " ";
-  base::debug::SetCrashKeyString(crash_key, crash_info.str());
-
-  // Query for errors on the GL context.
-  GLenum error = glGetError();
-  crash_info << "glerr:" << error;
-  base::debug::SetCrashKeyString(crash_key, crash_info.str());
-
-  // This flush will trigger the crash.
+  // This flush will trigger a crash.
   glFlush();
 
   if (gl::GLFence::IsSupported()) {
     backpressure_fences_[next_backpressure_fence_] = GLFence::Create();
     return next_backpressure_fence_++;
   }
-
-  base::debug::ClearCrashKeyString(crash_key);
   return kFinishFenceId;
 }
 
 void GLContextCGL::BackpressureFenceWait(uint64_t fence_id) {
   TRACE_EVENT0("gpu", "GLContextCGL::BackpressureFenceWait");
-  // TODO(ccameron): Remove this CHECK.
-  CHECK(CGLGetCurrentContext() == context_);
-  CHECK(context_);
   if (fence_id == kFinishFenceId) {
     glFinish();
     return;
diff --git a/ui/keyboard/BUILD.gn b/ui/keyboard/BUILD.gn
index 4d54cb7..deaa465 100644
--- a/ui/keyboard/BUILD.gn
+++ b/ui/keyboard/BUILD.gn
@@ -48,6 +48,8 @@
     "queued_container_type.h",
     "queued_display_change.cc",
     "queued_display_change.h",
+    "shaped_window_targeter.cc",
+    "shaped_window_targeter.h",
   ]
 
   defines = [ "KEYBOARD_IMPLEMENTATION" ]
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index cdae45a..298e41f 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -41,6 +41,7 @@
 #include "ui/keyboard/notification_manager.h"
 #include "ui/keyboard/queued_container_type.h"
 #include "ui/keyboard/queued_display_change.h"
+#include "ui/keyboard/shaped_window_targeter.h"
 #include "ui/ozone/public/input_controller.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/wm/core/window_animations.h"
@@ -792,6 +793,15 @@
     NotifyKeyboardBoundsChanging(visual_bounds_in_screen_);
 }
 
+void KeyboardController::SetHitTestBounds(
+    const std::vector<gfx::Rect>& bounds) {
+  if (!GetKeyboardWindow())
+    return;
+
+  GetKeyboardWindow()->SetEventTargeter(
+      std::make_unique<ShapedWindowTargeter>(bounds));
+}
+
 gfx::Rect KeyboardController::AdjustSetBoundsRequest(
     const gfx::Rect& display_bounds,
     const gfx::Rect& requested_bounds) const {
diff --git a/ui/keyboard/keyboard_controller.h b/ui/keyboard/keyboard_controller.h
index 02bbc6c..7c72a927 100644
--- a/ui/keyboard/keyboard_controller.h
+++ b/ui/keyboard/keyboard_controller.h
@@ -182,6 +182,10 @@
   // Set the area on the screen that are occluded by the keyboard.
   void SetOccludedBounds(const gfx::Rect& bounds);
 
+  // Set the areas on the keyboard window where events should be handled.
+  // Does not do anything if there is no keyboard window.
+  void SetHitTestBounds(const std::vector<gfx::Rect>& bounds);
+
   KeyboardControllerState GetStateForTest() const { return state_; }
 
   ContainerType GetActiveContainerType() const {
diff --git a/ui/keyboard/keyboard_ui.h b/ui/keyboard/keyboard_ui.h
index b1f08fa..08fc72a 100644
--- a/ui/keyboard/keyboard_ui.h
+++ b/ui/keyboard/keyboard_ui.h
@@ -38,11 +38,6 @@
   // Whether the keyboard window has been created.
   virtual bool HasKeyboardWindow() const = 0;
 
-  // Whether this window should do an overscroll to avoid occlusion by the
-  // virtual keyboard. IME windows and virtual keyboard windows should always
-  // avoid overscroll.
-  virtual bool ShouldWindowOverscroll(aura::Window* window) const = 0;
-
   // Gets the InputMethod that will provide notifications about changes in the
   // text input context.
   virtual ui::InputMethod* GetInputMethod() = 0;
diff --git a/ui/keyboard/shaped_window_targeter.cc b/ui/keyboard/shaped_window_targeter.cc
new file mode 100644
index 0000000..865fe468
--- /dev/null
+++ b/ui/keyboard/shaped_window_targeter.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 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 "ui/keyboard/shaped_window_targeter.h"
+
+#include <memory>
+
+#include "ui/gfx/geometry/rect.h"
+
+namespace keyboard {
+
+ShapedWindowTargeter::ShapedWindowTargeter(
+    const std::vector<gfx::Rect>& hit_test_rects)
+    : hit_test_rects_(hit_test_rects) {}
+
+ShapedWindowTargeter::~ShapedWindowTargeter() = default;
+
+std::unique_ptr<aura::WindowTargeter::HitTestRects>
+ShapedWindowTargeter::GetExtraHitTestShapeRects(aura::Window* target) const {
+  return std::make_unique<aura::WindowTargeter::HitTestRects>(hit_test_rects_);
+}
+
+}  // namespace keyboard
diff --git a/ui/keyboard/shaped_window_targeter.h b/ui/keyboard/shaped_window_targeter.h
new file mode 100644
index 0000000..df13a0c
--- /dev/null
+++ b/ui/keyboard/shaped_window_targeter.h
@@ -0,0 +1,32 @@
+// Copyright 2018 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 UI_KEYBOARD_SHAPED_WINDOW_TARGETER_H_
+#define UI_KEYBOARD_SHAPED_WINDOW_TARGETER_H_
+
+#include "base/macros.h"
+#include "ui/aura/window_targeter.h"
+
+namespace keyboard {
+
+// A WindowTargeter for windows with hit-test areas that can be defined by a
+// list of rectangles.
+class ShapedWindowTargeter : public aura::WindowTargeter {
+ public:
+  explicit ShapedWindowTargeter(const std::vector<gfx::Rect>& hit_test_rects);
+  ~ShapedWindowTargeter() override;
+
+ private:
+  // aura::WindowTargeter:
+  std::unique_ptr<aura::WindowTargeter::HitTestRects> GetExtraHitTestShapeRects(
+      aura::Window* target) const override;
+
+  std::vector<gfx::Rect> hit_test_rects_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShapedWindowTargeter);
+};
+
+}  // namespace keyboard
+
+#endif  // UI_KEYBOARD_SHAPED_WINDOW_TARGETER_H_
diff --git a/ui/keyboard/test/keyboard_test_util.cc b/ui/keyboard/test/keyboard_test_util.cc
index 9a0afe9..7aec1d5c 100644
--- a/ui/keyboard/test/keyboard_test_util.cc
+++ b/ui/keyboard/test/keyboard_test_util.cc
@@ -119,8 +119,4 @@
   return !!window_;
 }
 
-bool TestKeyboardUI::ShouldWindowOverscroll(aura::Window* window) const {
-  return true;
-}
-
 }  // namespace keyboard
diff --git a/ui/keyboard/test/keyboard_test_util.h b/ui/keyboard/test/keyboard_test_util.h
index 5716952..7a638b0 100644
--- a/ui/keyboard/test/keyboard_test_util.h
+++ b/ui/keyboard/test/keyboard_test_util.h
@@ -40,7 +40,6 @@
 
   // Overridden from KeyboardUI:
   bool HasKeyboardWindow() const override;
-  bool ShouldWindowOverscroll(aura::Window* window) const override;
   aura::Window* GetKeyboardWindow() override;
   ui::InputMethod* GetInputMethod() override;
   void ReloadKeyboardIfNeeded() override {}
diff --git a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
index 79aef70..793d06be 100644
--- a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
@@ -157,8 +157,7 @@
                          ->capture_window());
 }
 
-// TODO(http://crbug.com/864614): Fails flakily in mus with ws2.
-TEST_F(DesktopWindowTreeHostMusTest, DISABLED_Deactivate) {
+TEST_F(DesktopWindowTreeHostMusTest, Deactivate) {
   std::unique_ptr<Widget> widget1(CreateWidget());
   widget1->Show();
 
@@ -279,8 +278,7 @@
   waiter.Wait();
 }
 
-// TODO(http://crbug.com/864615): Fails consistently in mus with ws2.
-TEST_F(DesktopWindowTreeHostMusTest, DISABLED_StackAbove) {
+TEST_F(DesktopWindowTreeHostMusTest, StackAbove) {
   std::unique_ptr<Widget> widget1(CreateWidget(nullptr));
   widget1->Show();
 
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc
index 54744d6..ab1cd09 100644
--- a/ui/views/widget/widget_interactive_uitest.cc
+++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -1341,10 +1341,6 @@
 
 // Test that window state is not changed after getting out of full screen.
 TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
-  // TODO(http://crbug.com/864618): Fails flakily in mus with ws2.
-  if (IsMus())
-    return;
-
   Widget* toplevel = CreateTopLevelPlatformWidget();
 
   toplevel->Show();
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 3c77de47..fafce53 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -640,17 +640,8 @@
 
 class WidgetObserverTest : public WidgetTest, public WidgetObserver {
  public:
-  WidgetObserverTest()
-      : active_(nullptr),
-        widget_closed_(nullptr),
-        widget_activated_(nullptr),
-        widget_shown_(nullptr),
-        widget_hidden_(nullptr),
-        widget_bounds_changed_(nullptr),
-        widget_to_close_on_hide_(nullptr) {
-  }
-
-  ~WidgetObserverTest() override {}
+  WidgetObserverTest() = default;
+  ~WidgetObserverTest() override = default;
 
   // Set a widget to Close() the next time the Widget being observed is hidden.
   void CloseOnNextHide(Widget* widget) {
@@ -721,16 +712,16 @@
   const Widget* widget_bounds_changed() const { return widget_bounds_changed_; }
 
  private:
-  Widget* active_;
+  Widget* active_ = nullptr;
 
-  Widget* widget_closed_;
-  Widget* widget_activated_;
-  Widget* widget_deactivated_;
-  Widget* widget_shown_;
-  Widget* widget_hidden_;
-  Widget* widget_bounds_changed_;
+  Widget* widget_closed_ = nullptr;
+  Widget* widget_activated_ = nullptr;
+  Widget* widget_deactivated_ = nullptr;
+  Widget* widget_shown_ = nullptr;
+  Widget* widget_hidden_ = nullptr;
+  Widget* widget_bounds_changed_ = nullptr;
 
-  Widget* widget_to_close_on_hide_;
+  Widget* widget_to_close_on_hide_ = nullptr;
 };
 
 // This test appears to be flaky on Mac.
@@ -741,21 +732,14 @@
 #endif
 
 TEST_F(WidgetObserverTest, MAYBE_ActivationChange) {
-  // TODO(http://crbug.com/864800): Fails flakily in mus with ws2.
-  if (IsMus())
-    return;
-
-  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
   WidgetAutoclosePtr toplevel1(NewWidget());
   WidgetAutoclosePtr toplevel2(NewWidget());
 
   toplevel1->Show();
   toplevel2->Show();
-
   reset();
 
   toplevel1->Activate();
-
   RunPendingMessages();
   EXPECT_EQ(toplevel1.get(), widget_activated());
 
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
index fbe37a20..654b13cb 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="../../shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="cr_png_behavior.html">
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
index 2c8eb21..65965809 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
@@ -1,7 +1,7 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="../../icons.html">
+<link rel="import" href="../../shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys/iron-a11y-keys.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-selector/iron-selector.html">
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
index f0549d0b..0563c0f0 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="../../shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="cr_camera.html">
 <link rel="import" href="cr_picture_types.html">
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_png_behavior.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_png_behavior.html
index 4d7582e..141b0f2 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_png_behavior.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_png_behavior.html
@@ -1,2 +1,2 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
 <script src="cr_png_behavior.js"></script>
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html
index 056645f..0a55a398 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.html
@@ -1,8 +1,9 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
+
+<link rel="import" href="../../chromeos/network/cr_onc_types.html">
+<link rel="import" href="../../chromeos/network/network_icons.html">
+<link rel="import" href="../../hidden_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/network_icons.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 
 <dom-module id="cr-network-icon">
   <template>
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html
index 855875a..7a16782 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list.html
@@ -1,9 +1,10 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_list_item.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="../../../html/polymer.html">
+
+<link rel="import" href="../../chromeos/network/cr_network_list_item.html">
+<link rel="import" href="../../chromeos/network/cr_onc_types.html">
+<link rel="import" href="../../chromeos/network/cr_onc_types.html">
+<link rel="import" href="../../cr_scrollable_behavior.html">
+<link rel="import" href="../../shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html
index 4a9bbe3..7580886 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html
@@ -1,12 +1,12 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_icon.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
-<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="../../chromeos/network/cr_network_icon.html">
+<link rel="import" href="../../chromeos/network/cr_onc_types.html">
+<link rel="import" href="../../icons.html">
+<link rel="import" href="../../policy/cr_policy_indicator.html">
+<link rel="import" href="../../policy/cr_policy_network_behavior.html">
+<link rel="import" href="../../shared_style_css.html">
+<link rel="import" href="../../shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys/iron-a11y-keys.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html
index def03d7..a190e85 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.html
@@ -1,6 +1,7 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_list.html">
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="../../../html/polymer.html">
+
+<link rel="import" href="../../chromeos/network/cr_network_list.html">
+<link rel="import" href="../../chromeos/network/cr_onc_types.html">
 
 <dom-module id="cr-network-select">
   <template>
diff --git a/ui/webui/resources/cr_elements/chromeos/network/network_icons.html b/ui/webui/resources/cr_elements/chromeos/network/network_icons.html
index f7633f0..df5f47c 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/network_icons.html
+++ b/ui/webui/resources/cr_elements/chromeos/network/network_icons.html
@@ -1,4 +1,5 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
+
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
 <!-- These icons were converted from source .svg files. -->
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
index 1a959d70..3bb931d9a 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
@@ -1,9 +1,9 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
-<link rel="import" href="chrome://resources/html/util.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="../../html/assert.html">
+<link rel="import" href="../../html/cr/ui/focus_without_ink.html">
+<link rel="import" href="../../html/util.html">
+<link rel="import" href="../shared_vars_css.html">
 
 <dom-module id="cr-action-menu">
   <template>
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
index c3a75d69..64e279b4 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-ripple-behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
diff --git a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
index 65fadd0d..d9dcfac 100644
--- a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
+++ b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
@@ -1,9 +1,9 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="../hidden_style_css.html">
+<link rel="import" href="../cr_icons_css.html">
+<link rel="import" href="../shared_vars_css.html">
+<link rel="import" href="../../html/assert.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 
 <dom-module id="cr-dialog">
diff --git a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
index b143e8cd..a904f7f 100644
--- a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
+++ b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="../shared_vars_css.html">
 
 <dom-module id="cr-drawer">
   <template>
diff --git a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html
index eee1f57..a0166be6 100644
--- a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html
+++ b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html
@@ -1,6 +1,7 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="../../html/polymer.html">
+
+<link rel="import" href="../cr_icons_css.html">
+<link rel="import" href="../shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 
 <dom-module id="cr-expand-button">
diff --git a/ui/webui/resources/cr_elements/cr_icons_css.html b/ui/webui/resources/cr_elements/cr_icons_css.html
index a170851..6c346a3 100644
--- a/ui/webui/resources/cr_elements/cr_icons_css.html
+++ b/ui/webui/resources/cr_elements/cr_icons_css.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
 
 <!-- Common icon classes for Material Design WebUI. -->
 <dom-module id="cr-icons">
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input.html b/ui/webui/resources/cr_elements/cr_input/cr_input.html
index 6002d1f..6f88b1ec 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input.html
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="../../html/assert.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="../hidden_style_css.html">
 <link rel="import" href="../shared_vars_css.html">
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html b/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html
index a8b26e1..2b3e104 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="../shared_vars_css.html">
diff --git a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html
index be956c7..eab1f83 100644
--- a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html
+++ b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
 <dom-module id="cr-lazy-render">
   <template>
diff --git a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
index 8ae3c5d..2b12b55 100644
--- a/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
+++ b/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
@@ -1,8 +1,8 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="../cr_icons_css.html">
+<link rel="import" href="../hidden_style_css.html">
+<link rel="import" href="../shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-ripple-behavior.html">
 
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
index d4d7540..a28dad40 100644
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
+++ b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
@@ -1,7 +1,7 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/icon.html">
+<link rel="import" href="../shared_vars_css.html">
+<link rel="import" href="../../html/icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="cr_profile_avatar_selector_grid.html">
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.html b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.html
index 94a90d2..8c590b0 100644
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.html
+++ b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.html
@@ -1,7 +1,7 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/util.html">
+<link rel="import" href="../../html/assert.html">
+<link rel="import" href="../../html/util.html">
 
 <dom-module id="cr-profile-avatar-selector-grid">
   <template>
diff --git a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button.html b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button.html
index 492af7c..12f6081 100644
--- a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button.html
+++ b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="../hidden_style_css.html">
diff --git a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html
index c75f207..3da38f4 100644
--- a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html
+++ b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="../shared_vars_css.html">
diff --git a/ui/webui/resources/cr_elements/cr_scrollable_behavior.html b/ui/webui/resources/cr_elements/cr_scrollable_behavior.html
index dd48f42..ac1ac83 100644
--- a/ui/webui/resources/cr_elements/cr_scrollable_behavior.html
+++ b/ui/webui/resources/cr_elements/cr_scrollable_behavior.html
@@ -1,3 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
+
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 <script src="cr_scrollable_behavior.js"></script>
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
index 879bb7fc..05adf45 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="../shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-slider/paper-slider.html">
 
 <dom-module id="cr-slider">
diff --git a/ui/webui/resources/cr_elements/cr_toast/cr_toast.html b/ui/webui/resources/cr_elements/cr_toast/cr_toast.html
index d09b9a57..0f5bcbf 100644
--- a/ui/webui/resources/cr_elements/cr_toast/cr_toast.html
+++ b/ui/webui/resources/cr_elements/cr_toast/cr_toast.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
diff --git a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html
index 6e3b8f0e4..551ff31 100644
--- a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html
+++ b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-ripple-behavior.html">
 <link rel="import" href="../shared_vars_css.html">
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index ec50ca4..8fb18010 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -1,8 +1,8 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="../cr_icons_css.html">
+<link rel="import" href="../cr_toolbar/cr_toolbar_search_field.html">
+<link rel="import" href="../hidden_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
index 8b149e0..635bfe7 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
@@ -1,7 +1,8 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field_behavior.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="../../html/polymer.html">
+
+<link rel="import" href="../cr_search_field/cr_search_field_behavior.html">
+<link rel="import" href="../icons.html">
+<link rel="import" href="../shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
index a658e9c3..53dfb4a 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
@@ -1,7 +1,8 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="../../html/polymer.html">
+
+<link rel="import" href="../cr_icons_css.html">
+<link rel="import" href="../icons.html">
+<link rel="import" href="../paper_button_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 
diff --git a/ui/webui/resources/cr_elements/hidden_style_css.html b/ui/webui/resources/cr_elements/hidden_style_css.html
index 4c22288..9326a5e8 100644
--- a/ui/webui/resources/cr_elements/hidden_style_css.html
+++ b/ui/webui/resources/cr_elements/hidden_style_css.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
 
 <!-- Common style for Material Design WebUI, such that the |hidden| attributes
   works within Shadow DOM. -->
diff --git a/ui/webui/resources/cr_elements/icons.html b/ui/webui/resources/cr_elements/icons.html
index 47927f9..437eb4e 100644
--- a/ui/webui/resources/cr_elements/icons.html
+++ b/ui/webui/resources/cr_elements/icons.html
@@ -1,4 +1,5 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
+
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
 <!--
diff --git a/ui/webui/resources/cr_elements/paper_button_style_css.html b/ui/webui/resources/cr_elements/paper_button_style_css.html
index beec808..4d768d2 100644
--- a/ui/webui/resources/cr_elements/paper_button_style_css.html
+++ b/ui/webui/resources/cr_elements/paper_button_style_css.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="shared_vars_css.html">
 
 <!-- Common paper-button styling for Material Design WebUI. -->
 <dom-module id="paper-button-style">
diff --git a/ui/webui/resources/cr_elements/paper_tabs_style_css.html b/ui/webui/resources/cr_elements/paper_tabs_style_css.html
index 9ce5ce1a7..ca193b60 100644
--- a/ui/webui/resources/cr_elements/paper_tabs_style_css.html
+++ b/ui/webui/resources/cr_elements/paper_tabs_style_css.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
 <!-- Common paper-tabs styling for Material Design WebUI. -->
@@ -21,4 +21,4 @@
       }
     </style>
   </template>
-</dom-module>
\ No newline at end of file
+</dom-module>
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html b/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html
index 3f7e6614..105d311 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="../hidden_style_css.html">
 <link rel="import" href="cr_policy_indicator_behavior.html">
 <link rel="import" href="cr_tooltip_icon.html">
 
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.html b/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.html
index eefd837..5f8d7fde 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.html
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.html
@@ -1,2 +1,2 @@
-<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="../chromeos/network/cr_onc_types.html">
 <script src="cr_policy_network_behavior.js"></script>
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html b/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html
index 61440b07..94247f5 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="../hidden_style_css.html">
 <link rel="import" href="cr_policy_indicator_behavior.html">
 <link rel="import" href="cr_policy_network_behavior.html">
 <link rel="import" href="cr_tooltip_icon.html">
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html b/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html
index 555bb8c9..1940042 100644
--- a/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html
+++ b/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html
@@ -1,6 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="../hidden_style_css.html">
 <link rel="import" href="cr_policy_indicator_behavior.html">
 <link rel="import" href="cr_tooltip_icon.html">
 
diff --git a/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html b/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html
index 5f9958ed..c080e10 100644
--- a/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html
+++ b/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html
@@ -1,7 +1,7 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../../html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="../icons.html">
+<link rel="import" href="../shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html">
 
diff --git a/ui/webui/resources/cr_elements/search_highlight_style_css.html b/ui/webui/resources/cr_elements/search_highlight_style_css.html
index 451f86d..bebb5ec1 100644
--- a/ui/webui/resources/cr_elements/search_highlight_style_css.html
+++ b/ui/webui/resources/cr_elements/search_highlight_style_css.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
 
 <link rel="import" href="shared_vars_css.html">
 
diff --git a/ui/webui/resources/cr_elements/shared_style_css.html b/ui/webui/resources/cr_elements/shared_style_css.html
index 28cabebb..6b0631c 100644
--- a/ui/webui/resources/cr_elements/shared_style_css.html
+++ b/ui/webui/resources/cr_elements/shared_style_css.html
@@ -1,5 +1,6 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="../html/polymer.html">
+
+<link rel="import" href="shared_vars_css.html">
 <link rel="import" href="hidden_style_css.html">
 <link rel="import" href="cr_icons_css.html">
 
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index e870a4648..6a1ef0446 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -1,4 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
diff --git a/url/url_util.cc b/url/url_util.cc
index dd4e914..4bede9d 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -808,6 +808,7 @@
     }
   }
 
+  int output_initial_length = output->length();
   bool did_utf8_decode = false;
   bool did_isomorphic_decode = false;
   // Convert that 8-bit to UTF-16. It's not clear IE does this at all to
@@ -829,21 +830,21 @@
         i = next_character;
         did_utf8_decode = true;
       } else {
-        // If there are any sequences that are not valid UTF-8, we keep
-        // invalid code points and promote to UTF-16. We copy all characters
-        // from the current position to the end of the identified sequence.
-        while (i < next_character) {
-          output->push_back(static_cast<unsigned char>(unescaped_chars.at(i)));
-          i++;
-        }
-        output->push_back(static_cast<unsigned char>(unescaped_chars.at(i)));
+        // If there are any sequences that are not valid UTF-8, we
+        // revert |output| changes, and promote any bytes to UTF-16. We
+        // copy all characters from the beginning to the end of the
+        // identified sequence.
+        output->set_length(output_initial_length);
+        did_utf8_decode = false;
+        for (int j = 0; j < unescaped_chars.length(); ++j)
+          output->push_back(static_cast<unsigned char>(unescaped_chars.at(j)));
         did_isomorphic_decode = true;
+        break;
       }
     }
   }
 
-  if (did_utf8_decode && did_isomorphic_decode)
-    return DecodeURLResult::kMixed;
+  DCHECK(!(did_utf8_decode && did_isomorphic_decode));
   if (did_isomorphic_decode)
     return DecodeURLResult::kIsomorphic;
   if (did_utf8_decode)
diff --git a/url/url_util.h b/url/url_util.h
index 20b2344..32e7f0d 100644
--- a/url/url_util.h
+++ b/url/url_util.h
@@ -258,12 +258,13 @@
   // Did UTF-8 decode only.
   kUTF8,
   // Did byte to Unicode mapping only.
+  // https://infra.spec.whatwg.org/#isomorphic-decode
   kIsomorphic,
-  // Did both of UTF-8 decode and isomorphic decode.
-  kMixed,
 };
 
 // Unescapes the given string using URL escaping rules.
+// This function tries to decode non-ASCII characters in UTF-8 first,
+// then in isomorphic encoding if UTF-8 decoding failed.
 URL_EXPORT DecodeURLResult DecodeURLEscapeSequences(const char* input,
                                                     int length,
                                                     CanonOutputW* output);
diff --git a/url/url_util_unittest.cc b/url/url_util_unittest.cc
index 526d63fb..65f3435 100644
--- a/url/url_util_unittest.cc
+++ b/url/url_util_unittest.cc
@@ -245,9 +245,10 @@
   // Test the error behavior for invalid UTF-8.
   {
     const char invalid_input[] = "%e4%a0%e5%a5%bd";
-    const base::char16 invalid_expected[4] = {0x00e4, 0x00a0, 0x597d, 0};
+    const base::char16 invalid_expected[6] = {0x00e4, 0x00a0, 0x00e5,
+                                              0x00a5, 0x00bd, 0};
     RawCanonOutputT<base::char16> invalid_output;
-    EXPECT_EQ(DecodeURLResult::kMixed,
+    EXPECT_EQ(DecodeURLResult::kIsomorphic,
               DecodeURLEscapeSequences(invalid_input, strlen(invalid_input),
                                        &invalid_output));
     EXPECT_EQ(base::string16(invalid_expected),