diff --git a/DEPS b/DEPS
index 01c3d7b..fe9d108f 100644
--- a/DEPS
+++ b/DEPS
@@ -138,11 +138,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': '0cb2fdefc219df122cd764b333fce7e826ab5158',
+  'skia_revision': '66661df8e9f964b5113783a28a5aad7e29a0d21c',
   # 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': 'bcf517c0366d4af6dfb600e5344ef37e65a09d0b',
+  'v8_revision': '2cb25737c9eb7893ac752674e75d400b4419f67c',
   # 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.
@@ -150,15 +150,15 @@
   # 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': '5a808b86d8901b9cb7a4d174356c0c1b0e2fc0e3',
+  'angle_revision': 'ded5f903261a8bee3481aad98d7ed47308b59563',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '90cb260644cdcdb81e87c6e3bf3ec5885eb23f3b',
+  'swiftshader_revision': 'ca8e3d7c153e7b89f881c02c8ca7ba0581afc5b1',
   # 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': '12d7da217249af8585e35d0807ee8c3686870714',
+  'pdfium_revision': '4e2aa897a424d907282e089da36836576d80210a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -201,7 +201,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': 'a7b33124672f301cebe0ca94a67ca7d0362e3d6a',
+  'catapult_revision': '5db62d36c9b55e85168a772a66dcaa352d2754fd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -257,7 +257,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '5a06fa466186698bcb0757bea201828e47527b98',
+  'spv_tools_revision': 'f051812343eb6ed34f3a327e1e82bebc06aa6a0f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -807,7 +807,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '162eba28f2077dd9a64e326588b478b2c3cbdf37',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a730bef0e7549ed488c1d4c797c51e0751f23a96',
       'condition': 'checkout_linux',
   },
 
@@ -832,7 +832,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c5b8a73247c432dbdbc010997f47994e9c3fff43',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4ce909c11c06bca34057e56ccefc72a1bf1e7e8c',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1378,7 +1378,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/${{platform}}',
-          'version': 'git_revision:f3b1cb52b997b012c7f0381fd12d964a160bab17',
+          'version': 'git_revision:655c9feb61e01564abecebe451ca90c26035e938',
         },
       ],
       'dep_type': 'cipd',
@@ -1391,7 +1391,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fd40163541b48d82731dad362f4111481bde4e33',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@76ce2f554e7af6822d19b014c7f451dcf0069562',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/docs/images/webview_implementations_menu.png b/android_webview/docs/images/webview_implementations_menu.png
new file mode 100644
index 0000000..535ff9dd
--- /dev/null
+++ b/android_webview/docs/images/webview_implementations_menu.png
Binary files differ
diff --git a/android_webview/docs/navbar.md b/android_webview/docs/navbar.md
index da2e804b..29485b7 100644
--- a/android_webview/docs/navbar.md
+++ b/android_webview/docs/navbar.md
@@ -4,6 +4,7 @@
 * [Quick start](/android_webview/docs/quick-start.md)
 * [Device Setup](/android_webview/docs/device-setup.md)
 * [Test instructions](/android_webview/docs/test-instructions.md)
+* [WebView Beta](/android_webview/docs/prerelease.md)
 * [WebView Shell](/android_webview/docs/webview-shell.md)
 * [Commandline flags](/android_webview/docs/commandline-flags.md)
 * [Net debugging](/android_webview/docs/net-debugging.md)
diff --git a/android_webview/docs/prerelease.md b/android_webview/docs/prerelease.md
new file mode 100644
index 0000000..635ea9f
--- /dev/null
+++ b/android_webview/docs/prerelease.md
@@ -0,0 +1,121 @@
+# Try out WebView Beta, Dev, or Canary
+
+Using a pre-release channel of WebView allows you to test new, upcoming features
+and enhancements to WebView. This is especially useful for Android app
+developers who use WebView in their apps.
+
+[TOC]
+
+## Which channel do I want?
+
+Like [Chrome](https://www.chromium.org/getting-involved/dev-channel), WebView
+has four release channels:
+
+- Stable channel
+  - Installed and updated by default on every Android device with WebView
+  - Fully tested; least likely to crash or have other major bugs
+  - Updated every 2-3 weeks with minor releases, and every 6 weeks with major
+    releases
+- Beta channel
+  - Available on Android 5 (Lollipop) and later
+  - Tested before release, but not as extensively as stable
+  - One major update ahead of stable, minor updates every week
+- Dev channel
+  - Publicly available on Android 7 (Nougat) and later
+  - Two major updates ahead of stable, representing what is actively being
+    developed
+  - Updated once per week
+  - Minimally tested
+- Canary build
+  - Available on Android 7 (Nougat) and later
+  - Released daily
+  - Includes the latest code changes from the previous day
+  - Has not been tested or used
+
+On Android 7 (Nougat) and later, you can install multiple channels at the same
+time. This allows you to play with our latest code, while still keeping a tested
+version of WebView around.
+
+## How do I try a pre-release channel?
+
+Steps depend on your version of Android:
+
+### Android 7 through 9 (Nougat/Oreo/Pie)
+
+Pre-release channels must be downloaded and installed as separate apps, but one
+must be chosen to provide the system's WebView implementation at any given time.
+
+1. Download a pre-release channel of Chrome from the play store, available here:
+   - [Chrome Beta](https://play.google.com/store/apps/details?id=com.chrome.beta)
+   - [Chrome Dev](https://play.google.com/store/apps/details?id=com.chrome.dev)
+   - [Chrome Canary](https://play.google.com/store/apps/details?id=com.chrome.canary)
+2. Follow the [steps to enable Android's developer options
+   menu](https://developer.android.com/studio/debug/dev-options)
+3. Choose Developer Options > WebView implementation (see figure)
+
+   ![The "WebView implementation" menu](/android_webview/docs/images/webview_implementations_menu.png)
+
+4. Choose the Chrome channel that you would like to use for WebView
+
+#### Returning to stable WebView
+
+1. To return to WebView stable, select Chrome again in the WebView
+   implementation menu
+
+### Android 5 or 6 (Lollipop/Marshmallow) and Android TV
+
+Only one installation of WebView is allowed, but users can opt to receive the
+latest beta updates from the Play Store.
+
+1. [Join the beta tester program on Google
+   Play](https://play.google.com/apps/testing/com.google.android.webview)
+2. On your device, update Android System Webview [in the Play
+   Store](https://play.google.com/store/apps/details?id=com.google.android.webview)
+3. When the Play Store finishes updating, you will be using WebView beta!
+
+#### Returning to stable WebView
+
+1. [Leave the tester
+   program](https://play.google.com/apps/testing/com.google.android.webview)
+2. Uninstall all updates by visiting Settings > Apps > Android System WebView >
+   Three dots menu in the top right > Uninstall updates
+3. [Visit the Play Store
+   page](https://play.google.com/store/apps/details?id=com.google.android.webview)
+   one more time to install the latest updates to WebView stable, which will
+   include important security fixes.
+
+### Android 4.4 (KitKat) or earlier
+
+WebView does not receive updates on these versions of Android, so the
+pre-release channels of WebView are not available.
+
+## Reporting problems with pre-release WebView
+
+Any WebView-related bugs can be filed [here](https://goo.gl/9qkbdn).
+
+To best enable us to resolve the issue, please ensure you provide all of the
+information requested in the bug report template.
+
+## Command line tools
+
+Choosing your WebView implementation on Android 7 (Nougat) or later can also
+be done using adb, instead of the Settings UI:
+
+```shell
+adb shell cmd webviewupdate set-webview-implementation <packagename>
+```
+
+Package names are as follows:
+
+|App name                |Package name                            |
+|-----------------------:|----------------------------------------|
+|Chrome (stable)         |com.android.chrome                      |
+|Chrome Beta             |com.chrome.beta                         |
+|Chrome Dev              |com.chrome.dev                          |
+|Chrome Canary           |com.chrome.canary                       |
+
+## See also
+- [WebView channels in detail](/android_webview/docs/channels.md)
+- [Chrome Release
+  Channels](https://www.chromium.org/getting-involved/dev-channel)
+
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
index efd9942..88052e03 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -435,6 +435,23 @@
     }
 
     /**
+     * Adds a JavaScript interface to the AwContents. Does its work synchronously on the UI thread,
+     * and can be called from any thread. All the rules of {@link
+     * android.webkit.WebView#addJavascriptInterface} apply to this method (ex. you must call this
+     * <b>prior</b> to loading the frame you intend to load the JavaScript interface into).
+     *
+     * @param awContents the AwContents in which to insert the JavaScript interface.
+     * @param objectToInject the JavaScript interface to inject.
+     * @param javascriptIdentifier the name with which to refer to {@code objectToInject} from
+     *        JavaScript code.
+     */
+    public static void addJavascriptInterfaceOnUiThread(final AwContents awContents,
+            final Object objectToInject, final String javascriptIdentifier) {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> awContents.addJavascriptInterface(objectToInject, javascriptIdentifier));
+    }
+
+    /**
      * Wrapper around CriteriaHelper.pollInstrumentationThread. This uses AwActivityTestRule-specifc
      * timeouts and treats timeouts and exceptions as test failures automatically.
      */
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java
index f10a295..05b764d 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnRendererUnresponsiveTest.java
@@ -148,8 +148,7 @@
     }
 
     private void addJsBlockerInterface(final AwContents awContents, final JSBlocker blocker) {
-        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
-                () -> { awContents.addJavascriptInterface(blocker, "blocker"); });
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents, blocker, "blocker");
     }
 
     // This test requires the ability to terminate the renderer in order to recover from a
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
index 32bae58..21115d4 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
@@ -307,8 +307,7 @@
             AwActivityTestRule.enableJavaScriptOnUiThread(containerViews[i].getAwContents());
             final AwContents awContents = containerViews[i].getAwContents();
             final Test jsObject = new Test(i, awContents);
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                    () -> awContents.addJavascriptInterface(jsObject, "test"));
+            AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents, jsObject, "test");
             mActivityTestRule.loadDataSync(
                     awContents, contentsClient.getOnPageFinishedHelper(), html, "text/html", false);
             Assert.assertEquals(String.valueOf(i),
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index c80b01c8..a354c27d 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -450,13 +450,12 @@
                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final CallbackHelper callback = new CallbackHelper();
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            AwContents awContents = testView.getAwContents();
-            AwSettings awSettings = awContents.getSettings();
-            awSettings.setJavaScriptEnabled(true);
-            awContents.addJavascriptInterface(new JavaScriptObject(callback), "bridge");
-            awContents.evaluateJavaScriptForTests("window.bridge.run();", null);
-        });
+        AwContents awContents = testView.getAwContents();
+        AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(
+                awContents, new JavaScriptObject(callback), "bridge");
+        mActivityTestRule.executeJavaScriptAndWaitForResult(
+                awContents, mContentsClient, "window.bridge.run();");
         callback.waitForCallback(0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
index 51561c7..05bd2ee1 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
@@ -66,8 +66,7 @@
         }
 
         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> awContents.addJavascriptInterface(new Test(), "test"));
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents, new Test(), "test");
 
         mActivityTestRule.loadDataSync(
                 awContents, mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
@@ -107,10 +106,8 @@
             private int mValue;
         }
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            awContents1.addJavascriptInterface(new Test(1), "test");
-            awContents2.addJavascriptInterface(new Test(2), "test");
-        });
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents1, new Test(1), "test");
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents2, new Test(2), "test");
         final String html = "<html>Hello World</html>";
         mActivityTestRule.loadDataSync(
                 awContents1, mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
@@ -143,8 +140,7 @@
             private int mValue;
         }
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> awContents1.addJavascriptInterface(new Test(1), "test"));
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents1, new Test(1), "test");
         final String html = "<html>Hello World</html>";
         mActivityTestRule.loadDataSync(
                 awContents1, mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
@@ -158,8 +154,7 @@
         final AwContents awContents2 = view2.getAwContents();
         AwActivityTestRule.enableJavaScriptOnUiThread(awContents2);
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> awContents2.addJavascriptInterface(new Test(2), "test"));
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(awContents2, new Test(2), "test");
         mActivityTestRule.loadDataSync(
                 awContents2, client2.getOnPageFinishedHelper(), html, "text/html", false);
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
index 4baba6b..84e17af 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
@@ -4,8 +4,13 @@
 
 package org.chromium.android_webview.test;
 
+import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS;
+
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.webkit.JavascriptInterface;
+
+import com.google.common.util.concurrent.SettableFuture;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -23,6 +28,7 @@
 import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A test suite for WebView's network-related configuration. This tests WebView's default settings,
@@ -194,6 +200,60 @@
         }
     }
 
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "Network"})
+    public void testAccessControlAllowOriginHeader() throws Throwable {
+        mTestServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        try {
+            AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
+
+            final SettableFuture<Boolean> fetchResultFuture = SettableFuture.create();
+            Object injectedObject = new Object() {
+                @JavascriptInterface
+                public void success() {
+                    fetchResultFuture.set(true);
+                }
+                @JavascriptInterface
+                public void error() {
+                    fetchResultFuture.set(false);
+                }
+            };
+            AwActivityTestRule.addJavascriptInterfaceOnUiThread(
+                    mAwContents, injectedObject, "injectedObject");
+
+            // The test server will add the Access-Control-Allow-Origin header to the HTTP response
+            // for this resource. We should check WebView correctly respects this.
+            final String fetchWithAllowOrigin =
+                    mTestServer.getURL("/set-header?Access-Control-Allow-Origin:%20*");
+            String html = "<html>"
+                    + "  <head>"
+                    + "  </head>"
+                    + "  <body>"
+                    + "    HTML content does not matter."
+                    + "  </body>"
+                    + "</html>";
+            final String baseUrl = "http://some.origin.test/index.html";
+            mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
+                    mContentsClient.getOnPageFinishedHelper(), html,
+                    /* mimeType */ null, /* isBase64Encoded */ false, baseUrl,
+                    /* historyUrl */ null);
+
+            String script = "fetch('" + fetchWithAllowOrigin + "')"
+                    + "  .then(() => { injectedObject.success(); })"
+                    + "  .catch(() => { injectedObject.failure(); });";
+            mActivityTestRule.executeJavaScriptAndWaitForResult(
+                    mAwContents, mContentsClient, script);
+            Assert.assertTrue("fetch() should succeed, due to Access-Control-Allow-Origin header",
+                    fetchResultFuture.get(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            // If we timeout, this indicates the fetch() was erroneously blocked by CORS (as was the
+            // root cause of https://crbug.com/960165).
+        } finally {
+            mTestServer.stopAndDestroyServer();
+        }
+    }
+
     /**
      * Like {@link AwActivityTestRule#getJavaScriptResultBodyTextContent}, but it gets the text
      * content of the iframe instead. This assumes the main frame has only a single iframe.
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index 1eaedee..281c023 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -2184,9 +2184,8 @@
                     + "</body></html>";
             // Actual test. Blocking should trigger onerror handler.
             awSettings.setBlockNetworkLoads(true);
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                    () -> awContents.addJavascriptInterface(new AudioEvent(callback),
-                            "AudioEvent"));
+            AwActivityTestRule.addJavascriptInterfaceOnUiThread(
+                    awContents, new AudioEvent(callback), "AudioEvent");
             int count = callback.getCallCount();
             mActivityTestRule.loadDataSync(awContents, contentClient.getOnPageFinishedHelper(),
                     pageHtml, "text/html", false);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
index 074783d..52cebea2 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
@@ -115,8 +115,7 @@
         }
         final DummyJavaScriptInterface obj = new DummyJavaScriptInterface();
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> popupContents.addJavascriptInterface(obj, "dummy"));
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(popupContents, obj, "dummy");
 
         mActivityTestRule.loadPopupContents(mParentContents, popupInfo, null);
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
index 23439a4..fa730c3 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
@@ -138,8 +138,8 @@
         AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
 
         try {
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                    () -> mAwContents.addJavascriptInterface(mMessageObject, "messageObject"));
+            AwActivityTestRule.addJavascriptInterfaceOnUiThread(
+                    mAwContents, mMessageObject, "messageObject");
         } catch (Throwable t) {
             throw new RuntimeException(t);
         }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 6c24ecc..982009b 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1017,8 +1017,10 @@
     "voice_interaction/voice_interaction_controller.h",
     "wallpaper/wallpaper_base_view.cc",
     "wallpaper/wallpaper_base_view.h",
-    "wallpaper/wallpaper_controller_impl.cc",
-    "wallpaper/wallpaper_controller_impl.h",
+    "wallpaper/wallpaper_controller.cc",
+    "wallpaper/wallpaper_controller.h",
+    "wallpaper/wallpaper_controller_observer.h",
+    "wallpaper/wallpaper_info.h",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator.cc",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator.h",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator_observer.h",
diff --git a/ash/accelerators/debug_commands.cc b/ash/accelerators/debug_commands.cc
index 0258861e..d161a8b 100644
--- a/ash/accelerators/debug_commands.cc
+++ b/ash/accelerators/debug_commands.cc
@@ -11,7 +11,7 @@
 #include "ash/system/toast/toast_data.h"
 #include "ash/system/toast/toast_manager.h"
 #include "ash/touch/touch_devices_controller.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_state.h"
@@ -126,7 +126,8 @@
 
 void HandleToggleWallpaperMode() {
   static int index = 0;
-  auto* wallpaper_controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* wallpaper_controller =
+      Shell::Get()->wallpaper_controller();
   WallpaperInfo info("", WALLPAPER_LAYOUT_STRETCH, DEFAULT,
                      base::Time::Now().LocalMidnight());
   switch (++index % 4) {
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 8d54ed8..85cfb96 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -35,7 +35,7 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/voice_interaction/voice_interaction_controller.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/splitview/split_view_controller.h"
@@ -1019,9 +1019,9 @@
     client_->StartSearch(base::string16());
 }
 
-const std::vector<SkColor>&
-AppListControllerImpl::GetWallpaperProminentColors() {
-  return Shell::Get()->wallpaper_controller()->GetWallpaperColors();
+void AppListControllerImpl::GetWallpaperProminentColors(
+    GetWallpaperProminentColorsCallback callback) {
+  Shell::Get()->wallpaper_controller()->GetWallpaperColors(std::move(callback));
 }
 
 void AppListControllerImpl::ActivateItem(const std::string& id,
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 6d4ed88..430fb47 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -25,10 +25,10 @@
 #include "ash/public/cpp/app_list/app_list_controller.h"
 #include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
 #include "ash/public/cpp/shelf_types.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/session/session_observer.h"
 #include "ash/shell_observer.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
@@ -176,7 +176,8 @@
   void ViewShown(int64_t display_id) override;
   void ViewClosing() override;
   void ViewClosed() override;
-  const std::vector<SkColor>& GetWallpaperProminentColors() override;
+  void GetWallpaperProminentColors(
+      GetWallpaperProminentColorsCallback callback) override;
   void ActivateItem(const std::string& id,
                     int event_flags,
                     AppListLaunchedFrom launched_from) override;
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index e59ed53..64542f4e 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -110,7 +110,10 @@
   virtual void ViewClosed() = 0;
 
   // Gets the wallpaper prominent colors.
-  virtual const std::vector<SkColor>& GetWallpaperProminentColors() = 0;
+  using GetWallpaperProminentColorsCallback =
+      base::OnceCallback<void(const std::vector<SkColor>&)>;
+  virtual void GetWallpaperProminentColors(
+      GetWallpaperProminentColorsCallback callback) = 0;
 
   // Activates (opens) the item.
   virtual void ActivateItem(const std::string& id,
diff --git a/ash/app_list/test/app_list_test_view_delegate.cc b/ash/app_list/test/app_list_test_view_delegate.cc
index 958d5fe..af1a553 100644
--- a/ash/app_list/test/app_list_test_view_delegate.cc
+++ b/ash/app_list/test/app_list_test_view_delegate.cc
@@ -83,11 +83,6 @@
   search_model_->SetSearchEngineIsGoogle(is_google);
 }
 
-const std::vector<SkColor>&
-AppListTestViewDelegate::GetWallpaperProminentColors() {
-  return wallpaper_prominent_colors_;
-}
-
 void AppListTestViewDelegate::ActivateItem(
     const std::string& id,
     int event_flags,
diff --git a/ash/app_list/test/app_list_test_view_delegate.h b/ash/app_list/test/app_list_test_view_delegate.h
index 9eb472f0..7919d45 100644
--- a/ash/app_list/test/app_list_test_view_delegate.h
+++ b/ash/app_list/test/app_list_test_view_delegate.h
@@ -80,7 +80,8 @@
   void DismissAppList() override;
   void ViewClosing() override {}
   void ViewClosed() override {}
-  const std::vector<SkColor>& GetWallpaperProminentColors() override;
+  void GetWallpaperProminentColors(
+      GetWallpaperProminentColorsCallback callback) override {}
   void ActivateItem(const std::string& id,
                     int event_flags,
                     ash::AppListLaunchedFrom launched_from) override;
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 6eaa211..4174ff0 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -23,6 +23,7 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/wallpaper_types.h"
+#include "base/bind.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
@@ -130,20 +131,21 @@
   DISALLOW_COPY_AND_ASSIGN(SearchBoxFocusHost);
 };
 
-SkColor GetBackgroundShieldColor(const std::vector<SkColor>& colors,
+SkColor GetBackgroundShieldColor(const std::vector<SkColor>& prominent_colors,
                                  float color_opacity) {
   const U8CPU sk_opacity_value = static_cast<U8CPU>(255 * color_opacity);
 
   const SkColor default_color = SkColorSetA(
       app_list::AppListView::kDefaultBackgroundColor, sk_opacity_value);
 
-  if (colors.empty())
+  if (prominent_colors.empty())
     return default_color;
 
   DCHECK_EQ(static_cast<size_t>(ColorProfileType::NUM_OF_COLOR_PROFILES),
-            colors.size());
+            prominent_colors.size());
+
   const SkColor dark_muted =
-      colors[static_cast<int>(ColorProfileType::DARK_MUTED)];
+      prominent_colors[static_cast<int>(ColorProfileType::DARK_MUTED)];
   if (SK_ColorTRANSPARENT == dark_muted)
     return default_color;
 
@@ -1989,6 +1991,11 @@
   return coefficient * shield_opacity + (1 - coefficient) * shelf_opacity;
 }
 
+void AppListView::GetWallpaperProminentColors(
+    AppListViewDelegate::GetWallpaperProminentColorsCallback callback) {
+  delegate_->GetWallpaperProminentColors(std::move(callback));
+}
+
 void AppListView::SetBackgroundShieldColor() {
   // There is a chance when AppListView::OnWallpaperColorsChanged is called
   // from AppListViewDelegate, the |app_list_background_shield_| is not
@@ -2011,8 +2018,13 @@
     color_opacity = kAppListOpacityWithBlur;
   }
 
-  app_list_background_shield_->UpdateColor(GetBackgroundShieldColor(
-      delegate_->GetWallpaperProminentColors(), color_opacity));
+  GetWallpaperProminentColors(base::BindOnce(
+      [](base::WeakPtr<AppListView> self, float color_opacity,
+         const std::vector<SkColor>& prominent_colors) {
+        self->app_list_background_shield_->UpdateColor(
+            GetBackgroundShieldColor(prominent_colors, color_opacity));
+      },
+      weak_ptr_factory_.GetWeakPtr(), color_opacity));
 }
 
 void AppListView::RecordFolderMetrics() {
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index 3e6d166..133d1c3 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -411,7 +411,8 @@
   // Gets app list background opacity during dragging.
   float GetAppListBackgroundOpacityDuringDragging();
 
-  const std::vector<SkColor>& GetWallpaperProminentColors();
+  void GetWallpaperProminentColors(
+      AppListViewDelegate::GetWallpaperProminentColorsCallback callback);
   void SetBackgroundShieldColor();
 
   // Records the number of folders, and the number of items in folders for UMA
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index 379a2b3..bd53841a 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -396,18 +396,9 @@
 }
 
 void SearchBoxView::OnWallpaperColorsChanged() {
-  const auto& colors = view_delegate_->GetWallpaperProminentColors();
-  if (colors.empty())
-    return;
-
-  DCHECK_EQ(static_cast<size_t>(ColorProfileType::NUM_OF_COLOR_PROFILES),
-            colors.size());
-
-  SetSearchBoxColor(colors[static_cast<int>(ColorProfileType::DARK_MUTED)]);
-  UpdateSearchIcon();
-  search_box()->set_placeholder_text_color(search_box_color());
-  UpdateBackgroundColor(search_box::kSearchBoxBackgroundDefault);
-  SchedulePaint();
+  GetWallpaperProminentColors(
+      base::BindOnce(&SearchBoxView::OnWallpaperProminentColorsReceived,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void SearchBoxView::ProcessAutocomplete() {
@@ -448,6 +439,26 @@
   ClearAutocompleteText();
 }
 
+void SearchBoxView::GetWallpaperProminentColors(
+    AppListViewDelegate::GetWallpaperProminentColorsCallback callback) {
+  view_delegate_->GetWallpaperProminentColors(std::move(callback));
+}
+
+void SearchBoxView::OnWallpaperProminentColorsReceived(
+    const std::vector<SkColor>& prominent_colors) {
+  if (prominent_colors.empty())
+    return;
+  DCHECK_EQ(static_cast<size_t>(ColorProfileType::NUM_OF_COLOR_PROFILES),
+            prominent_colors.size());
+
+  SetSearchBoxColor(
+      prominent_colors[static_cast<int>(ColorProfileType::DARK_MUTED)]);
+  UpdateSearchIcon();
+  search_box()->set_placeholder_text_color(search_box_color());
+  UpdateBackgroundColor(search_box::kSearchBoxBackgroundDefault);
+  SchedulePaint();
+}
+
 void SearchBoxView::AcceptAutocompleteText() {
   if (!ShouldProcessAutocomplete())
     return;
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index 2af523e..5198637 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -109,6 +109,15 @@
   }
 
  private:
+  // Gets the wallpaper prominent colors.
+  void GetWallpaperProminentColors(
+      AppListViewDelegate::GetWallpaperProminentColorsCallback callback);
+
+  // Callback invoked when the wallpaper prominent colors are returned after
+  // calling |AppListViewDelegate::GetWallpaperProminentColors|.
+  void OnWallpaperProminentColorsReceived(
+      const std::vector<SkColor>& prominent_colors);
+
   // Notifies SearchBoxViewDelegate that the autocomplete text is valid.
   void AcceptAutocompleteText();
 
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc
index ade78c0e..5f55958 100644
--- a/ash/ash_prefs.cc
+++ b/ash/ash_prefs.cc
@@ -23,7 +23,7 @@
 #include "ash/system/power/power_prefs.h"
 #include "ash/system/session/logout_button_tray.h"
 #include "ash/touch/touch_devices_controller.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 
 namespace ash {
 
@@ -53,7 +53,7 @@
 
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry, bool for_test) {
   PaletteTray::RegisterLocalStatePrefs(registry);
-  WallpaperControllerImpl::RegisterLocalStatePrefs(registry);
+  WallpaperController::RegisterLocalStatePrefs(registry);
   BluetoothPowerController::RegisterLocalStatePrefs(registry);
   DetachableBaseHandler::RegisterPrefs(registry);
   PowerPrefs::RegisterLocalStatePrefs(registry);
diff --git a/ash/frame/non_client_frame_view_ash.cc b/ash/frame/non_client_frame_view_ash.cc
index a14f00ce..323cce96 100644
--- a/ash/frame/non_client_frame_view_ash.cc
+++ b/ash/frame/non_client_frame_view_ash.cc
@@ -146,9 +146,6 @@
   DISALLOW_COPY_AND_ASSIGN(NonClientFrameViewAshImmersiveHelper);
 };
 
-// static
-bool NonClientFrameViewAsh::use_empty_minimum_size_for_test_ = false;
-
 ///////////////////////////////////////////////////////////////////////////////
 // NonClientFrameViewAsh::OverlayView
 
@@ -380,7 +377,7 @@
 }
 
 gfx::Size NonClientFrameViewAsh::GetMinimumSize() const {
-  if (use_empty_minimum_size_for_test_ || !GetEnabled())
+  if (!GetEnabled())
     return gfx::Size();
 
   gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize());
diff --git a/ash/frame/non_client_frame_view_ash.h b/ash/frame/non_client_frame_view_ash.h
index 71e78fb48..2656e57 100644
--- a/ash/frame/non_client_frame_view_ash.h
+++ b/ash/frame/non_client_frame_view_ash.h
@@ -118,12 +118,6 @@
 
   views::Widget* frame() { return frame_; }
 
-  // Methods for testing.
-  static void set_use_empty_minimum_size_for_test(
-      bool use_empty_minimum_size_for_test) {
-    use_empty_minimum_size_for_test_ = use_empty_minimum_size_for_test;
-  }
-
  protected:
   // Called when overview mode or split view state changed. If overview mode
   // and split view mode are both active at the same time, the header of the
@@ -133,7 +127,6 @@
 
  private:
   class OverlayView;
-  friend class NonClientFrameViewAshSizeLock;
   friend class NonClientFrameViewAshTestWidgetDelegate;
   friend class TestWidgetConstraintsDelegate;
   friend class WindowServiceDelegateImplTest;
@@ -157,8 +150,6 @@
 
   OverlayView* overlay_view_ = nullptr;
 
-  static bool use_empty_minimum_size_for_test_;
-
   // Track whether the device is in overview mode. Set this to true when
   // overview mode started and false when overview mode finished. Use this to
   // check whether we should paint when splitview state changes instead of
diff --git a/ash/highlighter/highlighter_controller.cc b/ash/highlighter/highlighter_controller.cc
index 9d58217..4bff6bf 100644
--- a/ash/highlighter/highlighter_controller.cc
+++ b/ash/highlighter/highlighter_controller.cc
@@ -59,8 +59,7 @@
 
 }  // namespace
 
-HighlighterController::HighlighterController()
-    : binding_(this), weak_factory_(this) {
+HighlighterController::HighlighterController() : weak_factory_(this) {
   Shell::Get()->AddPreTargetHandler(this);
 }
 
@@ -100,19 +99,6 @@
     UpdateEnabledState(HighlighterEnabledState::kDisabledBySessionAbort);
 }
 
-void HighlighterController::BindRequest(
-    mojom::HighlighterControllerRequest request) {
-  binding_.Bind(std::move(request));
-}
-
-void HighlighterController::SetClient(
-    mojom::HighlighterControllerClientPtr client) {
-  client_ = std::move(client);
-  client_.set_connection_error_handler(
-      base::BindOnce(&HighlighterController::OnClientConnectionLost,
-                     weak_factory_.GetWeakPtr()));
-}
-
 void HighlighterController::SetEnabled(bool enabled) {
   FastInkPointerController::SetEnabled(enabled);
   if (enabled) {
@@ -132,13 +118,6 @@
     if (highlighter_view_ && !highlighter_view_->animating())
       DestroyPointerView();
   }
-
-  if (client_)
-    client_->HandleEnabledStateChange(enabled);
-}
-
-void HighlighterController::ExitHighlighterMode() {
-  CallExitCallback();
 }
 
 views::View* HighlighterController::GetPointerView() const {
@@ -241,9 +220,6 @@
             ? gfx::ToEnclosingRect(box)
             : gfx::ToEnclosingRect(
                   gfx::ScaleRect(box, GetScreenshotScale(current_window)));
-    if (client_)
-      client_->HandleSelection(selection_rect);
-
     for (auto& observer : observers_)
       observer.OnHighlighterSelectionRecognized(selection_rect);
 
@@ -302,21 +278,9 @@
   result_view_.reset();
 }
 
-void HighlighterController::OnClientConnectionLost() {
-  client_.reset();
-  binding_.Close();
-  // The client has detached, force-exit the highlighter mode.
-  CallExitCallback();
-}
-
 void HighlighterController::CallExitCallback() {
   if (!exit_callback_.is_null())
     std::move(exit_callback_).Run();
 }
 
-void HighlighterController::FlushMojoForTesting() {
-  if (client_)
-    client_.FlushForTesting();
-}
-
 }  // namespace ash
diff --git a/ash/highlighter/highlighter_controller.h b/ash/highlighter/highlighter_controller.h
index 4267dd6f..21ec70a2 100644
--- a/ash/highlighter/highlighter_controller.h
+++ b/ash/highlighter/highlighter_controller.h
@@ -9,16 +9,18 @@
 
 #include "ash/ash_export.h"
 #include "ash/components/fast_ink/fast_ink_pointer_controller.h"
-#include "ash/public/interfaces/highlighter_controller.mojom.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
-#include "mojo/public/cpp/bindings/binding.h"
 
 namespace base {
 class OneShotTimer;
 }
 
+namespace gfx {
+class Rect;
+}
+
 namespace ash {
 
 class HighlighterResultView;
@@ -42,8 +44,7 @@
 // Enables/disables highlighter as well as receives points
 // and passes them off to be rendered.
 class ASH_EXPORT HighlighterController
-    : public fast_ink::FastInkPointerController,
-      public mojom::HighlighterController {
+    : public fast_ink::FastInkPointerController {
  public:
   // Interface for classes that wish to be notified with highlighter status.
   class Observer {
@@ -79,12 +80,6 @@
   // calling this method is a no-op.
   void AbortSession();
 
-  void BindRequest(mojom::HighlighterControllerRequest request);
-
-  // mojom::HighlighterController:
-  void SetClient(mojom::HighlighterControllerClientPtr client) override;
-  void ExitHighlighterMode() override;
-
  private:
   friend class HighlighterControllerTestApi;
 
@@ -107,14 +102,9 @@
   // Destroys |result_view_|, if it exists.
   void DestroyResultView();
 
-  // Called when the mojo connection with the client is closed.
-  void OnClientConnectionLost();
-
   // Calls and clears the mode exit callback, if it is set.
   void CallExitCallback();
 
-  void FlushMojoForTesting();
-
   // Caches the highlighter enabled state.
   HighlighterEnabledState enabled_state_ =
       HighlighterEnabledState::kDisabledByUser;
@@ -151,12 +141,6 @@
   // If true, the mode is not exited until a valid selection is made.
   bool require_success_ = true;
 
-  // Binding for mojom::HighlighterController interface.
-  mojo::Binding<ash::mojom::HighlighterController> binding_;
-
-  // Interface to highlighter controller client (chrome).
-  mojom::HighlighterControllerClientPtr client_;
-
   base::ObserverList<Observer>::Unchecked observers_;
 
   base::WeakPtrFactory<HighlighterController> weak_factory_;
diff --git a/ash/highlighter/highlighter_controller_test_api.cc b/ash/highlighter/highlighter_controller_test_api.cc
index 89a7b77..1a49dbb 100644
--- a/ash/highlighter/highlighter_controller_test_api.cc
+++ b/ash/highlighter/highlighter_controller_test_api.cc
@@ -13,12 +13,12 @@
 
 HighlighterControllerTestApi::HighlighterControllerTestApi(
     HighlighterController* instance)
-    : binding_(this), instance_(instance) {
+    : instance_(instance) {
   AttachClient();
 }
 
 HighlighterControllerTestApi::~HighlighterControllerTestApi() {
-  if (binding_.is_bound())
+  if (scoped_observer_)
     DetachClient();
   if (instance_->enabled())
     instance_->SetEnabled(false);
@@ -26,25 +26,19 @@
 }
 
 void HighlighterControllerTestApi::AttachClient() {
-  DCHECK(!binding_.is_bound());
-  DCHECK(!highlighter_controller_);
-  instance_->BindRequest(mojo::MakeRequest(&highlighter_controller_));
-  ash::mojom::HighlighterControllerClientPtr client;
-  binding_.Bind(mojo::MakeRequest(&client));
-  highlighter_controller_->SetClient(std::move(client));
-  highlighter_controller_.FlushForTesting();
+  scoped_observer_ = std::make_unique<ScopedObserver>(this);
+  scoped_observer_->Add(instance_);
 }
 
 void HighlighterControllerTestApi::DetachClient() {
-  DCHECK(binding_.is_bound());
-  DCHECK(highlighter_controller_);
-  highlighter_controller_ = nullptr;
-  binding_.Close();
-  instance_->FlushMojoForTesting();
+  scoped_observer_.reset();
+  instance_->CallExitCallback();
 }
 
 void HighlighterControllerTestApi::SetEnabled(bool enabled) {
-  instance_->SetEnabled(enabled);
+  instance_->UpdateEnabledState(
+      enabled ? HighlighterEnabledState::kEnabled
+              : HighlighterEnabledState::kDisabledBySessionComplete);
 }
 
 void HighlighterControllerTestApi::DestroyPointerView() {
@@ -85,21 +79,22 @@
 }
 
 bool HighlighterControllerTestApi::HandleEnabledStateChangedCalled() {
-  instance_->FlushMojoForTesting();
   return handle_enabled_state_changed_called_;
 }
 
 bool HighlighterControllerTestApi::HandleSelectionCalled() {
-  instance_->FlushMojoForTesting();
   return handle_selection_called_;
 }
 
-void HighlighterControllerTestApi::HandleSelection(const gfx::Rect& rect) {
+void HighlighterControllerTestApi::OnHighlighterSelectionRecognized(
+    const gfx::Rect& rect) {
   handle_selection_called_ = true;
   selection_ = rect;
 }
 
-void HighlighterControllerTestApi::HandleEnabledStateChange(bool enabled) {
+void HighlighterControllerTestApi::OnHighlighterEnabledChanged(
+    HighlighterEnabledState state) {
+  const bool enabled = (state == HighlighterEnabledState::kEnabled);
   handle_enabled_state_changed_called_ = true;
   enabled_ = enabled;
 }
diff --git a/ash/highlighter/highlighter_controller_test_api.h b/ash/highlighter/highlighter_controller_test_api.h
index 8349d32..b5fbedd 100644
--- a/ash/highlighter/highlighter_controller_test_api.h
+++ b/ash/highlighter/highlighter_controller_test_api.h
@@ -5,9 +5,9 @@
 #ifndef ASH_HIGHLIGHTER_HIGHLIGHTER_CONTROLLER_TEST_API_H_
 #define ASH_HIGHLIGHTER_HIGHLIGHTER_CONTROLLER_TEST_API_H_
 
-#include "ash/public/interfaces/highlighter_controller.mojom.h"
+#include "ash/highlighter/highlighter_controller.h"
 #include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "base/scoped_observer.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace fast_ink {
@@ -16,13 +16,11 @@
 
 namespace ash {
 
-class HighlighterController;
-
 // An api for testing the HighlighterController class.
 // Implements ash::mojom::HighlighterControllerClient and binds itself as the
 // client to provide the tests with access to gesture recognition results.
 class HighlighterControllerTestApi
-    : public ash::mojom::HighlighterControllerClient {
+    : public ash::HighlighterController::Observer {
  public:
   explicit HighlighterControllerTestApi(HighlighterController* instance);
   ~HighlighterControllerTestApi() override;
@@ -46,28 +44,22 @@
   const fast_ink::FastInkPoints& predicted_points() const;
 
   void ResetEnabledState() { handle_enabled_state_changed_called_ = false; }
-  // Flushes the mojo connection, then checks whether HandleEnabledStateChange
-  // has been called on the client since the last call to ResetEnabledState.
   bool HandleEnabledStateChangedCalled();
   bool enabled() const { return enabled_; }
 
   void ResetSelection() { handle_selection_called_ = false; }
-  // Flushes the mojo connection, then checks whether HandleSelection
-  // has been called on the client since the last call to ResetSelection.
   bool HandleSelectionCalled();
   const gfx::Rect& selection() const { return selection_; }
 
  private:
+  using ScopedObserver =
+      ScopedObserver<HighlighterController, HighlighterController::Observer>;
+
   // HighlighterSelectionObserver:
-  void HandleSelection(const gfx::Rect& rect) override;
-  void HandleEnabledStateChange(bool enabled) override;
+  void OnHighlighterSelectionRecognized(const gfx::Rect& rect) override;
+  void OnHighlighterEnabledChanged(HighlighterEnabledState state) override;
 
-  // Binds to the client interface.
-  mojo::Binding<ash::mojom::HighlighterControllerClient> binding_;
-
-  // HighlighterController interface.
-  ash::mojom::HighlighterControllerPtr highlighter_controller_;
-
+  std::unique_ptr<ScopedObserver> scoped_observer_;
   HighlighterController* instance_;
 
   bool handle_selection_called_ = false;
diff --git a/ash/home_screen/home_screen_controller.cc b/ash/home_screen/home_screen_controller.cc
index 77d46ea6..e436114 100644
--- a/ash/home_screen/home_screen_controller.cc
+++ b/ash/home_screen/home_screen_controller.cc
@@ -10,7 +10,7 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_session.h"
diff --git a/ash/home_screen/home_screen_controller.h b/ash/home_screen/home_screen_controller.h
index fb6469b..ca51256 100644
--- a/ash/home_screen/home_screen_controller.h
+++ b/ash/home_screen/home_screen_controller.h
@@ -9,7 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/home_screen/home_screen_presenter.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "base/macros.h"
 
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index a1c241c..3c2ea1f 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -334,14 +334,6 @@
   NOTIMPLEMENTED();
 }
 
-void LoginScreenController::ShowWarningBanner(const base::string16& message) {
-  login_data_dispatcher_.ShowWarningBanner(message);
-}
-
-void LoginScreenController::HideWarningBanner() {
-  login_data_dispatcher_.HideWarningBanner();
-}
-
 void LoginScreenController::ClearErrors() {
   NOTIMPLEMENTED();
 }
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 4e5059077..6f2165c 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -118,8 +118,6 @@
                         const std::string& error_text,
                         const std::string& help_link_text,
                         int32_t help_topic_id) override;
-  void ShowWarningBanner(const base::string16& message) override;
-  void HideWarningBanner() override;
   void ClearErrors() override;
   void SetAuthType(const AccountId& account_id,
                    proximity_auth::mojom::AuthType auth_type,
diff --git a/ash/login/login_screen_controller_unittest.cc b/ash/login/login_screen_controller_unittest.cc
index d4c93af..abac013 100644
--- a/ash/login/login_screen_controller_unittest.cc
+++ b/ash/login/login_screen_controller_unittest.cc
@@ -15,7 +15,7 @@
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 75e8c83..8aa4961 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -874,13 +874,19 @@
   }
 }
 
-void LockContentsView::OnShowWarningBanner(const base::string16& message) {
-  DCHECK(!message.empty());
+void LockContentsView::OnWarningMessageUpdated(const base::string16& message) {
+  if (message.empty()) {
+    if (warning_banner_bubble_->GetVisible())
+      warning_banner_bubble_->Hide();
+    return;
+  }
+
   if (!CurrentBigUserView() || !CurrentBigUserView()->auth_user()) {
     LOG(ERROR) << "Unable to find the current active big user to show a "
                   "warning banner.";
     return;
   }
+
   if (warning_banner_bubble_->GetVisible())
     warning_banner_bubble_->Hide();
   // Shows warning banner as a persistent error bubble.
@@ -898,11 +904,6 @@
   warning_banner_bubble_->Show();
 }
 
-void LockContentsView::OnHideWarningBanner() {
-  if (warning_banner_bubble_->GetVisible())
-    warning_banner_bubble_->Hide();
-}
-
 void LockContentsView::OnLockScreenNoteStateChanged(
     mojom::TrayActionState state) {
   if (disable_lock_screen_note_)
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index d0a0d017..10cc494e 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -152,8 +152,7 @@
   void OnForceOnlineSignInForUser(const AccountId& user) override;
   void OnShowEasyUnlockIcon(const AccountId& user,
                             const EasyUnlockIconOptions& icon) override;
-  void OnShowWarningBanner(const base::string16& message) override;
-  void OnHideWarningBanner() override;
+  void OnWarningMessageUpdated(const base::string16& message) override;
   void OnSystemInfoChanged(bool show,
                            const std::string& os_version_label_text,
                            const std::string& enterprise_info_text,
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 0c0b2c4f..54e0a97 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -2167,15 +2167,15 @@
   EXPECT_FALSE(test_api.warning_banner_bubble()->GetVisible());
 
   // Verifies that a warning banner is shown by giving a non-empty message.
-  DataDispatcher()->ShowWarningBanner(base::ASCIIToUTF16("foo"));
+  DataDispatcher()->UpdateWarningMessage(base::ASCIIToUTF16("foo"));
   EXPECT_TRUE(test_api.warning_banner_bubble()->GetVisible());
 
   // Verifies that a warning banner is hidden by HideWarningBanner().
-  DataDispatcher()->HideWarningBanner();
+  DataDispatcher()->UpdateWarningMessage({});
   EXPECT_FALSE(test_api.warning_banner_bubble()->GetVisible());
 
   // Shows a warning banner again.
-  DataDispatcher()->ShowWarningBanner(base::ASCIIToUTF16("foo"));
+  DataDispatcher()->UpdateWarningMessage(base::ASCIIToUTF16("foo"));
   EXPECT_TRUE(test_api.warning_banner_bubble()->GetVisible());
 
   // Attempt and fail user auth - an auth error is expected to be shown.
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc
index b24f61f..91915f34 100644
--- a/ash/login/ui/lock_debug_view.cc
+++ b/ash/login/ui/lock_debug_view.cc
@@ -24,7 +24,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
@@ -436,12 +436,10 @@
                                     enterprise_info, bluetooth_name);
   }
 
-  void ShowWarningBanner(const base::string16& message) {
-    debug_dispatcher_.ShowWarningBanner(message);
+  void UpdateWarningMessage(const base::string16& message) {
+    debug_dispatcher_.UpdateWarningMessage(message);
   }
 
-  void HideWarningBanner() { debug_dispatcher_.HideWarningBanner(); }
-
   // LoginDataDispatcher::Observer:
   void OnUsersChanged(const std::vector<LoginUserInfo>& users) override {
     // Update root_users_ to new source data.
@@ -975,9 +973,9 @@
   // Show or hide warning banner.
   if (sender->GetID() == ButtonId::kGlobalToggleWarningBanner) {
     if (is_warning_banner_shown_) {
-      debug_data_dispatcher_->HideWarningBanner();
+      debug_data_dispatcher_->UpdateWarningMessage({});
     } else {
-      debug_data_dispatcher_->ShowWarningBanner(base::ASCIIToUTF16(
+      debug_data_dispatcher_->UpdateWarningMessage(base::ASCIIToUTF16(
           "A critical update is ready to install. Sign in to get started."));
     }
     is_warning_banner_shown_ = !is_warning_banner_shown_;
diff --git a/ash/login/ui/lock_screen.cc b/ash/login/ui/lock_screen.cc
index 7eedb40..4bf83d4 100644
--- a/ash/login/ui/lock_screen.cc
+++ b/ash/login/ui/lock_screen.cc
@@ -18,7 +18,7 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/tray_action/tray_action.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "chromeos/constants/chromeos_switches.h"
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index 93e6284c..72f2388 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -24,7 +24,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/night_light/time_of_day.h"
 #include "ash/system/toast/toast_manager.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "base/i18n/time_formatting.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/login/ui/login_big_user_view.cc b/ash/login/ui/login_big_user_view.cc
index c1e4a105..80afdbf2 100644
--- a/ash/login/ui/login_big_user_view.cc
+++ b/ash/login/ui/login_big_user_view.cc
@@ -5,7 +5,7 @@
 #include "ash/login/ui/login_big_user_view.h"
 #include "ash/public/cpp/login_constants.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "components/account_id/account_id.h"
 #include "ui/views/background.h"
 #include "ui/views/layout/fill_layout.h"
diff --git a/ash/login/ui/login_big_user_view.h b/ash/login/ui/login_big_user_view.h
index daeda31..4ac774fb 100644
--- a/ash/login/ui/login_big_user_view.h
+++ b/ash/login/ui/login_big_user_view.h
@@ -12,7 +12,7 @@
 #include "ash/login/ui/non_accessible_view.h"
 #include "ash/login/ui/parent_access_view.h"
 #include "ash/public/cpp/session/user_info.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 
 namespace ash {
 
diff --git a/ash/login/ui/login_data_dispatcher.cc b/ash/login/ui/login_data_dispatcher.cc
index b5070c0e..587e1a7 100644
--- a/ash/login/ui/login_data_dispatcher.cc
+++ b/ash/login/ui/login_data_dispatcher.cc
@@ -48,11 +48,9 @@
     const AccountId& user,
     const EasyUnlockIconOptions& icon) {}
 
-void LoginDataDispatcher::Observer::OnShowWarningBanner(
+void LoginDataDispatcher::Observer::OnWarningMessageUpdated(
     const base::string16& message) {}
 
-void LoginDataDispatcher::Observer::OnHideWarningBanner() {}
-
 void LoginDataDispatcher::Observer::OnSystemInfoChanged(
     bool show,
     const std::string& os_version_label_text,
@@ -167,14 +165,9 @@
     observer.OnShowEasyUnlockIcon(user, icon);
 }
 
-void LoginDataDispatcher::ShowWarningBanner(const base::string16& message) {
+void LoginDataDispatcher::UpdateWarningMessage(const base::string16& message) {
   for (auto& observer : observers_)
-    observer.OnShowWarningBanner(message);
-}
-
-void LoginDataDispatcher::HideWarningBanner() {
-  for (auto& observer : observers_)
-    observer.OnHideWarningBanner();
+    observer.OnWarningMessageUpdated(message);
 }
 
 void LoginDataDispatcher::SetSystemInfo(
diff --git a/ash/login/ui/login_data_dispatcher.h b/ash/login/ui/login_data_dispatcher.h
index c943ced..761b517 100644
--- a/ash/login/ui/login_data_dispatcher.h
+++ b/ash/login/ui/login_data_dispatcher.h
@@ -86,11 +86,9 @@
     virtual void OnShowEasyUnlockIcon(const AccountId& user,
                                       const EasyUnlockIconOptions& icon);
 
-    // Called when a warning banner message should be displayed.
-    virtual void OnShowWarningBanner(const base::string16& message);
-
-    // Called when a warning banner message should be hidden.
-    virtual void OnHideWarningBanner();
+    // Called when a warning message should be displayed, or hidden if |message|
+    // is empty.
+    virtual void OnWarningMessageUpdated(const base::string16& message);
 
     // Called when the system info has changed.
     virtual void OnSystemInfoChanged(bool show,
@@ -168,8 +166,7 @@
   void SetLockScreenNoteState(mojom::TrayActionState state);
   void ShowEasyUnlockIcon(const AccountId& user,
                           const EasyUnlockIconOptions& icon) override;
-  void ShowWarningBanner(const base::string16& message);
-  void HideWarningBanner();
+  void UpdateWarningMessage(const base::string16& message) override;
   void SetSystemInfo(bool show_if_hidden,
                      const std::string& os_version_label_text,
                      const std::string& enterprise_info_text,
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc
index c4cd4bcb..f4f87cc 100644
--- a/ash/login/ui/login_keyboard_test_base.cc
+++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -15,6 +15,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/command_line.h"
 #include "base/strings/strcat.h"
 
diff --git a/ash/login/ui/login_test_base.cc b/ash/login/ui/login_test_base.cc
index 86c34440..ab14fc2 100644
--- a/ash/login/ui/login_test_base.cc
+++ b/ash/login/ui/login_test_base.cc
@@ -13,7 +13,7 @@
 #include "ash/public/interfaces/tray_action.mojom.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "base/strings/strcat.h"
 #include "ui/views/widget/widget.h"
diff --git a/ash/login/ui/parent_access_view.cc b/ash/login/ui/parent_access_view.cc
index 61ef1676..af1d35b 100644
--- a/ash/login/ui/parent_access_view.cc
+++ b/ash/login/ui/parent_access_view.cc
@@ -15,7 +15,7 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/optional.h"
diff --git a/ash/login/ui/scrollable_users_list_view.cc b/ash/login/ui/scrollable_users_list_view.cc
index a7fddacf..76b2a02 100644
--- a/ash/login/ui/scrollable_users_list_view.cc
+++ b/ash/login/ui/scrollable_users_list_view.cc
@@ -13,7 +13,7 @@
 #include "ash/login/ui/views_utils.h"
 #include "ash/public/cpp/login_constants.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "base/numerics/ranges.h"
 #include "base/timer/timer.h"
@@ -387,7 +387,7 @@
 
   // Only draw a gradient if the wallpaper is blurred. Otherwise, draw a rounded
   // rectangle.
-  if (Shell::Get()->wallpaper_controller()->IsWallpaperBlurred()) {
+  if (ash::Shell::Get()->wallpaper_controller()->IsWallpaperBlurred()) {
     cc::PaintFlags flags;
 
     // Only draw a gradient if the content can be scrolled.
diff --git a/ash/login/ui/scrollable_users_list_view.h b/ash/login/ui/scrollable_users_list_view.h
index 6a4637f5..5e0e9e0 100644
--- a/ash/login/ui/scrollable_users_list_view.h
+++ b/ash/login/ui/scrollable_users_list_view.h
@@ -10,7 +10,7 @@
 #include "ash/ash_export.h"
 #include "ash/login/ui/login_display_style.h"
 #include "ash/login/ui/login_user_view.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "base/scoped_observer.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/views/controls/scroll_view.h"
@@ -97,7 +97,8 @@
 
   GradientParams gradient_params_;
 
-  ScopedObserver<WallpaperController, ScrollableUsersListView> observer_{this};
+  ScopedObserver<WallpaperController, WallpaperControllerObserver> observer_{
+      this};
 
   DISALLOW_COPY_AND_ASSIGN(ScrollableUsersListView);
 };
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index ffde7009..8a4a08a 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -17,7 +17,6 @@
 #include "ash/autotest/shelf_integration_test_api.h"
 #include "ash/display/cros_display_config.h"
 #include "ash/events/event_rewriter_controller.h"
-#include "ash/highlighter/highlighter_controller.h"
 #include "ash/ime/ime_controller.h"
 #include "ash/keyboard/ash_keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
@@ -35,6 +34,7 @@
 #include "ash/system/night_light/night_light_controller.h"
 #include "ash/tray_action/tray_action.h"
 #include "ash/voice_interaction/voice_interaction_controller.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -111,11 +111,6 @@
   Shell::Get()->event_rewriter_controller()->BindRequest(std::move(request));
 }
 
-void BindHighlighterControllerRequestOnMainThread(
-    mojom::HighlighterControllerRequest request) {
-  Shell::Get()->highlighter_controller()->BindRequest(std::move(request));
-}
-
 void BindImeControllerRequestOnMainThread(mojom::ImeControllerRequest request) {
   Shell::Get()->ime_controller()->BindRequest(std::move(request));
 }
@@ -177,6 +172,11 @@
   Shell::Get()->vpn_list()->BindRequest(std::move(request));
 }
 
+void BindWallpaperRequestOnMainThread(
+    mojom::WallpaperControllerRequest request) {
+  Shell::Get()->wallpaper_controller()->BindRequest(std::move(request));
+}
+
 }  // namespace
 
 void RegisterInterfaces(
@@ -227,9 +227,6 @@
       base::BindRepeating(&BindEventRewriterControllerRequestOnMainThread),
       main_thread_task_runner);
   registry->AddInterface(
-      base::BindRepeating(&BindHighlighterControllerRequestOnMainThread),
-      main_thread_task_runner);
-  registry->AddInterface(
       base::BindRepeating(&BindImeControllerRequestOnMainThread),
       main_thread_task_runner);
   registry->AddInterface(
@@ -261,6 +258,8 @@
       main_thread_task_runner);
   registry->AddInterface(base::BindRepeating(&BindVpnListRequestOnMainThread),
                          main_thread_task_runner);
+  registry->AddInterface(base::BindRepeating(&BindWallpaperRequestOnMainThread),
+                         main_thread_task_runner);
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kAshEnableTestInterfaces)) {
diff --git a/ash/multi_user/multi_user_window_manager_impl.cc b/ash/multi_user/multi_user_window_manager_impl.cc
index 22188ba..bdea8799 100644
--- a/ash/multi_user/multi_user_window_manager_impl.cc
+++ b/ash/multi_user/multi_user_window_manager_impl.cc
@@ -12,7 +12,6 @@
 #include "ash/public/cpp/multi_user_window_manager_delegate.h"
 #include "ash/public/cpp/multi_user_window_manager_observer.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/wallpaper_user_info.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
@@ -59,18 +58,20 @@
   return false;
 }
 
-WallpaperUserInfo WallpaperUserInfoForAccount(const AccountId& account_id) {
+mojom::WallpaperUserInfoPtr WallpaperUserInfoForAccount(
+    const AccountId& account_id) {
   DCHECK(account_id.is_valid());
-  WallpaperUserInfo wallpaper_user_info;
+  mojom::WallpaperUserInfoPtr wallpaper_user_info =
+      mojom::WallpaperUserInfo::New();
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
   for (const std::unique_ptr<UserSession>& user_session :
        session_controller->GetUserSessions()) {
     if (user_session->user_info.account_id == account_id) {
-      wallpaper_user_info.account_id = account_id;
-      wallpaper_user_info.type = user_session->user_info.type;
-      wallpaper_user_info.is_ephemeral = user_session->user_info.is_ephemeral;
-      wallpaper_user_info.has_gaia_account =
+      wallpaper_user_info->account_id = account_id;
+      wallpaper_user_info->type = user_session->user_info.type;
+      wallpaper_user_info->is_ephemeral = user_session->user_info.is_ephemeral;
+      wallpaper_user_info->has_gaia_account =
           user_session->user_info.has_gaia_account;
       return wallpaper_user_info;
     }
diff --git a/ash/multi_user/user_switch_animator.cc b/ash/multi_user/user_switch_animator.cc
index 78eabc79..7abe9c4b 100644
--- a/ash/multi_user/user_switch_animator.cc
+++ b/ash/multi_user/user_switch_animator.cc
@@ -7,7 +7,7 @@
 #include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/public/cpp/multi_user_window_manager_delegate.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_positioner.h"
 #include "base/bind.h"
@@ -82,11 +82,11 @@
 
 UserSwitchAnimator::UserSwitchAnimator(
     MultiUserWindowManagerImpl* owner,
-    const WallpaperUserInfo& wallpaper_user_info,
+    mojom::WallpaperUserInfoPtr wallpaper_user_info,
     base::TimeDelta animation_speed)
     : owner_(owner),
-      wallpaper_user_info_(wallpaper_user_info),
-      new_account_id_(wallpaper_user_info_.account_id),
+      wallpaper_user_info_(std::move(wallpaper_user_info)),
+      new_account_id_(wallpaper_user_info_->account_id),
       animation_speed_(animation_speed),
       animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
       screen_cover_(GetScreenCover(NULL)),
@@ -160,7 +160,8 @@
 }
 
 void UserSwitchAnimator::TransitionWallpaper(AnimationStep animation_step) {
-  auto* wallpaper_controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* wallpaper_controller =
+      Shell::Get()->wallpaper_controller();
 
   // Handle the wallpaper switch.
   if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) {
@@ -171,7 +172,8 @@
     wallpaper_controller->SetAnimationDuration(
         duration > kMinimalAnimationTime ? duration : kMinimalAnimationTime);
     if (screen_cover_ != NEW_USER_COVERS_SCREEN) {
-      wallpaper_controller->ShowUserWallpaper(wallpaper_user_info_);
+      DCHECK(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(std::move(wallpaper_user_info_));
       wallpaper_user_id_for_test_ =
           (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") +
           new_account_id_.Serialize();
@@ -179,8 +181,10 @@
   } else if (animation_step == ANIMATION_STEP_FINALIZE) {
     // Revert the wallpaper cross dissolve animation duration back to the
     // default.
-    if (screen_cover_ == NEW_USER_COVERS_SCREEN)
-      wallpaper_controller->ShowUserWallpaper(wallpaper_user_info_);
+    if (screen_cover_ == NEW_USER_COVERS_SCREEN) {
+      DCHECK(wallpaper_user_info_);
+      wallpaper_controller->ShowUserWallpaper(std::move(wallpaper_user_info_));
+    }
 
     // Coming here the wallpaper user id is the final result. No matter how we
     // got here.
diff --git a/ash/multi_user/user_switch_animator.h b/ash/multi_user/user_switch_animator.h
index 79a2414..78978e6 100644
--- a/ash/multi_user/user_switch_animator.h
+++ b/ash/multi_user/user_switch_animator.h
@@ -10,7 +10,7 @@
 #include <string>
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/wallpaper_user_info.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -39,7 +39,7 @@
   // Creates a UserSwitchAnimator to animate between the current user and
   // |user_info|.
   UserSwitchAnimator(MultiUserWindowManagerImpl* owner,
-                     const WallpaperUserInfo& user_info,
+                     mojom::WallpaperUserInfoPtr user_info,
                      base::TimeDelta animation_speed);
   ~UserSwitchAnimator();
 
@@ -103,7 +103,7 @@
 
   // Contains the wallpaper configuration for the user switching to. This is
   // passed to the WallpaperController at the right time.
-  WallpaperUserInfo wallpaper_user_info_;
+  mojom::WallpaperUserInfoPtr wallpaper_user_info_;
 
   // The new user to set.
   AccountId new_account_id_;
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 4247ccdb7..a6b0858 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -149,14 +149,7 @@
     "tablet_mode.h",
     "touch_uma.cc",
     "touch_uma.h",
-    "wallpaper_controller.cc",
-    "wallpaper_controller.h",
-    "wallpaper_controller_client.h",
-    "wallpaper_controller_observer.cc",
-    "wallpaper_controller_observer.h",
-    "wallpaper_info.h",
     "wallpaper_types.h",
-    "wallpaper_user_info.h",
     "window_animation_types.h",
     "window_properties.cc",
     "window_properties.h",
@@ -192,7 +185,6 @@
     "//ash/public/interfaces:interfaces_internal",
     "//base",
     "//components/session_manager:base",
-    "//components/user_manager",
     "//ui/gfx",
   ]
 
diff --git a/ash/public/cpp/login_screen_model.h b/ash/public/cpp/login_screen_model.h
index 4d44f088..6285807 100644
--- a/ash/public/cpp/login_screen_model.h
+++ b/ash/public/cpp/login_screen_model.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "ash/public/cpp/ash_public_export.h"
+#include "base/strings/string16.h"
 
 class AccountId;
 
@@ -31,6 +32,12 @@
   virtual void ShowEasyUnlockIcon(const AccountId& account_id,
                                   const EasyUnlockIconOptions& icon) = 0;
 
+  // Shows a warning banner message on the login screen. A warning banner is
+  // used to notify users of important messages before they log in to their
+  // session. (e.g. Tell the user that an update of the user data will start
+  // on login.) If |message| is empty, the banner will be hidden.
+  virtual void UpdateWarningMessage(const base::string16& message) = 0;
+
   // Set the users who are displayed on the login UI. |users| is filtered
   // and does not correspond to every user on the device.
   virtual void SetUserList(const std::vector<LoginUserInfo>& users) = 0;
diff --git a/ash/public/cpp/manifest.cc b/ash/public/cpp/manifest.cc
index 8859baa..4f3fabe 100644
--- a/ash/public/cpp/manifest.cc
+++ b/ash/public/cpp/manifest.cc
@@ -12,7 +12,6 @@
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/public/interfaces/cros_display_config.mojom.h"
 #include "ash/public/interfaces/event_rewriter_controller.mojom.h"
-#include "ash/public/interfaces/highlighter_controller.mojom.h"
 #include "ash/public/interfaces/ime_controller.mojom.h"
 #include "ash/public/interfaces/keyboard_controller.mojom.h"
 #include "ash/public/interfaces/kiosk_next_shell.mojom.h"
@@ -26,6 +25,7 @@
 #include "ash/public/interfaces/tray_action.mojom.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/public/interfaces/vpn_list.mojom.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/no_destructor.h"
 #include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h"
 #include "chromeos/services/network_config/public/mojom/constants.mojom.h"
@@ -68,13 +68,13 @@
                   mojom::AssistantVolumeControl,
                   mojom::KioskNextShellController,
                   mojom::CrosDisplayConfigController,
-                  mojom::EventRewriterController, mojom::HighlighterController,
-                  mojom::ImeController, mojom::KeyboardController,
-                  mojom::LocaleUpdateController, mojom::LoginScreen,
-                  mojom::MediaController, mojom::NightLightController,
-                  mojom::ShutdownController, mojom::TabletModeController,
-                  mojom::TrayAction, mojom::VoiceInteractionController,
-                  mojom::VpnList>())
+                  mojom::EventRewriterController, mojom::ImeController,
+                  mojom::KeyboardController, mojom::LocaleUpdateController,
+                  mojom::LoginScreen, mojom::MediaController,
+                  mojom::NightLightController, mojom::ShutdownController,
+                  mojom::TabletModeController, mojom::TrayAction,
+                  mojom::VoiceInteractionController, mojom::VpnList,
+                  mojom::WallpaperController>())
           .ExposeCapability("test", service_manager::Manifest::InterfaceList<
                                         mojom::ShelfIntegrationTestApi>())
           .RequireCapability("*", "accessibility")
diff --git a/ash/public/cpp/wallpaper_controller.cc b/ash/public/cpp/wallpaper_controller.cc
deleted file mode 100644
index 17eaa8b..0000000
--- a/ash/public/cpp/wallpaper_controller.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 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/public/cpp/wallpaper_controller.h"
-
-namespace ash {
-
-// static
-WallpaperController* WallpaperController::Get() {
-  return g_instance_;
-}
-
-// static
-WallpaperController* WallpaperController::g_instance_ = nullptr;
-
-}  // namespace ash
diff --git a/ash/public/cpp/wallpaper_controller.h b/ash/public/cpp/wallpaper_controller.h
deleted file mode 100644
index d570a36..0000000
--- a/ash/public/cpp/wallpaper_controller.h
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright 2019 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 ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_H_
-#define ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_H_
-
-#include <string>
-#include <vector>
-
-#include "ash/public/cpp/ash_public_export.h"
-#include "ash/public/cpp/wallpaper_info.h"
-#include "ash/public/cpp/wallpaper_types.h"
-#include "base/files/file_path.h"
-#include "base/time/time.h"
-
-namespace gfx {
-class ImageSkia;
-}
-
-namespace ash {
-
-class WallpaperControllerObserver;
-class WallpaperControllerClient;
-struct WallpaperUserInfo;
-
-// Used by Chrome to set the wallpaper displayed by ash.
-class ASH_PUBLIC_EXPORT WallpaperController {
- public:
-  static WallpaperController* Get();
-
-  // Do the initialization: Sets the client interface, the paths of wallpaper
-  // directories and the device wallpaper policy enforcement flag. The paths
-  // must be sent over IPC because chrome owns the concept of user data
-  // directory.
-  // |client|: The client interface.
-  // |user_data_path|: Directory where user data can be written.
-  // |chromeos_wallpapers_path|: Directory where downloaded chromeos wallpapers
-  //                             reside.
-  // |chromeos_custom_wallpapers_path|: Directory where custom wallpapers
-  //                                    reside.
-  // |device_policy_wallpaper_path|: The file path of the device policy
-  //                                 wallpaper (if any).
-  virtual void Init(WallpaperControllerClient* client,
-                    const base::FilePath& user_data_path,
-                    const base::FilePath& chromeos_wallpapers_path,
-                    const base::FilePath& chromeos_custom_wallpapers_path,
-                    const base::FilePath& device_policy_wallpaper_path) = 0;
-
-  // Sets wallpaper from a local file and updates the saved wallpaper info for
-  // the user.
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  // |file_name|: The name of the wallpaper file.
-  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
-  // |image|: The wallpaper image.
-  // |preview_mode|: If true, show the wallpaper immediately but doesn't change
-  //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
-  //                 called.
-  virtual void SetCustomWallpaper(const WallpaperUserInfo& user_info,
-                                  const std::string& wallpaper_files_id,
-                                  const std::string& file_name,
-                                  WallpaperLayout layout,
-                                  const gfx::ImageSkia& image,
-                                  bool preview_mode) = 0;
-
-  // Sets wallpaper from the Chrome OS wallpaper picker. If the wallpaper file
-  // corresponding to |url| already exists in local file system (i.e.
-  // |SetOnlineWallpaperFromData| was called earlier with the same |url|),
-  // returns true and sets wallpaper for the user, otherwise returns false.
-  // |user_info|: The user's information related to wallpaper.
-  // |url|: The wallpaper url.
-  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
-  // |preview_mode|: If true, show the wallpaper immediately but doesn't change
-  //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
-  //                 called.
-  // Responds with true if the wallpaper file exists in local file system.
-  using SetOnlineWallpaperIfExistsCallback = base::OnceCallback<void(bool)>;
-  virtual void SetOnlineWallpaperIfExists(
-      const WallpaperUserInfo& user_info,
-      const std::string& url,
-      WallpaperLayout layout,
-      bool preview_mode,
-      SetOnlineWallpaperIfExistsCallback callback) = 0;
-
-  // Sets wallpaper from the Chrome OS wallpaper picker and saves the wallpaper
-  // to local file system. After this, |SetOnlineWallpaperIfExists| will return
-  // true for the same |url|, so that there's no need to provide |image_data|
-  // when the same wallpaper needs to be set again or for another user.
-  // |user_info|: The user's information related to wallpaper.
-  // |url|: The wallpaper url.
-  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
-  // |preview_mode|: If true, show the wallpaper immediately but doesn't change
-  //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
-  //                 called.
-  // Responds with true if the wallpaper is set successfully (i.e. no decoding
-  // error etc.).
-  using SetOnlineWallpaperFromDataCallback = base::OnceCallback<void(bool)>;
-  virtual void SetOnlineWallpaperFromData(
-      const WallpaperUserInfo& user_info,
-      const std::string& image_data,
-      const std::string& url,
-      WallpaperLayout layout,
-      bool preview_mode,
-      SetOnlineWallpaperFromDataCallback callback) = 0;
-
-  // Sets the user's wallpaper to be the default wallpaper. Note: different user
-  // types may have different default wallpapers.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  // |show_wallpaper|: If false, don't show the new wallpaper now but only
-  //                   update cache.
-  virtual void SetDefaultWallpaper(const WallpaperUserInfo& user_info,
-                                   const std::string& wallpaper_files_id,
-                                   bool show_wallpaper) = 0;
-
-  // Sets the paths of the customized default wallpaper to be used wherever a
-  // default wallpaper is needed. If a default wallpaper is being shown, updates
-  // the screen to replace the old default wallpaper. Note: it doesn't change
-  // the default wallpaper for guest and child accounts.
-  // |customized_default_small_path|: The file path of the small-size customized
-  //                                  default wallpaper, if any.
-  // |customized_default_large_path|: The file path of the large-size customized
-  //                                  default wallpaper, if any.
-  virtual void SetCustomizedDefaultWallpaperPaths(
-      const base::FilePath& customized_default_small_path,
-      const base::FilePath& customized_default_large_path) = 0;
-
-  // Sets wallpaper from policy. If the user has logged in, show the policy
-  // wallpaper immediately, otherwise, the policy wallpaper will be shown the
-  // next time |ShowUserWallpaper| is called. Note: it is different from device
-  // policy.
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  // |data|: The data used to decode the image.
-  virtual void SetPolicyWallpaper(const WallpaperUserInfo& user_info,
-                                  const std::string& wallpaper_files_id,
-                                  const std::string& data) = 0;
-
-  // Sets the path of device policy wallpaper.
-  // |device_policy_wallpaper_path|: The file path of the device policy
-  //                                 wallpaper if it was set or empty value if
-  //                                 it was cleared.
-  virtual void SetDevicePolicyWallpaperPath(
-      const base::FilePath& device_policy_wallpaper_path) = 0;
-
-  // Sets wallpaper from a third-party app (as opposed to the Chrome OS
-  // wallpaper picker).
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  // |file_name|: The name of the wallpaper file.
-  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
-  // |image|: The wallpaper image.
-  // Returns if the wallpaper is allowed to be shown on screen. It's false if:
-  // 1) the user is not permitted to change wallpaper, or
-  // 2) updating the on-screen wallpaper is not allowed at the given moment.
-  virtual bool SetThirdPartyWallpaper(const WallpaperUserInfo& user_info,
-                                      const std::string& wallpaper_files_id,
-                                      const std::string& file_name,
-                                      WallpaperLayout layout,
-                                      const gfx::ImageSkia& image) = 0;
-
-  // Confirms the wallpaper being previewed to be set as the actual user
-  // wallpaper. Must be called in preview mode.
-  virtual void ConfirmPreviewWallpaper() = 0;
-
-  // Cancels the wallpaper preview and reverts to the user wallpaper. Must be
-  // called in preview mode.
-  virtual void CancelPreviewWallpaper() = 0;
-
-  // Updates the layout for the user's custom wallpaper and reloads the
-  // wallpaper with the new layout.
-  // |user_info|: The user's information related to wallpaper.
-  // |layout|: The new layout of the wallpaper.
-  virtual void UpdateCustomWallpaperLayout(const WallpaperUserInfo& user_info,
-                                           WallpaperLayout layout) = 0;
-
-  // Shows the user's wallpaper, which is determined in the following order:
-  // 1) Use device policy wallpaper if it exists AND we are at the login screen.
-  // 2) Use user policy wallpaper if it exists.
-  // 3) Use the wallpaper set by the user (either by |SetOnlineWallpaper| or
-  //    |SetCustomWallpaper|), if any.
-  // 4) Use the default wallpaper of this user.
-  virtual void ShowUserWallpaper(const WallpaperUserInfo& user_info) = 0;
-
-  // Used by the gaia-signin UI. Signin wallpaper is considered either as the
-  // device policy wallpaper or the default wallpaper.
-  virtual void ShowSigninWallpaper() = 0;
-
-  // Shows a one-shot wallpaper, which does not belong to any particular user
-  // and is not saved to file. Note: the wallpaper will never be dimmed or
-  // blurred because it's assumed that the caller wants to show the image as is
-  // when using this method.
-  virtual void ShowOneShotWallpaper(const gfx::ImageSkia& image) = 0;
-
-  // Shows a wallpaper that stays on top of everything except for the power off
-  // animation. All other wallpaper requests are ignored when the always-on-top
-  // wallpaper is being shown.
-  // |image_path|: The file path to read the image data from.
-  virtual void ShowAlwaysOnTopWallpaper(const base::FilePath& image_path) = 0;
-
-  // Removes the always-on-top wallpaper. The wallpaper will revert to the
-  // previous one, or a default one if there was none. No-op if the current
-  // wallpaper is not always-on-top.
-  virtual void RemoveAlwaysOnTopWallpaper() = 0;
-
-  // Removes all of the user's saved wallpapers and related info.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  virtual void RemoveUserWallpaper(const WallpaperUserInfo& user_info,
-                                   const std::string& wallpaper_files_id) = 0;
-
-  // Removes all of the user's saved wallpapers and related info if the
-  // wallpaper was set by |SetPolicyWallpaper|. In addition, sets the user's
-  // wallpaper to be the default. If the user has logged in, show the default
-  // wallpaper immediately, otherwise, the default wallpaper will be shown the
-  // next time |ShowUserWallpaper| is called.
-  // |user_info|: The user's information related to wallpaper.
-  // |wallpaper_files_id|: The file id for user_info.account_id.
-  virtual void RemovePolicyWallpaper(const WallpaperUserInfo& user_info,
-                                     const std::string& wallpaper_files_id) = 0;
-
-  // Returns the urls of the wallpapers that exist in local file system (i.e.
-  // |SetOnlineWallpaperFromData| was called earlier). The url is used as id
-  // to identify which wallpapers are available to be set offline.
-  using GetOfflineWallpaperListCallback =
-      base::OnceCallback<void(const std::vector<std::string>&)>;
-  virtual void GetOfflineWallpaperList(
-      GetOfflineWallpaperListCallback callback) = 0;
-
-  // Sets wallpaper animation duration. Passing an empty value disables the
-  // animation.
-  virtual void SetAnimationDuration(base::TimeDelta animation_duration) = 0;
-
-  // Opens the wallpaper picker if the active user is not controlled by policy
-  // and it's allowed to change wallpaper per the user type and the login state.
-  virtual void OpenWallpaperPickerIfAllowed() = 0;
-
-  // Minimizes all windows except the active window.
-  // |user_id_hash|: The hash value corresponding to |User::username_hash|.
-  virtual void MinimizeInactiveWindows(const std::string& user_id_hash) = 0;
-
-  // Restores all minimized windows to their previous states. This should only
-  // be called after calling |MinimizeInactiveWindows|.
-  // |user_id_hash|: The hash value corresponding to |User::username_hash|.
-  virtual void RestoreMinimizedWindows(const std::string& user_id_hash) = 0;
-
-  // Add and remove wallpaper observers.
-  virtual void AddObserver(WallpaperControllerObserver* observer) = 0;
-  virtual void RemoveObserver(WallpaperControllerObserver* observer) = 0;
-
-  // Returns the wallpaper image currently being shown.
-  virtual gfx::ImageSkia GetWallpaperImage() = 0;
-
-  // Returns the wallpaper prominent colors.
-  virtual const std::vector<SkColor>& GetWallpaperColors() = 0;
-
-  // Returns whether the current wallpaper is blurred.
-  virtual bool IsWallpaperBlurred() = 0;
-
-  // Returns true if the wallpaper of the currently active user (if any) is
-  // controlled by policy (excluding device policy). If there's no active user,
-  // returns false.
-  virtual bool IsActiveUserWallpaperControlledByPolicy() = 0;
-
-  // Returns a struct with info about the active user's wallpaper; the location
-  // is an empty string and the layout is invalid if there's no active user.
-  virtual WallpaperInfo GetActiveUserWallpaperInfo() = 0;
-
-  // Returns true if the wallpaper setting (used to open the wallpaper picker)
-  // should be visible.
-  virtual bool ShouldShowWallpaperSetting() = 0;
-
- protected:
-  static WallpaperController* g_instance_;
-};
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_H_
diff --git a/ash/public/cpp/wallpaper_controller_client.h b/ash/public/cpp/wallpaper_controller_client.h
deleted file mode 100644
index 8556241..0000000
--- a/ash/public/cpp/wallpaper_controller_client.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2019 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 ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_CLIENT_H_
-#define ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_CLIENT_H_
-
-#include "ash/public/cpp/ash_public_export.h"
-
-namespace ash {
-
-// Used by ash to control a Chrome client of the WallpaperController.
-class ASH_PUBLIC_EXPORT WallpaperControllerClient {
- public:
-  // Opens the wallpaper picker window.
-  virtual void OpenWallpaperPicker() = 0;
-
-  // Signals to the client that ash is ready to set wallpapers. The client is
-  // able to decide whatever the first wallpaper it wants to display.
-  virtual void OnReadyToSetWallpaper() = 0;
-
-  // Notifies the client that the animation of the first wallpaper since the
-  // controller initialization has completed.
-  // TODO(crbug.com/875128): Remove this after web-ui login code is completely
-  // removed.
-  virtual void OnFirstWallpaperAnimationFinished() = 0;
-};
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_CLIENT_H_
diff --git a/ash/public/cpp/wallpaper_controller_observer.cc b/ash/public/cpp/wallpaper_controller_observer.cc
deleted file mode 100644
index 3fc933e..0000000
--- a/ash/public/cpp/wallpaper_controller_observer.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2019 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/public/cpp/wallpaper_controller_observer.h"
-
-namespace ash {
-
-WallpaperControllerObserver::WallpaperControllerObserver() = default;
-
-WallpaperControllerObserver::~WallpaperControllerObserver() = default;
-
-}  // namespace ash
diff --git a/ash/public/cpp/wallpaper_controller_observer.h b/ash/public/cpp/wallpaper_controller_observer.h
deleted file mode 100644
index 6d58154..0000000
--- a/ash/public/cpp/wallpaper_controller_observer.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2019 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 ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_OBSERVER_H_
-#define ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_OBSERVER_H_
-
-#include "ash/public/cpp/ash_public_export.h"
-#include "base/macros.h"
-
-namespace ash {
-
-// Used to listen for wallpaper state changes.
-class ASH_PUBLIC_EXPORT WallpaperControllerObserver {
- public:
-  WallpaperControllerObserver();
-
-  // Invoked when the wallpaper is changed.
-  virtual void OnWallpaperChanged() {}
-
-  // Invoked when the colors extracted from the current wallpaper change.
-  virtual void OnWallpaperColorsChanged() {}
-
-  // Invoked when the blur state of the wallpaper changes.
-  // TODO(crbug.com/875128): Remove this after web-ui login code is completely
-  // removed.
-  virtual void OnWallpaperBlurChanged() {}
-
-  // Invoked when the wallpaper preview mode starts.
-  virtual void OnWallpaperPreviewStarted() {}
-
-  // Invoked when the wallpaper preview mode ends.
-  virtual void OnWallpaperPreviewEnded() {}
-
-  // Invoked when the first wallpaper is set. The first wallpaper is the one
-  // shown right after boot splash screen or after a session restart.
-  virtual void OnFirstWallpaperShown() {}
-
- protected:
-  virtual ~WallpaperControllerObserver();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WallpaperControllerObserver);
-};
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_WALLPAPER_CONTROLLER_OBSERVER_H_
diff --git a/ash/public/cpp/wallpaper_struct_traits.h b/ash/public/cpp/wallpaper_struct_traits.h
new file mode 100644
index 0000000..590eed9
--- /dev/null
+++ b/ash/public/cpp/wallpaper_struct_traits.h
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_WALLPAPER_STRUCT_TRAITS_H_
+#define ASH_PUBLIC_CPP_WALLPAPER_STRUCT_TRAITS_H_
+
+#include "ash/public/cpp/wallpaper_types.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<ash::mojom::WallpaperLayout, ash::WallpaperLayout> {
+  static ash::mojom::WallpaperLayout ToMojom(ash::WallpaperLayout input) {
+    switch (input) {
+      case ash::WALLPAPER_LAYOUT_CENTER:
+        return ash::mojom::WallpaperLayout::CENTER;
+      case ash::WALLPAPER_LAYOUT_CENTER_CROPPED:
+        return ash::mojom::WallpaperLayout::CENTER_CROPPED;
+      case ash::WALLPAPER_LAYOUT_STRETCH:
+        return ash::mojom::WallpaperLayout::STRETCH;
+      case ash::WALLPAPER_LAYOUT_TILE:
+        return ash::mojom::WallpaperLayout::TILE;
+      case ash::NUM_WALLPAPER_LAYOUT:
+        break;
+    }
+    NOTREACHED();
+    return ash::mojom::WallpaperLayout::CENTER;
+  }
+
+  static bool FromMojom(ash::mojom::WallpaperLayout input,
+                        ash::WallpaperLayout* out) {
+    switch (input) {
+      case ash::mojom::WallpaperLayout::CENTER:
+        *out = ash::WALLPAPER_LAYOUT_CENTER;
+        return true;
+      case ash::mojom::WallpaperLayout::CENTER_CROPPED:
+        *out = ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
+        return true;
+      case ash::mojom::WallpaperLayout::STRETCH:
+        *out = ash::WALLPAPER_LAYOUT_STRETCH;
+        return true;
+      case ash::mojom::WallpaperLayout::TILE:
+        *out = ash::WALLPAPER_LAYOUT_TILE;
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // ASH_PUBLIC_CPP_WALLPAPER_STRUCT_TRAITS_H_
diff --git a/ash/public/cpp/wallpaper_user_info.h b/ash/public/cpp/wallpaper_user_info.h
deleted file mode 100644
index 254d4964..0000000
--- a/ash/public/cpp/wallpaper_user_info.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019 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 ASH_PUBLIC_CPP_WALLPAPER_USER_INFO_H_
-#define ASH_PUBLIC_CPP_WALLPAPER_USER_INFO_H_
-
-#include "ash/public/cpp/ash_public_export.h"
-#include "components/account_id/account_id.h"
-#include "components/user_manager/user_type.h"
-
-namespace ash {
-
-// User info needed to set wallpapers. Clients must specify the user because
-// it's not always the same as the active user, e.g., when showing wallpapers
-// for different user pods at login screen, or setting wallpapers selectively
-// for primary user and active user during a multi-profile session.
-struct ASH_PUBLIC_EXPORT WallpaperUserInfo {
-  // The user's account id.
-  AccountId account_id = EmptyAccountId();
-
-  // The user type.
-  user_manager::UserType type = user_manager::USER_TYPE_REGULAR;
-
-  // True if the user's non-cryptohome data (wallpaper, avatar etc.) is
-  // ephemeral. See |UserManager::IsCurrentUserNonCryptohomeDataEphemeral| for
-  // more details.
-  bool is_ephemeral = false;
-
-  // True if the user has gaia account.
-  bool has_gaia_account = false;
-};
-
-}  // namespace ash
-
-#endif  // ASH_PUBLIC_CPP_WALLPAPER_USER_INFO_H_
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index 14536b5..175dbf3 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -27,7 +27,6 @@
     "constants.mojom",
     "cros_display_config.mojom",
     "event_rewriter_controller.mojom",
-    "highlighter_controller.mojom",
     "ime_controller.mojom",
     "ime_info.mojom",
     "keyboard_config.mojom",
@@ -45,6 +44,7 @@
     "update.mojom",
     "voice_interaction_controller.mojom",
     "vpn_list.mojom",
+    "wallpaper.mojom",
     "window_pin_type.mojom",
     "window_properties.mojom",
   ]
diff --git a/ash/public/interfaces/highlighter_controller.mojom b/ash/public/interfaces/highlighter_controller.mojom
deleted file mode 100644
index 4a643d0..0000000
--- a/ash/public/interfaces/highlighter_controller.mojom
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ash.mojom;
-
-import "ui/gfx/geometry/mojo/geometry.mojom";
-
-// Interface for ash client (e.g. Chrome) to connect to the highlighter
-// controller, the component implementing on-screen content selection
-// with a stylus.
-interface HighlighterController {
-  // Sets the client interface.
-  SetClient(HighlighterControllerClient client);
-
-  // Exits the highlighter mode if it is currently enabled.
-  ExitHighlighterMode();
-};
-
-// Interface for ash to notify the client (e.g. Chrome) about the highlighter
-// selection and state.
-interface HighlighterControllerClient {
-  // Called when when a valid selection is made. Selected rectangle is in
-  // screen coordinates, clipped to screen bounds if necessary.
-  HandleSelection(gfx.mojom.Rect rect);
-
-  // Called when the highlighter tool becomes enabled or disabled.
-  HandleEnabledStateChange(bool enabled);
-};
diff --git a/ash/public/interfaces/login_screen.mojom b/ash/public/interfaces/login_screen.mojom
index 80ac9302..37e7b83 100644
--- a/ash/public/interfaces/login_screen.mojom
+++ b/ash/public/interfaces/login_screen.mojom
@@ -63,18 +63,6 @@
                    string help_link_text,
                    int32 help_topic_id);
 
-  // Shows a warning banner message on the login screen. A warning banner is
-  // used to notify users of important messages before they log in to their
-  // session. (e.g. Tell the user that an update of the user data will start
-  // on login)
-  // |message|: The message to show.
-  ShowWarningBanner(mojo_base.mojom.String16 message);
-
-  // Hide a warning banner if it is displayed.
-  // TODO(fukino): Ideally chrome-side should not have this level of UI
-  // control. Make the API simpler and let ash determine the UI behavior.
-  HideWarningBanner();
-
   // Requests to close any displayed error messages in ash lock screen.
   ClearErrors();
 
diff --git a/ash/public/interfaces/typemaps.gni b/ash/public/interfaces/typemaps.gni
index 085a6c0..7305cb5 100644
--- a/ash/public/interfaces/typemaps.gni
+++ b/ash/public/interfaces/typemaps.gni
@@ -2,4 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-typemaps = [ "//ash/public/interfaces/shelf_integration_test_api.typemap" ]
+typemaps = [
+  "//ash/public/interfaces/shelf_integration_test_api.typemap",
+  "//ash/public/interfaces/wallpaper.typemap",
+]
diff --git a/ash/public/interfaces/wallpaper.mojom b/ash/public/interfaces/wallpaper.mojom
new file mode 100644
index 0000000..3e0d540
--- /dev/null
+++ b/ash/public/interfaces/wallpaper.mojom
@@ -0,0 +1,343 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.mojom;
+
+import "components/account_id/interfaces/account_id.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
+import "mojo/public/mojom/base/time.mojom";
+import "ui/gfx/image/mojo/image.mojom";
+import "url/mojom/url.mojom";
+
+// These values match ash::WallpaperLayout.
+enum WallpaperLayout {
+  CENTER,
+  CENTER_CROPPED,
+  STRETCH,
+  TILE,
+};
+
+// Matches user_manager::UserType.
+enum UserType {
+  // Regular user, has a user name and password.
+  REGULAR,
+
+  // Guest user, logs in without authentication.
+  GUEST,
+
+  // Public account user, logs in without authentication. Available only if
+  // enabled through policy.
+  PUBLIC_ACCOUNT,
+
+  // Supervised user, logs in only with local authentication.
+  SUPERVISED,
+
+  // Kiosk app robot, logs in without authentication.
+  KIOSK,
+
+  // Child user, with supervised options.
+  CHILD,
+
+  // Android app in kiosk mode, logs in without authentication.
+  ARC_KIOSK,
+
+  // Active Directory user. Authenticates against Active Directory server.
+  ACTIVE_DIRECTORY,
+};
+
+// User info needed to set wallpapers. Clients must specify the user because
+// it's not always the same with the active user, e.g., when showing wallpapers
+// for different user pods at login screen, or setting wallpapers selectively
+// for primary user and active user during a multi-profile session.
+struct WallpaperUserInfo {
+  // The user's account id.
+  signin.mojom.AccountId account_id;
+
+  // The user type.
+  UserType type;
+
+  // True if the user's non-cryptohome data (wallpaper, avatar etc.) is
+  // ephemeral. See |UserManager::IsCurrentUserNonCryptohomeDataEphemeral| for
+  // more details.
+  bool is_ephemeral;
+
+  // True if the user has gaia account.
+  bool has_gaia_account;
+};
+
+// Used by Chrome to set the wallpaper displayed by ash.
+interface WallpaperController {
+  // Do the initialization: Sets the client interface, the paths of wallpaper
+  // directories and the device wallpaper policy enforcement flag. The paths
+  // must be sent over IPC because chrome owns the concept of user data
+  // directory.
+  // |client|: The client interface.
+  // |user_data_path|: Directory where user data can be written.
+  // |chromeos_wallpapers_path|: Directory where downloaded chromeos wallpapers
+  //                             reside.
+  // |chromeos_custom_wallpapers_path|: Directory where custom wallpapers
+  //                                    reside.
+  // |device_policy_wallpaper_path|: The file path of the device policy
+  //                                 wallpaper (if any).
+  Init(WallpaperControllerClient client,
+       mojo_base.mojom.FilePath user_data_path,
+       mojo_base.mojom.FilePath chromeos_wallpapers_path,
+       mojo_base.mojom.FilePath chromeos_custom_wallpapers_path,
+       mojo_base.mojom.FilePath device_policy_wallpaper_path);
+
+  // Sets wallpaper from a local file and updates the saved wallpaper info for
+  // the user.
+  // |user_info|: The user's information related to wallpaper.
+  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |file_name|: The name of the wallpaper file.
+  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
+  // |image|: The wallpaper image.
+  // |preview_mode|: If true, show the wallpaper immediately but doesn't change
+  //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
+  //                 called.
+  SetCustomWallpaper(WallpaperUserInfo user_info,
+                     string wallpaper_files_id,
+                     string file_name,
+                     WallpaperLayout layout,
+                     gfx.mojom.ImageSkia image,
+                     bool preview_mode);
+
+  // Sets wallpaper from the Chrome OS wallpaper picker. If the wallpaper file
+  // corresponding to |url| already exists in local file system (i.e.
+  // |SetOnlineWallpaperFromData| was called earlier with the same |url|),
+  // returns true and sets wallpaper for the user, otherwise returns false.
+  // |user_info|: The user's information related to wallpaper.
+  // |url|: The wallpaper url.
+  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
+  // |preview_mode|: If true, show the wallpaper immediately but doesn't change
+  //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
+  //                 called.
+  // |file_exists|: If the wallpaper file exists in local file system.
+  SetOnlineWallpaperIfExists(WallpaperUserInfo user_info,
+                             string url,
+                             WallpaperLayout layout,
+                             bool preview_mode) => (bool file_exists);
+
+  // Sets wallpaper from the Chrome OS wallpaper picker and saves the wallpaper
+  // to local file system. After this, |SetOnlineWallpaperIfExists| will return
+  // true for the same |url|, so that there's no need to provide |image_data|
+  // when the same wallpaper needs to be set again or for another user.
+  // |user_info|: The user's information related to wallpaper.
+  // |url|: The wallpaper url.
+  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
+  // |preview_mode|: If true, show the wallpaper immediately but doesn't change
+  //                 the user wallpaper info until |ConfirmPreviewWallpaper| is
+  //                 called.
+  // |success|: If the wallpaper is set successfully (i.e. no decoding error
+  //            etc.).
+  SetOnlineWallpaperFromData(WallpaperUserInfo user_info,
+                             string image_data,
+                             string url,
+                             WallpaperLayout layout,
+                             bool preview_mode) => (bool success);
+
+  // Sets the user's wallpaper to be the default wallpaper. Note: different user
+  // types may have different default wallpapers.
+  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |show_wallpaper|: If false, don't show the new wallpaper now but only
+  //                   update cache.
+  SetDefaultWallpaper(WallpaperUserInfo user_info,
+                      string wallpaper_files_id,
+                      bool show_wallpaper);
+
+  // Sets the paths of the customized default wallpaper to be used wherever a
+  // default wallpaper is needed. If a default wallpaper is being shown, updates
+  // the screen to replace the old default wallpaper. Note: it doesn't change
+  // the default wallpaper for guest and child accounts.
+  // |customized_default_small_path|: The file path of the small-size customized
+  //                                  default wallpaper, if any.
+  // |customized_default_large_path|: The file path of the large-size customized
+  //                                  default wallpaper, if any.
+  SetCustomizedDefaultWallpaperPaths(
+      mojo_base.mojom.FilePath customized_default_small_path,
+      mojo_base.mojom.FilePath customized_default_large_path);
+
+  // Sets wallpaper from policy. If the user has logged in, show the policy
+  // wallpaper immediately, otherwise, the policy wallpaper will be shown the
+  // next time |ShowUserWallpaper| is called. Note: it is different from device
+  // policy.
+  // |user_info|: The user's information related to wallpaper.
+  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |data|: The data used to decode the image.
+  SetPolicyWallpaper(WallpaperUserInfo user_info,
+                     string wallpaper_files_id,
+                     string data);
+
+  // Sets the path of device policy wallpaper.
+  // |device_policy_wallpaper_path|: The file path of the device policy
+  //                                 wallpaper if it was set or empty value if
+  //                                 it was cleared.
+  SetDevicePolicyWallpaperPath(
+      mojo_base.mojom.FilePath device_policy_wallpaper_path);
+
+  // Sets wallpaper from a third-party app (as opposed to the Chrome OS
+  // wallpaper picker).
+  // |user_info|: The user's information related to wallpaper.
+  // |wallpaper_files_id|: The file id for user_info.account_id.
+  // |file_name|: The name of the wallpaper file.
+  // |layout|: The layout of the wallpaper, used for wallpaper resizing.
+  // |image|: The wallpaper image.
+  // |allowed|: If the wallpaper is allowed to be shown on screen. It's false if
+  //            1) the user is not permitted to change wallpaper, or
+  //            2) updating the on-screen wallpaper is not allowed at the
+  //               given moment.
+  // |image_id|: A unique id assigned to the image. Clients may be interested in
+  //             observing all wallpaper changes and acting differently based on
+  //             if the wallpaper change is due to their own request. In order
+  //             to do so, they should compare this value with the one that's
+  //             returned by |OnWallpaperChanged|.
+  SetThirdPartyWallpaper(WallpaperUserInfo user_info,
+                         string wallpaper_files_id,
+                         string file_name,
+                         WallpaperLayout layout,
+                         gfx.mojom.ImageSkia image)
+                         => (bool allowed, uint32 image_id);
+
+  // Confirms the wallpaper being previewed to be set as the actual user
+  // wallpaper. Must be called in preview mode.
+  ConfirmPreviewWallpaper();
+
+  // Cancels the wallpaper preview and reverts to the user wallpaper. Must be
+  // called in preview mode.
+  CancelPreviewWallpaper();
+
+  // Updates the layout for the user's custom wallpaper and reloads the
+  // wallpaper with the new layout.
+  // |user_info|: The user's information related to wallpaper.
+  // |layout|: The new layout of the wallpaper.
+  UpdateCustomWallpaperLayout(WallpaperUserInfo user_info,
+                              WallpaperLayout layout);
+
+  // Shows the user's wallpaper, which is determined in the following order:
+  // 1) Use device policy wallpaper if it exists AND we are at the login screen.
+  // 2) Use user policy wallpaper if it exists.
+  // 3) Use the wallpaper set by the user (either by |SetOnlineWallpaper| or
+  //    |SetCustomWallpaper|), if any.
+  // 4) Use the default wallpaper of this user.
+  ShowUserWallpaper(WallpaperUserInfo user_info);
+
+  // Used by the gaia-signin UI. Signin wallpaper is considered either as the
+  // device policy wallpaper or the default wallpaper.
+  ShowSigninWallpaper();
+
+  // Shows a one-shot wallpaper, which does not belong to any particular user
+  // and is not saved to file. Note: the wallpaper will never be dimmed or
+  // blurred because it's assumed that the caller wants to show the image as is
+  // when using this method.
+  ShowOneShotWallpaper(gfx.mojom.ImageSkia image);
+
+  // Shows a wallpaper that stays on top of everything except for the power off
+  // animation. All other wallpaper requests are ignored when the always-on-top
+  // wallpaper is being shown.
+  // |image_path|: The file path to read the image data from.
+  ShowAlwaysOnTopWallpaper(mojo_base.mojom.FilePath image_path);
+
+  // Removes the always-on-top wallpaper. The wallpaper will revert to the
+  // previous one, or a default one if there was none. No-op if the current
+  // wallpaper is not always-on-top.
+  RemoveAlwaysOnTopWallpaper();
+
+  // Removes all of the user's saved wallpapers and related info.
+  // |wallpaper_files_id|: The file id for user_info.account_id.
+  RemoveUserWallpaper(WallpaperUserInfo user_info, string wallpaper_files_id);
+
+  // Removes all of the user's saved wallpapers and related info if the
+  // wallpaper was set by |SetPolicyWallpaper|. In addition, sets the user's
+  // wallpaper to be the default. If the user has logged in, show the default
+  // wallpaper immediately, otherwise, the default wallpaper will be shown the
+  // next time |ShowUserWallpaper| is called.
+  // |user_info|: The user's information related to wallpaper.
+  // |wallpaper_files_id|: The file id for user_info.account_id.
+  RemovePolicyWallpaper(WallpaperUserInfo user_info, string wallpaper_files_id);
+
+  // Returns the urls of the wallpapers that exist in local file system (i.e.
+  // |SetOnlineWallpaperFromData| was called earlier). The url is used as id
+  // to identify which wallpapers are available to be set offline.
+  GetOfflineWallpaperList() => (array<string> url_list);
+
+  // Sets wallpaper animation duration. Passing an empty value disables the
+  // animation.
+  SetAnimationDuration(mojo_base.mojom.TimeDelta animation_duration);
+
+  // Opens the wallpaper picker if the active user is not controlled by policy
+  // and it's allowed to change wallpaper per the user type and the login state.
+  OpenWallpaperPickerIfAllowed();
+
+  // Minimizes all windows except the active window.
+  // |user_id_hash|: The hash value corresponding to |User::username_hash|.
+  MinimizeInactiveWindows(string user_id_hash);
+
+  // Restores all minimized windows to their previous states. This should only
+  // be called after calling |MinimizeInactiveWindows|.
+  // |user_id_hash|: The hash value corresponding to |User::username_hash|.
+  RestoreMinimizedWindows(string user_id_hash);
+
+  // Calling this method triggers an initial notification of the wallpaper
+  // state. Observers are automatically removed as their connections are closed.
+  AddObserver(associated WallpaperObserver observer);
+
+  // Returns the wallpaper image currently being shown.
+  GetWallpaperImage() => (gfx.mojom.ImageSkia image);
+
+  // Returns the wallpaper prominent colors.
+  GetWallpaperColors() => (array<uint32> prominent_colors);
+
+  // Returns whether the current wallpaper is blurred.
+  IsWallpaperBlurred() => (bool blurred);
+
+  // Returns true if the wallpaper of the currently active user (if any) is
+  // controlled by policy (excluding device policy). If there's no active user,
+  // returns false.
+  IsActiveUserWallpaperControlledByPolicy() => (bool controlled);
+
+  // Returns the location and the layout of the active user's wallpaper. The
+  // location is either a url or a file path, corresponding to
+  // |WallpaperInfo.location|. Returns an empty string and an invalid layout if
+  // there's no active user.
+  GetActiveUserWallpaperInfo() => (string location, WallpaperLayout layout);
+
+  // Returns true if the wallpaper setting (used to open the wallpaper picker)
+  // should be visible.
+  ShouldShowWallpaperSetting() => (bool show);
+};
+
+// Used by ash to control a Chrome client.
+interface WallpaperControllerClient {
+  // Opens the wallpaper picker window.
+  OpenWallpaperPicker();
+
+  // Signals to the client that ash is ready to set wallpapers. The client is
+  // able to decide whatever the first wallpaper it wants to display.
+  OnReadyToSetWallpaper();
+
+  // Notifies the client that the animation of the first wallpaper since the
+  // controller initialization has completed.
+  // TODO(crbug.com/875128): Remove this after web-ui login code is completely
+  // removed.
+  OnFirstWallpaperAnimationFinished();
+};
+
+// Used to listen for wallpaper state changed.
+interface WallpaperObserver {
+  // Invoked when the wallpaper is changed. |image_id| is the unique id assigned
+  // to the current wallpaper image. It should only be used to compare against
+  // the value returned by a setting wallpaper request earlier
+  // (e.g. SetThirdPartyWallpaper). This value is unique to each |ImageSkia|
+  // object and is different for two objects with the same pixels.
+  OnWallpaperChanged(uint32 image_id);
+
+  // Invoked when the colors extracted from the current wallpaper change. Colors
+  // are ordered and are referenced in wallpaper::ColorProfileType.
+  OnWallpaperColorsChanged(array<uint32> prominent_colors);
+
+  // Invoked when the blur state of the wallpaper changes.
+  // TODO(crbug.com/875128): Remove this after web-ui login code is completely
+  // removed.
+  OnWallpaperBlurChanged(bool blurred);
+};
diff --git a/ash/public/interfaces/wallpaper.typemap b/ash/public/interfaces/wallpaper.typemap
new file mode 100644
index 0000000..01525b3
--- /dev/null
+++ b/ash/public/interfaces/wallpaper.typemap
@@ -0,0 +1,20 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//ash/public/interfaces/wallpaper.mojom"
+public_headers = [
+  "//ash/public/cpp/wallpaper_types.h",
+  "//components/user_manager/user_type.h",
+]
+traits_headers = [
+  "//ash/public/cpp/wallpaper_struct_traits.h",
+  "//ash/public/interfaces/user_info_traits.h",
+]
+public_deps = [
+  "//components/user_manager",
+]
+type_mappings = [
+  "ash.mojom.WallpaperLayout=ash::WallpaperLayout",
+  "ash.mojom.UserType=user_manager::UserType",
+]
diff --git a/ash/shelf/shelf_background_animator.cc b/ash/shelf/shelf_background_animator.cc
index 7b03596..79f24ba 100644
--- a/ash/shelf/shelf_background_animator.cc
+++ b/ash/shelf/shelf_background_animator.cc
@@ -13,7 +13,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_background_animator_observer.h"
 #include "ash/shelf/shelf_constants.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/color_analysis.h"
 #include "ui/gfx/color_palette.h"
@@ -57,7 +57,7 @@
 ShelfBackgroundAnimator::ShelfBackgroundAnimator(
     ShelfBackgroundType background_type,
     Shelf* shelf,
-    WallpaperControllerImpl* wallpaper_controller)
+    WallpaperController* wallpaper_controller)
     : shelf_(shelf), wallpaper_controller_(wallpaper_controller) {
   if (wallpaper_controller_)
     wallpaper_controller_->AddObserver(this);
diff --git a/ash/shelf/shelf_background_animator.h b/ash/shelf/shelf_background_animator.h
index d43884c..022fbeb 100644
--- a/ash/shelf/shelf_background_animator.h
+++ b/ash/shelf/shelf_background_animator.h
@@ -10,8 +10,8 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/cpp/shelf_types.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/shelf/shelf_observer.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -27,7 +27,7 @@
 class Shelf;
 class ShelfBackgroundAnimatorObserver;
 class ShelfBackgroundAnimatorTestApi;
-class WallpaperControllerImpl;
+class WallpaperController;
 
 // Central controller for the Shelf and Dock opacity animations.
 //
@@ -51,7 +51,7 @@
   // wallpaper changes if not null.
   ShelfBackgroundAnimator(ShelfBackgroundType background_type,
                           Shelf* shelf,
-                          WallpaperControllerImpl* wallpaper_controller);
+                          WallpaperController* wallpaper_controller);
   ~ShelfBackgroundAnimator() override;
 
   ShelfBackgroundType target_background_type() const {
@@ -159,7 +159,7 @@
   Shelf* shelf_;
 
   // The wallpaper controller to observe for changes and to extract colors from.
-  WallpaperControllerImpl* wallpaper_controller_;
+  WallpaperController* wallpaper_controller_;
 
   // The background type that this is animating towards or has reached.
   ShelfBackgroundType target_background_type_ = SHELF_BACKGROUND_DEFAULT;
diff --git a/ash/shelf/shelf_context_menu_model.cc b/ash/shelf/shelf_context_menu_model.cc
index 0117a53..38c578f 100644
--- a/ash/shelf/shelf_context_menu_model.cc
+++ b/ash/shelf/shelf_context_menu_model.cc
@@ -21,7 +21,7 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/metrics/user_metrics.h"
 #include "base/numerics/safe_conversions.h"
diff --git a/ash/shelf/shelf_context_menu_model_unittest.cc b/ash/shelf/shelf_context_menu_model_unittest.cc
index 6d239a3c..70397b9 100644
--- a/ash/shelf/shelf_context_menu_model_unittest.cc
+++ b/ash/shelf/shelf_context_menu_model_unittest.cc
@@ -6,13 +6,12 @@
 
 #include "ash/public/cpp/app_menu_constants.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
-#include "ash/public/cpp/wallpaper_controller_client.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test_shell_delegate.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/display/display.h"
@@ -41,20 +40,27 @@
 };
 
 // A test wallpaper controller client class.
-class TestWallpaperControllerClient : public WallpaperControllerClient {
+class TestWallpaperControllerClient : public mojom::WallpaperControllerClient {
  public:
-  TestWallpaperControllerClient() = default;
-  ~TestWallpaperControllerClient() = default;
+  TestWallpaperControllerClient() : binding_(this) {}
+  ~TestWallpaperControllerClient() override = default;
 
   size_t open_count() const { return open_count_; }
 
-  // WallpaperControllerClient:
+  mojom::WallpaperControllerClientPtr CreateInterfacePtr() {
+    mojom::WallpaperControllerClientPtr ptr;
+    binding_.Bind(mojo::MakeRequest(&ptr));
+    return ptr;
+  }
+
+  // mojom::WallpaperControllerClient:
   void OpenWallpaperPicker() override { open_count_++; }
   void OnReadyToSetWallpaper() override {}
   void OnFirstWallpaperAnimationFinished() override {}
 
  private:
   size_t open_count_ = 0;
+  mojo::Binding<mojom::WallpaperControllerClient> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(TestWallpaperControllerClient);
 };
@@ -141,11 +147,13 @@
   EXPECT_TRUE(submenu->IsItemCheckedAt(0));
 
   TestWallpaperControllerClient client;
-  Shell::Get()->wallpaper_controller()->SetClientForTesting(&client);
+  Shell::Get()->wallpaper_controller()->SetClientForTesting(
+      client.CreateInterfacePtr());
   EXPECT_EQ(0u, client.open_count());
 
   // Click the third option, wallpaper picker. It should open.
   menu3.ActivatedAt(2);
+  Shell::Get()->wallpaper_controller()->FlushForTesting();
   EXPECT_EQ(1u, client.open_count());
 }
 
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index e96a726..b41962a0 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -30,7 +30,7 @@
 #include "ash/shell.h"
 #include "ash/system/locale/locale_update_controller.h"
 #include "ash/system/status_area_widget.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/fullscreen_window_finder.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/mru_window_tracker.h"
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index c93365c..1c72070 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -11,12 +11,12 @@
 #include "ash/ash_export.h"
 #include "ash/home_screen/home_launcher_gesture_handler_observer.h"
 #include "ash/public/cpp/shelf_types.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/rotator/screen_rotation_animator_observer.h"
 #include "ash/session/session_observer.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell_observer.h"
 #include "ash/system/locale/locale_update_controller.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wm/lock_state_observer.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "ash/wm/wm_snap_to_pixel_layout_manager.h"
@@ -45,7 +45,6 @@
 class ShelfLayoutManagerObserver;
 class ShelfLayoutManagerTest;
 class ShelfWidget;
-class WallpaperController;
 
 // ShelfLayoutManager is the layout manager responsible for the shelf and
 // status widgets. The shelf is given the total available width and told the
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 475f414..47f2bf3c 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -26,7 +26,6 @@
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
@@ -42,7 +41,7 @@
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/window_factory.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -1264,7 +1263,8 @@
 // Assertions around the login screen.
 TEST_F(ShelfLayoutManagerTest, VisibleWhenLoginScreenShowing) {
   Shelf* shelf = GetPrimaryShelf();
-  auto* wallpaper_controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* wallpaper_controller =
+      Shell::Get()->wallpaper_controller();
   WallpaperShownWaiter waiter;
 
   SessionInfo info;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index e9cb7e7..854765e63 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -46,7 +46,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test_shell_delegate.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wallpaper/wallpaper_controller_test_api.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/i18n/rtl.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index 063bb6f..5fc3fc9 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -120,7 +120,7 @@
 #include "ash/tray_action/tray_action.h"
 #include "ash/utility/screenshot_controller.h"
 #include "ash/voice_interaction/voice_interaction_controller.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wayland/wayland_server_controller.h"
 #include "ash/wm/ash_focus_rules.h"
 #include "ash/wm/container_finder.h"
@@ -909,8 +909,7 @@
   // Shelf, and WallPaper could be created by the factory.
   views::FocusManagerFactory::Install(new AshFocusManagerFactory);
 
-  wallpaper_controller_ =
-      std::make_unique<WallpaperControllerImpl>(local_state_);
+  wallpaper_controller_ = std::make_unique<WallpaperController>(local_state_);
 
   window_positioner_ = std::make_unique<WindowPositioner>();
 
diff --git a/ash/shell.h b/ash/shell.h
index 39245a3..568cbe8 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -188,7 +188,7 @@
 class VideoDetector;
 class VoiceInteractionController;
 class VpnList;
-class WallpaperControllerImpl;
+class WallpaperController;
 class WaylandServerController;
 class WindowCycleController;
 class WindowPositioner;
@@ -496,7 +496,7 @@
     return voice_interaction_controller_.get();
   }
   VpnList* vpn_list() { return vpn_list_.get(); }
-  WallpaperControllerImpl* wallpaper_controller() {
+  WallpaperController* wallpaper_controller() {
     return wallpaper_controller_.get();
   }
   WindowCycleController* window_cycle_controller() {
@@ -693,7 +693,7 @@
   std::unique_ptr<TrayAction> tray_action_;
   std::unique_ptr<VoiceInteractionController> voice_interaction_controller_;
   std::unique_ptr<VpnList> vpn_list_;
-  std::unique_ptr<WallpaperControllerImpl> wallpaper_controller_;
+  std::unique_ptr<WallpaperController> wallpaper_controller_;
   std::unique_ptr<WindowCycleController> window_cycle_controller_;
   std::unique_ptr<OverviewController> overview_controller_;
   // Owned by |focus_controller_|.
diff --git a/ash/system/network/network_list_view.cc b/ash/system/network/network_list_view.cc
index e7be515..4b695f3 100644
--- a/ash/system/network/network_list_view.cc
+++ b/ash/system/network/network_list_view.cc
@@ -27,8 +27,6 @@
 #include "ash/system/tray/tri_view.h"
 #include "base/i18n/number_formatting.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chromeos/network/network_handler.h"
-#include "chromeos/network/proxy/ui_proxy_config_service.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
 #include "chromeos/services/network_config/public/mojom/constants.mojom.h"
 #include "components/device_event_log/device_event_log.h"
@@ -51,9 +49,11 @@
 using chromeos::network_config::mojom::DeviceStateType;
 using chromeos::network_config::mojom::FilterType;
 using chromeos::network_config::mojom::NetworkFilter;
+using chromeos::network_config::mojom::NetworkStateProperties;
 using chromeos::network_config::mojom::NetworkStatePropertiesPtr;
 using chromeos::network_config::mojom::NetworkType;
 using chromeos::network_config::mojom::ONCSource;
+using chromeos::network_config::mojom::ProxyMode;
 
 namespace ash {
 namespace tray {
@@ -223,15 +223,9 @@
   // Keep an index where the next child should be inserted.
   int index = 0;
 
-  bool using_proxy = false;
-  // TODO(https://crbug.com/718072): Create UIProxyConfigService under mash, or
-  // provide this via network_config.mojom.
-  if (!::features::IsMultiProcessMash()) {
-    using_proxy = chromeos::NetworkHandler::Get()
-                      ->ui_proxy_config_service()
-                      ->HasDefaultNetworkProxyConfigured();
-  }
-
+  const NetworkStateProperties* default_network = model_->default_network();
+  bool using_proxy = default_network &&
+                     default_network->proxy_mode == ProxyMode::kFixedServers;
   // Show a warning that the connection might be monitored if connected to a VPN
   // or if the default network has a proxy installed.
   if (vpn_connected_ || using_proxy) {
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index b545db8..1f42194 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -26,7 +26,7 @@
 #include "ash/system/screen_layout_observer.h"
 #include "ash/test/ash_test_views_delegate.h"
 #include "ash/test_shell_delegate.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
diff --git a/ash/wallpaper/wallpaper_base_view.cc b/ash/wallpaper/wallpaper_base_view.cc
index a792f97..ffc2e4be0 100644
--- a/ash/wallpaper/wallpaper_base_view.cc
+++ b/ash/wallpaper/wallpaper_base_view.cc
@@ -7,7 +7,7 @@
 #include "ash/public/cpp/login_constants.h"
 #include "ash/public/cpp/wallpaper_types.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ui/gfx/canvas.h"
@@ -62,7 +62,7 @@
   // to fill the wallpaper. Ideally the image should be larger than the largest
   // display supported, if not we will scale and center it if the layout is
   // WALLPAPER_LAYOUT_CENTER_CROPPED.
-  auto* controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* controller = Shell::Get()->wallpaper_controller();
   gfx::ImageSkia wallpaper = controller->GetWallpaper();
   WallpaperLayout layout = controller->GetWallpaperLayout();
 
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller.cc
similarity index 77%
rename from ash/wallpaper/wallpaper_controller_impl.cc
rename to ash/wallpaper/wallpaper_controller.cc
index 4aded0b1..587ea0d 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 
 #include <memory>
 #include <numeric>
@@ -14,12 +14,11 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/login_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/wallpaper_controller_client.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_color_calculator.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_decoder.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer.h"
@@ -122,21 +121,21 @@
 }
 
 // Returns the appropriate wallpaper resolution for all root windows.
-WallpaperControllerImpl::WallpaperResolution GetAppropriateResolution() {
-  gfx::Size size = WallpaperControllerImpl::GetMaxDisplaySizeInNative();
+WallpaperController::WallpaperResolution GetAppropriateResolution() {
+  gfx::Size size = WallpaperController::GetMaxDisplaySizeInNative();
   return (size.width() > kSmallWallpaperMaxWidth ||
           size.height() > kSmallWallpaperMaxHeight)
-             ? WallpaperControllerImpl::WALLPAPER_RESOLUTION_LARGE
-             : WallpaperControllerImpl::WALLPAPER_RESOLUTION_SMALL;
+             ? WallpaperController::WALLPAPER_RESOLUTION_LARGE
+             : WallpaperController::WALLPAPER_RESOLUTION_SMALL;
 }
 
 // Returns the path of the online wallpaper corresponding to |url| and
 // |resolution|.
 base::FilePath GetOnlineWallpaperPath(
     const std::string& url,
-    WallpaperControllerImpl::WallpaperResolution resolution) {
+    WallpaperController::WallpaperResolution resolution) {
   std::string file_name = GURL(url).ExtractFileName();
-  if (resolution == WallpaperControllerImpl::WALLPAPER_RESOLUTION_SMALL) {
+  if (resolution == WallpaperController::WALLPAPER_RESOLUTION_SMALL) {
     file_name = base::FilePath(file_name)
                     .InsertBeforeExtension(kSmallWallpaperSuffix)
                     .value();
@@ -147,11 +146,11 @@
 
 // Returns wallpaper subdirectory name for current resolution.
 std::string GetCustomWallpaperSubdirForCurrentResolution() {
-  WallpaperControllerImpl::WallpaperResolution resolution =
+  WallpaperController::WallpaperResolution resolution =
       GetAppropriateResolution();
-  return resolution == WallpaperControllerImpl::WALLPAPER_RESOLUTION_SMALL
-             ? WallpaperControllerImpl::kSmallWallpaperSubDir
-             : WallpaperControllerImpl::kLargeWallpaperSubDir;
+  return resolution == WallpaperController::WALLPAPER_RESOLUTION_SMALL
+             ? WallpaperController::kSmallWallpaperSubDir
+             : WallpaperController::kLargeWallpaperSubDir;
 }
 
 // Resizes |image| to a resolution which is nearest to |preferred_width| and
@@ -295,21 +294,21 @@
 // Creates all new custom wallpaper directories for |wallpaper_files_id| if they
 // don't exist.
 void EnsureCustomWallpaperDirectories(const std::string& wallpaper_files_id) {
-  base::FilePath dir = WallpaperControllerImpl::GetCustomWallpaperDir(
-                           WallpaperControllerImpl::kSmallWallpaperSubDir)
+  base::FilePath dir = WallpaperController::GetCustomWallpaperDir(
+                           WallpaperController::kSmallWallpaperSubDir)
                            .Append(wallpaper_files_id);
   if (!base::PathExists(dir))
     base::CreateDirectory(dir);
 
-  dir = WallpaperControllerImpl::GetCustomWallpaperDir(
-            WallpaperControllerImpl::kLargeWallpaperSubDir)
+  dir = WallpaperController::GetCustomWallpaperDir(
+            WallpaperController::kLargeWallpaperSubDir)
             .Append(wallpaper_files_id);
 
   if (!base::PathExists(dir))
     base::CreateDirectory(dir);
 
-  dir = WallpaperControllerImpl::GetCustomWallpaperDir(
-            WallpaperControllerImpl::kOriginalWallpaperSubDir)
+  dir = WallpaperController::GetCustomWallpaperDir(
+            WallpaperController::kOriginalWallpaperSubDir)
             .Append(wallpaper_files_id);
   if (!base::PathExists(dir))
     base::CreateDirectory(dir);
@@ -321,27 +320,27 @@
                          const base::FilePath& original_path,
                          WallpaperLayout layout,
                          gfx::ImageSkia image) {
-  base::DeleteFile(WallpaperControllerImpl::GetCustomWallpaperDir(
-                       WallpaperControllerImpl::kOriginalWallpaperSubDir)
+  base::DeleteFile(WallpaperController::GetCustomWallpaperDir(
+                       WallpaperController::kOriginalWallpaperSubDir)
                        .Append(wallpaper_files_id),
                    true /* recursive */);
-  base::DeleteFile(WallpaperControllerImpl::GetCustomWallpaperDir(
-                       WallpaperControllerImpl::kSmallWallpaperSubDir)
+  base::DeleteFile(WallpaperController::GetCustomWallpaperDir(
+                       WallpaperController::kSmallWallpaperSubDir)
                        .Append(wallpaper_files_id),
                    true /* recursive */);
-  base::DeleteFile(WallpaperControllerImpl::GetCustomWallpaperDir(
-                       WallpaperControllerImpl::kLargeWallpaperSubDir)
+  base::DeleteFile(WallpaperController::GetCustomWallpaperDir(
+                       WallpaperController::kLargeWallpaperSubDir)
                        .Append(wallpaper_files_id),
                    true /* recursive */);
   EnsureCustomWallpaperDirectories(wallpaper_files_id);
   const std::string file_name = original_path.BaseName().value();
   const base::FilePath small_wallpaper_path =
-      WallpaperControllerImpl::GetCustomWallpaperPath(
-          WallpaperControllerImpl::kSmallWallpaperSubDir, wallpaper_files_id,
+      WallpaperController::GetCustomWallpaperPath(
+          WallpaperController::kSmallWallpaperSubDir, wallpaper_files_id,
           file_name);
   const base::FilePath large_wallpaper_path =
-      WallpaperControllerImpl::GetCustomWallpaperPath(
-          WallpaperControllerImpl::kLargeWallpaperSubDir, wallpaper_files_id,
+      WallpaperController::GetCustomWallpaperPath(
+          WallpaperController::kLargeWallpaperSubDir, wallpaper_files_id,
           file_name);
 
   // Re-encode orginal file to jpeg format and saves the result in case that
@@ -365,30 +364,28 @@
          *active_user_type == user_manager::USER_TYPE_KIOSK_APP;
 }
 
-// Returns the currently active user session (at index 0).
-const UserSession* GetActiveUserSession() {
-  return Shell::Get()->session_controller()->GetUserSession(/*user index=*/0);
-}
-
 // Checks if |account_id| is the current active user.
 bool IsActiveUser(const AccountId& account_id) {
-  const UserSession* const session = GetActiveUserSession();
-  return session && session->user_info.account_id == account_id;
+  // The current active user has index 0.
+  const UserSession* const active_user_session =
+      Shell::Get()->session_controller()->GetUserSession(/*user index=*/0);
+  return active_user_session &&
+         active_user_session->user_info.account_id == account_id;
 }
 
 // Returns the file path of the wallpaper corresponding to |url| if it exists in
 // local file system, otherwise returns an empty file path.
 base::FilePath GetExistingOnlineWallpaperPath(const std::string& url) {
-  WallpaperControllerImpl::WallpaperResolution resolution =
+  WallpaperController::WallpaperResolution resolution =
       GetAppropriateResolution();
   base::FilePath wallpaper_path = GetOnlineWallpaperPath(url, resolution);
   if (base::PathExists(wallpaper_path))
     return wallpaper_path;
 
   // Falls back to the large wallpaper if the small one doesn't exist.
-  if (resolution == WallpaperControllerImpl::WALLPAPER_RESOLUTION_SMALL) {
+  if (resolution == WallpaperController::WALLPAPER_RESOLUTION_SMALL) {
     wallpaper_path = GetOnlineWallpaperPath(
-        url, WallpaperControllerImpl::WALLPAPER_RESOLUTION_LARGE);
+        url, WallpaperController::WALLPAPER_RESOLUTION_LARGE);
     if (base::PathExists(wallpaper_path))
       return wallpaper_path;
   }
@@ -407,18 +404,18 @@
   }
   ResizeAndSaveWallpaper(
       image,
-      GetOnlineWallpaperPath(
-          url, WallpaperControllerImpl::WALLPAPER_RESOLUTION_LARGE),
+      GetOnlineWallpaperPath(url,
+                             WallpaperController::WALLPAPER_RESOLUTION_LARGE),
       layout, image.width(), image.height());
   ResizeAndSaveWallpaper(
       image,
-      GetOnlineWallpaperPath(
-          url, WallpaperControllerImpl::WALLPAPER_RESOLUTION_SMALL),
+      GetOnlineWallpaperPath(url,
+                             WallpaperController::WALLPAPER_RESOLUTION_SMALL),
       WALLPAPER_LAYOUT_CENTER_CROPPED, kSmallWallpaperMaxWidth,
       kSmallWallpaperMaxHeight);
 }
 
-// Implementation of |WallpaperControllerImpl::GetOfflineWallpaper|.
+// Implementation of |WallpaperController::GetOfflineWallpaper|.
 std::vector<std::string> GetOfflineWallpaperListImpl() {
   DCHECK(!GlobalChromeOSWallpapersDir().empty());
   std::vector<std::string> url_list;
@@ -441,11 +438,11 @@
 
 }  // namespace
 
-const char WallpaperControllerImpl::kSmallWallpaperSubDir[] = "small";
-const char WallpaperControllerImpl::kLargeWallpaperSubDir[] = "large";
-const char WallpaperControllerImpl::kOriginalWallpaperSubDir[] = "original";
+const char WallpaperController::kSmallWallpaperSubDir[] = "small";
+const char WallpaperController::kLargeWallpaperSubDir[] = "large";
+const char WallpaperController::kOriginalWallpaperSubDir[] = "original";
 
-WallpaperControllerImpl::WallpaperControllerImpl(PrefService* local_state)
+WallpaperController::WallpaperController(PrefService* local_state)
     : locked_(false),
       wallpaper_mode_(WALLPAPER_NONE),
       color_profiles_(GetProminentColorProfiles()),
@@ -456,8 +453,6 @@
       scoped_session_observer_(this),
       local_state_(local_state),
       weak_factory_(this) {
-  DCHECK(!g_instance_);
-  g_instance_ = this;
   DCHECK(!color_profiles_.empty());
   prominent_colors_ =
       std::vector<SkColor>(color_profiles_.size(), kInvalidWallpaperColor);
@@ -465,7 +460,7 @@
   Shell::Get()->AddShellObserver(this);
 }
 
-WallpaperControllerImpl::~WallpaperControllerImpl() {
+WallpaperController::~WallpaperController() {
   if (current_wallpaper_)
     current_wallpaper_->RemoveObserver(this);
   if (color_calculator_)
@@ -473,19 +468,17 @@
   Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
   weak_factory_.InvalidateWeakPtrs();
-  DCHECK_EQ(g_instance_, this);
-  g_instance_ = nullptr;
 }
 
 // static
-void WallpaperControllerImpl::RegisterLocalStatePrefs(
+void WallpaperController::RegisterLocalStatePrefs(
     PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(prefs::kUserWallpaperInfo);
   registry->RegisterDictionaryPref(prefs::kWallpaperColors);
 }
 
 // static
-gfx::Size WallpaperControllerImpl::GetMaxDisplaySizeInNative() {
+gfx::Size WallpaperController::GetMaxDisplaySizeInNative() {
   // Return an empty size for test environments where the screen is null.
   if (!display::Screen::GetScreen())
     return gfx::Size();
@@ -509,7 +502,7 @@
 }
 
 // static
-base::FilePath WallpaperControllerImpl::GetCustomWallpaperPath(
+base::FilePath WallpaperController::GetCustomWallpaperPath(
     const std::string& sub_dir,
     const std::string& wallpaper_files_id,
     const std::string& file_name) {
@@ -518,21 +511,21 @@
 }
 
 // static
-base::FilePath WallpaperControllerImpl::GetCustomWallpaperDir(
+base::FilePath WallpaperController::GetCustomWallpaperDir(
     const std::string& sub_dir) {
   DCHECK(!GlobalChromeOSCustomWallpapersDir().empty());
   return GlobalChromeOSCustomWallpapersDir().Append(sub_dir);
 }
 
 // static
-void WallpaperControllerImpl::SetWallpaperFromPath(
+void WallpaperController::SetWallpaperFromPath(
     const AccountId& account_id,
     const user_manager::UserType& user_type,
     const WallpaperInfo& info,
     const base::FilePath& wallpaper_path,
     bool show_wallpaper,
     const scoped_refptr<base::SingleThreadTaskRunner>& reply_task_runner,
-    base::WeakPtr<WallpaperControllerImpl> weak_ptr) {
+    base::WeakPtr<WallpaperController> weak_ptr) {
   base::FilePath valid_path = wallpaper_path;
   if (!base::PathExists(valid_path)) {
     // Falls back to the original file if the file with correct resolution does
@@ -547,37 +540,51 @@
                << " doesn't exist. Falls back to default wallpaper.";
     reply_task_runner->PostTask(
         FROM_HERE,
-        base::BindOnce(&WallpaperControllerImpl::SetDefaultWallpaperImpl,
-                       weak_ptr, account_id, user_type, show_wallpaper));
+        base::BindOnce(&WallpaperController::SetDefaultWallpaperImpl, weak_ptr,
+                       account_id, user_type, show_wallpaper));
   } else {
     reply_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(&WallpaperControllerImpl::StartDecodeFromPath,
+        FROM_HERE, base::BindOnce(&WallpaperController::StartDecodeFromPath,
                                   weak_ptr, account_id, user_type, valid_path,
                                   info, show_wallpaper));
   }
 }
 
-SkColor WallpaperControllerImpl::GetProminentColor(
+void WallpaperController::BindRequest(
+    mojom::WallpaperControllerRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void WallpaperController::AddObserver(WallpaperControllerObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void WallpaperController::RemoveObserver(
+    WallpaperControllerObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+SkColor WallpaperController::GetProminentColor(
     ColorProfile color_profile) const {
   ColorProfileType type = GetColorProfileType(color_profile);
   return prominent_colors_[static_cast<int>(type)];
 }
 
-gfx::ImageSkia WallpaperControllerImpl::GetWallpaper() const {
+gfx::ImageSkia WallpaperController::GetWallpaper() const {
   return current_wallpaper_ ? current_wallpaper_->image() : gfx::ImageSkia();
 }
 
-WallpaperLayout WallpaperControllerImpl::GetWallpaperLayout() const {
+WallpaperLayout WallpaperController::GetWallpaperLayout() const {
   return current_wallpaper_ ? current_wallpaper_->wallpaper_info().layout
                             : NUM_WALLPAPER_LAYOUT;
 }
 
-WallpaperType WallpaperControllerImpl::GetWallpaperType() const {
+WallpaperType WallpaperController::GetWallpaperType() const {
   return current_wallpaper_ ? current_wallpaper_->wallpaper_info().type
                             : WALLPAPER_TYPE_COUNT;
 }
 
-bool WallpaperControllerImpl::ShouldShowInitialAnimation() {
+bool WallpaperController::ShouldShowInitialAnimation() {
   // The slower initial animation is only applicable if:
   // 1) It's the first run after system boot, not after user sign-out.
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -598,7 +605,7 @@
   return true;
 }
 
-void WallpaperControllerImpl::OnWallpaperAnimationFinished() {
+void WallpaperController::OnWallpaperAnimationFinished() {
   // TODO(crbug.com/875128): Remove this after web-ui login code is completely
   // removed.
   if (wallpaper_controller_client_ && is_first_wallpaper_) {
@@ -606,19 +613,19 @@
   }
 }
 
-bool WallpaperControllerImpl::CanOpenWallpaperPicker() {
-  return ShouldShowWallpaperSetting() &&
-         !IsActiveUserWallpaperControlledByPolicy();
+bool WallpaperController::CanOpenWallpaperPicker() {
+  return ShouldShowWallpaperSettingImpl() &&
+         !IsActiveUserWallpaperControlledByPolicyImpl();
 }
 
-bool WallpaperControllerImpl::HasShownAnyWallpaper() const {
+bool WallpaperController::HasShownAnyWallpaper() const {
   return !!current_wallpaper_;
 }
 
-void WallpaperControllerImpl::ShowWallpaperImage(const gfx::ImageSkia& image,
-                                                 WallpaperInfo info,
-                                                 bool preview_mode,
-                                                 bool always_on_top) {
+void WallpaperController::ShowWallpaperImage(const gfx::ImageSkia& image,
+                                             WallpaperInfo info,
+                                             bool preview_mode,
+                                             bool always_on_top) {
   // Prevent showing other wallpapers if there is an always-on-top wallpaper.
   if (is_always_on_top_wallpaper_ && !always_on_top)
     return;
@@ -665,22 +672,23 @@
     for (auto& observer : observers_)
       observer.OnFirstWallpaperShown();
   }
-  for (auto& observer : observers_)
-    observer.OnWallpaperChanged();
+  mojo_observers_.ForAllPtrs([this](mojom::WallpaperObserver* observer) {
+    observer->OnWallpaperChanged(current_wallpaper_->original_image_id());
+  });
 
   wallpaper_mode_ = WALLPAPER_IMAGE;
   InstallDesktopControllerForAllWindows();
   ++wallpaper_count_for_testing_;
 }
 
-bool WallpaperControllerImpl::IsPolicyControlled(const AccountId& account_id,
-                                                 bool is_ephemeral) const {
+bool WallpaperController::IsPolicyControlled(const AccountId& account_id,
+                                             bool is_ephemeral) const {
   WallpaperInfo info;
   return GetUserWallpaperInfo(account_id, &info, is_ephemeral) &&
          info.type == POLICY;
 }
 
-void WallpaperControllerImpl::UpdateWallpaperBlur(bool blur) {
+void WallpaperController::UpdateWallpaperBlur(bool blur) {
   bool needs_blur = blur && IsBlurAllowed();
   if (needs_blur == is_wallpaper_blurred_)
     return;
@@ -693,9 +701,12 @@
   is_wallpaper_blurred_ = needs_blur;
   for (auto& observer : observers_)
     observer.OnWallpaperBlurChanged();
+  mojo_observers_.ForAllPtrs([this](mojom::WallpaperObserver* observer) {
+    observer->OnWallpaperBlurChanged(is_wallpaper_blurred_);
+  });
 }
 
-bool WallpaperControllerImpl::ShouldApplyDimming() const {
+bool WallpaperController::ShouldApplyDimming() const {
   // Dim the wallpaper in a blocked user session or in tablet mode unless during
   // wallpaper preview.
   const bool should_dim =
@@ -707,17 +718,17 @@
   return should_dim && !IsOneShotWallpaper();
 }
 
-bool WallpaperControllerImpl::IsBlurAllowed() const {
+bool WallpaperController::IsBlurAllowed() const {
   return !IsDevicePolicyWallpaper() && !IsOneShotWallpaper();
 }
 
-bool WallpaperControllerImpl::IsWallpaperBlurred() const {
+bool WallpaperController::IsWallpaperBlurred() const {
   return is_wallpaper_blurred_;
 }
 
-bool WallpaperControllerImpl::SetUserWallpaperInfo(const AccountId& account_id,
-                                                   const WallpaperInfo& info,
-                                                   bool is_ephemeral) {
+bool WallpaperController::SetUserWallpaperInfo(const AccountId& account_id,
+                                               const WallpaperInfo& info,
+                                               bool is_ephemeral) {
   if (is_ephemeral) {
     ephemeral_users_wallpaper_info_[account_id] = info;
     return true;
@@ -749,9 +760,9 @@
   return true;
 }
 
-bool WallpaperControllerImpl::GetUserWallpaperInfo(const AccountId& account_id,
-                                                   WallpaperInfo* info,
-                                                   bool is_ephemeral) const {
+bool WallpaperController::GetUserWallpaperInfo(const AccountId& account_id,
+                                               WallpaperInfo* info,
+                                               bool is_ephemeral) const {
   if (is_ephemeral) {
     // Ephemeral users do not save anything to local state. Return true if the
     // info can be found in the map, otherwise return false.
@@ -796,8 +807,8 @@
   return true;
 }
 
-bool WallpaperControllerImpl::GetWallpaperFromCache(const AccountId& account_id,
-                                                    gfx::ImageSkia* image) {
+bool WallpaperController::GetWallpaperFromCache(const AccountId& account_id,
+                                                gfx::ImageSkia* image) {
   CustomWallpaperMap::const_iterator it = wallpaper_cache_map_.find(account_id);
   if (it != wallpaper_cache_map_.end() && !it->second.second.isNull()) {
     *image = it->second.second;
@@ -806,8 +817,8 @@
   return false;
 }
 
-bool WallpaperControllerImpl::GetPathFromCache(const AccountId& account_id,
-                                               base::FilePath* path) {
+bool WallpaperController::GetPathFromCache(const AccountId& account_id,
+                                           base::FilePath* path) {
   CustomWallpaperMap::const_iterator it = wallpaper_cache_map_.find(account_id);
   if (it != wallpaper_cache_map_.end()) {
     *path = it->second.first;
@@ -816,7 +827,7 @@
   return false;
 }
 
-void WallpaperControllerImpl::AddFirstWallpaperAnimationEndCallback(
+void WallpaperController::AddFirstWallpaperAnimationEndCallback(
     base::OnceClosure callback,
     aura::Window* window) {
   WallpaperWidgetController* wallpaper_widget_controller =
@@ -830,58 +841,57 @@
   }
 }
 
-void WallpaperControllerImpl::StartDecodeFromPath(
+void WallpaperController::StartDecodeFromPath(
     const AccountId& account_id,
     const user_manager::UserType& user_type,
     const base::FilePath& wallpaper_path,
     const WallpaperInfo& info,
     bool show_wallpaper) {
   ReadAndDecodeWallpaper(
-      base::BindOnce(&WallpaperControllerImpl::OnWallpaperDecoded,
+      base::BindOnce(&WallpaperController::OnWallpaperDecoded,
                      weak_factory_.GetWeakPtr(), account_id, user_type,
                      wallpaper_path, info, show_wallpaper),
       sequenced_task_runner_, wallpaper_path);
 }
 
-void WallpaperControllerImpl::Init(
-    WallpaperControllerClient* client,
+void WallpaperController::Init(
+    mojom::WallpaperControllerClientPtr client,
     const base::FilePath& user_data_path,
     const base::FilePath& chromeos_wallpapers_path,
     const base::FilePath& chromeos_custom_wallpapers_path,
     const base::FilePath& device_policy_wallpaper_path) {
-  DCHECK(!wallpaper_controller_client_);
+  DCHECK(!wallpaper_controller_client_.get());
+  wallpaper_controller_client_ = std::move(client);
+
   DCHECK(local_state_);
-  wallpaper_controller_client_ = client;
+  wallpaper_controller_client_->OnReadyToSetWallpaper();
 
   SetGlobalUserDataDir(user_data_path);
   SetGlobalChromeOSWallpapersDir(chromeos_wallpapers_path);
   SetGlobalChromeOSCustomWallpapersDir(chromeos_custom_wallpapers_path);
   SetDevicePolicyWallpaperPath(device_policy_wallpaper_path);
-
-  wallpaper_controller_client_->OnReadyToSetWallpaper();
 }
 
-void WallpaperControllerImpl::SetCustomWallpaper(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::SetCustomWallpaper(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     WallpaperLayout layout,
     const gfx::ImageSkia& image,
     bool preview_mode) {
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
-  if (!CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral))
+  if (!CanSetUserWallpaper(user_info->account_id, user_info->is_ephemeral))
     return;
 
-  const bool is_active_user = IsActiveUser(user_info.account_id);
+  const bool is_active_user = IsActiveUser(user_info->account_id);
   if (preview_mode) {
     DCHECK(is_active_user);
-    confirm_preview_wallpaper_callback_ =
-        base::BindOnce(&WallpaperControllerImpl::SaveAndSetWallpaper,
-                       weak_factory_.GetWeakPtr(), user_info,
-                       wallpaper_files_id, file_name, CUSTOMIZED, layout,
-                       /*show_wallpaper=*/false, image);
+    confirm_preview_wallpaper_callback_ = base::BindOnce(
+        &WallpaperController::SaveAndSetWallpaper, weak_factory_.GetWeakPtr(),
+        std::move(user_info), wallpaper_files_id, file_name, CUSTOMIZED, layout,
+        /*show_wallpaper=*/false, image);
     reload_preview_wallpaper_callback_ =
-        base::BindRepeating(&WallpaperControllerImpl::ShowWallpaperImage,
+        base::BindRepeating(&WallpaperController::ShowWallpaperImage,
                             weak_factory_.GetWeakPtr(), image,
                             WallpaperInfo{std::string(), layout, CUSTOMIZED,
                                           base::Time::Now().LocalMidnight()},
@@ -889,46 +899,49 @@
     // Show the preview wallpaper.
     reload_preview_wallpaper_callback_.Run();
   } else {
-    SaveAndSetWallpaper(user_info, wallpaper_files_id, file_name, CUSTOMIZED,
-                        layout, /*show_wallpaper=*/is_active_user, image);
+    SaveAndSetWallpaper(std::move(user_info), wallpaper_files_id, file_name,
+                        CUSTOMIZED, layout, /*show_wallpaper=*/is_active_user,
+                        image);
   }
 }
 
-void WallpaperControllerImpl::SetOnlineWallpaperIfExists(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::SetOnlineWallpaperIfExists(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& url,
     WallpaperLayout layout,
     bool preview_mode,
     SetOnlineWallpaperIfExistsCallback callback) {
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
-  DCHECK(CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral));
+  DCHECK(CanSetUserWallpaper(user_info->account_id, user_info->is_ephemeral));
 
-  const OnlineWallpaperParams params = {
-      user_info.account_id, user_info.is_ephemeral, url, layout, preview_mode};
+  const OnlineWallpaperParams params = {user_info->account_id,
+                                        user_info->is_ephemeral, url, layout,
+                                        preview_mode};
   base::PostTaskAndReplyWithResult(
       sequenced_task_runner_.get(), FROM_HERE,
       base::BindOnce(&GetExistingOnlineWallpaperPath, url),
-      base::BindOnce(&WallpaperControllerImpl::SetOnlineWallpaperFromPath,
+      base::BindOnce(&WallpaperController::SetOnlineWallpaperFromPath,
                      weak_factory_.GetWeakPtr(), std::move(callback), params));
 }
 
-void WallpaperControllerImpl::SetOnlineWallpaperFromData(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::SetOnlineWallpaperFromData(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& image_data,
     const std::string& url,
     WallpaperLayout layout,
     bool preview_mode,
     SetOnlineWallpaperFromDataCallback callback) {
   if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted() ||
-      !CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral)) {
+      !CanSetUserWallpaper(user_info->account_id, user_info->is_ephemeral)) {
     std::move(callback).Run(/*success=*/false);
     return;
   }
 
-  const OnlineWallpaperParams params = {
-      user_info.account_id, user_info.is_ephemeral, url, layout, preview_mode};
+  const OnlineWallpaperParams params = {user_info->account_id,
+                                        user_info->is_ephemeral, url, layout,
+                                        preview_mode};
   LoadedCallback decoded_callback =
-      base::BindOnce(&WallpaperControllerImpl::OnOnlineWallpaperDecoded,
+      base::BindOnce(&WallpaperController::OnOnlineWallpaperDecoded,
                      weak_factory_.GetWeakPtr(), params, /*save_file=*/true,
                      std::move(callback));
   if (bypass_decode_for_testing_) {
@@ -944,26 +957,27 @@
                   std::move(decoded_callback));
 }
 
-void WallpaperControllerImpl::SetDefaultWallpaper(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::SetDefaultWallpaper(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     bool show_wallpaper) {
-  if (!CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral))
+  if (!CanSetUserWallpaper(user_info->account_id, user_info->is_ephemeral))
     return;
 
-  RemoveUserWallpaper(user_info, wallpaper_files_id);
-  if (!InitializeUserWallpaperInfo(user_info.account_id,
-                                   user_info.is_ephemeral)) {
+  const AccountId account_id = user_info->account_id;
+  const bool is_ephemeral = user_info->is_ephemeral;
+  const user_manager::UserType type = user_info->type;
+
+  RemoveUserWallpaper(std::move(user_info), wallpaper_files_id);
+  if (!InitializeUserWallpaperInfo(account_id, is_ephemeral)) {
     LOG(ERROR) << "Initializing user wallpaper info fails. This should never "
                   "happen except in tests.";
   }
-  if (show_wallpaper) {
-    SetDefaultWallpaperImpl(user_info.account_id, user_info.type,
-                            /*show_wallpaper=*/true);
-  }
+  if (show_wallpaper)
+    SetDefaultWallpaperImpl(account_id, type, /*show_wallpaper=*/true);
 }
 
-void WallpaperControllerImpl::SetCustomizedDefaultWallpaperPaths(
+void WallpaperController::SetCustomizedDefaultWallpaperPaths(
     const base::FilePath& customized_default_small_path,
     const base::FilePath& customized_default_large_path) {
   customized_default_small_path_ = customized_default_small_path;
@@ -980,8 +994,8 @@
                           show_wallpaper);
 }
 
-void WallpaperControllerImpl::SetPolicyWallpaper(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::SetPolicyWallpaper(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     const std::string& data) {
   // There is no visible wallpaper in kiosk mode.
@@ -992,9 +1006,9 @@
   const bool show_wallpaper =
       Shell::Get()->session_controller()->IsActiveUserSessionStarted();
   LoadedCallback callback = base::BindOnce(
-      &WallpaperControllerImpl::SaveAndSetWallpaper, weak_factory_.GetWeakPtr(),
-      user_info, wallpaper_files_id, kPolicyWallpaperFile, POLICY,
-      WALLPAPER_LAYOUT_CENTER_CROPPED, show_wallpaper);
+      &WallpaperController::SaveAndSetWallpaper, weak_factory_.GetWeakPtr(),
+      base::Passed(&user_info), wallpaper_files_id, kPolicyWallpaperFile,
+      POLICY, WALLPAPER_LAYOUT_CENTER_CROPPED, show_wallpaper);
 
   if (bypass_decode_for_testing_) {
     std::move(callback).Run(CreateSolidColorWallpaper(kDefaultWallpaperColor));
@@ -1006,7 +1020,7 @@
                   std::move(callback));
 }
 
-void WallpaperControllerImpl::SetDevicePolicyWallpaperPath(
+void WallpaperController::SetDevicePolicyWallpaperPath(
     const base::FilePath& device_policy_wallpaper_path) {
   const bool was_device_policy_wallpaper_enforced =
       !device_policy_wallpaper_path_.empty();
@@ -1024,24 +1038,27 @@
   }
 }
 
-bool WallpaperControllerImpl::SetThirdPartyWallpaper(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::SetThirdPartyWallpaper(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     WallpaperLayout layout,
-    const gfx::ImageSkia& image) {
+    const gfx::ImageSkia& image,
+    SetThirdPartyWallpaperCallback callback) {
+  const uint32_t image_id = WallpaperResizer::GetImageId(image);
   bool allowed_to_set_wallpaper =
-      CanSetUserWallpaper(user_info.account_id, user_info.is_ephemeral);
-  bool allowed_to_show_wallpaper = IsActiveUser(user_info.account_id);
+      CanSetUserWallpaper(user_info->account_id, user_info->is_ephemeral);
+  bool allowed_to_show_wallpaper = IsActiveUser(user_info->account_id);
+  std::move(callback).Run(allowed_to_set_wallpaper && allowed_to_show_wallpaper,
+                          image_id);
 
   if (allowed_to_set_wallpaper) {
-    SaveAndSetWallpaper(user_info, wallpaper_files_id, file_name, CUSTOMIZED,
-                        layout, allowed_to_show_wallpaper, image);
+    SaveAndSetWallpaper(std::move(user_info), wallpaper_files_id, file_name,
+                        CUSTOMIZED, layout, allowed_to_show_wallpaper, image);
   }
-  return allowed_to_set_wallpaper && allowed_to_show_wallpaper;
 }
 
-void WallpaperControllerImpl::ConfirmPreviewWallpaper() {
+void WallpaperController::ConfirmPreviewWallpaper() {
   if (!confirm_preview_wallpaper_callback_) {
     DCHECK(!reload_preview_wallpaper_callback_);
     return;
@@ -1052,7 +1069,7 @@
     observer.OnWallpaperPreviewEnded();
 }
 
-void WallpaperControllerImpl::CancelPreviewWallpaper() {
+void WallpaperController::CancelPreviewWallpaper() {
   if (!confirm_preview_wallpaper_callback_) {
     DCHECK(!reload_preview_wallpaper_callback_);
     return;
@@ -1064,18 +1081,21 @@
     observer.OnWallpaperPreviewEnded();
 }
 
-void WallpaperControllerImpl::UpdateCustomWallpaperLayout(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::UpdateCustomWallpaperLayout(
+    mojom::WallpaperUserInfoPtr user_info,
     WallpaperLayout layout) {
   // This method has a very specific use case: the user should be active and
   // have a custom wallpaper.
-  const UserSession* const session = GetActiveUserSession();
-  if (!session || session->user_info.account_id != user_info.account_id)
+  // The currently active user has index 0.
+  const UserSession* const active_user_session =
+      Shell::Get()->session_controller()->GetUserSession(/*user index=*/0);
+  if (!active_user_session ||
+      active_user_session->user_info.account_id != user_info->account_id) {
     return;
-
+  }
   WallpaperInfo info;
-  if (!GetUserWallpaperInfo(user_info.account_id, &info,
-                            user_info.is_ephemeral) ||
+  if (!GetUserWallpaperInfo(user_info->account_id, &info,
+                            user_info->is_ephemeral) ||
       info.type != CUSTOMIZED) {
     return;
   }
@@ -1083,20 +1103,21 @@
     return;
 
   info.layout = layout;
-  if (!SetUserWallpaperInfo(user_info.account_id, info,
-                            user_info.is_ephemeral)) {
+  if (!SetUserWallpaperInfo(user_info->account_id, info,
+                            user_info->is_ephemeral)) {
     LOG(ERROR) << "Setting user wallpaper info fails. This should never happen "
                   "except in tests.";
   }
-  ShowUserWallpaper(user_info);
+  ShowUserWallpaper(std::move(user_info));
 }
 
-void WallpaperControllerImpl::ShowUserWallpaper(
-    const WallpaperUserInfo& user_info) {
-  current_user_ = user_info;
+void WallpaperController::ShowUserWallpaper(
+    mojom::WallpaperUserInfoPtr user_info) {
+  current_user_ = std::move(user_info);
+  const user_manager::UserType user_type = current_user_->type;
 
-  if (current_user_.type == user_manager::USER_TYPE_KIOSK_APP ||
-      current_user_.type == user_manager::USER_TYPE_ARC_KIOSK_APP) {
+  if (user_type == user_manager::USER_TYPE_KIOSK_APP ||
+      user_type == user_manager::USER_TYPE_ARC_KIOSK_APP) {
     return;
   }
 
@@ -1105,8 +1126,8 @@
     return;
   }
 
-  const AccountId account_id = current_user_.account_id;
-  const bool is_ephemeral = current_user_.is_ephemeral;
+  const AccountId account_id = current_user_->account_id;
+  const bool is_ephemeral = current_user_->is_ephemeral;
   WallpaperInfo info;
   if (!GetUserWallpaperInfo(account_id, &info, is_ephemeral)) {
     if (!InitializeUserWallpaperInfo(account_id, is_ephemeral))
@@ -1129,14 +1150,14 @@
   }
 
   if (info.type == DEFAULT) {
-    SetDefaultWallpaperImpl(account_id, current_user_.type,
+    SetDefaultWallpaperImpl(account_id, current_user_->type,
                             /*show_wallpaper=*/true);
     return;
   }
 
   if (info.type != CUSTOMIZED && info.type != POLICY && info.type != DEVICE) {
     // Load wallpaper according to WallpaperInfo.
-    SetWallpaperFromInfo(account_id, current_user_.type, info,
+    SetWallpaperFromInfo(account_id, current_user_->type, info,
                          /*show_wallpaper=*/true);
     return;
   }
@@ -1168,14 +1189,14 @@
 
   sequenced_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&SetWallpaperFromPath, account_id, current_user_.type,
+      base::BindOnce(&SetWallpaperFromPath, account_id, current_user_->type,
                      info, wallpaper_path, /*show_wallpaper=*/true,
                      base::ThreadTaskRunnerHandle::Get(),
                      weak_factory_.GetWeakPtr()));
 }
 
-void WallpaperControllerImpl::ShowSigninWallpaper() {
-  current_user_ = WallpaperUserInfo();
+void WallpaperController::ShowSigninWallpaper() {
+  current_user_.reset();
   if (ShouldSetDevicePolicyWallpaper()) {
     SetDevicePolicyWallpaper();
   } else {
@@ -1184,8 +1205,7 @@
   }
 }
 
-void WallpaperControllerImpl::ShowOneShotWallpaper(
-    const gfx::ImageSkia& image) {
+void WallpaperController::ShowOneShotWallpaper(const gfx::ImageSkia& image) {
   const WallpaperInfo info = {
       std::string(), WallpaperLayout::WALLPAPER_LAYOUT_STRETCH,
       WallpaperType::ONE_SHOT, base::Time::Now().LocalMidnight()};
@@ -1193,19 +1213,19 @@
                      /*always_on_top=*/false);
 }
 
-void WallpaperControllerImpl::ShowAlwaysOnTopWallpaper(
+void WallpaperController::ShowAlwaysOnTopWallpaper(
     const base::FilePath& image_path) {
   is_always_on_top_wallpaper_ = true;
   const WallpaperInfo info = {
       std::string(), WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED,
       WallpaperType::ONE_SHOT, base::Time::Now().LocalMidnight()};
   ReadAndDecodeWallpaper(
-      base::BindOnce(&WallpaperControllerImpl::OnAlwaysOnTopWallpaperDecoded,
+      base::BindOnce(&WallpaperController::OnAlwaysOnTopWallpaperDecoded,
                      weak_factory_.GetWeakPtr(), info),
       sequenced_task_runner_, image_path);
 }
 
-void WallpaperControllerImpl::RemoveAlwaysOnTopWallpaper() {
+void WallpaperController::RemoveAlwaysOnTopWallpaper() {
   if (!is_always_on_top_wallpaper_) {
     DCHECK(!reload_always_on_top_wallpaper_callback_);
     return;
@@ -1215,17 +1235,17 @@
   ReloadWallpaper(/*clear_cache=*/false);
 }
 
-void WallpaperControllerImpl::RemoveUserWallpaper(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::RemoveUserWallpaper(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id) {
-  RemoveUserWallpaperInfo(user_info.account_id, user_info.is_ephemeral);
-  RemoveUserWallpaperImpl(user_info.account_id, wallpaper_files_id);
+  RemoveUserWallpaperInfo(user_info->account_id, user_info->is_ephemeral);
+  RemoveUserWallpaperImpl(user_info->account_id, wallpaper_files_id);
 }
 
-void WallpaperControllerImpl::RemovePolicyWallpaper(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::RemovePolicyWallpaper(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id) {
-  if (!IsPolicyControlled(user_info.account_id, user_info.is_ephemeral))
+  if (!IsPolicyControlled(user_info->account_id, user_info->is_ephemeral))
     return;
 
   // Updates the screen only when the user has logged in.
@@ -1233,29 +1253,29 @@
       Shell::Get()->session_controller()->IsActiveUserSessionStarted();
   // Removes the wallpaper info so that the user is no longer policy controlled,
   // otherwise setting default wallpaper is not allowed.
-  RemoveUserWallpaperInfo(user_info.account_id, user_info.is_ephemeral);
-  SetDefaultWallpaper(user_info, wallpaper_files_id, show_wallpaper);
+  RemoveUserWallpaperInfo(user_info->account_id, user_info->is_ephemeral);
+  SetDefaultWallpaper(std::move(user_info), wallpaper_files_id, show_wallpaper);
 }
 
-void WallpaperControllerImpl::GetOfflineWallpaperList(
+void WallpaperController::GetOfflineWallpaperList(
     GetOfflineWallpaperListCallback callback) {
   base::PostTaskAndReplyWithResult(sequenced_task_runner_.get(), FROM_HERE,
                                    base::BindOnce(&GetOfflineWallpaperListImpl),
                                    std::move(callback));
 }
 
-void WallpaperControllerImpl::SetAnimationDuration(
+void WallpaperController::SetAnimationDuration(
     base::TimeDelta animation_duration) {
   animation_duration_ = animation_duration;
 }
 
-void WallpaperControllerImpl::OpenWallpaperPickerIfAllowed() {
+void WallpaperController::OpenWallpaperPickerIfAllowed() {
   if (wallpaper_controller_client_ && CanOpenWallpaperPicker()) {
     wallpaper_controller_client_->OpenWallpaperPicker();
   }
 }
 
-void WallpaperControllerImpl::MinimizeInactiveWindows(
+void WallpaperController::MinimizeInactiveWindows(
     const std::string& user_id_hash) {
   if (!window_state_manager_)
     window_state_manager_ = std::make_unique<WallpaperWindowStateManager>();
@@ -1263,7 +1283,7 @@
   window_state_manager_->MinimizeInactiveWindows(user_id_hash);
 }
 
-void WallpaperControllerImpl::RestoreMinimizedWindows(
+void WallpaperController::RestoreMinimizedWindows(
     const std::string& user_id_hash) {
   if (!window_state_manager_) {
     NOTREACHED() << "This should only be called after calling "
@@ -1273,62 +1293,50 @@
   window_state_manager_->RestoreMinimizedWindows(user_id_hash);
 }
 
-void WallpaperControllerImpl::AddObserver(
-    WallpaperControllerObserver* observer) {
-  observers_.AddObserver(observer);
+void WallpaperController::AddObserver(
+    mojom::WallpaperObserverAssociatedPtrInfo observer) {
+  mojom::WallpaperObserverAssociatedPtr observer_ptr;
+  observer_ptr.Bind(std::move(observer));
+  observer_ptr->OnWallpaperColorsChanged(prominent_colors_);
+  mojo_observers_.AddPtr(std::move(observer_ptr));
 }
 
-void WallpaperControllerImpl::RemoveObserver(
-    WallpaperControllerObserver* observer) {
-  observers_.RemoveObserver(observer);
+void WallpaperController::GetWallpaperImage(
+    GetWallpaperImageCallback callback) {
+  std::move(callback).Run(GetWallpaper());
 }
 
-gfx::ImageSkia WallpaperControllerImpl::GetWallpaperImage() {
-  return GetWallpaper();
+void WallpaperController::GetWallpaperColors(
+    GetWallpaperColorsCallback callback) {
+  std::move(callback).Run(prominent_colors_);
 }
 
-const std::vector<SkColor>& WallpaperControllerImpl::GetWallpaperColors() {
-  return prominent_colors_;
+void WallpaperController::IsWallpaperBlurred(
+    IsWallpaperBlurredCallback callback) {
+  std::move(callback).Run(is_wallpaper_blurred_);
 }
 
-bool WallpaperControllerImpl::IsWallpaperBlurred() {
-  return is_wallpaper_blurred_;
+void WallpaperController::IsActiveUserWallpaperControlledByPolicy(
+    IsActiveUserWallpaperControlledByPolicyCallback callback) {
+  std::move(callback).Run(IsActiveUserWallpaperControlledByPolicyImpl());
 }
 
-bool WallpaperControllerImpl::IsActiveUserWallpaperControlledByPolicy() {
-  const UserSession* const active_user_session = GetActiveUserSession();
-  if (!active_user_session)
-    return false;
-  return IsPolicyControlled(active_user_session->user_info.account_id,
-                            active_user_session->user_info.is_ephemeral);
-}
-
-WallpaperInfo WallpaperControllerImpl::GetActiveUserWallpaperInfo() {
+void WallpaperController::GetActiveUserWallpaperInfo(
+    GetActiveUserWallpaperInfoCallback callback) {
   WallpaperInfo info;
-  const UserSession* const active_user_session = GetActiveUserSession();
-  if (!active_user_session ||
-      !GetUserWallpaperInfo(active_user_session->user_info.account_id, &info,
-                            active_user_session->user_info.is_ephemeral)) {
-    info.location = std::string();
-    info.layout = NUM_WALLPAPER_LAYOUT;
+  if (!GetActiveUserWallpaperInfoImpl(&info)) {
+    std::move(callback).Run(std::string(), ash::NUM_WALLPAPER_LAYOUT);
+    return;
   }
-
-  return info;
+  std::move(callback).Run(info.location, info.layout);
 }
 
-bool WallpaperControllerImpl::ShouldShowWallpaperSetting() {
-  const UserSession* const active_user_session = GetActiveUserSession();
-  if (!active_user_session)
-    return false;
-
-  user_manager::UserType active_user_type = active_user_session->user_info.type;
-  return active_user_type == user_manager::USER_TYPE_REGULAR ||
-         active_user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT ||
-         active_user_type == user_manager::USER_TYPE_SUPERVISED ||
-         active_user_type == user_manager::USER_TYPE_CHILD;
+void WallpaperController::ShouldShowWallpaperSetting(
+    ShouldShowWallpaperSettingCallback callback) {
+  std::move(callback).Run(ShouldShowWallpaperSettingImpl());
 }
 
-void WallpaperControllerImpl::OnDisplayConfigurationChanged() {
+void WallpaperController::OnDisplayConfigurationChanged() {
   gfx::Size max_display_size = GetMaxDisplaySizeInNative();
   if (current_max_display_size_ == max_display_size)
     return;
@@ -1339,12 +1347,12 @@
     GetInternalDisplayCompositorLock();
     timer_.Start(
         FROM_HERE, wallpaper_reload_delay_,
-        base::BindRepeating(&WallpaperControllerImpl::ReloadWallpaper,
+        base::BindRepeating(&WallpaperController::ReloadWallpaper,
                             weak_factory_.GetWeakPtr(), /*clear_cache=*/false));
   }
 }
 
-void WallpaperControllerImpl::OnRootWindowAdded(aura::Window* root_window) {
+void WallpaperController::OnRootWindowAdded(aura::Window* root_window) {
   // The wallpaper hasn't been set yet.
   if (wallpaper_mode_ == WALLPAPER_NONE)
     return;
@@ -1360,20 +1368,20 @@
   InstallDesktopController(root_window);
 }
 
-void WallpaperControllerImpl::OnShellInitialized() {
+void WallpaperController::OnShellInitialized() {
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
 }
 
-void WallpaperControllerImpl::OnShellDestroying() {
+void WallpaperController::OnShellDestroying() {
   Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
 }
 
-void WallpaperControllerImpl::OnWallpaperResized() {
+void WallpaperController::OnWallpaperResized() {
   CalculateWallpaperColors();
   compositor_lock_.reset();
 }
 
-void WallpaperControllerImpl::OnColorCalculationComplete() {
+void WallpaperController::OnColorCalculationComplete() {
   const std::vector<SkColor> colors = color_calculator_->prominent_colors();
   color_calculator_.reset();
   // Use |WallpaperInfo::location| as the key for storing |prominent_colors_| in
@@ -1386,7 +1394,7 @@
   SetProminentColors(colors);
 }
 
-void WallpaperControllerImpl::OnSessionStateChanged(
+void WallpaperController::OnSessionStateChanged(
     session_manager::SessionState state) {
   // Replace the device policy wallpaper with a user wallpaper if necessary.
   if (IsDevicePolicyWallpaper() && !ShouldSetDevicePolicyWallpaper())
@@ -1413,19 +1421,19 @@
     MoveToLockedContainer();
 }
 
-void WallpaperControllerImpl::OnTabletModeStarted() {
+void WallpaperController::OnTabletModeStarted() {
   RepaintWallpaper();
 }
 
-void WallpaperControllerImpl::OnTabletModeEnded() {
+void WallpaperController::OnTabletModeEnded() {
   RepaintWallpaper();
 }
 
-void WallpaperControllerImpl::CompositorLockTimedOut() {
+void WallpaperController::CompositorLockTimedOut() {
   compositor_lock_.reset();
 }
 
-void WallpaperControllerImpl::InitializePathsForTesting(
+void WallpaperController::InitializePathsForTesting(
     const base::FilePath& user_data_path,
     const base::FilePath& chromeos_wallpapers_path,
     const base::FilePath& chromeos_custom_wallpapers_path) {
@@ -1434,25 +1442,30 @@
   SetGlobalChromeOSCustomWallpapersDir(chromeos_custom_wallpapers_path);
 }
 
-void WallpaperControllerImpl::ShowDefaultWallpaperForTesting() {
+void WallpaperController::ShowDefaultWallpaperForTesting() {
   SetDefaultWallpaperImpl(EmptyAccountId(), user_manager::USER_TYPE_REGULAR,
                           /*show_wallpaper=*/true);
 }
 
-void WallpaperControllerImpl::CreateEmptyWallpaperForTesting() {
+void WallpaperController::CreateEmptyWallpaperForTesting() {
   ResetProminentColors();
   current_wallpaper_.reset();
   wallpaper_mode_ = WALLPAPER_IMAGE;
   InstallDesktopControllerForAllWindows();
 }
 
-void WallpaperControllerImpl::SetClientForTesting(
-    WallpaperControllerClient* client) {
-  wallpaper_controller_client_ = client;
+void WallpaperController::SetClientForTesting(
+    mojom::WallpaperControllerClientPtr client) {
+  wallpaper_controller_client_ = std::move(client);
 }
 
-void WallpaperControllerImpl::InstallDesktopController(
-    aura::Window* root_window) {
+void WallpaperController::FlushForTesting() {
+  if (wallpaper_controller_client_)
+    wallpaper_controller_client_.FlushForTesting();
+  mojo_observers_.FlushForTesting();
+}
+
+void WallpaperController::InstallDesktopController(aura::Window* root_window) {
   DCHECK_EQ(WALLPAPER_IMAGE, wallpaper_mode_);
 
   const bool is_wallpaper_blurred =
@@ -1462,6 +1475,9 @@
     is_wallpaper_blurred_ = is_wallpaper_blurred;
     for (auto& observer : observers_)
       observer.OnWallpaperBlurChanged();
+    mojo_observers_.ForAllPtrs([this](mojom::WallpaperObserver* observer) {
+      observer->OnWallpaperBlurChanged(is_wallpaper_blurred_);
+    });
   }
 
   const int container_id = GetWallpaperContainerId(locked_);
@@ -1495,13 +1511,13 @@
                                                   current_wallpaper_view, blur);
 }
 
-void WallpaperControllerImpl::InstallDesktopControllerForAllWindows() {
+void WallpaperController::InstallDesktopControllerForAllWindows() {
   for (aura::Window* root : Shell::GetAllRootWindows())
     InstallDesktopController(root);
   current_max_display_size_ = GetMaxDisplaySizeInNative();
 }
 
-bool WallpaperControllerImpl::ReparentWallpaper(int container) {
+bool WallpaperController::ReparentWallpaper(int container) {
   bool moved = false;
   for (auto* root_window_controller : Shell::GetAllRootWindowControllers()) {
     if (root_window_controller->wallpaper_widget_controller()->Reparent(
@@ -1512,7 +1528,7 @@
   return moved;
 }
 
-int WallpaperControllerImpl::GetWallpaperContainerId(bool locked) {
+int WallpaperController::GetWallpaperContainerId(bool locked) {
   if (is_always_on_top_wallpaper_)
     return kShellWindowId_AlwaysOnTopWallpaperContainer;
 
@@ -1520,9 +1536,8 @@
                 : kShellWindowId_WallpaperContainer;
 }
 
-void WallpaperControllerImpl::RemoveUserWallpaperInfo(
-    const AccountId& account_id,
-    bool is_ephemeral) {
+void WallpaperController::RemoveUserWallpaperInfo(const AccountId& account_id,
+                                                  bool is_ephemeral) {
   if (wallpaper_cache_map_.find(account_id) != wallpaper_cache_map_.end())
     wallpaper_cache_map_.erase(account_id);
 
@@ -1540,7 +1555,7 @@
   wallpaper_colors_update->RemoveWithoutPathExpansion(info.location, nullptr);
 }
 
-void WallpaperControllerImpl::RemoveUserWallpaperImpl(
+void WallpaperController::RemoveUserWallpaperImpl(
     const AccountId& account_id,
     const std::string& wallpaper_files_id) {
   if (wallpaper_files_id.empty())
@@ -1567,7 +1582,7 @@
       base::BindOnce(&DeleteWallpaperInList, std::move(files_to_remove)));
 }
 
-void WallpaperControllerImpl::SetDefaultWallpaperImpl(
+void WallpaperController::SetDefaultWallpaperImpl(
     const AccountId& account_id,
     const user_manager::UserType& user_type,
     bool show_wallpaper) {
@@ -1621,15 +1636,15 @@
                               cached_default_wallpaper_.image);
   } else {
     ReadAndDecodeWallpaper(
-        base::BindOnce(&WallpaperControllerImpl::OnDefaultWallpaperDecoded,
+        base::BindOnce(&WallpaperController::OnDefaultWallpaperDecoded,
                        weak_factory_.GetWeakPtr(), file_path, layout,
                        show_wallpaper),
         sequenced_task_runner_, file_path);
   }
 }
 
-bool WallpaperControllerImpl::CanSetUserWallpaper(const AccountId& account_id,
-                                                  bool is_ephemeral) const {
+bool WallpaperController::CanSetUserWallpaper(const AccountId& account_id,
+                                              bool is_ephemeral) const {
   // There is no visible wallpaper in kiosk mode.
   if (IsInKioskMode())
     return false;
@@ -1639,7 +1654,7 @@
   return true;
 }
 
-bool WallpaperControllerImpl::WallpaperIsAlreadyLoaded(
+bool WallpaperController::WallpaperIsAlreadyLoaded(
     const gfx::ImageSkia& image,
     bool compare_layouts,
     WallpaperLayout layout) const {
@@ -1654,7 +1669,7 @@
          current_wallpaper_->original_image_id();
 }
 
-void WallpaperControllerImpl::ReadAndDecodeWallpaper(
+void WallpaperController::ReadAndDecodeWallpaper(
     LoadedCallback callback,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     const base::FilePath& file_path) {
@@ -1671,7 +1686,7 @@
                      base::Passed(base::WrapUnique(data))));
 }
 
-bool WallpaperControllerImpl::InitializeUserWallpaperInfo(
+bool WallpaperController::InitializeUserWallpaperInfo(
     const AccountId& account_id,
     bool is_ephemeral) {
   const WallpaperInfo info = {std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
@@ -1679,7 +1694,7 @@
   return SetUserWallpaperInfo(account_id, info, is_ephemeral);
 }
 
-void WallpaperControllerImpl::SetOnlineWallpaperFromPath(
+void WallpaperController::SetOnlineWallpaperFromPath(
     SetOnlineWallpaperIfExistsCallback callback,
     const OnlineWallpaperParams& params,
     const base::FilePath& file_path) {
@@ -1687,14 +1702,14 @@
   std::move(callback).Run(file_exists);
   if (file_exists) {
     ReadAndDecodeWallpaper(
-        base::BindOnce(&WallpaperControllerImpl::OnOnlineWallpaperDecoded,
+        base::BindOnce(&WallpaperController::OnOnlineWallpaperDecoded,
                        weak_factory_.GetWeakPtr(), params, /*save_file=*/false,
                        SetOnlineWallpaperFromDataCallback()),
         sequenced_task_runner_, file_path);
   }
 }
 
-void WallpaperControllerImpl::OnOnlineWallpaperDecoded(
+void WallpaperController::OnOnlineWallpaperDecoded(
     const OnlineWallpaperParams& params,
     bool save_file,
     SetOnlineWallpaperFromDataCallback callback,
@@ -1719,10 +1734,10 @@
   if (params.preview_mode) {
     DCHECK(is_active_user);
     confirm_preview_wallpaper_callback_ = base::BindOnce(
-        &WallpaperControllerImpl::SetOnlineWallpaperImpl,
+        &WallpaperController::SetOnlineWallpaperImpl,
         weak_factory_.GetWeakPtr(), params, image, /*show_wallpaper=*/false);
     reload_preview_wallpaper_callback_ =
-        base::BindRepeating(&WallpaperControllerImpl::ShowWallpaperImage,
+        base::BindRepeating(&WallpaperController::ShowWallpaperImage,
                             weak_factory_.GetWeakPtr(), image,
                             WallpaperInfo{params.url, params.layout, ONLINE,
                                           base::Time::Now().LocalMidnight()},
@@ -1734,7 +1749,7 @@
   }
 }
 
-void WallpaperControllerImpl::SetOnlineWallpaperImpl(
+void WallpaperController::SetOnlineWallpaperImpl(
     const OnlineWallpaperParams& params,
     const gfx::ImageSkia& image,
     bool show_wallpaper) {
@@ -1754,7 +1769,7 @@
       CustomWallpaperElement(base::FilePath(), image);
 }
 
-void WallpaperControllerImpl::SetWallpaperFromInfo(
+void WallpaperController::SetWallpaperFromInfo(
     const AccountId& account_id,
     const user_manager::UserType& user_type,
     const WallpaperInfo& info,
@@ -1791,7 +1806,7 @@
       return;
 
     ReadAndDecodeWallpaper(
-        base::BindOnce(&WallpaperControllerImpl::OnWallpaperDecoded,
+        base::BindOnce(&WallpaperController::OnWallpaperDecoded,
                        weak_factory_.GetWeakPtr(), account_id, user_type,
                        wallpaper_path, info, show_wallpaper),
         sequenced_task_runner_, wallpaper_path);
@@ -1805,14 +1820,14 @@
     wallpaper_path = GlobalUserDataDir().Append(info.location);
 
     ReadAndDecodeWallpaper(
-        base::BindOnce(&WallpaperControllerImpl::OnWallpaperDecoded,
+        base::BindOnce(&WallpaperController::OnWallpaperDecoded,
                        weak_factory_.GetWeakPtr(), account_id, user_type,
                        wallpaper_path, info, show_wallpaper),
         sequenced_task_runner_, wallpaper_path);
   }
 }
 
-void WallpaperControllerImpl::OnDefaultWallpaperDecoded(
+void WallpaperController::OnDefaultWallpaperDecoded(
     const base::FilePath& path,
     WallpaperLayout layout,
     bool show_wallpaper,
@@ -1835,8 +1850,8 @@
   }
 }
 
-void WallpaperControllerImpl::SaveAndSetWallpaper(
-    const WallpaperUserInfo& user_info,
+void WallpaperController::SaveAndSetWallpaper(
+    mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     WallpaperType type,
@@ -1857,20 +1872,20 @@
   // appropriate wallpaper resolution.
   WallpaperInfo info = {relative_path, layout, type,
                         base::Time::Now().LocalMidnight()};
-  if (!SetUserWallpaperInfo(user_info.account_id, info,
-                            user_info.is_ephemeral)) {
+  if (!SetUserWallpaperInfo(user_info->account_id, info,
+                            user_info->is_ephemeral)) {
     LOG(ERROR) << "Setting user wallpaper info fails. This should never happen "
                   "except in tests.";
   }
 
   base::FilePath wallpaper_path =
-      GetCustomWallpaperPath(WallpaperControllerImpl::kOriginalWallpaperSubDir,
+      GetCustomWallpaperPath(WallpaperController::kOriginalWallpaperSubDir,
                              wallpaper_files_id, file_name);
 
   const bool should_save_to_disk =
-      !user_info.is_ephemeral ||
+      !user_info->is_ephemeral ||
       (type == POLICY &&
-       user_info.type == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+       user_info->type == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
 
   if (should_save_to_disk) {
     image.EnsureRepsForSupportedScales();
@@ -1891,11 +1906,11 @@
                        /*always_on_top=*/false);
   }
 
-  wallpaper_cache_map_[user_info.account_id] =
+  wallpaper_cache_map_[user_info->account_id] =
       CustomWallpaperElement(wallpaper_path, image);
 }
 
-void WallpaperControllerImpl::OnWallpaperDecoded(
+void WallpaperController::OnWallpaperDecoded(
     const AccountId& account_id,
     const user_manager::UserType& user_type,
     const base::FilePath& path,
@@ -1917,7 +1932,7 @@
   }
 }
 
-void WallpaperControllerImpl::ReloadWallpaper(bool clear_cache) {
+void WallpaperController::ReloadWallpaper(bool clear_cache) {
   current_wallpaper_.reset();
   if (clear_cache)
     wallpaper_cache_map_.clear();
@@ -1926,13 +1941,13 @@
     reload_always_on_top_wallpaper_callback_.Run();
   else if (reload_preview_wallpaper_callback_)
     reload_preview_wallpaper_callback_.Run();
-  else if (current_user_.account_id.is_valid())
-    ShowUserWallpaper(current_user_);
+  else if (current_user_)
+    ShowUserWallpaper(std::move(current_user_));
   else
     ShowSigninWallpaper();
 }
 
-void WallpaperControllerImpl::SetProminentColors(
+void WallpaperController::SetProminentColors(
     const std::vector<SkColor>& colors) {
   if (prominent_colors_ == colors)
     return;
@@ -1940,15 +1955,18 @@
   prominent_colors_ = colors;
   for (auto& observer : observers_)
     observer.OnWallpaperColorsChanged();
+  mojo_observers_.ForAllPtrs([this](mojom::WallpaperObserver* observer) {
+    observer->OnWallpaperColorsChanged(prominent_colors_);
+  });
 }
 
-void WallpaperControllerImpl::ResetProminentColors() {
+void WallpaperController::ResetProminentColors() {
   static const std::vector<SkColor> kInvalidColors(color_profiles_.size(),
                                                    kInvalidWallpaperColor);
   SetProminentColors(kInvalidColors);
 }
 
-void WallpaperControllerImpl::CalculateWallpaperColors() {
+void WallpaperController::CalculateWallpaperColors() {
   if (!current_wallpaper_)
     return;
 
@@ -1984,14 +2002,14 @@
   }
 }
 
-bool WallpaperControllerImpl::ShouldCalculateColors() const {
+bool WallpaperController::ShouldCalculateColors() const {
   gfx::ImageSkia image = GetWallpaper();
   return Shell::Get()->session_controller()->GetSessionState() ==
              session_manager::SessionState::ACTIVE &&
          !image.isNull();
 }
 
-void WallpaperControllerImpl::CacheProminentColors(
+void WallpaperController::CacheProminentColors(
     const std::vector<SkColor>& colors,
     const std::string& current_location) {
   if (!local_state_)
@@ -2005,7 +2023,7 @@
                                                    std::move(wallpaper_colors));
 }
 
-base::Optional<std::vector<SkColor>> WallpaperControllerImpl::GetCachedColors(
+base::Optional<std::vector<SkColor>> WallpaperController::GetCachedColors(
     const std::string& current_location) const {
   base::Optional<std::vector<SkColor>> cached_colors_out;
   const base::ListValue* prominent_colors = nullptr;
@@ -2023,7 +2041,7 @@
   return cached_colors_out;
 }
 
-void WallpaperControllerImpl::OnAlwaysOnTopWallpaperDecoded(
+void WallpaperController::OnAlwaysOnTopWallpaperDecoded(
     const WallpaperInfo& info,
     const gfx::ImageSkia& image) {
   // Do nothing if |RemoveAlwaysOnTopWallpaper| was called before decoding
@@ -2035,13 +2053,13 @@
     return;
   }
   reload_always_on_top_wallpaper_callback_ =
-      base::BindRepeating(&WallpaperControllerImpl::ShowWallpaperImage,
+      base::BindRepeating(&WallpaperController::ShowWallpaperImage,
                           weak_factory_.GetWeakPtr(), image, info,
                           /*preview_mode=*/false, /*always_on_top=*/true);
   reload_always_on_top_wallpaper_callback_.Run();
 }
 
-bool WallpaperControllerImpl::MoveToLockedContainer() {
+bool WallpaperController::MoveToLockedContainer() {
   if (locked_)
     return false;
 
@@ -2049,7 +2067,7 @@
   return ReparentWallpaper(GetWallpaperContainerId(true));
 }
 
-bool WallpaperControllerImpl::MoveToUnlockedContainer() {
+bool WallpaperController::MoveToUnlockedContainer() {
   if (!locked_)
     return false;
 
@@ -2057,17 +2075,17 @@
   return ReparentWallpaper(GetWallpaperContainerId(false));
 }
 
-bool WallpaperControllerImpl::IsDevicePolicyWallpaper() const {
+bool WallpaperController::IsDevicePolicyWallpaper() const {
   return current_wallpaper_ &&
          current_wallpaper_->wallpaper_info().type == WallpaperType::DEVICE;
 }
 
-bool WallpaperControllerImpl::IsOneShotWallpaper() const {
+bool WallpaperController::IsOneShotWallpaper() const {
   return current_wallpaper_ &&
          current_wallpaper_->wallpaper_info().type == WallpaperType::ONE_SHOT;
 }
 
-bool WallpaperControllerImpl::ShouldSetDevicePolicyWallpaper() const {
+bool WallpaperController::ShouldSetDevicePolicyWallpaper() const {
   // Only allow the device wallpaper if the policy is in effect for enterprise
   // managed devices.
   if (device_policy_wallpaper_path_.empty())
@@ -2078,16 +2096,15 @@
          session_manager::SessionState::LOGIN_PRIMARY;
 }
 
-void WallpaperControllerImpl::SetDevicePolicyWallpaper() {
+void WallpaperController::SetDevicePolicyWallpaper() {
   DCHECK(ShouldSetDevicePolicyWallpaper());
   ReadAndDecodeWallpaper(
-      base::BindRepeating(
-          &WallpaperControllerImpl::OnDevicePolicyWallpaperDecoded,
-          weak_factory_.GetWeakPtr()),
+      base::BindRepeating(&WallpaperController::OnDevicePolicyWallpaperDecoded,
+                          weak_factory_.GetWeakPtr()),
       sequenced_task_runner_.get(), device_policy_wallpaper_path_);
 }
 
-void WallpaperControllerImpl::OnDevicePolicyWallpaperDecoded(
+void WallpaperController::OnDevicePolicyWallpaperDecoded(
     const gfx::ImageSkia& image) {
   // It might be possible that the device policy controlled wallpaper finishes
   // decoding after the user logs in. In this case do nothing.
@@ -2108,7 +2125,46 @@
   }
 }
 
-void WallpaperControllerImpl::GetInternalDisplayCompositorLock() {
+bool WallpaperController::IsActiveUserWallpaperControlledByPolicyImpl() const {
+  // The currently active user has index 0.
+  const UserSession* const active_user_session =
+      Shell::Get()->session_controller()->GetUserSession(/*user index=*/0);
+  if (!active_user_session)
+    return false;
+  return IsPolicyControlled(active_user_session->user_info.account_id,
+                            active_user_session->user_info.is_ephemeral);
+}
+
+bool WallpaperController::GetActiveUserWallpaperInfoImpl(
+    WallpaperInfo* info_out) const {
+  // The currently active user has index 0.
+  const UserSession* const active_user_session =
+      Shell::Get()->session_controller()->GetUserSession(/*user index=*/0);
+  if (!active_user_session)
+    return false;
+
+  if (!GetUserWallpaperInfo(active_user_session->user_info.account_id, info_out,
+                            active_user_session->user_info.is_ephemeral)) {
+    return false;
+  }
+  return true;
+}
+
+bool WallpaperController::ShouldShowWallpaperSettingImpl() const {
+  // The currently active user has index 0.
+  const UserSession* const active_user_session =
+      Shell::Get()->session_controller()->GetUserSession(/*user index=*/0);
+  if (!active_user_session)
+    return false;
+
+  user_manager::UserType active_user_type = active_user_session->user_info.type;
+  return active_user_type == user_manager::USER_TYPE_REGULAR ||
+         active_user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT ||
+         active_user_type == user_manager::USER_TYPE_SUPERVISED ||
+         active_user_type == user_manager::USER_TYPE_CHILD;
+}
+
+void WallpaperController::GetInternalDisplayCompositorLock() {
   if (!display::Display::HasInternalDisplay())
     return;
 
@@ -2121,7 +2177,7 @@
       this, kCompositorLockTimeout);
 }
 
-void WallpaperControllerImpl::RepaintWallpaper() {
+void WallpaperController::RepaintWallpaper() {
   for (auto* root_window_controller : Shell::GetAllRootWindowControllers()) {
     auto* wallpaper_view =
         root_window_controller->wallpaper_widget_controller()->wallpaper_view();
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller.h
similarity index 86%
rename from ash/wallpaper/wallpaper_controller_impl.h
rename to ash/wallpaper/wallpaper_controller.h
index c605894..1f31c1b9 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_WALLPAPER_WALLPAPER_CONTROLLER_IMPL_H_
-#define ASH_WALLPAPER_WALLPAPER_CONTROLLER_IMPL_H_
+#ifndef ASH_WALLPAPER_WALLPAPER_CONTROLLER_H_
+#define ASH_WALLPAPER_WALLPAPER_CONTROLLER_H_
 
 #include <map>
 #include <memory>
@@ -13,12 +13,11 @@
 
 #include "ash/ash_export.h"
 #include "ash/display/window_tree_host_manager.h"
-#include "ash/public/cpp/wallpaper_controller.h"
-#include "ash/public/cpp/wallpaper_info.h"
 #include "ash/public/cpp/wallpaper_types.h"
-#include "ash/public/cpp/wallpaper_user_info.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "ash/session/session_observer.h"
 #include "ash/shell_observer.h"
+#include "ash/wallpaper/wallpaper_info.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_color_calculator_observer.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer_observer.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
@@ -27,6 +26,8 @@
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
 #include "components/user_manager/user_type.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "ui/compositor/compositor_lock.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -43,6 +44,7 @@
 namespace ash {
 
 class WallpaperColorCalculator;
+class WallpaperControllerObserver;
 class WallpaperResizer;
 class WallpaperWindowStateManager;
 
@@ -61,15 +63,14 @@
 //   - Move wallpaper to locked container(s) when session state is not ACTIVE to
 //     hide the user desktop and move it to unlocked container when session
 //     state is ACTIVE;
-class ASH_EXPORT WallpaperControllerImpl
-    : public WallpaperController,
-      public WindowTreeHostManager::Observer,
-      public ShellObserver,
-      public WallpaperResizerObserver,
-      public WallpaperColorCalculatorObserver,
-      public SessionObserver,
-      public TabletModeObserver,
-      public ui::CompositorLockClient {
+class ASH_EXPORT WallpaperController : public mojom::WallpaperController,
+                                       public WindowTreeHostManager::Observer,
+                                       public ShellObserver,
+                                       public WallpaperResizerObserver,
+                                       public WallpaperColorCalculatorObserver,
+                                       public SessionObserver,
+                                       public TabletModeObserver,
+                                       public ui::CompositorLockClient {
  public:
   enum WallpaperResolution {
     WALLPAPER_RESOLUTION_LARGE,
@@ -81,8 +82,8 @@
   static const char kLargeWallpaperSubDir[];
   static const char kOriginalWallpaperSubDir[];
 
-  explicit WallpaperControllerImpl(PrefService* local_state);
-  ~WallpaperControllerImpl() override;
+  explicit WallpaperController(PrefService* local_state);
+  ~WallpaperController() override;
 
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
@@ -112,7 +113,14 @@
       const base::FilePath& wallpaper_path,
       bool show_wallpaper,
       const scoped_refptr<base::SingleThreadTaskRunner>& reply_task_runner,
-      base::WeakPtr<WallpaperControllerImpl> weak_ptr);
+      base::WeakPtr<WallpaperController> weak_ptr);
+
+  // Binds the mojom::WallpaperController interface request to this object.
+  void BindRequest(mojom::WallpaperControllerRequest request);
+
+  // Add/Remove observers.
+  void AddObserver(WallpaperControllerObserver* observer);
+  void RemoveObserver(WallpaperControllerObserver* observer);
 
   // Returns the prominent color based on |color_profile|.
   SkColor GetProminentColor(color_utils::ColorProfile color_profile) const;
@@ -210,59 +218,60 @@
                            const WallpaperInfo& info,
                            bool show_wallpaper);
 
-  // WallpaperController:
-  void Init(WallpaperControllerClient* client,
+  // mojom::WallpaperController:
+  void Init(mojom::WallpaperControllerClientPtr client,
             const base::FilePath& user_data_path,
             const base::FilePath& chromeos_wallpapers_path,
             const base::FilePath& chromeos_custom_wallpapers_path,
             const base::FilePath& device_policy_wallpaper_path) override;
-  void SetCustomWallpaper(const WallpaperUserInfo& user_info,
+  void SetCustomWallpaper(mojom::WallpaperUserInfoPtr user_info,
                           const std::string& wallpaper_files_id,
                           const std::string& file_name,
                           WallpaperLayout layout,
                           const gfx::ImageSkia& image,
                           bool preview_mode) override;
   void SetOnlineWallpaperIfExists(
-      const WallpaperUserInfo& user_info,
+      mojom::WallpaperUserInfoPtr user_info,
       const std::string& url,
       WallpaperLayout layout,
       bool preview_mode,
       SetOnlineWallpaperIfExistsCallback callback) override;
   void SetOnlineWallpaperFromData(
-      const WallpaperUserInfo& user_info,
+      mojom::WallpaperUserInfoPtr user_info,
       const std::string& image_data,
       const std::string& url,
       WallpaperLayout layout,
       bool preview_mode,
       SetOnlineWallpaperFromDataCallback callback) override;
-  void SetDefaultWallpaper(const WallpaperUserInfo& user_info,
+  void SetDefaultWallpaper(mojom::WallpaperUserInfoPtr user_info,
                            const std::string& wallpaper_files_id,
                            bool show_wallpaper) override;
   void SetCustomizedDefaultWallpaperPaths(
       const base::FilePath& customized_default_small_path,
       const base::FilePath& customized_default_large_path) override;
-  void SetPolicyWallpaper(const WallpaperUserInfo& user_info,
+  void SetPolicyWallpaper(mojom::WallpaperUserInfoPtr user_info,
                           const std::string& wallpaper_files_id,
                           const std::string& data) override;
   void SetDevicePolicyWallpaperPath(
       const base::FilePath& device_policy_wallpaper_path) override;
-  bool SetThirdPartyWallpaper(const WallpaperUserInfo& user_info,
+  void SetThirdPartyWallpaper(mojom::WallpaperUserInfoPtr user_info,
                               const std::string& wallpaper_files_id,
                               const std::string& file_name,
                               WallpaperLayout layout,
-                              const gfx::ImageSkia& image) override;
+                              const gfx::ImageSkia& image,
+                              SetThirdPartyWallpaperCallback callback) override;
   void ConfirmPreviewWallpaper() override;
   void CancelPreviewWallpaper() override;
-  void UpdateCustomWallpaperLayout(const WallpaperUserInfo& user_info,
+  void UpdateCustomWallpaperLayout(mojom::WallpaperUserInfoPtr user_info,
                                    WallpaperLayout layout) override;
-  void ShowUserWallpaper(const WallpaperUserInfo& user_info) override;
+  void ShowUserWallpaper(mojom::WallpaperUserInfoPtr user_info) override;
   void ShowSigninWallpaper() override;
   void ShowOneShotWallpaper(const gfx::ImageSkia& image) override;
   void ShowAlwaysOnTopWallpaper(const base::FilePath& image_path) override;
   void RemoveAlwaysOnTopWallpaper() override;
-  void RemoveUserWallpaper(const WallpaperUserInfo& user_info,
+  void RemoveUserWallpaper(mojom::WallpaperUserInfoPtr user_info,
                            const std::string& wallpaper_files_id) override;
-  void RemovePolicyWallpaper(const WallpaperUserInfo& user_info,
+  void RemovePolicyWallpaper(mojom::WallpaperUserInfoPtr user_info,
                              const std::string& wallpaper_files_id) override;
   void GetOfflineWallpaperList(
       GetOfflineWallpaperListCallback callback) override;
@@ -270,14 +279,16 @@
   void OpenWallpaperPickerIfAllowed() override;
   void MinimizeInactiveWindows(const std::string& user_id_hash) override;
   void RestoreMinimizedWindows(const std::string& user_id_hash) override;
-  void AddObserver(WallpaperControllerObserver* observer) override;
-  void RemoveObserver(WallpaperControllerObserver* observer) override;
-  gfx::ImageSkia GetWallpaperImage() override;
-  const std::vector<SkColor>& GetWallpaperColors() override;
-  bool IsWallpaperBlurred() override;
-  bool IsActiveUserWallpaperControlledByPolicy() override;
-  WallpaperInfo GetActiveUserWallpaperInfo() override;
-  bool ShouldShowWallpaperSetting() override;
+  void AddObserver(mojom::WallpaperObserverAssociatedPtrInfo observer) override;
+  void GetWallpaperImage(GetWallpaperImageCallback callback) override;
+  void GetWallpaperColors(GetWallpaperColorsCallback callback) override;
+  void IsWallpaperBlurred(IsWallpaperBlurredCallback callback) override;
+  void IsActiveUserWallpaperControlledByPolicy(
+      IsActiveUserWallpaperControlledByPolicyCallback callback) override;
+  void GetActiveUserWallpaperInfo(
+      GetActiveUserWallpaperInfoCallback callback) override;
+  void ShouldShowWallpaperSetting(
+      ShouldShowWallpaperSettingCallback callback) override;
 
   // WindowTreeHostManager::Observer:
   void OnDisplayConfigurationChanged() override;
@@ -320,8 +331,11 @@
   // empty widget for those tests to prevent crashes.
   void CreateEmptyWallpaperForTesting();
 
-  // Sets a test client.
-  void SetClientForTesting(WallpaperControllerClient* client);
+  // Sets a test client interface with empty file paths.
+  void SetClientForTesting(mojom::WallpaperControllerClientPtr client);
+
+  // Flushes the mojo message pipe to chrome.
+  void FlushForTesting();
 
   void set_wallpaper_reload_no_delay_for_test() {
     wallpaper_reload_delay_ = base::TimeDelta::FromMilliseconds(0);
@@ -443,7 +457,7 @@
   // policy wallpaper for public accounts. Shows the wallpaper immediately if
   // |show_wallpaper| is true, otherwise only sets the wallpaper info and
   // updates the cache.
-  void SaveAndSetWallpaper(const WallpaperUserInfo& user_info,
+  void SaveAndSetWallpaper(mojom::WallpaperUserInfoPtr user_info,
                            const std::string& wallpaper_files_id,
                            const std::string& file_name,
                            WallpaperType type,
@@ -528,6 +542,15 @@
   // Called when the device policy controlled wallpaper has been decoded.
   void OnDevicePolicyWallpaperDecoded(const gfx::ImageSkia& image);
 
+  // Implementation of |IsActiveUserWallpaperControlledByPolicy|.
+  bool IsActiveUserWallpaperControlledByPolicyImpl() const;
+
+  // Implementation of |GetActiveUserWallpaperInfo|.
+  bool GetActiveUserWallpaperInfoImpl(WallpaperInfo* info_out) const;
+
+  // Implementation of |ShouldShowWallpaperSetting|.
+  bool ShouldShowWallpaperSettingImpl() const;
+
   // When wallpaper resizes, we can check which displays will be affected. For
   // simplicity, we only lock the compositor for the internal display.
   void GetInternalDisplayCompositorLock();
@@ -541,10 +564,15 @@
   WallpaperMode wallpaper_mode_;
 
   // Client interface in chrome browser.
-  WallpaperControllerClient* wallpaper_controller_client_ = nullptr;
+  mojom::WallpaperControllerClientPtr wallpaper_controller_client_;
+
+  // Bindings for the WallpaperController interface.
+  mojo::BindingSet<mojom::WallpaperController> bindings_;
 
   base::ObserverList<WallpaperControllerObserver>::Unchecked observers_;
 
+  mojo::AssociatedInterfacePtrSet<mojom::WallpaperObserver> mojo_observers_;
+
   std::unique_ptr<WallpaperResizer> current_wallpaper_;
 
   // Asynchronous task to extract colors from the wallpaper.
@@ -566,7 +594,7 @@
   std::map<AccountId, WallpaperInfo> ephemeral_users_wallpaper_info_;
 
   // Cached user info of the current user.
-  WallpaperUserInfo current_user_;
+  mojom::WallpaperUserInfoPtr current_user_;
 
   // Cached wallpapers of users.
   CustomWallpaperMap wallpaper_cache_map_;
@@ -633,15 +661,15 @@
   std::vector<base::FilePath> decode_requests_for_testing_;
 
   // PrefService provided by Shell when constructing this.
-  // Valid for the lifetime of ash::Shell which owns WallpaperControllerImpl.
+  // Valid for the lifetime of ash::Shell which owns WallpaperController.
   // May be null in tests.
   PrefService* local_state_ = nullptr;
 
-  base::WeakPtrFactory<WallpaperControllerImpl> weak_factory_;
+  base::WeakPtrFactory<WallpaperController> weak_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(WallpaperControllerImpl);
+  DISALLOW_COPY_AND_ASSIGN(WallpaperController);
 };
 
 }  // namespace ash
 
-#endif  // ASH_WALLPAPER_WALLPAPER_CONTROLLER_IMPL_H_
+#endif  // ASH_WALLPAPER_WALLPAPER_CONTROLLER_H_
diff --git a/ash/wallpaper/wallpaper_controller_observer.h b/ash/wallpaper/wallpaper_controller_observer.h
new file mode 100644
index 0000000..c8c1199
--- /dev/null
+++ b/ash/wallpaper/wallpaper_controller_observer.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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 ASH_WALLPAPER_WALLPAPER_CONTROLLER_OBSERVER_H_
+#define ASH_WALLPAPER_WALLPAPER_CONTROLLER_OBSERVER_H_
+
+#include "ash/ash_export.h"
+
+namespace ash {
+
+class ASH_EXPORT WallpaperControllerObserver {
+ public:
+  // Invoked when the colors extracted from the current wallpaper change.
+  virtual void OnWallpaperColorsChanged() {}
+
+  // Invoked when the blur state of the wallpaper changes.
+  virtual void OnWallpaperBlurChanged() {}
+
+  // Invoked when the wallpaper preview mode starts.
+  virtual void OnWallpaperPreviewStarted() {}
+
+  // Invoked when the wallpaper preview mode ends.
+  virtual void OnWallpaperPreviewEnded() {}
+
+  // Invoked when the first wallpaper is set. The first wallpaper is the one
+  // shown right after boot splash screen or after a session restart.
+  virtual void OnFirstWallpaperShown() {}
+
+ protected:
+  virtual ~WallpaperControllerObserver() {}
+};
+
+}  // namespace ash
+
+#endif  // ASH_WALLPAPER_WALLPAPER_CONTROLLER_OBSERVER_H_
diff --git a/ash/wallpaper/wallpaper_controller_test_api.cc b/ash/wallpaper/wallpaper_controller_test_api.cc
index f0dc2de..a60389f8 100644
--- a/ash/wallpaper/wallpaper_controller_test_api.cc
+++ b/ash/wallpaper/wallpaper_controller_test_api.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/wallpaper/wallpaper_controller_test_api.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/bind.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
@@ -26,7 +26,7 @@
 }  // namespace
 
 WallpaperControllerTestApi::WallpaperControllerTestApi(
-    WallpaperControllerImpl* controller)
+    WallpaperController* controller)
     : controller_(controller) {}
 
 WallpaperControllerTestApi::~WallpaperControllerTestApi() = default;
@@ -44,13 +44,13 @@
   // Preview mode is considered active when the two callbacks have non-empty
   // values. Their specific values don't matter for testing purpose.
   controller_->confirm_preview_wallpaper_callback_ =
-      base::BindOnce(&WallpaperControllerImpl::SetWallpaperFromInfo,
+      base::BindOnce(&WallpaperController::SetWallpaperFromInfo,
                      controller_->weak_factory_.GetWeakPtr(),
                      AccountId::FromUserEmail("user@test.com"),
                      user_manager::USER_TYPE_REGULAR, kTestWallpaperInfo,
                      /*show_wallpaper=*/true);
   controller_->reload_preview_wallpaper_callback_ = base::BindRepeating(
-      &WallpaperControllerImpl::ShowWallpaperImage,
+      &WallpaperController::ShowWallpaperImage,
       controller_->weak_factory_.GetWeakPtr(),
       CreateImageWithColor(SK_ColorBLUE), kTestWallpaperInfo,
       /*preview_mode=*/true, /*always_on_top=*/false);
diff --git a/ash/wallpaper/wallpaper_controller_test_api.h b/ash/wallpaper/wallpaper_controller_test_api.h
index 90aca5b..02f11f64 100644
--- a/ash/wallpaper/wallpaper_controller_test_api.h
+++ b/ash/wallpaper/wallpaper_controller_test_api.h
@@ -11,14 +11,14 @@
 
 namespace ash {
 
-class WallpaperControllerImpl;
+class WallpaperController;
 
 class ASH_EXPORT WallpaperControllerTestApi {
  public:
-  explicit WallpaperControllerTestApi(WallpaperControllerImpl* controller);
+  explicit WallpaperControllerTestApi(WallpaperController* controller);
   virtual ~WallpaperControllerTestApi();
 
-  // Creates and sets a new wallpaper that causes the prominent color of the
+  // Creates and sets a new wallpaper that cause the prominent color of the
   // |controller_| to be a valid (i.e. not kInvalidWallpaperColor) color. The
   // WallpaperControllerObservers should be notified as well. This assumes the
   // default DARK-MUTED luma-saturation ranges are in effect.
@@ -35,7 +35,7 @@
   void EndWallpaperPreview(bool confirm_preview_wallpaper);
 
  private:
-  WallpaperControllerImpl* controller_;
+  WallpaperController* controller_;
 
   DISALLOW_COPY_AND_ASSIGN(WallpaperControllerTestApi);
 };
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index c061a4e..f962980 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 
 #include <cmath>
 #include <cstdlib>
@@ -10,12 +10,12 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer.h"
 #include "ash/wallpaper/wallpaper_view.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
@@ -31,6 +31,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/time/time_override.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/window.h"
@@ -153,9 +154,8 @@
                                       const std::string& wallpaper_files_id,
                                       const std::string& file_name) {
   base::ScopedAllowBlockingForTesting allow_blocking;
-  base::FilePath wallpaper_path =
-      WallpaperControllerImpl::GetCustomWallpaperPath(
-          sub_dir, wallpaper_files_id, file_name);
+  base::FilePath wallpaper_path = WallpaperController::GetCustomWallpaperPath(
+      sub_dir, wallpaper_files_id, file_name);
   if (!base::DirectoryExists(wallpaper_path.DirName()))
     base::CreateDirectory(wallpaper_path.DirName());
 
@@ -166,16 +166,16 @@
   const std::string wallpaper_file_id = GetDummyFileId(account_id);
 
   base::FilePath small_wallpaper_dir =
-      WallpaperControllerImpl::GetCustomWallpaperDir(
-          WallpaperControllerImpl::kSmallWallpaperSubDir)
+      WallpaperController::GetCustomWallpaperDir(
+          WallpaperController::kSmallWallpaperSubDir)
           .Append(wallpaper_file_id);
   base::FilePath large_wallpaper_dir =
-      WallpaperControllerImpl::GetCustomWallpaperDir(
-          WallpaperControllerImpl::kLargeWallpaperSubDir)
+      WallpaperController::GetCustomWallpaperDir(
+          WallpaperController::kLargeWallpaperSubDir)
           .Append(wallpaper_file_id);
   base::FilePath original_wallpaper_dir =
-      WallpaperControllerImpl::GetCustomWallpaperDir(
-          WallpaperControllerImpl::kOriginalWallpaperSubDir)
+      WallpaperController::GetCustomWallpaperDir(
+          WallpaperController::kOriginalWallpaperSubDir)
           .Append(wallpaper_file_id);
 
   while (base::PathExists(small_wallpaper_dir) ||
@@ -220,33 +220,50 @@
   }
 }
 
-// A test implementation of the WallpaperControllerObserver interface.
-class TestWallpaperControllerObserver : public WallpaperControllerObserver {
+// A test implementation of the WallpaperObserver mojo interface.
+class TestWallpaperObserver : public mojom::WallpaperObserver {
  public:
-  explicit TestWallpaperControllerObserver(WallpaperController* controller)
-      : controller_(controller) {
-    controller_->AddObserver(this);
+  TestWallpaperObserver() = default;
+  ~TestWallpaperObserver() override = default;
+
+  // mojom::WallpaperObserver:
+  void OnWallpaperChanged(uint32_t image_id) override {}
+
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override {
+    ++wallpaper_colors_changed_count_;
+    if (run_loop_)
+      run_loop_->Quit();
   }
 
-  ~TestWallpaperControllerObserver() override {
-    controller_->RemoveObserver(this);
+  void OnWallpaperBlurChanged(bool blurred) override {}
+
+  int wallpaper_colors_changed_count() const {
+    return wallpaper_colors_changed_count_;
   }
 
-  // WallpaperControllerObserver
-  void OnWallpaperColorsChanged() override { ++colors_changed_count_; }
-  void OnWallpaperBlurChanged() override { ++blur_changed_count_; }
-  void OnFirstWallpaperShown() override { ++first_shown_count_; }
-
-  int colors_changed_count() const { return colors_changed_count_; }
-  int blur_changed_count() const { return blur_changed_count_; }
-  int first_shown_count() const { return first_shown_count_; }
+  void set_run_loop(base::RunLoop* loop) { run_loop_ = loop; }
 
  private:
-  WallpaperController* controller_;
-  int colors_changed_count_ = 0;
-  int blur_changed_count_ = 0;
-  int first_shown_count_ = 0;
+  base::RunLoop* run_loop_ = nullptr;
+  int wallpaper_colors_changed_count_ = 0;
 
+  DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserver);
+};
+
+class TestWallpaperControllerObserver : public WallpaperControllerObserver {
+ public:
+  TestWallpaperControllerObserver() = default;
+
+  void OnWallpaperBlurChanged() override { ++wallpaper_blur_changed_count_; }
+  void OnFirstWallpaperShown() override { ++first_wallpaper_shown_count_; }
+
+  void Reset() { wallpaper_blur_changed_count_ = 0; }
+
+  int wallpaper_blur_changed_count_ = 0;
+  int first_wallpaper_shown_count_ = 0;
+
+ private:
   DISALLOW_COPY_AND_ASSIGN(TestWallpaperControllerObserver);
 };
 
@@ -352,15 +369,16 @@
                          base::Time::Now().LocalMidnight());
   }
 
-  // Helper function to create a |WallpaperUserInfo| structwith default values.
-  // In addition, clear the wallpaper count and the decoding request list. May
-  // be called multiple times for the same |account_id|.
-  WallpaperUserInfo InitializeUser(const AccountId& account_id) {
-    WallpaperUserInfo wallpaper_user_info;
-    wallpaper_user_info.account_id = account_id;
-    wallpaper_user_info.type = user_manager::USER_TYPE_REGULAR;
-    wallpaper_user_info.is_ephemeral = false;
-    wallpaper_user_info.has_gaia_account = true;
+  // Helper function to create a new |mojom::WallpaperUserInfoPtr| instance with
+  // default values. In addition, clear the wallpaper count and the decoding
+  // request list. May be called multiple times for the same |account_id|.
+  mojom::WallpaperUserInfoPtr InitializeUser(const AccountId& account_id) {
+    mojom::WallpaperUserInfoPtr wallpaper_user_info =
+        mojom::WallpaperUserInfo::New();
+    wallpaper_user_info->account_id = account_id;
+    wallpaper_user_info->type = user_manager::USER_TYPE_REGULAR;
+    wallpaper_user_info->is_ephemeral = false;
+    wallpaper_user_info->has_gaia_account = true;
     ClearWallpaperCount();
     ClearDecodeFilePaths();
 
@@ -374,10 +392,10 @@
     std::string wallpaper_files_id = GetDummyFileId(account_id);
     std::string file_name = GetDummyFileName(account_id);
     base::FilePath small_wallpaper_path =
-        GetCustomWallpaperPath(WallpaperControllerImpl::kSmallWallpaperSubDir,
+        GetCustomWallpaperPath(WallpaperController::kSmallWallpaperSubDir,
                                wallpaper_files_id, file_name);
     base::FilePath large_wallpaper_path =
-        GetCustomWallpaperPath(WallpaperControllerImpl::kLargeWallpaperSubDir,
+        GetCustomWallpaperPath(WallpaperController::kLargeWallpaperSubDir,
                                wallpaper_files_id, file_name);
 
     // Saves the small/large resolution wallpapers to small/large custom
@@ -464,7 +482,7 @@
 
   // A helper to test the behavior of setting online wallpaper after the image
   // is decoded. This is needed because image decoding is not supported in unit
-  // tests.
+  // tests (the connector for the mojo service manager is null).
   void SetOnlineWallpaperFromImage(
       const AccountId& account_id,
       const gfx::ImageSkia& image,
@@ -472,8 +490,8 @@
       WallpaperLayout layout,
       bool save_file,
       bool preview_mode,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback callback) {
-    const WallpaperControllerImpl::OnlineWallpaperParams params = {
+      WallpaperController::SetOnlineWallpaperFromDataCallback callback) {
+    const WallpaperController::OnlineWallpaperParams params = {
         account_id, false /*is_ephemeral=*/, url, layout, preview_mode};
     controller_->OnOnlineWallpaperDecoded(params, save_file,
                                           std::move(callback), image);
@@ -490,6 +508,11 @@
   // Wrapper for private ShouldCalculateColors().
   bool ShouldCalculateColors() { return controller_->ShouldCalculateColors(); }
 
+  // Wrapper for private IsActiveUserWallpaperControlledByPolicyImpl().
+  bool IsActiveUserWallpaperControlledByPolicy() {
+    return controller_->IsActiveUserWallpaperControlledByPolicyImpl();
+  }
+
   // Wrapper for private IsDevicePolicyWallpaper().
   bool IsDevicePolicyWallpaper() {
     return controller_->IsDevicePolicyWallpaper();
@@ -515,7 +538,7 @@
     return controller_->GetWallpaperContainerId(controller_->locked_);
   }
 
-  WallpaperControllerImpl* controller_;  // Not owned.
+  WallpaperController* controller_;  // Not owned.
 
   base::ScopedTempDir user_data_dir_;
   base::ScopedTempDir online_wallpaper_dir_;
@@ -527,7 +550,7 @@
 };
 
 TEST_F(WallpaperControllerTest, BasicReparenting) {
-  WallpaperControllerImpl* controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* controller = Shell::Get()->wallpaper_controller();
   controller->CreateEmptyWallpaperForTesting();
 
   // Wallpaper view/window exists in the wallpaper container and nothing is in
@@ -559,7 +582,7 @@
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
   // Create the wallpaper and its view.
-  WallpaperControllerImpl* controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* controller = Shell::Get()->wallpaper_controller();
   controller->CreateEmptyWallpaperForTesting();
 
   // The new wallpaper is ready to animate.
@@ -584,7 +607,7 @@
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
   // Reset wallpaper state, see ControllerOwnership above.
-  WallpaperControllerImpl* controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* controller = Shell::Get()->wallpaper_controller();
   controller->CreateEmptyWallpaperForTesting();
 
   // Run wallpaper show animation to completion.
@@ -632,7 +655,7 @@
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
   // Reset wallpaper state, see ControllerOwnership above.
-  WallpaperControllerImpl* controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* controller = Shell::Get()->wallpaper_controller();
   controller->CreateEmptyWallpaperForTesting();
 
   // Run wallpaper show animation to completion.
@@ -697,32 +720,32 @@
   // Device scale factor shouldn't affect the native size.
   UpdateDisplay("1000x300*2");
   EXPECT_EQ("1000x300",
-            WallpaperControllerImpl::GetMaxDisplaySizeInNative().ToString());
+            WallpaperController::GetMaxDisplaySizeInNative().ToString());
 
   // Rotated display should return the rotated size.
   UpdateDisplay("1000x300*2/r");
   EXPECT_EQ("300x1000",
-            WallpaperControllerImpl::GetMaxDisplaySizeInNative().ToString());
+            WallpaperController::GetMaxDisplaySizeInNative().ToString());
 
   // UI Scaling shouldn't affect the native size.
   UpdateDisplay("1000x300*2@1.5");
   EXPECT_EQ("1000x300",
-            WallpaperControllerImpl::GetMaxDisplaySizeInNative().ToString());
+            WallpaperController::GetMaxDisplaySizeInNative().ToString());
 
   // First display has maximum size.
   UpdateDisplay("400x300,100x100");
   EXPECT_EQ("400x300",
-            WallpaperControllerImpl::GetMaxDisplaySizeInNative().ToString());
+            WallpaperController::GetMaxDisplaySizeInNative().ToString());
 
   // Second display has maximum size.
   UpdateDisplay("400x300,500x600");
   EXPECT_EQ("500x600",
-            WallpaperControllerImpl::GetMaxDisplaySizeInNative().ToString());
+            WallpaperController::GetMaxDisplaySizeInNative().ToString());
 
   // Maximum width and height belongs to different displays.
   UpdateDisplay("400x300,100x500");
   EXPECT_EQ("400x500",
-            WallpaperControllerImpl::GetMaxDisplaySizeInNative().ToString());
+            WallpaperController::GetMaxDisplaySizeInNative().ToString());
 }
 
 // Test that the wallpaper is always fitted to the native display resolution
@@ -836,15 +859,27 @@
   EXPECT_FALSE(ShouldCalculateColors());
 }
 
-TEST_F(WallpaperControllerTest, EnableShelfColoringNotifiesObservers) {
-  TestWallpaperControllerObserver observer(controller_);
-  EXPECT_EQ(0, observer.colors_changed_count());
+TEST_F(WallpaperControllerTest, MojoWallpaperObserverTest) {
+  TestWallpaperObserver observer;
+  mojom::WallpaperObserverAssociatedPtr observer_ptr;
+  mojo::AssociatedBinding<mojom::WallpaperObserver> binding(
+      &observer, mojo::MakeRequestAssociatedWithDedicatedPipe(&observer_ptr));
+  controller_->AddObserver(observer_ptr.PassInterface());
+  controller_->FlushForTesting();
+
+  // Adding an observer fires OnWallpaperColorsChanged() immediately.
+  EXPECT_EQ(1, observer.wallpaper_colors_changed_count());
 
   // Enable shelf coloring will set a customized wallpaper image and change
   // session state to ACTIVE, which will trigger wallpaper colors calculation.
+  base::RunLoop run_loop;
+  observer.set_run_loop(&run_loop);
   EnableShelfColoring();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, observer.colors_changed_count());
+  // Color calculation may be asynchronous.
+  run_loop.Run();
+  // Mojo methods are called after color calculation finishes.
+  controller_->FlushForTesting();
+  EXPECT_EQ(2, observer.wallpaper_colors_changed_count());
 }
 
 TEST_F(WallpaperControllerTest, SetCustomWallpaper) {
@@ -926,7 +961,7 @@
   controller_->SetOnlineWallpaperFromData(
       InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
       layout, false /*preview_mode=*/,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
+      WallpaperController::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), ONLINE);
@@ -980,7 +1015,7 @@
   controller_->SetOnlineWallpaperFromData(
       InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl2,
       layout, false /*preview_mode=*/,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
+      WallpaperController::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
@@ -1111,11 +1146,21 @@
   // Set a third-party wallpaper for |kUser1|.
   const WallpaperLayout layout = WALLPAPER_LAYOUT_CENTER;
   gfx::ImageSkia third_party_wallpaper = CreateImage(640, 480, kWallpaperColor);
-  EXPECT_TRUE(controller_->SetThirdPartyWallpaper(
+  bool allowed_to_update_wallpaper = false;
+  std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
+  controller_->SetThirdPartyWallpaper(
       InitializeUser(account_id_1), wallpaper_files_id_1, file_name_1, layout,
-      third_party_wallpaper));
+      third_party_wallpaper,
+      base::BindLambdaForTesting([&allowed_to_update_wallpaper, &run_loop](
+                                     bool allowed, uint32_t image_id) {
+        allowed_to_update_wallpaper = allowed;
+        run_loop->Quit();
+      }));
+  run_loop->Run();
   // Verify the wallpaper is shown.
   EXPECT_EQ(1, GetWallpaperCount());
+  // Verify the callback function gets the correct value.
+  EXPECT_TRUE(allowed_to_update_wallpaper);
   // Verify the user wallpaper info is updated.
   EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
                                                 false /*is_ephemeral=*/));
@@ -1125,14 +1170,23 @@
   EXPECT_EQ(wallpaper_info, expected_wallpaper_info);
 
   // Switch active user to |kUser2|, but set another third-party wallpaper for
-  // |kUser1|; the operation should not be allowed, because |kUser1| is not the
-  // active user.
+  // |kUser1|.
+  allowed_to_update_wallpaper = true;
   SimulateUserLogin(kUser2);
-  EXPECT_FALSE(controller_->SetThirdPartyWallpaper(
+  run_loop.reset(new base::RunLoop());
+  controller_->SetThirdPartyWallpaper(
       InitializeUser(account_id_1), wallpaper_files_id_2, file_name_2, layout,
-      third_party_wallpaper));
-  // Verify the wallpaper is not shown.
+      third_party_wallpaper,
+      base::BindLambdaForTesting([&allowed_to_update_wallpaper, &run_loop](
+                                     bool allowed, uint32_t image_id) {
+        allowed_to_update_wallpaper = allowed;
+        run_loop->Quit();
+      }));
+  run_loop->Run();
+  // Verify the wallpaper is not shown because |kUser1| is not the active user.
   EXPECT_EQ(0, GetWallpaperCount());
+  // Verify the callback function gets the correct value.
+  EXPECT_FALSE(allowed_to_update_wallpaper);
   // Verify the wallpaper info for |kUser1| is updated, because setting
   // wallpaper is still allowed for non-active users.
   EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_1, &wallpaper_info,
@@ -1150,20 +1204,30 @@
   RunAllTasksUntilIdle();
   EXPECT_TRUE(
       controller_->IsPolicyControlled(account_id_2, false /*is_ephemeral=*/));
-  EXPECT_TRUE(controller_->IsActiveUserWallpaperControlledByPolicy());
+  EXPECT_TRUE(IsActiveUserWallpaperControlledByPolicy());
 
-  // Setting a third-party wallpaper for |kUser2| should not be allowed, because
-  // third-party wallpapers cannot be set for policy controlled users.
-  EXPECT_FALSE(controller_->SetThirdPartyWallpaper(
+  // Set a third-party wallpaper for |kUser2|.
+  allowed_to_update_wallpaper = true;
+  run_loop.reset(new base::RunLoop());
+  controller_->SetThirdPartyWallpaper(
       InitializeUser(account_id_2), wallpaper_files_id_1, file_name_1, layout,
-      third_party_wallpaper));
-  // Verify the wallpaper is not shown.
+      third_party_wallpaper,
+      base::BindLambdaForTesting([&allowed_to_update_wallpaper, &run_loop](
+                                     bool allowed, uint32_t image_id) {
+        allowed_to_update_wallpaper = allowed;
+        run_loop->Quit();
+      }));
+  run_loop->Run();
+  // Verify the wallpaper is not shown because third-party wallpaper cannot be
+  // set for policy controlled users.
   EXPECT_EQ(0, GetWallpaperCount());
+  // Verify the callback gets the correct value.
+  EXPECT_FALSE(allowed_to_update_wallpaper);
   // Verify |kUser2| is still policy controlled and has the policy wallpaper
   // info.
   EXPECT_TRUE(
       controller_->IsPolicyControlled(account_id_2, false /*is_ephemeral=*/));
-  EXPECT_TRUE(controller_->IsActiveUserWallpaperControlledByPolicy());
+  EXPECT_TRUE(IsActiveUserWallpaperControlledByPolicy());
   EXPECT_TRUE(controller_->GetUserWallpaperInfo(account_id_2, &wallpaper_info,
                                                 false /*is_ephemeral=*/));
   WallpaperInfo policy_wallpaper_info(base::FilePath(wallpaper_files_id_2)
@@ -1263,10 +1327,12 @@
   // path.
   UpdateDisplay("1600x1200");
   RunAllTasksUntilIdle();
-  WallpaperUserInfo wallpaper_user_info = InitializeUser(child_account_id);
-  wallpaper_user_info.type = user_manager::USER_TYPE_CHILD;
-  controller_->SetDefaultWallpaper(
-      wallpaper_user_info, child_wallpaper_files_id, true /*show_wallpaper=*/);
+  mojom::WallpaperUserInfoPtr wallpaper_user_info =
+      InitializeUser(child_account_id);
+  wallpaper_user_info->type = user_manager::USER_TYPE_CHILD;
+  controller_->SetDefaultWallpaper(std::move(wallpaper_user_info),
+                                   child_wallpaper_files_id,
+                                   true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
@@ -1279,9 +1345,10 @@
   UpdateDisplay("800x600");
   RunAllTasksUntilIdle();
   wallpaper_user_info = InitializeUser(child_account_id);
-  wallpaper_user_info.type = user_manager::USER_TYPE_CHILD;
-  controller_->SetDefaultWallpaper(
-      wallpaper_user_info, child_wallpaper_files_id, true /*show_wallpaper=*/);
+  wallpaper_user_info->type = user_manager::USER_TYPE_CHILD;
+  controller_->SetDefaultWallpaper(std::move(wallpaper_user_info),
+                                   child_wallpaper_files_id,
+                                   true /*show_wallpaper=*/);
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), DEFAULT);
@@ -1458,7 +1525,7 @@
   controller_->SetOnlineWallpaperFromData(
       InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
       WALLPAPER_LAYOUT_CENTER, false /*preview_mode=*/,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
+      WallpaperController::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_TRUE(
       controller_->GetWallpaperFromCache(account_id_1, &cached_wallpaper));
@@ -1502,10 +1569,10 @@
 TEST_F(WallpaperControllerTest, ShowCustomWallpaperWithCorrectResolution) {
   CreateDefaultWallpapers();
   const base::FilePath small_custom_wallpaper_path =
-      GetCustomWallpaperPath(WallpaperControllerImpl::kSmallWallpaperSubDir,
+      GetCustomWallpaperPath(WallpaperController::kSmallWallpaperSubDir,
                              wallpaper_files_id_1, file_name_1);
   const base::FilePath large_custom_wallpaper_path =
-      GetCustomWallpaperPath(WallpaperControllerImpl::kLargeWallpaperSubDir,
+      GetCustomWallpaperPath(WallpaperController::kLargeWallpaperSubDir,
                              wallpaper_files_id_1, file_name_1);
   const base::FilePath small_default_wallpaper_path =
       default_wallpaper_dir_.GetPath().Append(kDefaultSmallWallpaperName);
@@ -1693,7 +1760,7 @@
   controller_->SetOnlineWallpaperFromData(
       InitializeUser(account_id_1), std::string() /*image_data=*/, kDummyUrl,
       layout, false /*preview_mode=*/,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
+      WallpaperController::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(controller_->GetWallpaperType(), ONLINE);
@@ -1723,7 +1790,7 @@
 TEST_F(WallpaperControllerTest, RemoveUserWithCustomWallpaper) {
   SimulateUserLogin(kUser1);
   base::FilePath small_wallpaper_path_1 =
-      GetCustomWallpaperPath(WallpaperControllerImpl::kSmallWallpaperSubDir,
+      GetCustomWallpaperPath(WallpaperController::kSmallWallpaperSubDir,
                              wallpaper_files_id_1, file_name_1);
   // Set a custom wallpaper for |kUser1| and verify the wallpaper exists.
   CreateAndSaveWallpapers(account_id_1);
@@ -1732,7 +1799,7 @@
   // Now login another user and set a custom wallpaper for the user.
   SimulateUserLogin(kUser2);
   base::FilePath small_wallpaper_path_2 = GetCustomWallpaperPath(
-      WallpaperControllerImpl::kSmallWallpaperSubDir, wallpaper_files_id_2,
+      WallpaperController::kSmallWallpaperSubDir, wallpaper_files_id_2,
       GetDummyFileName(account_id_2));
   CreateAndSaveWallpapers(account_id_2);
   EXPECT_TRUE(base::PathExists(small_wallpaper_path_2));
@@ -1754,7 +1821,7 @@
 TEST_F(WallpaperControllerTest, RemoveUserWithDefaultWallpaper) {
   SimulateUserLogin(kUser1);
   base::FilePath small_wallpaper_path_1 =
-      GetCustomWallpaperPath(WallpaperControllerImpl::kSmallWallpaperSubDir,
+      GetCustomWallpaperPath(WallpaperController::kSmallWallpaperSubDir,
                              wallpaper_files_id_1, file_name_1);
   // Set a custom wallpaper for |kUser1| and verify the wallpaper exists.
   CreateAndSaveWallpapers(account_id_1);
@@ -1779,73 +1846,78 @@
   // Simulate the login screen. Verify that it returns false since there's no
   // active user.
   ClearLogin();
-  EXPECT_FALSE(controller_->IsActiveUserWallpaperControlledByPolicy());
+  EXPECT_FALSE(IsActiveUserWallpaperControlledByPolicy());
 
   SimulateUserLogin(kUser1);
-  EXPECT_FALSE(controller_->IsActiveUserWallpaperControlledByPolicy());
+  EXPECT_FALSE(IsActiveUserWallpaperControlledByPolicy());
   // Set a policy wallpaper for the active user. Verify that the active user
   // becomes policy controlled.
   controller_->SetPolicyWallpaper(InitializeUser(account_id_1),
                                   wallpaper_files_id_1,
                                   std::string() /*data=*/);
   RunAllTasksUntilIdle();
-  EXPECT_TRUE(controller_->IsActiveUserWallpaperControlledByPolicy());
+  EXPECT_TRUE(IsActiveUserWallpaperControlledByPolicy());
 
   // Switch the active user. Verify the active user is not policy controlled.
   SimulateUserLogin(kUser2);
-  EXPECT_FALSE(controller_->IsActiveUserWallpaperControlledByPolicy());
+  EXPECT_FALSE(IsActiveUserWallpaperControlledByPolicy());
 
   // Logs out. Verify that it returns false since there's no active user.
   ClearLogin();
-  EXPECT_FALSE(controller_->IsActiveUserWallpaperControlledByPolicy());
+  EXPECT_FALSE(IsActiveUserWallpaperControlledByPolicy());
 }
 
 TEST_F(WallpaperControllerTest, WallpaperBlur) {
-  TestWallpaperControllerObserver observer(controller_);
-
   ASSERT_TRUE(controller_->IsBlurAllowed());
   ASSERT_FALSE(controller_->IsWallpaperBlurred());
 
+  TestWallpaperControllerObserver observer;
+  controller_->AddObserver(&observer);
+
   SetSessionState(SessionState::ACTIVE);
   EXPECT_FALSE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(0, observer.blur_changed_count());
+  EXPECT_EQ(0, observer.wallpaper_blur_changed_count_);
 
   SetSessionState(SessionState::LOCKED);
   EXPECT_TRUE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(1, observer.blur_changed_count());
+  EXPECT_EQ(1, observer.wallpaper_blur_changed_count_);
 
   SetSessionState(SessionState::LOGGED_IN_NOT_ACTIVE);
   EXPECT_FALSE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(2, observer.blur_changed_count());
+  EXPECT_EQ(2, observer.wallpaper_blur_changed_count_);
 
   SetSessionState(SessionState::LOGIN_SECONDARY);
   EXPECT_TRUE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(3, observer.blur_changed_count());
+  EXPECT_EQ(3, observer.wallpaper_blur_changed_count_);
 
   // Blur state does not change below.
+  observer.Reset();
   SetSessionState(SessionState::LOGIN_PRIMARY);
   EXPECT_TRUE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(3, observer.blur_changed_count());
+  EXPECT_EQ(0, observer.wallpaper_blur_changed_count_);
 
   SetSessionState(SessionState::OOBE);
   EXPECT_TRUE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(3, observer.blur_changed_count());
+  EXPECT_EQ(0, observer.wallpaper_blur_changed_count_);
 
   SetSessionState(SessionState::UNKNOWN);
   EXPECT_TRUE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(3, observer.blur_changed_count());
+  EXPECT_EQ(0, observer.wallpaper_blur_changed_count_);
+
+  controller_->RemoveObserver(&observer);
 }
 
 TEST_F(WallpaperControllerTest, WallpaperBlurDuringLockScreenTransition) {
-  TestWallpaperControllerObserver observer(controller_);
-
   ASSERT_TRUE(controller_->IsBlurAllowed());
   ASSERT_FALSE(controller_->IsWallpaperBlurred());
 
+  TestWallpaperControllerObserver observer;
+  controller_->AddObserver(&observer);
+
   // Simulate lock and unlock sequence.
   controller_->UpdateWallpaperBlur(true);
   EXPECT_TRUE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(1, observer.blur_changed_count());
+  EXPECT_EQ(1, observer.wallpaper_blur_changed_count_);
 
   SetSessionState(SessionState::LOCKED);
   EXPECT_TRUE(controller_->IsWallpaperBlurred());
@@ -1854,7 +1926,9 @@
   // UpdateWallpaperBlur(false)
   SetSessionState(SessionState::ACTIVE);
   EXPECT_FALSE(controller_->IsWallpaperBlurred());
-  EXPECT_EQ(2, observer.blur_changed_count());
+  EXPECT_EQ(2, observer.wallpaper_blur_changed_count_);
+
+  controller_->RemoveObserver(&observer);
 }
 
 TEST_F(WallpaperControllerTest, OnlyShowDevicePolicyWallpaperOnLoginScreen) {
@@ -2120,7 +2194,7 @@
   SetOnlineWallpaperFromImage(
       account_id_1, online_wallpaper, kDummyUrl, layout, false /*save_file=*/,
       true /*preview_mode=*/,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
+      WallpaperController::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(online_wallpaper_color, GetWallpaperColor());
@@ -2220,7 +2294,7 @@
   SetOnlineWallpaperFromImage(
       account_id_1, online_wallpaper, kDummyUrl, layout, false /*save_file=*/,
       true /*preview_mode=*/,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
+      WallpaperController::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
@@ -2238,7 +2312,7 @@
   SetOnlineWallpaperFromImage(
       account_id_1, synced_online_wallpaper, kDummyUrl2, layout,
       false /*save_file=*/, false /*preview_mode=*/,
-      WallpaperControllerImpl::SetOnlineWallpaperFromDataCallback());
+      WallpaperController::SetOnlineWallpaperFromDataCallback());
   RunAllTasksUntilIdle();
   EXPECT_EQ(0, GetWallpaperCount());
   EXPECT_EQ(kWallpaperColor, GetWallpaperColor());
@@ -2369,9 +2443,10 @@
 }
 
 TEST_F(WallpaperControllerTest, OnFirstWallpaperShown) {
-  TestWallpaperControllerObserver observer(controller_);
+  TestWallpaperControllerObserver observer;
+  controller_->AddObserver(&observer);
   EXPECT_EQ(0, GetWallpaperCount());
-  EXPECT_EQ(0, observer.first_shown_count());
+  EXPECT_EQ(0, observer.first_wallpaper_shown_count_);
   // Show the first wallpaper, verify the observer is notified.
   controller_->ShowWallpaperImage(CreateImage(640, 480, SK_ColorBLUE),
                                   CreateWallpaperInfo(WALLPAPER_LAYOUT_STRETCH),
@@ -2380,7 +2455,7 @@
   RunAllTasksUntilIdle();
   EXPECT_EQ(SK_ColorBLUE, GetWallpaperColor());
   EXPECT_EQ(1, GetWallpaperCount());
-  EXPECT_EQ(1, observer.first_shown_count());
+  EXPECT_EQ(1, observer.first_wallpaper_shown_count_);
   // Show the second wallpaper, verify the observer is not notified.
   controller_->ShowWallpaperImage(CreateImage(640, 480, SK_ColorCYAN),
                                   CreateWallpaperInfo(WALLPAPER_LAYOUT_STRETCH),
@@ -2389,15 +2464,17 @@
   RunAllTasksUntilIdle();
   EXPECT_EQ(SK_ColorCYAN, GetWallpaperColor());
   EXPECT_EQ(2, GetWallpaperCount());
-  EXPECT_EQ(1, observer.first_shown_count());
+  EXPECT_EQ(1, observer.first_wallpaper_shown_count_);
+  controller_->RemoveObserver(&observer);
 }
 
 // Although ephemeral users' custom wallpapers are not saved to disk, they
 // should be kept within the user session. Test for https://crbug.com/825237.
 TEST_F(WallpaperControllerTest, ShowWallpaperForEphemeralUser) {
   auto initialize_ephemeral_user = [&](const AccountId& account_id) {
-    WallpaperUserInfo wallpaper_user_info = InitializeUser(account_id);
-    wallpaper_user_info.is_ephemeral = true;
+    mojom::WallpaperUserInfoPtr wallpaper_user_info =
+        InitializeUser(account_id);
+    wallpaper_user_info->is_ephemeral = true;
     return wallpaper_user_info;
   };
 
diff --git a/ash/public/cpp/wallpaper_info.h b/ash/wallpaper/wallpaper_info.h
similarity index 89%
rename from ash/public/cpp/wallpaper_info.h
rename to ash/wallpaper/wallpaper_info.h
index c88ddf4..671f32c3 100644
--- a/ash/public/cpp/wallpaper_info.h
+++ b/ash/wallpaper/wallpaper_info.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_PUBLIC_CPP_WALLPAPER_INFO_H_
-#define ASH_PUBLIC_CPP_WALLPAPER_INFO_H_
+#ifndef ASH_WALLPAPER_WALLPAPER_INFO_H_
+#define ASH_WALLPAPER_WALLPAPER_INFO_H_
 
 #include "ash/public/cpp/wallpaper_types.h"
 #include "base/time/time.h"
@@ -40,4 +40,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_PUBLIC_CPP_WALLPAPER_INFO_H_
+#endif  // ASH_WALLPAPER_WALLPAPER_INFO_H_
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_resizer.h b/ash/wallpaper/wallpaper_utils/wallpaper_resizer.h
index bc78add8..f0b9efc0 100644
--- a/ash/wallpaper/wallpaper_utils/wallpaper_resizer.h
+++ b/ash/wallpaper/wallpaper_utils/wallpaper_resizer.h
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/wallpaper_info.h"
+#include "ash/wallpaper/wallpaper_info.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer_observer.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
diff --git a/ash/wallpaper/wallpaper_view.cc b/ash/wallpaper/wallpaper_view.cc
index 46e19e3..3783273e 100644
--- a/ash/wallpaper/wallpaper_view.cc
+++ b/ash/wallpaper/wallpaper_view.cc
@@ -8,7 +8,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_utils.h"
@@ -210,7 +210,7 @@
                                      int blur,
                                      float opacity,
                                      WallpaperView** out_wallpaper_view) {
-  auto* controller = Shell::Get()->wallpaper_controller();
+  WallpaperController* controller = Shell::Get()->wallpaper_controller();
 
   views::Widget* wallpaper_widget = new views::Widget;
   views::Widget::InitParams params(
diff --git a/ash/wallpaper/wallpaper_widget_controller.cc b/ash/wallpaper/wallpaper_widget_controller.cc
index f6b2395..73eabab0 100644
--- a/ash/wallpaper/wallpaper_widget_controller.cc
+++ b/ash/wallpaper/wallpaper_widget_controller.cc
@@ -9,7 +9,7 @@
 #include "ash/ash_export.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "base/scoped_observer.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc
index aed6ed59..f21237a 100644
--- a/ash/wm/lock_state_controller.cc
+++ b/ash/wm/lock_state_controller.cc
@@ -17,7 +17,7 @@
 #include "ash/shell_delegate.h"
 #include "ash/shutdown_controller.h"
 #include "ash/shutdown_reason.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/session_state_animator.h"
 #include "ash/wm/session_state_animator_impl.h"
 #include "base/bind.h"
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index f97087ca..049d959 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -13,7 +13,7 @@
 #include "ash/scoped_animation_disabler.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wallpaper/wallpaper_view.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
 #include "ash/wm/mru_window_tracker.h"
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 85ee620..ec3837bb 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -18,7 +18,6 @@
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/drag_drop/drag_drop_controller.h"
-#include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/fps_counter.h"
@@ -144,7 +143,6 @@
   // AshTestBase:
   void SetUp() override {
     AshTestBase::SetUp();
-    NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(true);
 
     aura::Env::GetInstance()->set_throttle_input_on_resize_for_testing(false);
     shelf_view_test_api_ = std::make_unique<ShelfViewTestAPI>(
@@ -161,7 +159,6 @@
         false);
     FpsCounter::SetForceReportZeroAnimationForTest(false);
     trace_names_.clear();
-    NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(false);
     AshTestBase::TearDown();
   }
 
@@ -2522,13 +2519,13 @@
 // the window is to be letter or pillar fitted.
 TEST_F(OverviewSessionTest, Backdrop) {
   // Add three windows which in overview mode will be considered wide, tall and
-  // normal. Window |wide|, with size (400, 160) will be resized to (200, 160)
-  // when the 400x200 is rotated to 200x400, and should be considered a normal
+  // normal. Window |wide|, with size (400, 160) will be resized to (300, 160)
+  // when the 400x300 is rotated to 300x400, and should be considered a normal
   // overview window after display change.
-  UpdateDisplay("400x200");
+  UpdateDisplay("400x300");
   std::unique_ptr<aura::Window> wide(CreateTestWindow(gfx::Rect(400, 160)));
-  std::unique_ptr<aura::Window> tall(CreateTestWindow(gfx::Rect(50, 200)));
-  std::unique_ptr<aura::Window> normal(CreateTestWindow(gfx::Rect(200, 200)));
+  std::unique_ptr<aura::Window> tall(CreateTestWindow(gfx::Rect(100, 300)));
+  std::unique_ptr<aura::Window> normal(CreateTestWindow(gfx::Rect(300, 300)));
 
   ToggleOverview();
   base::RunLoop().RunUntilIdle();
diff --git a/ash/wm/overview/scoped_overview_transform_window_unittest.cc b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
index 963dc3e..9e677d04 100644
--- a/ash/wm/overview/scoped_overview_transform_window_unittest.cc
+++ b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "ash/wm/overview/scoped_overview_transform_window.h"
 
-#include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/test/ash_test_base.h"
@@ -212,19 +211,14 @@
 
 // Tests the cases when very wide or tall windows enter overview mode.
 TEST_F(ScopedOverviewTransformWindowTest, ExtremeWindowBounds) {
-  NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(true);
-
   // Add three windows which in overview mode will be considered wide, tall and
-  // normal. Window |wide|, with size (400, 160) will be resized to (200, 160)
-  // when the 400x200 is rotated to 200x400, and should be considered a normal
+  // normal. Window |wide|, with size (400, 160) will be resized to (300, 160)
+  // when the 400x300 is rotated to 300x400, and should be considered a normal
   // overview window after display change.
-  UpdateDisplay("400x200");
-  std::unique_ptr<aura::Window> wide =
-      CreateTestWindow(gfx::Rect(10, 10, 400, 160));
-  std::unique_ptr<aura::Window> tall =
-      CreateTestWindow(gfx::Rect(10, 10, 50, 200));
-  std::unique_ptr<aura::Window> normal =
-      CreateTestWindow(gfx::Rect(10, 10, 200, 200));
+  UpdateDisplay("400x300");
+  std::unique_ptr<aura::Window> wide = CreateTestWindow(gfx::Rect(400, 160));
+  std::unique_ptr<aura::Window> tall = CreateTestWindow(gfx::Rect(100, 300));
+  std::unique_ptr<aura::Window> normal = CreateTestWindow(gfx::Rect(300, 300));
 
   ScopedOverviewTransformWindow scoped_wide(nullptr, wide.get());
   ScopedOverviewTransformWindow scoped_tall(nullptr, tall.get());
@@ -251,8 +245,6 @@
   EXPECT_EQ(GridWindowFillMode::kNormal, scoped_wide.type());
   EXPECT_EQ(GridWindowFillMode::kPillarBoxed, scoped_tall.type());
   EXPECT_EQ(GridWindowFillMode::kNormal, scoped_normal.type());
-
-  NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(false);
 }
 
 // Tests that transients which should be invisible in overview do not have their
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 71be4014..0d589fd 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -22,6 +22,7 @@
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/mru_window_tracker.h"
diff --git a/ash/wm/workspace/backdrop_controller.cc b/ash/wm/workspace/backdrop_controller.cc
index eb292da..6cbeb55 100644
--- a/ash/wm/workspace/backdrop_controller.cc
+++ b/ash/wm/workspace/backdrop_controller.cc
@@ -15,7 +15,7 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
-#include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_controller.h"
diff --git a/ash/wm/workspace/backdrop_controller.h b/ash/wm/workspace/backdrop_controller.h
index a2aaf0c..6cc60d1 100644
--- a/ash/wm/workspace/backdrop_controller.h
+++ b/ash/wm/workspace/backdrop_controller.h
@@ -10,8 +10,8 @@
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/split_view.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/shell_observer.h"
+#include "ash/wallpaper/wallpaper_controller_observer.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "base/macros.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index f1a5a32b3..ed010e5 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -147,34 +147,14 @@
 
 }  // namespace
 
-// NOTE: many of these tests use NonClientFrameViewAshSizeLock. This is needed
-// as the tests assume a minimum size of 0x0. In mash the minimum size, for
-// top-level windows, is not 0x0, so without this the tests fails.
-// TODO(sky): update the tests so that this isn't necessary.
-class NonClientFrameViewAshSizeLock {
- public:
-  NonClientFrameViewAshSizeLock() {
-    NonClientFrameViewAsh::use_empty_minimum_size_for_test_ = true;
-  }
-  ~NonClientFrameViewAshSizeLock() {
-    NonClientFrameViewAsh::use_empty_minimum_size_for_test_ = false;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NonClientFrameViewAshSizeLock);
-};
-
 using WorkspaceLayoutManagerTest = AshTestBase;
 
 // Verifies that a window containing a restore coordinate will be restored to
 // to the size prior to minimize, keeping the restore rectangle in tact (if
 // there is one).
 TEST_F(WorkspaceLayoutManagerTest, RestoreFromMinimizeKeepsRestore) {
-  // See comment at top of file for why this is needed.
-  NonClientFrameViewAshSizeLock min_size_lock;
-  std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4)));
-  gfx::Rect bounds(10, 15, 25, 35);
-  window->SetBounds(bounds);
+  std::unique_ptr<aura::Window> window(
+      CreateTestWindow(gfx::Rect(10, 15, 125, 35)));
 
   wm::WindowState* window_state = wm::GetWindowState(window.get());
 
@@ -183,16 +163,16 @@
   window_state->Minimize();
   window_state->Restore();
   EXPECT_EQ("0,0 100x100", window_state->GetRestoreBoundsInScreen().ToString());
-  EXPECT_EQ("10,15 25x35", window->bounds().ToString());
+  EXPECT_EQ("10,15 125x35", window->bounds().ToString());
 
   UpdateDisplay("400x300,500x400");
-  window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100), GetSecondaryDisplay());
+  window->SetBoundsInScreen(gfx::Rect(600, 0, 125, 100), GetSecondaryDisplay());
   EXPECT_EQ(Shell::Get()->GetAllRootWindows()[1], window->GetRootWindow());
   window_state->Minimize();
   // This will not be used for un-minimizing window.
   window_state->SetRestoreBoundsInScreen(gfx::Rect(0, 0, 100, 100));
   window_state->Restore();
-  EXPECT_EQ("600,0 100x100", window->GetBoundsInScreen().ToString());
+  EXPECT_EQ("600,0 125x100", window->GetBoundsInScreen().ToString());
 
   // Make sure the unminimized window moves inside the display when
   // 2nd display is disconnected.
@@ -235,59 +215,55 @@
 }
 
 TEST_F(WorkspaceLayoutManagerTest, KeepRestoredWindowInDisplay) {
-  // See comment at top of file for why this is needed.
-  NonClientFrameViewAshSizeLock min_size_lock;
   std::unique_ptr<aura::Window> window(
-      CreateTestWindow(gfx::Rect(1, 2, 30, 40)));
+      CreateTestWindow(gfx::Rect(1, 2, 130, 40)));
   wm::WindowState* window_state = wm::GetWindowState(window.get());
 
   // Maximized -> Normal transition.
   window_state->Maximize();
-  window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40));
+  window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 130, 40));
   window_state->Restore();
   EXPECT_TRUE(
       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
   // Y bounds should not be negative.
-  EXPECT_EQ("-5,0 30x40", window->bounds().ToString());
+  EXPECT_GE(window->bounds().y(), 0);
 
   // Minimized -> Normal transition.
-  window->SetBounds(gfx::Rect(-100, -100, 30, 40));
+  window->SetBounds(gfx::Rect(-100, -100, 130, 40));
   window_state->Minimize();
   EXPECT_FALSE(
       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
-  EXPECT_EQ("-100,-100 30x40", window->bounds().ToString());
+  EXPECT_EQ("-100,-100 130x40", window->bounds().ToString());
   window->Show();
   EXPECT_TRUE(
       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
   // Y bounds should not be negative.
-  EXPECT_EQ("-5,0 30x40", window->bounds().ToString());
+  EXPECT_GE(window->bounds().y(), 0);
 
   // Fullscreen -> Normal transition.
-  window->SetBounds(gfx::Rect(0, 0, 30, 40));  // reset bounds.
-  ASSERT_EQ("0,0 30x40", window->bounds().ToString());
+  window->SetBounds(gfx::Rect(0, 0, 130, 40));  // reset bounds.
+  ASSERT_EQ("0,0 130x40", window->bounds().ToString());
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
   EXPECT_EQ(window->bounds(), window->GetRootWindow()->bounds());
-  window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40));
+  window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 130, 40));
   window_state->Restore();
   EXPECT_TRUE(
       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
   // Y bounds should not be negative.
-  EXPECT_EQ("-5,0 30x40", window->bounds().ToString());
+  EXPECT_GE(window->bounds().y(), 0);
 }
 
 TEST_F(WorkspaceLayoutManagerTest, MaximizeInDisplayToBeRestored) {
-  // See comment at top of file for why this is needed.
-  NonClientFrameViewAshSizeLock min_size_lock;
   UpdateDisplay("300x400,400x500");
 
   aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
 
   std::unique_ptr<aura::Window> window(
-      CreateTestWindow(gfx::Rect(1, 2, 30, 40)));
+      CreateTestWindow(gfx::Rect(1, 2, 130, 40)));
   EXPECT_EQ(root_windows[0], window->GetRootWindow());
 
   wm::WindowState* window_state = wm::GetWindowState(window.get());
-  window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
+  window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 130, 40));
   // Maximize the window in 2nd display as the restore bounds
   // is inside 2nd display.
   window_state->Maximize();
@@ -298,11 +274,11 @@
 
   window_state->Restore();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
-  EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString());
+  EXPECT_EQ("400,0 130x40", window->GetBoundsInScreen().ToString());
 
   // If the restore bounds intersects with the current display,
   // don't move.
-  window_state->SetRestoreBoundsInScreen(gfx::Rect(295, 0, 30, 40));
+  window_state->SetRestoreBoundsInScreen(gfx::Rect(295, 0, 130, 40));
   window_state->Maximize();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
   EXPECT_EQ(
@@ -311,13 +287,13 @@
 
   window_state->Restore();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
-  EXPECT_EQ("295,0 30x40", window->GetBoundsInScreen().ToString());
+  EXPECT_EQ("295,0 130x40", window->GetBoundsInScreen().ToString());
 
   // Restoring widget state.
   std::unique_ptr<views::Widget> w1(new views::Widget);
   views::Widget::InitParams params;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  params.delegate = new MaximizeDelegateView(gfx::Rect(400, 0, 30, 40));
+  params.delegate = new MaximizeDelegateView(gfx::Rect(400, 0, 130, 40));
   params.context = CurrentContext();
   w1->Init(params);
   EXPECT_EQ(root_windows[0], w1->GetNativeWindow()->GetRootWindow());
@@ -329,12 +305,10 @@
       w1->GetWindowBoundsInScreen().ToString());
   w1->Restore();
   EXPECT_EQ(root_windows[1], w1->GetNativeWindow()->GetRootWindow());
-  EXPECT_EQ("400,0 30x40", w1->GetWindowBoundsInScreen().ToString());
+  EXPECT_EQ("400,0 130x40", w1->GetWindowBoundsInScreen().ToString());
 }
 
 TEST_F(WorkspaceLayoutManagerTest, FullscreenInDisplayToBeRestored) {
-  // See comment at top of file for why this is needed.
-  NonClientFrameViewAshSizeLock min_size_lock;
   UpdateDisplay("300x400,400x500");
 
   aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
@@ -344,7 +318,7 @@
   EXPECT_EQ(root_windows[0], window->GetRootWindow());
 
   wm::WindowState* window_state = wm::GetWindowState(window.get());
-  window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
+  window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 130, 40));
   // Maximize the window in 2nd display as the restore bounds
   // is inside 2nd display.
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
@@ -353,18 +327,18 @@
 
   window_state->Restore();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
-  EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString());
+  EXPECT_EQ("400,0 130x40", window->GetBoundsInScreen().ToString());
 
   // If the restore bounds intersects with the current display,
   // don't move.
-  window_state->SetRestoreBoundsInScreen(gfx::Rect(295, 0, 30, 40));
+  window_state->SetRestoreBoundsInScreen(gfx::Rect(295, 0, 130, 40));
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
   EXPECT_EQ("300,0 400x500", window->GetBoundsInScreen().ToString());
 
   window_state->Restore();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
-  EXPECT_EQ("295,0 30x40", window->GetBoundsInScreen().ToString());
+  EXPECT_EQ("295,0 130x40", window->GetBoundsInScreen().ToString());
 }
 
 // aura::WindowObserver implementation used by
@@ -1701,9 +1675,6 @@
 TEST_F(WorkspaceLayoutManagerKeyboardTest, ChildWindowFocused) {
   ScopedStickyKeyboardEnabler sticky_enabler;
 
-  // See comment at top of file for why this is needed.
-  NonClientFrameViewAshSizeLock min_size_lock;
-
   InitKeyboardBounds();
 
   gfx::Rect work_area(GetPrimaryDisplay().work_area());
@@ -1733,15 +1704,10 @@
 TEST_F(WorkspaceLayoutManagerKeyboardTest, AdjustWindowForA11yKeyboard) {
   ScopedStickyKeyboardEnabler sticky_enabler;
 
-  // See comment at top of file for why this is needed.
-  NonClientFrameViewAshSizeLock min_size_lock;
   InitKeyboardBounds();
   gfx::Rect work_area(GetPrimaryDisplay().work_area());
 
   std::unique_ptr<aura::Window> window(CreateToplevelTestWindow(work_area));
-  // The additional SetBounds() is needed as the aura-mus case uses Widget,
-  // which alters the supplied bounds.
-  window->SetBounds(work_area);
 
   int available_height =
       GetPrimaryDisplay().bounds().height() - keyboard_bounds().height();
@@ -1787,9 +1753,6 @@
   InitKeyboardBounds();
 
   std::unique_ptr<aura::Window> window(CreateTestWindow(keyboard_bounds()));
-  // The additional SetBounds() is needed as the aura-mus case uses Widget,
-  // which alters the supplied bounds.
-  window->SetBounds(keyboard_bounds());
   wm::GetWindowState(window.get())->set_ignore_keyboard_bounds_change(true);
   wm::ActivateWindow(window.get());
 
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 5ebe066..3e4596b 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -2184,7 +2184,14 @@
   generic_allocator.root()->Free(ptr);
 }
 
-TEST_F(PartitionAllocTest, ZeroFill) {
+// TODO(crbug.com/966169): This test is flaky on Fuchsia.
+#if defined(OS_FUCHSIA)
+#define MAYBE_ZeroFill DISABLED_ZeroFill
+#else
+#define MAYBE_ZeroFill ZeroFill
+#endif  // defined(OS_FUCHSIA)
+
+TEST_F(PartitionAllocTest, MAYBE_ZeroFill) {
   constexpr static size_t kAllZerosSentinel =
       std::numeric_limits<size_t>::max();
   for (size_t size : kTestSizes) {
diff --git a/base/android/jni_array.cc b/base/android/jni_array.cc
index 054853d9..80efbe11d 100644
--- a/base/android/jni_array.cc
+++ b/base/android/jni_array.cc
@@ -171,6 +171,25 @@
   return ScopedJavaLocalRef<jobjectArray>(env, joa);
 }
 
+ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStringArray(
+    JNIEnv* env,
+    const std::vector<std::vector<string16>>& vec_outer) {
+  ScopedJavaLocalRef<jclass> string_array_clazz =
+      GetClass(env, "[Ljava/lang/String;");
+
+  jobjectArray joa =
+      env->NewObjectArray(vec_outer.size(), string_array_clazz.obj(), NULL);
+  CheckException(env);
+
+  for (size_t i = 0; i < vec_outer.size(); ++i) {
+    ScopedJavaLocalRef<jobjectArray> inner =
+        ToJavaArrayOfStrings(env, vec_outer[i]);
+    env->SetObjectArrayElement(joa, i, inner.obj());
+  }
+
+  return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
 ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
     JNIEnv* env, const std::vector<string16>& v) {
   ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String");
@@ -329,6 +348,23 @@
   }
 }
 
+void Java2dStringArrayTo2dStringVector(
+    JNIEnv* env,
+    const JavaRef<jobjectArray>& array,
+    std::vector<std::vector<string16>>* out) {
+  DCHECK(out);
+  size_t len = SafeGetArrayLength(env, array);
+  out->resize(len);
+  for (size_t i = 0; i < len; ++i) {
+    ScopedJavaLocalRef<jobjectArray> strings_array(
+        env,
+        static_cast<jobjectArray>(env->GetObjectArrayElement(array.obj(), i)));
+
+    out->at(i).clear();
+    AppendJavaStringArrayToStringVector(env, strings_array, &out->at(i));
+  }
+}
+
 void JavaArrayOfIntArrayToIntVector(JNIEnv* env,
                                     const JavaRef<jobjectArray>& array,
                                     std::vector<std::vector<int>>* out) {
diff --git a/base/android/jni_array.h b/base/android/jni_array.h
index 917f1d0..f7a7273 100644
--- a/base/android/jni_array.h
+++ b/base/android/jni_array.h
@@ -78,6 +78,10 @@
 BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
     JNIEnv* env,  const std::vector<string16>& v);
 
+BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStringArray(
+    JNIEnv* env,
+    const std::vector<std::vector<string16>>& v);
+
 // Converts a Java string array to a native array.
 BASE_EXPORT void AppendJavaStringArrayToStringVector(
     JNIEnv* env,
@@ -144,6 +148,14 @@
     const JavaRef<jobjectArray>& array,
     std::vector<std::string>* out);
 
+// Assuming |array| is an String[][] (array of String arrays), replaces the
+// content of |out| with the corresponding vector of string vectors. No UTF-8
+// conversion is performed.
+BASE_EXPORT void Java2dStringArrayTo2dStringVector(
+    JNIEnv* env,
+    const JavaRef<jobjectArray>& array,
+    std::vector<std::vector<string16>>* out);
+
 // Assuming |array| is an int[][] (array of int arrays), replaces the
 // contents of |out| with the corresponding vectors of ints.
 BASE_EXPORT void JavaArrayOfIntArrayToIntVector(
diff --git a/base/android/jni_array_unittest.cc b/base/android/jni_array_unittest.cc
index 2ddf0ff..024b56f 100644
--- a/base/android/jni_array_unittest.cc
+++ b/base/android/jni_array_unittest.cc
@@ -6,12 +6,15 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <algorithm>
 
 #include <limits>
 
 #include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -177,6 +180,21 @@
   }
 }
 
+TEST(JniArray, ArrayOfStringArrayConversion) {
+  std::vector<std::vector<string16>> kArrays = {
+      {ASCIIToUTF16("a"), ASCIIToUTF16("f")},
+      {ASCIIToUTF16("a"), ASCIIToUTF16("")},
+      {},
+      {ASCIIToUTF16("")}};
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobjectArray> joa = ToJavaArrayOfStringArray(env, kArrays);
+
+  std::vector<std::vector<string16>> out;
+  Java2dStringArrayTo2dStringVector(env, joa, &out);
+  ASSERT_TRUE(kArrays == out);
+}
+
 TEST(JniArray, FloatConversions) {
   const float kFloats[] = { 0.0f, 1.0f, -10.0f};
   const size_t kLen = base::size(kFloats);
@@ -352,6 +370,46 @@
   }
 }
 
+TEST(JniArray, JavaArrayOfStringArrayToVectorOfStringVector) {
+  const std::vector<std::vector<string16>> kArrays = {
+      {ASCIIToUTF16("a"), ASCIIToUTF16("f")},
+      {ASCIIToUTF16("a"), ASCIIToUTF16("")},
+      {},
+      {ASCIIToUTF16("")}};
+
+  JNIEnv* env = AttachCurrentThread();
+
+  ScopedJavaLocalRef<jobjectArray> array(
+      env, env->NewObjectArray(kArrays.size(),
+                               env->FindClass("[Ljava/lang/String;"), NULL));
+  ASSERT_TRUE(array);
+
+  ScopedJavaLocalRef<jclass> string_clazz(env,
+                                          env->FindClass("java/lang/String"));
+  ASSERT_TRUE(string_clazz);
+
+  for (size_t i = 0; i < kArrays.size(); ++i) {
+    const std::vector<string16>& child_data = kArrays[i];
+
+    ScopedJavaLocalRef<jobjectArray> child_array(
+        env, env->NewObjectArray(child_data.size(), string_clazz.obj(), NULL));
+    ASSERT_TRUE(child_array);
+
+    for (size_t j = 0; j < child_data.size(); ++j) {
+      ScopedJavaLocalRef<jstring> item =
+          base::android::ConvertUTF16ToJavaString(env, child_data[j]);
+      env->SetObjectArrayElement(child_array.obj(), j, item.obj());
+      ASSERT_FALSE(HasException(env));
+    }
+    env->SetObjectArrayElement(array.obj(), i, child_array.obj());
+  }
+
+  std::vector<std::vector<string16>> vec;
+  Java2dStringArrayTo2dStringVector(env, array, &vec);
+
+  ASSERT_EQ(kArrays, vec);
+}
+
 TEST(JniArray, JavaArrayOfIntArrayToIntVector) {
   const size_t kNumItems = 4;
   JNIEnv* env = AttachCurrentThread();
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 27fe4aa..a5a53d5 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -422,6 +422,7 @@
     "//components/signin/core/browser:signin_enums_javagen",
     "//components/supervised_user_error_page:enums_srcjar",
     "//components/ui_metrics:ui_metrics_enums_java",
+    "//chrome/browser/notifications/scheduler:jni_enums",
     "//chrome/browser/ui:tab_model_enums_java",
     "//net:effective_connection_type_java",
     ":vr_build_config",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 93cfbd3..3e963fd 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -71,6 +71,7 @@
   "java/src/org/chromium/chrome/browser/accessibility/FontSizePrefs.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenu.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java",
+  "java/src/org/chromium/chrome/browser/appmenu/AppMenuBlocker.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuButtonHelper.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuButtonHelperImpl.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinator.java",
@@ -292,6 +293,7 @@
   "java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java",
   "java/src/org/chromium/chrome/browser/contacts_picker/TopView.java",
   "java/src/org/chromium/chrome/browser/content/ContentUtils.java",
+  "java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java",
   "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java",
@@ -692,10 +694,10 @@
   "java/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelper.java",
   "java/src/org/chromium/chrome/browser/gcore/LifecycleHook.java",
   "java/src/org/chromium/chrome/browser/gesturenav/AndroidUiNavigationGlow.java",
-  "java/src/org/chromium/chrome/browser/gesturenav/ArrowChipView.java",
   "java/src/org/chromium/chrome/browser/gesturenav/CompositorNavigationGlow.java",
   "java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationDelegate.java",
   "java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java",
+  "java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java",
   "java/src/org/chromium/chrome/browser/gesturenav/NavigationGlow.java",
   "java/src/org/chromium/chrome/browser/gesturenav/NavigationGlowFactory.java",
   "java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java
index e63e811..928efe1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java
@@ -12,7 +12,6 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter;
@@ -132,20 +131,4 @@
 
         return selector.getCurrentModel().indexOf(tabs.get(tabs.size() - 1));
     }
-
-    /**
-     * This method judges whether the current move from {@code curIndex} to {@code newIndex} in
-     * {@code tabModel} is a move within one TabGroup or not.
-     * @param tabModel   The TabModel that owns the moving tab.
-     * @param curIndex   The current index of the moving tab.
-     * @param newIndex   The new index of the moving tab.
-     * @return Whether is move happens within one TabGroup or not.
-     */
-    public static boolean isMoveInSameGroup(TabModel tabModel, int curIndex, int newIndex) {
-        int rootId = tabModel.getTabAt(curIndex).getRootId();
-        for (int i = Math.min(newIndex, curIndex); i <= Math.max(newIndex, curIndex); i++) {
-            if (tabModel.getTabAt(i).getRootId() != rootId) return false;
-        }
-        return true;
-    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index c408b68..3a55f47 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -233,6 +233,8 @@
 
     private final TabModelObserver mTabModelObserver;
 
+    private TabGroupModelFilter.Observer mTabGroupObserver;
+
     /**
      * Interface for implementing a {@link Runnable} that takes a tabId for a generic action.
      */
@@ -324,12 +326,69 @@
 
             @Override
             public void didMoveTab(Tab tab, int newIndex, int curIndex) {
+                if (mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
+                                instanceof TabGroupModelFilter) {
+                    return;
+                }
                 onTabMoved(tab, newIndex, curIndex);
             }
         };
 
         mTabModelSelector.getTabModelFilterProvider().addTabModelFilterObserver(mTabModelObserver);
 
+        if (mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
+                        instanceof TabGroupModelFilter) {
+            mTabGroupObserver = new TabGroupModelFilter.Observer() {
+                @Override
+                public void didMoveWithinGroup(
+                        Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {
+                    int curPosition = mModel.indexFromId(movedTab.getId());
+                    TabModel tabModel = mTabModelSelector.getCurrentModel();
+
+                    if (!isValidMovePosition(curPosition)) return;
+
+                    Tab destinationTab = tabModel.getTabAt(tabModelNewIndex > tabModelOldIndex
+                                    ? tabModelNewIndex - 1
+                                    : tabModelNewIndex + 1);
+
+                    int newPosition = mModel.indexFromId(destinationTab.getId());
+                    if (!isValidMovePosition(newPosition)) return;
+                    mModel.move(curPosition, newPosition);
+                }
+
+                @Override
+                public void didMoveTabGroup(
+                        Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {
+                    List<Tab> relatedTabs = getRelatedTabsForId(movedTab.getId());
+                    Tab currentGroupSelectedTab =
+                            TabGroupUtils.getSelectedTabInGroupForTab(mTabModelSelector, movedTab);
+                    TabModel tabModel = mTabModelSelector.getCurrentModel();
+                    int curPosition = mModel.indexFromId(currentGroupSelectedTab.getId());
+                    if (!isValidMovePosition(curPosition)) return;
+
+                    // Find the tab which was in the destination index before this move. Use that
+                    // tab to figure out the new position.
+                    int destinationTabIndex = tabModelNewIndex > tabModelOldIndex
+                            ? tabModelNewIndex - relatedTabs.size()
+                            : tabModelNewIndex + 1;
+                    Tab destinationTab = tabModel.getTabAt(destinationTabIndex);
+                    Tab destinationGroupSelectedTab = TabGroupUtils.getSelectedTabInGroupForTab(
+                            mTabModelSelector, destinationTab);
+                    int newPosition = mModel.indexFromId(destinationGroupSelectedTab.getId());
+                    if (!isValidMovePosition(newPosition)) return;
+
+                    mModel.move(curPosition, newPosition);
+                }
+            };
+
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                     false))
+                    .addTabGroupObserver(mTabGroupObserver);
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                     true))
+                    .addTabGroupObserver(mTabGroupObserver);
+        }
+
         // TODO(meiliang): follow up with unit tests to test the close signal is sent correctly with
         // the recommendedNextTab.
         mTabClosedListener = new TabActionListener() {
@@ -424,54 +483,12 @@
     }
 
     private void onTabMoved(Tab tab, int newIndex, int curIndex) {
-        List<Tab> relatedTabs = getRelatedTabsForId(tab.getId());
-        TabModel tabModel = mTabModelSelector.getCurrentModel();
-
         // Handle move without groups enabled.
         if (mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
                         instanceof EmptyTabModelFilter) {
             if (!isValidMovePosition(curIndex) || !isValidMovePosition(newIndex)) return;
             mModel.move(curIndex, newIndex);
-            return;
         }
-
-        // Handle move with groups enabled.
-        // When move within one group.
-        if (TabGroupUtils.isMoveInSameGroup(tabModel, curIndex, newIndex)) {
-            int curPosition = mModel.indexFromId(tab.getId());
-            if (!isValidMovePosition(curPosition)) return;
-            Tab destinationTab =
-                    tabModel.getTabAt(newIndex > curIndex ? newIndex - 1 : newIndex + 1);
-            int newPosition = mModel.indexFromId(destinationTab.getId());
-            if (!isValidMovePosition(newPosition)) return;
-            mModel.move(curPosition, newPosition);
-            return;
-        }
-
-        // When move between groups.
-        // Only proceed when all members of the group are moved to target index in TabModel.
-        // Regardless of the size of tab group, one tab(group) only proceeds once here per
-        // move.
-        int lastTabInGroupIndex = newIndex - relatedTabs.size() + 1;
-        if (!relatedTabs.contains(tabModel.getTabAt(lastTabInGroupIndex))) return;
-
-        // Locate current position of the moving tab in TabListModel.
-        Tab currentGroupSelectedTab =
-                TabGroupUtils.getSelectedTabInGroupForTab(mTabModelSelector, tab);
-        int curPosition = mModel.indexFromId(currentGroupSelectedTab.getId());
-        if (!isValidMovePosition(curPosition)) return;
-
-        // Find the tab which was in the destination index before this move. Use that tab to
-        // figure out the new position.
-        int destinationTabIndex =
-                newIndex > curIndex ? newIndex - relatedTabs.size() : newIndex + 1;
-        Tab destinationTab = tabModel.getTabAt(destinationTabIndex);
-        Tab destinationGroupSelectedTab =
-                TabGroupUtils.getSelectedTabInGroupForTab(mTabModelSelector, destinationTab);
-        int newPosition = mModel.indexFromId(destinationGroupSelectedTab.getId());
-        if (!isValidMovePosition(newPosition)) return;
-
-        mModel.move(curPosition, newPosition);
     }
 
     private boolean isValidMovePosition(int position) {
@@ -613,6 +630,14 @@
             mTabModelSelector.getTabModelFilterProvider().removeTabModelFilterObserver(
                     mTabModelObserver);
         }
+        if (mTabGroupObserver != null) {
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                     false))
+                    .removeTabGroupObserver(mTabGroupObserver);
+            ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                     true))
+                    .removeTabGroupObserver(mTabGroupObserver);
+        }
         if (mComponentCallbacks != null) {
             ContextUtils.getApplicationContext().unregisterComponentCallbacks(mComponentCallbacks);
         }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 4631062..954cef6 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -100,6 +100,8 @@
     ArgumentCaptor<TabObserver> mTabObserverCaptor;
     @Captor
     ArgumentCaptor<Callback<Drawable>> mCallbackCaptor;
+    @Captor
+    ArgumentCaptor<TabGroupModelFilter.Observer> mTabGroupModelFilterObserverCaptor;
 
     private Tab mTab1;
     private Tab mTab2;
@@ -432,7 +434,7 @@
 
     @Test
     public void tabMovementWithGroup_Forward() {
-        initAndAssertAllProperties();
+        setUpForTabGroupOperation();
 
         // Assume that moveTab in TabModel is finished.
         doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
@@ -443,7 +445,7 @@
         assertThat(mModel.get(1).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
         assertThat(mModel.get(1).get(TabProperties.TITLE), equalTo(TAB2_TITLE));
 
-        mTabModelObserverCaptor.getValue().didMoveTab(mTab2, POSITION1, POSITION2);
+        mTabGroupModelFilterObserverCaptor.getValue().didMoveTabGroup(mTab2, POSITION2, POSITION1);
 
         assertThat(mModel.size(), equalTo(2));
         assertThat(mModel.get(0).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
@@ -452,7 +454,7 @@
 
     @Test
     public void tabMovementWithGroup_Backward() {
-        initAndAssertAllProperties();
+        setUpForTabGroupOperation();
 
         // Assume that moveTab in TabModel is finished.
         doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
@@ -463,7 +465,7 @@
         assertThat(mModel.get(1).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
         assertThat(mModel.get(1).get(TabProperties.TITLE), equalTo(TAB2_TITLE));
 
-        mTabModelObserverCaptor.getValue().didMoveTab(mTab1, POSITION2, POSITION1);
+        mTabGroupModelFilterObserverCaptor.getValue().didMoveTabGroup(mTab1, POSITION1, POSITION2);
 
         assertThat(mModel.size(), equalTo(2));
         assertThat(mModel.get(0).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
@@ -472,7 +474,7 @@
 
     @Test
     public void tabMovementWithinGroup_Forward() {
-        initAndAssertAllProperties();
+        setUpForTabGroupOperation();
 
         // Assume that moveTab in TabModel is finished.
         doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
@@ -485,7 +487,8 @@
         assertThat(mModel.get(1).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
         assertThat(mModel.get(1).get(TabProperties.TITLE), equalTo(TAB2_TITLE));
 
-        mTabModelObserverCaptor.getValue().didMoveTab(mTab2, POSITION1, POSITION2);
+        mTabGroupModelFilterObserverCaptor.getValue().didMoveWithinGroup(
+                mTab2, POSITION2, POSITION1);
 
         assertThat(mModel.size(), equalTo(2));
         assertThat(mModel.get(0).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
@@ -494,7 +497,7 @@
 
     @Test
     public void tabMovementWithinGroup_Backward() {
-        initAndAssertAllProperties();
+        setUpForTabGroupOperation();
 
         // Assume that moveTab in TabModel is finished.
         doReturn(mTab1).when(mTabModel).getTabAt(POSITION2);
@@ -507,7 +510,8 @@
         assertThat(mModel.get(1).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
         assertThat(mModel.get(1).get(TabProperties.TITLE), equalTo(TAB2_TITLE));
 
-        mTabModelObserverCaptor.getValue().didMoveTab(mTab1, POSITION2, POSITION1);
+        mTabGroupModelFilterObserverCaptor.getValue().didMoveWithinGroup(
+                mTab1, POSITION1, POSITION2);
 
         assertThat(mModel.size(), equalTo(2));
         assertThat(mModel.get(0).get(TabProperties.TAB_ID), equalTo(TAB2_ID));
@@ -570,4 +574,19 @@
         doReturn(position).when(viewHolder).getAdapterPosition();
         return viewHolder;
     }
+
+    private void setUpForTabGroupOperation() {
+        doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
+        doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getTabModelFilter(true);
+        doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getTabModelFilter(false);
+        doNothing()
+                .when(mTabGroupModelFilter)
+                .addTabGroupObserver(mTabGroupModelFilterObserverCaptor.capture());
+
+        mMediator = new TabListMediator(mModel, mTabModelSelector,
+                mTabContentManager::getTabThumbnailWithCallback, null, mTabListFaviconProvider,
+                false, null, null, getClass().getSimpleName());
+
+        initAndAssertAllProperties();
+    }
 }
diff --git a/chrome/android/java/res/layout/navigation_bubble.xml b/chrome/android/java/res/layout/navigation_bubble.xml
new file mode 100644
index 0000000..a9ca81d
--- /dev/null
+++ b/chrome/android/java/res/layout/navigation_bubble.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 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.
+ -->
+
+<org.chromium.chrome.browser.gesturenav.NavigationBubble
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:minHeight="@dimen/navigation_bubble_default_height"
+    android:gravity="center_vertical"
+    android:orientation="horizontal">
+
+    <org.chromium.ui.widget.ChromeImageView
+        android:id="@+id/navigation_bubble_icon"
+        android:layout_width="@dimen/navigation_bubble_icon_size"
+        android:layout_height="@dimen/navigation_bubble_icon_size"
+        android:layout_marginStart="@dimen/navigation_bubble_icon_leading_margin" />
+    <TextView
+        android:id="@+id/navigation_bubble_text"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:paddingStart="@dimen/navigation_bubble_horizontal_padding"
+        android:paddingTop="@dimen/navigation_bubble_vertical_padding"
+        android:paddingEnd="@dimen/navigation_bubble_horizontal_padding"
+        android:paddingBottom="@dimen/navigation_bubble_vertical_padding"
+        android:textAppearance="@style/TextAppearance.BlackTitle2"
+        android:maxLines="1"
+        android:ellipsize="end" />
+</org.chromium.chrome.browser.gesturenav.NavigationBubble>
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index bee945a..c152412 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -181,6 +181,11 @@
     <color name="payments_section_edit_background">@color/modern_secondary_color</color>
     <color name="payments_section_chevron">#B2B2B2</color>
 
+    <!-- History Navigation UI colors -->
+    <color name="navigation_bubble_stroke_color">@color/hairline_stroke_color</color>
+    <color name="navigation_bubble_background_color">@color/default_bg_color_elev_4</color>
+    <color name="navigation_bubble_ripple_color">@color/modern_grey_800</color>
+
     <!-- Other colors -->
     <color name="media_viewer_bg">#000000</color>
     <color name="image_viewer_bg">#0E0E0E</color>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index bd8b03c..1ac2935 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -598,4 +598,11 @@
     <dimen name="radio_button_with_description_lateral_padding">16dp</dimen>
     <dimen name="radio_button_with_description_vertical_padding">10dp</dimen>
 
+    <!-- History Navigation UI Item -->
+    <dimen name="navigation_bubble_border_width">1dp</dimen>
+    <dimen name="navigation_bubble_default_height">32dp</dimen>
+    <dimen name="navigation_bubble_vertical_padding">4dp</dimen>
+    <dimen name="navigation_bubble_horizontal_padding">8dp</dimen>
+    <dimen name="navigation_bubble_icon_size">20dp</dimen>
+    <dimen name="navigation_bubble_icon_leading_margin">8dp</dimen>
 </resources>
diff --git a/chrome/android/java/res_night/values-night/colors.xml b/chrome/android/java/res_night/values-night/colors.xml
index 1fbdf9c..0685d50 100644
--- a/chrome/android/java/res_night/values-night/colors.xml
+++ b/chrome/android/java/res_night/values-night/colors.xml
@@ -31,4 +31,8 @@
     <!-- Photo Picker colors -->
     <color name="photo_picker_tile_bg_color">@color/modern_grey_800</color>
     <color name="photo_picker_special_tile_bg_color">@color/modern_grey_800</color>
+
+    <!-- History Navigation UI colors -->
+    <color name="navigation_bubble_ripple_color">@android:color/white</color>
+
 </resources>
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 4edabe3..d2e46a9c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -57,6 +57,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate;
 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
+import org.chromium.chrome.browser.appmenu.AppMenuBlocker;
 import org.chromium.chrome.browser.appmenu.AppMenuDelegate;
 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegateImpl;
@@ -200,7 +201,8 @@
         extends AsyncInitializationActivity
         implements TabCreatorManager, AccessibilityStateChangeListener, PolicyChangeListener,
                    ContextualSearchTabPromotionDelegate, SnackbarManageable, SceneChangeObserver,
-                   StatusBarColorController.StatusBarColorProvider, AppMenuDelegate {
+                   StatusBarColorController.StatusBarColorProvider, AppMenuDelegate,
+                   AppMenuBlocker {
     /**
      * No control container to inflate during initialization.
      */
@@ -1538,7 +1540,7 @@
 
     @CallSuper
     @Override
-    public boolean shouldShowAppMenu() {
+    public boolean canShowAppMenu() {
         if (isActivityFinishingOrDestroyed()) return false;
 
         @ActivityState
@@ -1548,22 +1550,6 @@
             return false;
         }
 
-        // TODO(https://crbug.com/956260): Move UI state related logic out of ChromeActivity.
-
-        // Do not show the menu if Contextual Search panel is opened.
-        if (mContextualSearchManager != null && mContextualSearchManager.isSearchPanelOpened()) {
-            return false;
-        }
-
-        if (getEphemeralTabPanel() != null && getEphemeralTabPanel().isPanelOpened()) {
-            return false;
-        }
-
-        // Do not show the menu if we are in find in page view.
-        if (mFindToolbarManager != null && mFindToolbarManager.isShowing() && !isTablet()) {
-            return false;
-        }
-
         return true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 352cf81..e128b8a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -2256,7 +2256,7 @@
     // App Menu related code -----------------------------------------------------------------------
 
     @Override
-    public boolean shouldShowAppMenu() {
+    public boolean canShowAppMenu() {
         // The popup menu relies on the model created during the full UI initialization, so do not
         // attempt to show the menu until the UI creation has finished.
         if (!mUIWithNativeInitialized) return false;
@@ -2266,7 +2266,7 @@
         Tab tab = getActivityTab();
         if (tab != null && TabModalPresenter.isDialogShowing(tab)) return false;
 
-        return super.shouldShowAppMenu();
+        return super.canShowAppMenu();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuBlocker.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuBlocker.java
new file mode 100644
index 0000000..78797206
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuBlocker.java
@@ -0,0 +1,16 @@
+// Copyright 2019 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.chrome.browser.appmenu;
+
+/**
+ * An interface that may be used to block the app menu from showing (e.g. when other conflicting UI
+ * is showing). To register, see {@link AppMenuCoordinator#registerAppMenuBlocker(AppMenuBlocker)}.
+ */
+public interface AppMenuBlocker {
+    /**
+     * @return Whether the app menu can be shown.
+     */
+    boolean canShowAppMenu();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinator.java
index decad4e..e78a365 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinator.java
@@ -26,4 +26,15 @@
      * @return The {@link AppMenuPropertiesDelegate} associated with this activity.
      */
     AppMenuPropertiesDelegate getAppMenuPropertiesDelegate();
+
+    /**
+     * Registers an {@link AppMenuBlocker} used to help determine whether the app menu can be shown.
+     * @param blocker An {@link AppMenuBlocker} to check before attempting to show the app menu.
+     */
+    void registerAppMenuBlocker(AppMenuBlocker blocker);
+
+    /**
+     * @param blocker The {@link AppMenuBlocker} to unregister.
+     */
+    void unregisterAppMenuBlocker(AppMenuBlocker blocker);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java
index f0a1e1f..c7fd9d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuCoordinatorImpl.java
@@ -118,7 +118,7 @@
 
     @Override
     public void showAppMenuForKeyboardEvent() {
-        if (mAppMenuHandler == null || !mAppMenuDelegate.shouldShowAppMenu()) return;
+        if (mAppMenuHandler == null || !mAppMenuHandler.shouldShowAppMenu()) return;
 
         boolean hasPermanentMenuKey = ViewConfiguration.get(mContext).hasPermanentMenuKey();
         mAppMenuHandler.showAppMenu(
@@ -136,6 +136,16 @@
         return mAppMenuPropertiesDelegate;
     }
 
+    @Override
+    public void registerAppMenuBlocker(AppMenuBlocker blocker) {
+        mAppMenuHandler.registerAppMenuBlocker(blocker);
+    }
+
+    @Override
+    public void unregisterAppMenuBlocker(AppMenuBlocker blocker) {
+        mAppMenuHandler.unregisterAppMenuBlocker(blocker);
+    }
+
     // Testing methods
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDelegate.java
index 85469c8..838c17e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDelegate.java
@@ -23,9 +23,4 @@
      *         should be using.
      */
     AppMenuPropertiesDelegate createAppMenuPropertiesDelegate();
-
-    /**
-     * @return Whether the app menu should be shown.
-     */
-    boolean shouldShowAppMenu();
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandlerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandlerImpl.java
index cdfa8ce..349910e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandlerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandlerImpl.java
@@ -31,6 +31,7 @@
 import org.chromium.chrome.browser.widget.textbubble.TextBubble;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Object responsible for handling the creation, showing, hiding of the AppMenu and notifying the
@@ -42,7 +43,8 @@
     private AppMenu mAppMenu;
     private AppMenuDragHelper mAppMenuDragHelper;
     private Menu mMenu;
-    private final ArrayList<AppMenuObserver> mObservers;
+    private final List<AppMenuBlocker> mBlockers;
+    private final List<AppMenuObserver> mObservers;
     private final int mMenuResourceId;
     private final View mHardwareButtonMenuAnchor;
 
@@ -86,6 +88,7 @@
         mAppMenuDelegate = appMenuDelegate;
         mDelegate = delegate;
         mDecorView = decorView;
+        mBlockers = new ArrayList<>();
         mObservers = new ArrayList<>();
         mMenuResourceId = menuResourceId;
         mHardwareButtonMenuAnchor = mDecorView.findViewById(R.id.menu_anchor_stub);
@@ -163,7 +166,7 @@
     // TODO(crbug.com/635567): Fix this properly.
     @SuppressLint("ResourceType")
     boolean showAppMenu(View anchorView, boolean startDragging, boolean showFromBottom) {
-        if (!mAppMenuDelegate.shouldShowAppMenu() || isAppMenuShowing()) return false;
+        if (!shouldShowAppMenu() || isAppMenuShowing()) return false;
 
         TextBubble.dismissBubbles();
         boolean isByPermanentButton = false;
@@ -343,4 +346,26 @@
     void onFooterViewInflated(View view) {
         if (mDelegate != null) mDelegate.onFooterViewInflated(this, view);
     }
+
+    /**
+     * Registers an {@link AppMenuBlocker} used to help determine whether the app menu can be shown.
+     * @param blocker An {@link AppMenuBlocker} to check before attempting to show the app menu.
+     */
+    void registerAppMenuBlocker(AppMenuBlocker blocker) {
+        if (!mBlockers.contains(blocker)) mBlockers.add(blocker);
+    }
+
+    /**
+     * @param blocker The {@link AppMenuBlocker} to unregister.
+     */
+    void unregisterAppMenuBlocker(AppMenuBlocker blocker) {
+        mBlockers.remove(blocker);
+    }
+
+    boolean shouldShowAppMenu() {
+        for (int i = 0; i < mBlockers.size(); i++) {
+            if (!mBlockers.get(i).canShowAppMenu()) return false;
+        }
+        return true;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java
new file mode 100644
index 0000000..ef7e18bd
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java
@@ -0,0 +1,29 @@
+// Copyright 2019 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.chrome.browser.content_capture;
+
+import org.chromium.chrome.browser.history.HistoryDeletionBridge;
+import org.chromium.chrome.browser.history.HistoryDeletionInfo;
+import org.chromium.components.content_capture.ContentCaptureController;
+
+/** History deletion observer that calls ContentCapture methods. */
+public class ContentCaptureHistoryDeletionObserver implements HistoryDeletionBridge.Observer {
+    /** Observer method when a bit of history is deleted. */
+    @Override
+    public void onURLsDeleted(HistoryDeletionInfo historyDeletionInfo) {
+        ContentCaptureController contentCaptureController = ContentCaptureController.getInstance();
+        if (contentCaptureController == null) return;
+
+        if (historyDeletionInfo.isTimeRangeForAllTime()
+                || (historyDeletionInfo.isTimeRangeValid()
+                        && historyDeletionInfo.getTimeRangeBegin()
+                                != historyDeletionInfo.getTimeRangeEnd())) {
+            contentCaptureController.clearAllContentCaptureData();
+        } else {
+            contentCaptureController.clearContentCaptureDataForURLs(
+                    historyDeletionInfo.getDeletedURLs());
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/content_capture/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/content_capture/OWNERS
new file mode 100644
index 0000000..2d0a24b
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/content_capture/OWNERS
@@ -0,0 +1,4 @@
+tedchoc@chromium.org
+wylieb@chomium.org
+
+file://components/content_capture/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index d09918b..bb99ba7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -704,10 +704,10 @@
     }
 
     @Override
-    public boolean shouldShowAppMenu() {
+    public boolean canShowAppMenu() {
         if (getActivityTab() == null || !getToolbarManager().isInitialized()) return false;
 
-        return super.shouldShowAppMenu();
+        return super.canShowAppMenu();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/ArrowChipView.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/ArrowChipView.java
deleted file mode 100644
index d38eb98..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/ArrowChipView.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 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.chrome.browser.gesturenav;
-
-import android.content.Context;
-import android.support.annotation.StyleRes;
-import android.view.View;
-import android.view.animation.Animation.AnimationListener;
-
-import org.chromium.chrome.R;
-import org.chromium.ui.widget.ChipView;
-
-/**
- * A widget for arrow and an optional 'close chrome' indicator used for overscroll navigation.
- * TODO(jinsukkim): Implement this using a custom view.
- */
-public final class ArrowChipView extends ChipView {
-    private AnimationListener mListener;
-
-    public ArrowChipView(Context context, @StyleRes int style) {
-        super(context, style);
-        getPrimaryTextView().setText(context.getResources().getString(
-                R.string.overscroll_navigation_close_chrome, context.getString(R.string.app_name)));
-        getPrimaryTextView().setVisibility(View.GONE);
-    }
-
-    /**
-     * Sets {@link AnimationListener} used when the widget disappears at the end of
-     * user gesture.
-     * @param listener Listener object.
-     */
-    public void setAnimationListener(AnimationListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * @return {@code true} if the widget is showing the close chrome indicator text.
-     */
-    public boolean isShowingCaption() {
-        return getPrimaryTextView().getVisibility() == View.VISIBLE;
-    }
-
-    /**
-     * Shows or hides the close chrome indicator.
-     * @param on {@code true} if the indicator should appear.
-     */
-    public void showCaption(boolean on) {
-        if (on && !isShowingCaption()) {
-            getPrimaryTextView().setVisibility(View.VISIBLE);
-            // Measure the width again after the indicator text becomes visible.
-            measure(0, 0);
-        } else if (!on && isShowingCaption()) {
-            getPrimaryTextView().setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    public void onAnimationStart() {
-        super.onAnimationStart();
-        if (mListener != null) {
-            mListener.onAnimationStart(getAnimation());
-        }
-    }
-
-    @Override
-    public void onAnimationEnd() {
-        super.onAnimationEnd();
-        if (mListener != null) {
-            mListener.onAnimationEnd(getAnimation());
-        }
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java
new file mode 100644
index 0000000..3f36639
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java
@@ -0,0 +1,128 @@
+// Copyright 2019 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.chrome.browser.gesturenav;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation.AnimationListener;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.chrome.R;
+import org.chromium.ui.widget.ChromeImageView;
+import org.chromium.ui.widget.RippleBackgroundHelper;
+
+/**
+ * View class for a bubble used in gesture navigation UI that consists of an icon
+ * and an optional text.
+ */
+public class NavigationBubble extends LinearLayout {
+    private final RippleBackgroundHelper mRippleBackgroundHelper;
+
+    private ChromeImageView mIcon;
+    private TextView mText;
+    private AnimationListener mListener;
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public NavigationBubble(Context context) {
+        this(context, null);
+    }
+
+    public NavigationBubble(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        // Reset icon and background. Height is used as corner radius to ensure we have a circle.
+        mRippleBackgroundHelper = new RippleBackgroundHelper(this,
+                R.color.navigation_bubble_background_color, R.color.navigation_bubble_ripple_color,
+                getResources().getDimensionPixelSize(R.dimen.navigation_bubble_default_height),
+                R.color.navigation_bubble_stroke_color, R.dimen.navigation_bubble_border_width);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIcon = findViewById(R.id.navigation_bubble_icon);
+        mText = findViewById(R.id.navigation_bubble_text);
+    }
+
+    /**
+     * Sets {@link AnimationListener} used when the widget disappears at the end of
+     * user gesture.
+     * @param listener Listener object.
+     */
+    public void setAnimationListener(AnimationListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * @return {@code true} if the widget is showing the close chrome indicator text.
+     */
+    public boolean isShowingCaption() {
+        return getTextView().getVisibility() == View.VISIBLE;
+    }
+
+    /**
+     * Shows or hides the close chrome indicator.
+     * @param on {@code true} if the indicator should appear.
+     */
+    public void showCaption(boolean on) {
+        if (on && !isShowingCaption()) {
+            getTextView().setVisibility(View.VISIBLE);
+            // Measure the width again after the indicator text becomes visible.
+            measure(0, 0);
+        } else if (!on && isShowingCaption()) {
+            getTextView().setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public void onAnimationStart() {
+        super.onAnimationStart();
+        if (mListener != null) {
+            mListener.onAnimationStart(getAnimation());
+        }
+    }
+
+    @Override
+    public void onAnimationEnd() {
+        super.onAnimationEnd();
+        if (mListener != null) {
+            mListener.onAnimationEnd(getAnimation());
+        }
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        if (mRippleBackgroundHelper != null) {
+            mRippleBackgroundHelper.onDrawableStateChanged();
+        }
+    }
+
+    /**
+     * Sets the icon at the start of the icon view.
+     * @param icon The resource id pointing to the icon.
+     */
+    public void setIcon(@DrawableRes int icon) {
+        mIcon.setVisibility(ViewGroup.VISIBLE);
+        mIcon.setImageResource(icon);
+
+        // Sets the correct tinting on the icon.
+        ApiCompatibilityUtils.setImageTintList(mIcon, mText.getTextColors());
+    }
+
+    /**
+     * Returns the {@link TextView} that contains the label of the widget.
+     * @return A {@link TextView}.
+     */
+    public TextView getTextView() {
+        return mText;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
index 5029bc55..a1f71f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.support.annotation.IntDef;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AlphaAnimation;
@@ -99,7 +100,7 @@
     // True while side gesture is in progress.
     private boolean mIsBeingDragged;
 
-    private ArrowChipView mArrowView;
+    private NavigationBubble mArrowView;
     private int mArrowViewWidth;
 
     // Start position for animation moving the UI back to original offset.
@@ -158,7 +159,11 @@
         final float density = getResources().getDisplayMetrics().density;
         mCircleWidth = (int) (CIRCLE_DIAMETER_DP * density);
 
-        mArrowView = new ArrowChipView(getContext(), R.style.AssistiveChip);
+        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+        mArrowView = (NavigationBubble) layoutInflater.inflate(R.layout.navigation_bubble, null);
+        mArrowView.getTextView().setText(
+                getResources().getString(R.string.overscroll_navigation_close_chrome,
+                        getContext().getString(R.string.app_name)));
         mArrowViewWidth = mCircleWidth;
         addView(mArrowView);
 
@@ -226,8 +231,7 @@
     public void setDirection(boolean forward) {
         mIsForward = forward;
         mArrowView.setIcon(
-                forward ? R.drawable.ic_arrow_forward_blue_24dp : R.drawable.ic_arrow_back_24dp,
-                true);
+                forward ? R.drawable.ic_arrow_forward_blue_24dp : R.drawable.ic_arrow_back_24dp);
     }
 
     public void setEnableCloseIndicator(boolean enable) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index 1d56f507..6d7b883 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -44,11 +44,13 @@
 import org.chromium.chrome.browser.banners.AppBannerManager;
 import org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProvider;
 import org.chromium.chrome.browser.contacts_picker.ContactsPickerDialog;
+import org.chromium.chrome.browser.content_capture.ContentCaptureHistoryDeletionObserver;
 import org.chromium.chrome.browser.crash.LogcatExtractionRunnable;
 import org.chromium.chrome.browser.crash.MinidumpUploadService;
 import org.chromium.chrome.browser.download.DownloadController;
 import org.chromium.chrome.browser.download.DownloadManagerService;
 import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor;
+import org.chromium.chrome.browser.history.HistoryDeletionBridge;
 import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory;
 import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator;
 import org.chromium.chrome.browser.incognito.IncognitoTabLauncher;
@@ -265,6 +267,8 @@
         });
 
         SearchWidgetProvider.initialize();
+        HistoryDeletionBridge.getInstance().addObserver(
+                new ContentCaptureHistoryDeletionObserver());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
index a31b9f5..bec2314 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
@@ -9,6 +9,7 @@
 import android.support.annotation.NonNull;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
@@ -43,6 +44,29 @@
     private static SharedPreferences sPref;
 
     /**
+     * An interface to be notified about changes to a {@link TabGroupModelFilter}.
+     */
+    public interface Observer {
+        /**
+         * This method is called after a group is moved.
+         *
+         * @param movedTab The tab which has been moved. This is the last tab within the group.
+         * @param tabModelOldIndex The old index of the {@code movedTab} in the {@link TabModel}.
+         * @param tabModelNewIndex The new index of the {@code movedTab} in the {@link TabModel}.
+         */
+        void didMoveTabGroup(Tab movedTab, int tabModelOldIndex, int tabModelNewIndex);
+
+        /**
+         * This method is called after a tab within a group is moved.
+         *
+         * @param movedTab The tab which has been moved.
+         * @param tabModelOldIndex The old index of the {@code movedTab} in the {@link TabModel}.
+         * @param tabModelNewIndex The new index of the {@code movedTab} in the {@link TabModel}.
+         */
+        void didMoveWithinGroup(Tab movedTab, int tabModelOldIndex, int tabModelNewIndex);
+    }
+
+    /**
      * This class is a representation of a group of tabs. It knows the last selected tab within the
      * group.
      */
@@ -108,6 +132,7 @@
             return ids.get(position - 1);
         }
     }
+    private ObserverList<Observer> mGroupFilterObserver = new ObserverList<>();
     private Map<Integer, Integer> mGroupIdToGroupIndexMap = new HashMap<>();
     private Map<Integer, TabGroup> mGroupIdToGroupMap = new HashMap<>();
     private int mCurrentGroupIndex = TabList.INVALID_TAB_INDEX;
@@ -132,6 +157,22 @@
     }
 
     /**
+     * This method adds a {@link Observer} to be notified on {@link TabGroupModelFilter} changes.
+     * @param observer The {@link Observer} to add.
+     */
+    public void addTabGroupObserver(Observer observer) {
+        mGroupFilterObserver.addObserver(observer);
+    }
+
+    /**
+     * This method removes a {@link Observer}.
+     * @param observer The {@link Observer} to remove.
+     */
+    public void removeTabGroupObserver(Observer observer) {
+        mGroupFilterObserver.removeObserver(observer);
+    }
+
+    /**
      * @return Number of {@link TabGroup}s that has at least two tabs.
      */
     public int getTabGroupCount() {
@@ -340,6 +381,38 @@
         return mAbsentSelectedTab == null;
     }
 
+    @Override
+    public void didMoveTab(Tab tab, int newIndex, int curIndex) {
+        super.didMoveTab(tab, newIndex, curIndex);
+
+        if (isMoveWithinGroup(tab, curIndex, newIndex)) {
+            for (Observer observer : mGroupFilterObserver) {
+                observer.didMoveWithinGroup(tab, curIndex, newIndex);
+            }
+        } else {
+            if (!hasFinishedMovingGroup(tab, newIndex)) return;
+            for (Observer observer : mGroupFilterObserver) {
+                observer.didMoveTabGroup(tab, curIndex, newIndex);
+            }
+        }
+    }
+
+    private boolean isMoveWithinGroup(
+            Tab movedTab, int oldIndexInTabModel, int newIndexInTabModel) {
+        int startIndex = Math.min(oldIndexInTabModel, newIndexInTabModel);
+        int endIndex = Math.max(oldIndexInTabModel, newIndexInTabModel);
+        for (int i = startIndex; i <= endIndex; i++) {
+            if (getTabModel().getTabAt(i).getRootId() != movedTab.getRootId()) return false;
+        }
+        return true;
+    }
+
+    private boolean hasFinishedMovingGroup(Tab movedTab, int newIndexInTabModel) {
+        TabGroup tabGroup = mGroupIdToGroupMap.get(movedTab.getRootId());
+        int offsetIndex = Math.abs(newIndexInTabModel - tabGroup.size() + 1);
+        return tabGroup.contains(getTabModel().getTabAt(offsetIndex).getId());
+    }
+
     // TabList implementation.
     @Override
     public boolean isIncognito() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 00b0e0cd..91d0c53 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -1803,7 +1803,7 @@
 
         if (inTabSwitcherMode) {
             if (mUrlFocusLayoutAnimator != null && mUrlFocusLayoutAnimator.isRunning()) {
-                mUrlFocusLayoutAnimator.end();
+                mUrlFocusLayoutAnimator.cancel();
                 mUrlFocusLayoutAnimator = null;
                 // After finishing the animation, force a re-layout of the location bar,
                 // so that the final translation position is correct (since onMeasure updates
@@ -2106,7 +2106,7 @@
 
     private void triggerUrlFocusAnimation(final boolean hasFocus) {
         if (mUrlFocusLayoutAnimator != null && mUrlFocusLayoutAnimator.isRunning()) {
-            mUrlFocusLayoutAnimator.end();
+            mUrlFocusLayoutAnimator.cancel();
             mUrlFocusLayoutAnimator = null;
         }
         if (mExperimentalButtonAnimationRunning) mExperimentalButtonAnimator.end();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 2b23ced..a83dcc9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -9,10 +9,12 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.appmenu.AppMenuBlocker;
 import org.chromium.chrome.browser.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.appmenu.AppMenuCoordinatorFactory;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
+import org.chromium.ui.base.DeviceFormFactor;
 
 /**
  * The root UI coordinator. This class will eventually be responsible for inflating and managing
@@ -21,8 +23,9 @@
  * The specific things this component will manage and how it will hook into Chrome*Activity are
  * still being discussed See https://crbug.com/931496.
  */
-public class RootUiCoordinator
-        implements Destroyable, InflationObserver, ChromeActivity.MenuOrKeyboardActionHandler {
+public class RootUiCoordinator implements Destroyable, InflationObserver,
+                                          ChromeActivity.MenuOrKeyboardActionHandler,
+                                          AppMenuBlocker {
     protected ChromeActivity mActivity;
     protected @Nullable AppMenuCoordinator mAppMenuCoordinator;
 
@@ -41,7 +44,11 @@
     public void destroy() {
         mActivity.unregisterMenuOrKeyboardActionHandler(this);
         mActivity = null;
-        if (mAppMenuCoordinator != null) mAppMenuCoordinator.destroy();
+        if (mAppMenuCoordinator != null) {
+            mAppMenuCoordinator.unregisterAppMenuBlocker(this);
+            mAppMenuCoordinator.unregisterAppMenuBlocker(mActivity);
+            mAppMenuCoordinator.destroy();
+        }
     }
 
     @Override
@@ -59,6 +66,8 @@
             mActivity.getToolbarManager().onAppMenuInitialized(
                     mAppMenuCoordinator.getAppMenuHandler(),
                     mAppMenuCoordinator.getAppMenuPropertiesDelegate());
+            mAppMenuCoordinator.registerAppMenuBlocker(this);
+            mAppMenuCoordinator.registerAppMenuBlocker(mActivity);
         } else if (mActivity.getToolbarManager() != null) {
             mActivity.getToolbarManager().getToolbar().disableMenuButton();
         }
@@ -78,4 +87,30 @@
     public AppMenuCoordinator getAppMenuCoordinatorForTesting() {
         return mAppMenuCoordinator;
     }
+
+    @Override
+    public boolean canShowAppMenu() {
+        // TODO(https:crbug.com/931496): Eventually the ContextualSearchManager, EphemeralTabPanel,
+        // and FindToolbarManager will all be owned by this class.
+
+        // Do not show the menu if Contextual Search panel is opened.
+        if (mActivity.getContextualSearchManager() != null
+                && mActivity.getContextualSearchManager().isSearchPanelOpened()) {
+            return false;
+        }
+
+        if (mActivity.getEphemeralTabPanel() != null
+                && mActivity.getEphemeralTabPanel().isPanelOpened()) {
+            return false;
+        }
+
+        // Do not show the menu if we are in find in page view.
+        if (mActivity.getFindToolbarManager() != null
+                && mActivity.getFindToolbarManager().isShowing()
+                && !DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity)) {
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
index 8becdfde..2ad329f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
@@ -20,6 +20,7 @@
 import org.chromium.base.UserData;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.WebContents;
@@ -71,6 +72,11 @@
             webContents.onHide();
         }
 
+        InfoBarContainer infoBarContainer = InfoBarContainer.get(mTab);
+        if (infoBarContainer != null) {
+            infoBarContainer.setHidden(true);
+        }
+
         if (isViewAttached()) {
             updateFqdnText();
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
index e719ec14..7ab5512 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
@@ -17,6 +17,7 @@
 import android.os.Bundle;
 import android.support.annotation.IntDef;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
@@ -38,6 +39,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -62,28 +64,39 @@
         private static final int PARAM_TEXT_INDEX = 2;
         private static final int PARAM_URL_INDEX = 3;
         private String[] mData;
+        private boolean mIsShareMethodPost;
+        private boolean mIsShareEncTypeMultipart;
+        private String[] mFileNames;
+        private String[][] mFileAccepts;
 
         public ShareTarget() {
-            this(null, null, null, null);
+            this(null, null, null, null, false, false, null, null);
         }
 
-        public ShareTarget(String action, String paramTitle, String paramText, String paramUrl) {
+        public ShareTarget(String action, String paramTitle, String paramText, String paramUrl,
+                boolean isMethodPost, boolean isEncTypeMultipart, String[] fileNames,
+                String[][] fileAccepts) {
             mData = new String[4];
             mData[ACTION_INDEX] = replaceNullWithEmpty(action);
             mData[PARAM_TITLE_INDEX] = replaceNullWithEmpty(paramTitle);
             mData[PARAM_TEXT_INDEX] = replaceNullWithEmpty(paramText);
             mData[PARAM_URL_INDEX] = replaceNullWithEmpty(paramUrl);
+            mIsShareMethodPost = isMethodPost;
+            mIsShareEncTypeMultipart = isEncTypeMultipart;
+
+            mFileNames = fileNames != null ? fileNames : new String[0];
+            mFileAccepts = fileAccepts != null ? fileAccepts : new String[0][];
         }
 
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof ShareTarget)) return false;
-            return Arrays.equals(mData, ((ShareTarget) o).mData);
-        }
-
-        @Override
-        public int hashCode() {
-            return Arrays.hashCode(mData);
+            ShareTarget shareTarget = (ShareTarget) o;
+            return Arrays.equals(mData, shareTarget.mData)
+                    && mIsShareMethodPost == shareTarget.mIsShareMethodPost
+                    && mIsShareEncTypeMultipart == shareTarget.mIsShareEncTypeMultipart
+                    && Arrays.equals(mFileNames, shareTarget.mFileNames)
+                    && Arrays.deepEquals(mFileAccepts, shareTarget.mFileAccepts);
         }
 
         public String getAction() {
@@ -101,6 +114,22 @@
         public String getParamUrl() {
             return mData[PARAM_URL_INDEX];
         }
+
+        public boolean isShareMethodPost() {
+            return mIsShareMethodPost;
+        }
+
+        public boolean isShareEncTypeMultipart() {
+            return mIsShareEncTypeMultipart;
+        }
+
+        public String[] getFileNames() {
+            return mFileNames;
+        }
+
+        public String[][] getFileAccepts() {
+            return mFileAccepts;
+        }
     }
 
     public static final String RESOURCE_NAME = "name";
@@ -125,6 +154,7 @@
     private String mManifestStartUrl;
     private @WebApkDistributor int mDistributor;
     private ShareTarget mShareTarget;
+    private String mShareTargetActivityName;
     private Map<String, String> mIconUrlToMurmur2HashMap;
     private boolean mIsSplashProvidedByWebApk;
 
@@ -292,7 +322,11 @@
 
         int splashIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.SPLASH_ID, 0);
         Bitmap splashIcon = decodeBitmapFromDrawable(res, splashIconId);
-        ShareTarget shareTarget = extractAndMergeShareTargets(webApkPackageName);
+
+        Pair<String, ShareTarget> shareTargetActivityNameAndData =
+                extractFirstShareTarget(webApkPackageName);
+        String shareTargetActivityName = shareTargetActivityNameAndData.first;
+        ShareTarget shareTarget = shareTargetActivityNameAndData.second;
 
         boolean isSplashProvidedByWebApk =
                 (canUseSplashFromContentProvider && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
@@ -302,13 +336,12 @@
                 new Icon(primaryIcon), new Icon(badgeIcon), new Icon(splashIcon), name, shortName,
                 displayMode, orientation, source, themeColor, backgroundColor, webApkPackageName,
                 shellApkVersion, manifestUrl, manifestStartUrl, distributor,
-                iconUrlToMurmur2HashMap, shareTarget, forceNavigation, isSplashProvidedByWebApk,
-                shareData);
+                iconUrlToMurmur2HashMap, shareTarget, shareTargetActivityName, forceNavigation,
+                isSplashProvidedByWebApk, shareData);
     }
 
     /**
      * Construct a {@link WebApkInfo} instance.
-     *
      * @param id                       ID for the WebAPK.
      * @param url                      URL that the WebAPK should navigate to when launched.
      * @param scope                    Scope for the WebAPK.
@@ -331,7 +364,9 @@
      * @param distributor              The source from where the WebAPK is installed.
      * @param iconUrlToMurmur2HashMap  Map of the WebAPK's icon URLs to Murmur2 hashes of the
      *                                 icon untransformed bytes.
-     * @param shareTarget              Data about WebAPK's share intent handlers.
+     * @param shareTarget              shareTarget data for {@link shareTargetActivityName}
+     * @param shareTargetActivityName  Name of activity or activity alias in WebAPK which handles
+     *                                 share intents
      * @param forceNavigation          Whether the WebAPK should navigate to {@link url} if the
      *                                 WebAPK is already open.
      * @param isSplashProvidedByWebApk Whether the WebAPK (1) launches an internal activity to
@@ -345,7 +380,8 @@
             long backgroundColor, String webApkPackageName, int shellApkVersion, String manifestUrl,
             String manifestStartUrl, @WebApkDistributor int distributor,
             Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
-            boolean forceNavigation, boolean isSplashProvidedByWebApk, ShareData shareData) {
+            String shareTargetActivityName, boolean forceNavigation,
+            boolean isSplashProvidedByWebApk, ShareData shareData) {
         if (id == null || url == null || manifestStartUrl == null || webApkPackageName == null) {
             Log.e(TAG,
                     "Incomplete data provided: " + id + ", " + url + ", " + manifestStartUrl + ", "
@@ -363,8 +399,8 @@
         return new WebApkInfo(id, url, scope, primaryIcon, badgeIcon, splashIcon, name, shortName,
                 displayMode, orientation, source, themeColor, backgroundColor, webApkPackageName,
                 shellApkVersion, manifestUrl, manifestStartUrl, distributor,
-                iconUrlToMurmur2HashMap, shareTarget, forceNavigation, isSplashProvidedByWebApk,
-                shareData);
+                iconUrlToMurmur2HashMap, shareTarget, shareTargetActivityName, forceNavigation,
+                isSplashProvidedByWebApk, shareData);
     }
 
     protected WebApkInfo(String id, String url, String scope, Icon primaryIcon, Icon badgeIcon,
@@ -373,7 +409,8 @@
             String webApkPackageName, int shellApkVersion, String manifestUrl,
             String manifestStartUrl, @WebApkDistributor int distributor,
             Map<String, String> iconUrlToMurmur2HashMap, ShareTarget shareTarget,
-            boolean forceNavigation, boolean isSplashProvidedByWebApk, ShareData shareData) {
+            String shareTargetActivityName, boolean forceNavigation,
+            boolean isSplashProvidedByWebApk, ShareData shareData) {
         super(id, url, scope, primaryIcon, name, shortName, displayMode, orientation, source,
                 themeColor, backgroundColor, null /* splash_screen_url */,
                 false /* isIconGenerated */, false /* isIconAdaptive */, forceNavigation);
@@ -387,11 +424,11 @@
         mIconUrlToMurmur2HashMap = iconUrlToMurmur2HashMap;
         mIsSplashProvidedByWebApk = isSplashProvidedByWebApk;
         mShareData = shareData;
-
         mShareTarget = shareTarget;
         if (mShareTarget == null) {
             mShareTarget = new ShareTarget();
         }
+        mShareTargetActivityName = shareTargetActivityName;
     }
 
     protected WebApkInfo() {}
@@ -415,6 +452,13 @@
         return mShareTarget;
     }
 
+    /**
+     * Returns name of activity or activity alias in WebAPK which handles share intents.
+     */
+    public String shareTargetActivityName() {
+        return mShareTargetActivityName;
+    }
+
     @Override
     public boolean isForWebApk() {
         return true;
@@ -580,8 +624,11 @@
         }
     }
 
-    /** Returns data about the share intent handlers for the given WebAPK. */
-    private static ShareTarget extractAndMergeShareTargets(String webApkPackageName) {
+    /**
+     * Returns the name of activity or activity alias in WebAPK which handles share intents, and
+     * the data about the handler.
+     */
+    private static Pair<String, ShareTarget> extractFirstShareTarget(String webApkPackageName) {
         Intent shareIntent = new Intent();
         shareIntent.setAction(Intent.ACTION_SEND);
         shareIntent.setPackage(webApkPackageName);
@@ -596,16 +643,45 @@
                 continue;
             }
 
-            return new ShareTarget(
+            String shareTargetActivityName = resolveInfo.activityInfo.name;
+
+            String shareAction =
+                    IntentUtils.safeGetString(shareTargetMetaData, WebApkMetaDataKeys.SHARE_ACTION);
+            if (TextUtils.isEmpty(shareAction)) {
+                return new Pair<>(null, new ShareTarget());
+            }
+
+            String encodedFileNames = IntentUtils.safeGetString(
+                    shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_NAMES);
+            String[] fileNames = WebApkShareTargetUtil.decodeJsonStringArray(encodedFileNames);
+
+            String encodedFileAccepts = IntentUtils.safeGetString(
+                    shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_ACCEPTS);
+            String[][] fileAccepts = WebApkShareTargetUtil.decodeJsonAccepts(encodedFileAccepts);
+
+            String shareMethod =
+                    IntentUtils.safeGetString(shareTargetMetaData, WebApkMetaDataKeys.SHARE_METHOD);
+            boolean isShareMethodPost =
+                    shareMethod != null && shareMethod.toUpperCase(Locale.ENGLISH).equals("POST");
+
+            String shareEncType = IntentUtils.safeGetString(
+                    shareTargetMetaData, WebApkMetaDataKeys.SHARE_ENCTYPE);
+            boolean isShareEncTypeMultipart = shareEncType != null
+                    && shareEncType.toLowerCase(Locale.ENGLISH).equals("multipart/form-data");
+
+            ShareTarget target = new ShareTarget(
                     IntentUtils.safeGetString(shareTargetMetaData, WebApkMetaDataKeys.SHARE_ACTION),
                     IntentUtils.safeGetString(
                             shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_TITLE),
                     IntentUtils.safeGetString(
                             shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_TEXT),
                     IntentUtils.safeGetString(
-                            shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_URL));
+                            shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_URL),
+                    isShareMethodPost, isShareEncTypeMultipart, fileNames, fileAccepts);
+
+            return new Pair<>(shareTargetActivityName, target);
         }
-        return null;
+        return new Pair<>(null, new ShareTarget());
     }
 
     /** Returns the value if it is non-null. Returns an empty string otherwise. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java
index 4923cff..e577ec7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java
@@ -12,8 +12,9 @@
 public class WebApkPostShareTargetNavigator {
     public static boolean navigateIfPostShareTarget(
             WebApkInfo webApkInfo, WebContents webContents) {
-        WebApkShareTargetUtil.PostData postData = WebApkShareTargetUtil.computePostData(
-                webApkInfo.webApkPackageName(), webApkInfo.shareData());
+        WebApkShareTargetUtil.PostData postData =
+                WebApkShareTargetUtil.computePostData(webApkInfo.shareTargetActivityName(),
+                        webApkInfo.shareTarget(), webApkInfo.shareData());
         if (postData == null) {
             return false;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java
index e8bad23..21983f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java
@@ -4,13 +4,11 @@
 
 package org.chromium.chrome.browser.webapps;
 
-import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.OpenableColumns;
+import android.text.TextUtils;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -26,13 +24,15 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
 import java.util.Locale;
 
 /**
  * Computes data for Post Share Target.
  */
 public class WebApkShareTargetUtil {
+    private static final String TAG = "WebApkShareTargetUtil";
+
     // A class containing data required to generate a share target post request.
     protected static class PostData {
         public boolean isMultipartEncoding;
@@ -57,32 +57,6 @@
         }
     }
 
-    private static Bundle computeShareTargetMetaData(
-            String apkPackageName, WebApkInfo.ShareData shareData) {
-        if (shareData == null) {
-            return null;
-        }
-        ActivityInfo shareActivityInfo;
-        try {
-            shareActivityInfo =
-                    ContextUtils.getApplicationContext().getPackageManager().getActivityInfo(
-                            new ComponentName(apkPackageName, shareData.shareActivityClassName),
-                            PackageManager.GET_META_DATA);
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-
-        if (shareActivityInfo == null) {
-            return null;
-        }
-        return shareActivityInfo.metaData;
-    }
-
-    private static boolean enctypeFromMetaDataIsMultipart(Bundle metaData) {
-        String enctype = IntentUtils.safeGetString(metaData, WebApkMetaDataKeys.SHARE_ENCTYPE);
-        return enctype != null && "multipart/form-data".equals(enctype.toLowerCase(Locale.ENGLISH));
-    }
-
     private static byte[] readStringFromContentUri(Uri uri) {
         try (InputStream inputStream =
                         ContextUtils.getApplicationContext().getContentResolver().openInputStream(
@@ -114,23 +88,42 @@
         return uri.getPath();
     }
 
-    private static ArrayList<String> decodeJsonStringArray(JSONArray jsonArray)
-            throws JSONException {
-        ArrayList<String> originalData = new ArrayList<>();
-        for (int i = 0; i < jsonArray.length(); i++) {
-            originalData.add(jsonArray.getString(i));
+    public static String[] decodeJsonStringArray(String encodedJsonArray) {
+        if (encodedJsonArray == null) {
+            return null;
         }
-        return originalData;
+
+        try {
+            JSONArray jsonArray = new JSONArray(encodedJsonArray);
+            String[] originalData = new String[jsonArray.length()];
+            for (int i = 0; i < jsonArray.length(); i++) {
+                originalData[i] = jsonArray.getString(i);
+            }
+            return originalData;
+        } catch (JSONException e) {
+        }
+        return null;
     }
 
-    private static ArrayList<ArrayList<String>> decodeJsonAccepts(String string)
-            throws JSONException {
-        JSONArray jsonArray = new JSONArray(string);
-        ArrayList<ArrayList<String>> originalData = new ArrayList<>();
-        for (int i = 0; i < jsonArray.length(); i++) {
-            originalData.add(decodeJsonStringArray(jsonArray.getJSONArray(i)));
+    public static String[][] decodeJsonAccepts(String encodedAcceptsArray) {
+        if (encodedAcceptsArray == null) {
+            return null;
         }
-        return originalData;
+        try {
+            JSONArray jsonArray = new JSONArray(encodedAcceptsArray);
+            String[][] originalData = new String[jsonArray.length()][];
+            for (int i = 0; i < jsonArray.length(); i++) {
+                String[] childArr = new String[jsonArray.getJSONArray(i).length()];
+                for (int j = 0; j < childArr.length; j++) {
+                    childArr[j] = jsonArray.getJSONArray(i).getString(j);
+                }
+                originalData[i] = childArr;
+            }
+            return originalData;
+        } catch (JSONException e) {
+        }
+
+        return null;
     }
 
     protected static boolean methodFromShareTargetMetaDataIsPost(Bundle metaData) {
@@ -138,22 +131,13 @@
         return method != null && "POST".equals(method.toUpperCase(Locale.ENGLISH));
     }
 
-    protected static void addFilesToMultipartPostData(PostData postData, String encodedFileNames,
-            String encodedFileAccepts, ArrayList<Uri> shareFiles) {
-        if (encodedFileNames == null || encodedFileAccepts == null || shareFiles == null) {
+    protected static void addFilesToMultipartPostData(PostData postData, String[] fileNames,
+            String[][] fileAccepts, ArrayList<Uri> shareFiles) {
+        if (fileNames == null || fileAccepts == null || shareFiles == null) {
             return;
         }
 
-        ArrayList<String> names;
-        ArrayList<ArrayList<String>> accepts;
-        try {
-            names = decodeJsonStringArray(new JSONArray(encodedFileNames));
-            accepts = decodeJsonAccepts(encodedFileAccepts);
-        } catch (JSONException e) {
-            return;
-        }
-
-        if (names.size() != accepts.size()) {
+        if (fileNames.length != fileAccepts.length) {
             return;
         }
 
@@ -166,13 +150,14 @@
                     continue;
                 }
 
-                for (int i = 0; i < names.size(); i++) {
-                    List<String> mimeTypeList = accepts.get(i);
-                    MimeTypeFilter mimeTypeFilter = new MimeTypeFilter(mimeTypeList, false);
+                for (int i = 0; i < fileNames.length; i++) {
+                    String[] mimeTypeList = fileAccepts[i];
+                    MimeTypeFilter mimeTypeFilter =
+                            new MimeTypeFilter(Arrays.asList(mimeTypeList), false);
                     if (mimeTypeFilter.accept(fileUri, fileType)) {
                         byte[] fileContent = readStringFromContentUri(fileUri);
                         if (fileContent != null) {
-                            postData.add(names.get(i), fileContent, fileName, fileType);
+                            postData.add(fileNames[i], fileContent, fileName, fileType);
                         }
                         break;
                     }
@@ -181,40 +166,31 @@
         }
     }
 
-    protected static PostData computePostData(
-            String apkPackageName, WebApkInfo.ShareData shareData) {
-        Bundle shareTargetMetaData = computeShareTargetMetaData(apkPackageName, shareData);
-        if (shareTargetMetaData == null
-                || !methodFromShareTargetMetaDataIsPost(shareTargetMetaData)) {
+    protected static PostData computePostData(String shareTargetActivityName,
+            WebApkInfo.ShareTarget shareTarget, WebApkInfo.ShareData shareData) {
+        if (shareTarget == null || !shareTarget.isShareMethodPost() || shareData == null
+                || !shareData.shareActivityClassName.equals(shareTargetActivityName)) {
             return null;
         }
 
-        PostData postData = new PostData(enctypeFromMetaDataIsMultipart(shareTargetMetaData));
+        PostData postData = new PostData(shareTarget.isShareEncTypeMultipart());
 
-        String shareTitleName = IntentUtils.safeGetString(
-                shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_TITLE);
-        if (shareTitleName != null && shareData.subject != null) {
-            postData.add(shareTitleName, ApiCompatibilityUtils.getBytesUtf8(shareData.subject), "",
-                    "text/plain");
+        if (!TextUtils.isEmpty(shareTarget.getParamTitle()) && shareData.subject != null) {
+            postData.add(shareTarget.getParamTitle(),
+                    ApiCompatibilityUtils.getBytesUtf8(shareData.subject), "", "text/plain");
         }
 
-        String shareTextName =
-                IntentUtils.safeGetString(shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_TEXT);
-        if (shareTextName != null && shareData.text != null) {
-            postData.add(shareTextName, ApiCompatibilityUtils.getBytesUtf8(shareData.text), "",
-                    "text/plain");
+        if (!TextUtils.isEmpty(shareTarget.getParamText()) && shareData.text != null) {
+            postData.add(shareTarget.getParamText(),
+                    ApiCompatibilityUtils.getBytesUtf8(shareData.text), "", "text/plain");
         }
 
         if (!postData.isMultipartEncoding) {
             return postData;
         }
 
-        addFilesToMultipartPostData(postData,
-                IntentUtils.safeGetString(
-                        shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_NAMES),
-                IntentUtils.safeGetString(
-                        shareTargetMetaData, WebApkMetaDataKeys.SHARE_PARAM_ACCEPTS),
-                shareData.files);
+        addFilesToMultipartPostData(postData, shareTarget.getFileNames(),
+                shareTarget.getFileAccepts(), shareData.files);
 
         return postData;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
index 75ec95d8..5a87979f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
@@ -100,7 +100,9 @@
             Bitmap primaryIconBitmap, String badgeIconUrl, String badgeIconMurmur2Hash,
             Bitmap badgeIconBitmap, String[] iconUrls, @WebDisplayMode int displayMode,
             int orientation, long themeColor, long backgroundColor, String shareAction,
-            String shareParamsTitle, String shareParamsText, String shareParamsUrl) {
+            String shareParamsTitle, String shareParamsText, String shareParamsUrl,
+            boolean isShareMethodPost, boolean isShareEncTypeMultipart,
+            String[] shareParamsFileNames, String[][] shareParamsAccepts) {
         HashMap<String, String> iconUrlToMurmur2HashMap = new HashMap<String, String>();
         for (String iconUrl : iconUrls) {
             String murmur2Hash = null;
@@ -112,15 +114,19 @@
             iconUrlToMurmur2HashMap.put(iconUrl, murmur2Hash);
         }
 
-        WebApkInfo.ShareTarget shareTarget = new WebApkInfo.ShareTarget(
-                shareAction, shareParamsTitle, shareParamsText, shareParamsUrl);
+        // When share action is empty, we use a default empty share target
+        WebApkInfo.ShareTarget shareTarget = TextUtils.isEmpty(shareAction)
+                ? new WebApkInfo.ShareTarget()
+                : new WebApkInfo.ShareTarget(shareAction, shareParamsTitle, shareParamsText,
+                        shareParamsUrl, isShareMethodPost, isShareEncTypeMultipart,
+                        shareParamsFileNames, shareParamsAccepts);
 
         WebApkInfo info = WebApkInfo.create(mOldInfo.id(), mOldInfo.uri().toString(), scopeUrl,
                 new WebApkInfo.Icon(primaryIconBitmap), new WebApkInfo.Icon(badgeIconBitmap), null,
                 name, shortName, displayMode, orientation, mOldInfo.source(), themeColor,
                 backgroundColor, mOldInfo.webApkPackageName(), mOldInfo.shellApkVersion(),
                 mOldInfo.manifestUrl(), manifestStartUrl, WebApkInfo.WebApkDistributor.BROWSER,
-                iconUrlToMurmur2HashMap, shareTarget, mOldInfo.shouldForceNavigation(),
+                iconUrlToMurmur2HashMap, shareTarget, null, mOldInfo.shouldForceNavigation(),
                 mOldInfo.isSplashProvidedByWebApk(), null);
         mObserver.onGotManifestData(info, primaryIconUrl, badgeIconUrl);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index b5aeb736..d66d815 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -378,8 +378,10 @@
                 info.displayMode(), info.orientation(), info.themeColor(), info.backgroundColor(),
                 info.shareTarget().getAction(), info.shareTarget().getParamTitle(),
                 info.shareTarget().getParamText(), info.shareTarget().getParamUrl(),
-                info.manifestUrl(), info.webApkPackageName(), versionCode, isManifestStale,
-                updateReason, callback);
+                info.shareTarget().isShareMethodPost(),
+                info.shareTarget().isShareEncTypeMultipart(), info.shareTarget().getFileNames(),
+                info.shareTarget().getFileAccepts(), info.manifestUrl(), info.webApkPackageName(),
+                versionCode, isManifestStale, updateReason, callback);
     }
 
     @NativeMethods
@@ -390,7 +392,9 @@
                 String[] iconHashes, @WebDisplayMode int displayMode, int orientation,
                 long themeColor, long backgroundColor, String shareTargetAction,
                 String shareTargetParamTitle, String shareTargetParamText,
-                String shareTargetParamUrl, String manifestUrl, String webApkPackage,
+                String shareTargetParamUrl, boolean shareTargetParamIsMethodPost,
+                boolean shareTargetParamIsEncTypeMultipart, String[] shareTargetParamFileNames,
+                Object[] shareTargetParamAccepts, String manifestUrl, String webApkPackage,
                 int webApkVersion, boolean isManifestStale, @WebApkUpdateReason int updateReason,
                 Callback<Boolean> callback);
         public void updateWebApkFromFile(String updateRequestPath, WebApkUpdateCallback callback);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
index f6a5721..f2c7d63 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
@@ -105,8 +105,8 @@
             WebApkInfo oldInfo = WebApkInfo.create("", "", scopeUrl, null, null, null, null, null,
                     -1, -1, -1, -1, -1, "random.package", -1, manifestUrl, "",
                     WebApkInfo.WebApkDistributor.BROWSER, new HashMap<String, String>(), null,
-                    false /* forceNavigation */, false /* isSplashProvidedByWebApk */,
-                    null /* shareData */);
+                    null /*shareTargetActivityName*/, false /* forceNavigation */,
+                    false /* isSplashProvidedByWebApk */, null /* shareData */);
             fetcher.start(mTab, oldInfo, observer);
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 4dea71e..a90af0c7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -151,14 +151,17 @@
         final TestWebApkUpdateManager updateManager = new TestWebApkUpdateManager(waiter, storage);
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            WebApkInfo info = WebApkInfo.create(WEBAPK_ID, "", creationData.scope, null, null, null,
-                    creationData.name, creationData.shortName, creationData.displayMode,
-                    creationData.orientation, 0, creationData.themeColor,
-                    creationData.backgroundColor, "",
+            WebApkInfo info = WebApkInfo.create(
+                    WEBAPK_ID, "", creationData.scope, null, null, null, creationData.name,
+                    creationData.shortName, creationData.displayMode, creationData.orientation, 0,
+                    creationData.themeColor, creationData.backgroundColor, "",
                     WebApkVersion.REQUEST_UPDATE_FOR_SHELL_APK_VERSION, creationData.manifestUrl,
                     creationData.startUrl, WebApkInfo.WebApkDistributor.BROWSER,
-                    creationData.iconUrlToMurmur2HashMap, null, false /* forceNavigation */,
-                    false /* isSplashProvidedByWebApk */, null /* shareData */);
+                    creationData.iconUrlToMurmur2HashMap, null, null /*shareTargetActivityName*/,
+                    false /* forceNavigation */, false /* isSplashProvidedByWebApk */,
+                    null /* shareData */
+
+            );
             updateManager.updateIfNeeded(mTab, info);
         });
         waiter.waitForCallback(0);
@@ -202,4 +205,17 @@
                 mTestServerRule.getServer(), mTab, WEBAPK_MANIFEST_URL);
         Assert.assertTrue(checkUpdateNeeded(creationData));
     }
+
+    @Test
+    @MediumTest
+    @Feature({"WebApk"})
+    public void testNoUpdateForPagesWithoutWST() throws Exception {
+        CreationData creationData = defaultCreationData();
+        creationData.startUrl = mTestServerRule.getServer().getURL(
+                "/chrome/test/data/banners/manifest_test_page.html");
+
+        WebappTestPage.navigateToServiceWorkerPageWithManifest(
+                mTestServerRule.getServer(), mTab, WEBAPK_MANIFEST_URL);
+        Assert.assertFalse(checkUpdateNeeded(creationData));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
index 6cb64a8c..0979c1a8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
@@ -132,10 +132,12 @@
                 ? WebappInfo.create("", webappStartUrlOrScopeUrl, null, null, null, null,
                         displayMode, 0, 0, 0, 0, null, false /* isIconGenerated */,
                         false /* isIconAdaptive */, false /* forceNavigation */)
-                : WebApkInfo.create("", "", webappStartUrlOrScopeUrl, null, null, null, null, null,
-                        displayMode, 0, 0, 0, 0, "", 0, null, "",
-                        WebApkInfo.WebApkDistributor.BROWSER, null, null,
-                        false /* forceNavigation */, false /* isSplashProvidedByWebApk */,
-                        null /* shareData */);
+                : WebApkInfo.create(
+                        "", "", webappStartUrlOrScopeUrl, null, null, null, null, null, displayMode,
+                        0, 0, 0, 0, "", 0, null, "", WebApkInfo.WebApkDistributor.BROWSER, null,
+                        null, null /*shareTargetActivityName*/, false /* forceNavigation */,
+                        false /* isSplashProvidedByWebApk */, null /* shareData */
+
+                );
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtilTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtilTest.java
index cdbc86e..29a96e8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtilTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtilTest.java
@@ -105,21 +105,31 @@
     @Test
     public void testGET() {
         Bundle shareActivityBundle = new Bundle();
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_METHOD, "GET");
         shareActivityBundle.putString(
                 WebApkMetaDataKeys.SHARE_ENCTYPE, "application/x-www-form-urlencoded");
         registerWebApk(shareActivityBundle);
 
         Intent intent = createBasicShareIntent();
-        WebApkInfo info = WebApkInfo.create(intent);
+        WebApkInfo infoWithShareMethodGet = WebApkInfo.create(intent);
 
         Assert.assertEquals(null,
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData()));
+                WebApkShareTargetUtilShadow.computePostData(
+                        WebApkTestHelper.getGeneratedShareTargetActivityClassName(0),
+                        infoWithShareMethodGet.shareTarget(), infoWithShareMethodGet.shareData()));
 
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_METHOD, "POST");
         registerWebApk(shareActivityBundle);
+        // recreating the WebApkInfo because the shareActivityBundle used for registerWebApk() has
+        // changed
+        WebApkInfo infoWithShareMethodPost = WebApkInfo.create(intent);
+
         Assert.assertNotEquals(null,
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData()));
+                WebApkShareTargetUtilShadow.computePostData(
+                        WebApkTestHelper.getGeneratedShareTargetActivityClassName(0),
+                        infoWithShareMethodPost.shareTarget(),
+                        infoWithShareMethodPost.shareData()));
     }
 
     /**
@@ -132,6 +142,8 @@
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TITLE, "title");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TEXT, "text");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_METHOD, "POST");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
+
         shareActivityBundle.putString(
                 WebApkMetaDataKeys.SHARE_ENCTYPE, "application/x-www-form-urlencoded");
         registerWebApk(shareActivityBundle);
@@ -141,8 +153,9 @@
         intent.putExtra(Intent.EXTRA_TEXT, "extra_text");
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         assertPostData(postData, new String[] {"title", "text"},
                 new String[] {"extra_subject", "extra_text"}, new String[] {"", ""},
@@ -150,13 +163,15 @@
     }
 
     /**
-     * Test that multipart/form-data with no names/accepts output a null postdata.
+     * Test that
+     * multipart/form-data with no names/accepts output a null postdata.
      */
     @Test
     public void testPostMultipartWithNoNamesNoAccepts() {
         Bundle shareActivityBundle = new Bundle();
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_METHOD, "POST");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ENCTYPE, "multipart/form-data");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
         // Note that names and accepts are not specified
         registerWebApk(shareActivityBundle);
 
@@ -167,8 +182,9 @@
 
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         assertPostData(
                 postData, new String[] {}, new String[] {}, new String[] {}, new String[] {});
@@ -185,14 +201,16 @@
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ENCTYPE, "multipart/form-data");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_NAMES, "[\"name\"]");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_ACCEPTS, "[[\"image/*\"]]");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
         registerWebApk(shareActivityBundle);
 
         Intent intent = createBasicShareIntent();
         // Intent.EXTRA_STREAM is not specified.
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         assertPostData(
                 postData, new String[] {}, new String[] {}, new String[] {}, new String[] {});
@@ -205,6 +223,7 @@
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ENCTYPE, "multipart/form-data");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_NAMES, "[\"name\"]");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_ACCEPTS, "[[\"image/*\"]]");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
         registerWebApk(shareActivityBundle);
 
         Intent intent = createBasicShareIntent();
@@ -214,8 +233,9 @@
 
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         assertPostData(postData, new String[] {"name"}, new String[] {"content"},
                 new String[] {"filename"}, new String[] {"image/gif"});
@@ -231,6 +251,7 @@
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TEXT, "share-text");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TITLE, "share-title");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_URL, "share-url");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
 
         registerWebApk(shareActivityBundle);
 
@@ -240,8 +261,9 @@
 
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         assertPostData(postData, new String[] {"share-title", "share-text"},
                 new String[] {"shared_subject_value", "shared_text_value"}, new String[] {"", ""},
@@ -258,6 +280,7 @@
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TEXT, "share-text");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TITLE, "share-title");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_URL, "share-url");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
 
         registerWebApk(shareActivityBundle);
 
@@ -267,8 +290,9 @@
 
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         assertPostData(postData, new String[] {"share-title", "share-text"},
                 new String[] {"shared_subject_value", "shared_text_value"}, new String[] {"", ""},
@@ -285,6 +309,7 @@
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TEXT, "share-text");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TITLE, "share-title");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_URL, "share-url");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
 
         registerWebApk(shareActivityBundle);
 
@@ -298,8 +323,9 @@
 
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         assertPostData(postData, new String[] {"share-title", "share-text", "name"},
                 new String[] {"shared_subject_value", "shared_text_value", "content"},
@@ -317,6 +343,7 @@
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TEXT, "share-text");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_TITLE, "share-title");
         shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_PARAM_URL, "share-url");
+        shareActivityBundle.putString(WebApkMetaDataKeys.SHARE_ACTION, "/share.html");
 
         registerWebApk(shareActivityBundle);
 
@@ -330,8 +357,9 @@
 
         WebApkInfo info = WebApkInfo.create(intent);
 
-        WebApkShareTargetUtilShadow.PostData postData =
-                WebApkShareTargetUtilShadow.computePostData(WEBAPK_PACKAGE_NAME, info.shareData());
+        WebApkShareTargetUtilShadow.PostData postData = WebApkShareTargetUtilShadow.computePostData(
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(0), info.shareTarget(),
+                info.shareData());
 
         // with invalid name parameter from Android manifest, we ignore the file sharing part.
         assertPostData(postData, new String[] {"share-title", "share-text"},
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
index e50e7b1..b5bd04f2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
@@ -17,6 +17,7 @@
 import android.graphics.Color;
 import android.os.Bundle;
 
+import org.json.JSONArray;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -46,6 +47,7 @@
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -84,6 +86,13 @@
     private static final long BACKGROUND_COLOR = 2L;
     private static final String SHARE_TARGET_ACTION = "/share_action.html";
     private static final String SHARE_TARGET_PARAM_TITLE = "share_params_title";
+    private static final String SHARE_TARGET_METHOD_GET = "GET";
+    private static final String SHARE_TARGET_METHOD_POST = "POST";
+    private static final String SHARE_TARGET_ENC_TYPE_MULTIPART = "multipart/form-data";
+    private static final String[] SHARE_TARGET_FILE_NAMES = new String[] {"file_1", "file_2"};
+    private static final String[][] SHARE_TARGET_ACCEPTS =
+            new String[][] {new String[] {"file_1_accept_1", "file_1_accept_2"},
+                    new String[] {"file_2_accept_2", "file_2_accept_2"}};
 
     /** Different name than the one used in {@link defaultManifestData()}. */
     private static final String DIFFERENT_NAME = "Different Name";
@@ -117,7 +126,9 @@
                 String[] iconHashes, @WebDisplayMode int displayMode, int orientation,
                 long themeColor, long backgroundColor, String shareTargetAction,
                 String shareTargetParamTitle, String shareTargetParamText,
-                String shareTargetParamUrl, String manifestUrl, String webApkPackage,
+                String shareTargetParamUrl, boolean shareTargetParamIsMethodPost,
+                boolean shareTargetParamIsEncTypeMultipart, String[] shareTargetParamFileNames,
+                Object[] shareTargetParamAccepts, String manifestUrl, String webApkPackage,
                 int webApkVersion, boolean isManifestStale, @WebApkUpdateReason int updateReason,
                 Callback<Boolean> callback) {}
 
@@ -205,6 +216,10 @@
         public long backgroundColor;
         public String shareTargetAction;
         public String shareTargetParamTitle;
+        public String shareTargetMethod;
+        public String shareTargetEncType;
+        public String[] shareTargetFileNames;
+        public String[][] shareTargetFileAccepts;
     }
 
     private static String getWebApkId(String packageName) {
@@ -251,6 +266,30 @@
         shareTargetMetaData.putString(
                 WebApkMetaDataKeys.SHARE_PARAM_TITLE, manifestData.shareTargetParamTitle);
 
+        shareTargetMetaData.putString(
+                WebApkMetaDataKeys.SHARE_METHOD, manifestData.shareTargetMethod);
+        shareTargetMetaData.putString(
+                WebApkMetaDataKeys.SHARE_ENCTYPE, manifestData.shareTargetEncType);
+
+        shareTargetMetaData.remove(WebApkMetaDataKeys.SHARE_PARAM_NAMES);
+        if (manifestData.shareTargetFileNames != null) {
+            JSONArray fileNamesJson =
+                    new JSONArray(Arrays.asList(manifestData.shareTargetFileNames));
+            shareTargetMetaData.putString(
+                    WebApkMetaDataKeys.SHARE_PARAM_NAMES, fileNamesJson.toString());
+        }
+
+        shareTargetMetaData.remove(WebApkMetaDataKeys.SHARE_PARAM_ACCEPTS);
+        if (manifestData.shareTargetFileAccepts != null) {
+            JSONArray acceptJson = new JSONArray();
+
+            for (String[] acceptArr : manifestData.shareTargetFileAccepts) {
+                acceptJson.put(new JSONArray(Arrays.asList(acceptArr)));
+            }
+            shareTargetMetaData.putString(
+                    WebApkMetaDataKeys.SHARE_PARAM_ACCEPTS, acceptJson.toString());
+        }
+
         WebApkTestHelper.registerWebApkWithMetaData(
                 packageName, metaData, new Bundle[] {shareTargetMetaData});
     }
@@ -276,6 +315,12 @@
         manifestData.backgroundColor = BACKGROUND_COLOR;
         manifestData.shareTargetAction = SHARE_TARGET_ACTION;
         manifestData.shareTargetParamTitle = SHARE_TARGET_PARAM_TITLE;
+
+        manifestData.shareTargetMethod = SHARE_TARGET_METHOD_GET;
+        manifestData.shareTargetEncType = SHARE_TARGET_ENC_TYPE_MULTIPART;
+        manifestData.shareTargetFileNames = SHARE_TARGET_FILE_NAMES.clone();
+        manifestData.shareTargetFileAccepts =
+                Arrays.stream(SHARE_TARGET_ACCEPTS).map(String[] ::clone).toArray(String[][] ::new);
         return manifestData;
     }
 
@@ -291,8 +336,15 @@
                 WEB_MANIFEST_URL, manifestData.startUrl, WebApkInfo.WebApkDistributor.BROWSER,
                 manifestData.iconUrlToMurmur2HashMap,
                 new WebApkInfo.ShareTarget(manifestData.shareTargetAction,
-                        manifestData.shareTargetParamTitle, null, null),
-                false /* forceNavigation */, false /* isSplashProvidedByWebApk */, null);
+                        manifestData.shareTargetParamTitle, null, null,
+                        manifestData.shareTargetMethod != null
+                                && manifestData.shareTargetMethod.equals(SHARE_TARGET_METHOD_POST),
+                        manifestData.shareTargetEncType != null
+                                && manifestData.shareTargetEncType.equals(
+                                        SHARE_TARGET_ENC_TYPE_MULTIPART),
+                        manifestData.shareTargetFileNames, manifestData.shareTargetFileAccepts),
+                null /* shareTargetActivityName */, false /* forceNavigation */,
+                false /* isSplashProvidedByWebApk */, null /* shareData */);
     }
 
     /**
@@ -749,6 +801,68 @@
         assertTrue(checkUpdateNeededForFetchedManifest(oldData, fetchedData));
     }
 
+    @Test
+    public void testShareTargetV2ChangedShouldUpgrade() {
+        ManifestData oldData = defaultManifestData();
+
+        ManifestData fetchedData1 = defaultManifestData();
+        fetchedData1.shareTargetFileNames[0] = "changed";
+        assertTrue(checkUpdateNeededForFetchedManifest(oldData, fetchedData1));
+
+        ManifestData fetchedData2 = defaultManifestData();
+        fetchedData2.shareTargetFileAccepts[1] = new String[] {};
+        assertTrue(checkUpdateNeededForFetchedManifest(oldData, fetchedData2));
+
+        ManifestData fetchedData3 = defaultManifestData();
+        fetchedData3.shareTargetFileAccepts[1][1] = "changed";
+        assertTrue(checkUpdateNeededForFetchedManifest(oldData, fetchedData3));
+    }
+
+    @Test
+    public void testShareTargetV2UpgradeFromV1() {
+        ManifestData oldNoShareTarget = defaultManifestData();
+        oldNoShareTarget.shareTargetAction = null;
+        oldNoShareTarget.shareTargetParamTitle = null;
+        oldNoShareTarget.shareTargetMethod = null;
+        oldNoShareTarget.shareTargetEncType = null;
+        oldNoShareTarget.shareTargetFileNames = null;
+        oldNoShareTarget.shareTargetFileAccepts = null;
+
+        ManifestData fetchedNoShareTarget2 = defaultManifestData();
+        fetchedNoShareTarget2.shareTargetAction = null;
+        fetchedNoShareTarget2.shareTargetParamTitle = null;
+        fetchedNoShareTarget2.shareTargetMethod = null;
+        fetchedNoShareTarget2.shareTargetEncType = null;
+        fetchedNoShareTarget2.shareTargetFileNames = null;
+        fetchedNoShareTarget2.shareTargetFileAccepts = null;
+
+        assertFalse(checkUpdateNeededForFetchedManifest(oldNoShareTarget, fetchedNoShareTarget2));
+
+        ManifestData oldV1ShareTarget = defaultManifestData();
+        oldV1ShareTarget.shareTargetMethod = null;
+        oldV1ShareTarget.shareTargetEncType = null;
+        oldV1ShareTarget.shareTargetFileNames = null;
+        oldV1ShareTarget.shareTargetFileAccepts = null;
+
+        ManifestData fetchedV1ShareTarget = defaultManifestData();
+        fetchedV1ShareTarget.shareTargetMethod = null;
+        fetchedV1ShareTarget.shareTargetEncType = null;
+        fetchedV1ShareTarget.shareTargetFileNames = null;
+        fetchedV1ShareTarget.shareTargetFileAccepts = null;
+        assertFalse(checkUpdateNeededForFetchedManifest(oldV1ShareTarget, fetchedV1ShareTarget));
+
+        ManifestData oldV2ShareTarget = defaultManifestData();
+        ManifestData fetchedV2ShareTarget = defaultManifestData();
+        assertFalse(checkUpdateNeededForFetchedManifest(oldV2ShareTarget, fetchedV2ShareTarget));
+
+        assertTrue(checkUpdateNeededForFetchedManifest(oldNoShareTarget, fetchedV1ShareTarget));
+        assertTrue(checkUpdateNeededForFetchedManifest(oldNoShareTarget, fetchedV2ShareTarget));
+        assertTrue(checkUpdateNeededForFetchedManifest(oldV1ShareTarget, fetchedV2ShareTarget));
+        assertTrue(checkUpdateNeededForFetchedManifest(fetchedV2ShareTarget, fetchedV1ShareTarget));
+        assertTrue(checkUpdateNeededForFetchedManifest(fetchedV2ShareTarget, oldNoShareTarget));
+        assertTrue(checkUpdateNeededForFetchedManifest(fetchedV1ShareTarget, oldNoShareTarget));
+    }
+
     /**
      * Test that an upgrade is not requested when the Web Manifest did not change and the Web
      * Manifest scope is empty.
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 77a7c15..7d1d321 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-76.0.3805.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-76.0.3806.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/extensions_strings.grdp b/chrome/app/extensions_strings.grdp
index b3a1db4..fd3d828 100644
--- a/chrome/app/extensions_strings.grdp
+++ b/chrome/app/extensions_strings.grdp
@@ -295,6 +295,9 @@
   <message name="IDS_EXTENSIONS_LOADING_ACTIVITIES" desc="The message shown to the user when the activity log for an extension is currently being fetched from the API.">
     Fetching activities...
   </message>
+  <message name="IDS_MISSING_OR_UNINSTALLED_EXTENSION" desc="The placeholder for the activity log page header if the extension does not exist or is not installed.">
+    Missing or uninstalled extension
+  </message>
   <message name="IDS_EXTENSIONS_NO_ACTIVITIES" desc="The message shown to the user when an extension has no recent activities.">
     No recent activities
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9ff87bb..e7c65a9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -333,8 +333,8 @@
     "component_updater/recovery_component_installer.h",
     "component_updater/recovery_improved_component_installer.cc",
     "component_updater/recovery_improved_component_installer.h",
-    "component_updater/sth_set_component_installer.cc",
-    "component_updater/sth_set_component_installer.h",
+    "component_updater/sth_set_component_remover.cc",
+    "component_updater/sth_set_component_remover.h",
     "component_updater/subresource_filter_component_installer.cc",
     "component_updater/subresource_filter_component_installer.h",
     "component_updater/supervised_user_whitelist_installer.cc",
@@ -5525,47 +5525,37 @@
   }
 }
 
-static_library("test_support_ui") {
-  testonly = true
+if (!is_android) {
+  static_library("test_support_ui") {
+    testonly = true
+    configs += [ "//build/config:precompiled_headers" ]
 
-  # Always include this via the main test support UI target.
-  visibility = [ "//chrome/test:test_support_ui" ]
+    # Always include this via the main test support UI target.
+    visibility = [ "//chrome/test:test_support_ui" ]
 
-  sources = [
-    "signin/token_revoker_test_utils.cc",
-    "signin/token_revoker_test_utils.h",
-  ]
-
-  deps = [
-    "//components/metrics:test_support",
-    "//components/password_manager/core/browser:test_support",
-    "//components/translate/content/common",
-    "//skia",
-    "//testing/gtest",
-    "//third_party/blink/public:blink_headers",
-  ]
-
-  public_deps = [
-    "//net:test_support",
-  ]
-
-  configs += [ "//build/config:precompiled_headers" ]
-
-  if (!is_android) {
-    sources += [
+    sources = [
       "password_manager/password_manager_test_base.cc",
       "password_manager/password_manager_test_base.h",
+      "signin/token_revoker_test_utils.cc",
+      "signin/token_revoker_test_utils.h",
       "ui/webui/signin/login_ui_test_utils.cc",
       "ui/webui/signin/login_ui_test_utils.h",
       "ui/webui/web_ui_test_handler.cc",
       "ui/webui/web_ui_test_handler.h",
     ]
 
-    deps += [
+    deps = [
       ":browser",
       "//chrome:browser_tests_pak",
       "//chrome/common:mojo_bindings",
       "//chrome/test/data:web_ui_test_bindings",
+      "//components/metrics:test_support",
+      "//components/password_manager/core/browser:test_support",
+      "//components/translate/content/common",
+    ]
+
+    public_deps = [
+      "//net:test_support",
     ]
   }
 }
diff --git a/chrome/browser/android/provider/chrome_browser_provider.cc b/chrome/browser/android/provider/chrome_browser_provider.cc
index 36e397d7..7f89ab47 100644
--- a/chrome/browser/android/provider/chrome_browser_provider.cc
+++ b/chrome/browser/android/provider/chrome_browser_provider.cc
@@ -136,22 +136,6 @@
   return env->CallIntMethod(integer_obj.obj(), int_value, NULL);
 }
 
-std::vector<base::string16> ConvertJStringArrayToString16Array(
-    JNIEnv* env,
-    const JavaRef<jobjectArray>& array) {
-  std::vector<base::string16> results;
-  if (!array.is_null()) {
-    jsize len = env->GetArrayLength(array.obj());
-    for (int i = 0; i < len; i++) {
-      ScopedJavaLocalRef<jstring> j_str(
-          env,
-          static_cast<jstring>(env->GetObjectArrayElement(array.obj(), i)));
-      results.push_back(ConvertJavaStringToUTF16(env, j_str));
-    }
-  }
-  return results;
-}
-
 // ------------- Utility methods used by tasks ------------- //
 
 // Parse the given url and return a GURL, appending the default scheme
@@ -931,8 +915,8 @@
     }
   }
 
-  std::vector<base::string16> where_args =
-      ConvertJStringArrayToString16Array(env, selection_args);
+  std::vector<base::string16> where_args;
+  AppendJavaStringArrayToStringVector(env, selection_args, &where_args);
 
   std::string where_clause;
   if (selections) {
@@ -976,8 +960,8 @@
                                             date, favicon, title, visits,
                                             parent_id, &row, bookmark_model_);
 
-  std::vector<base::string16> where_args =
-      ConvertJStringArrayToString16Array(env, selection_args);
+  std::vector<base::string16> where_args;
+  AppendJavaStringArrayToStringVector(env, selection_args, &where_args);
 
   std::string where_clause;
   if (selections)
@@ -992,8 +976,8 @@
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& selections,
     const JavaParamRef<jobjectArray>& selection_args) {
-  std::vector<base::string16> where_args =
-      ConvertJStringArrayToString16Array(env, selection_args);
+  std::vector<base::string16> where_args;
+  AppendJavaStringArrayToStringVector(env, selection_args, &where_args);
 
   std::string where_clause;
   if (selections)
@@ -1008,8 +992,8 @@
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& selections,
     const JavaParamRef<jobjectArray>& selection_args) {
-  std::vector<base::string16> where_args =
-      ConvertJStringArrayToString16Array(env, selection_args);
+  std::vector<base::string16> where_args;
+  AppendJavaStringArrayToStringVector(env, selection_args, &where_args);
 
   std::string where_clause;
   if (selections)
@@ -1069,8 +1053,8 @@
     }
   }
 
-  std::vector<base::string16> where_args =
-      ConvertJStringArrayToString16Array(env, selection_args);
+  std::vector<base::string16> where_args;
+  AppendJavaStringArrayToStringVector(env, selection_args, &where_args);
 
   std::string where_clause;
   if (selections) {
@@ -1107,8 +1091,8 @@
   history::SearchRow row;
   JNI_ChromeBrowserProvider_FillSearchRow(env, obj, search_term, date, &row);
 
-  std::vector<base::string16> where_args = ConvertJStringArrayToString16Array(
-      env, selection_args);
+  std::vector<base::string16> where_args;
+  AppendJavaStringArrayToStringVector(env, selection_args, &where_args);
 
   std::string where_clause;
   if (selections)
@@ -1125,8 +1109,8 @@
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& selections,
     const JavaParamRef<jobjectArray>& selection_args) {
-  std::vector<base::string16> where_args =
-      ConvertJStringArrayToString16Array(env, selection_args);
+  std::vector<base::string16> where_args;
+  AppendJavaStringArrayToStringVector(env, selection_args, &where_args);
 
   std::string where_clause;
   if (selections)
diff --git a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
index 13e7609b..08e8682 100644
--- a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
+++ b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
@@ -227,7 +227,11 @@
   ScopedJavaLocalRef<jstring> java_share_params_title;
   ScopedJavaLocalRef<jstring> java_share_params_text;
   ScopedJavaLocalRef<jstring> java_share_params_url;
-  if (info_.share_target.has_value()) {
+  jboolean java_share_params_is_method_post = false;
+  jboolean java_share_params_is_enctype_multipart = false;
+  ScopedJavaLocalRef<jobjectArray> java_share_params_file_names;
+  ScopedJavaLocalRef<jobjectArray> java_share_params_accepts;
+  if (info_.share_target.has_value() && info_.share_target->action.is_valid()) {
     java_share_action = base::android::ConvertUTF8ToJavaString(
         env, info_.share_target->action.spec());
     java_share_params_title = base::android::ConvertUTF16ToJavaString(
@@ -236,6 +240,22 @@
         env, info_.share_target->params.text);
     java_share_params_url = base::android::ConvertUTF16ToJavaString(
         env, info_.share_target->params.url);
+
+    java_share_params_is_method_post =
+        (info_.share_target->method == ShareTarget::kPost);
+    java_share_params_is_enctype_multipart =
+        (info_.share_target->enctype == ShareTarget::kMultipart);
+
+    std::vector<base::string16> file_names;
+    std::vector<std::vector<base::string16>> accepts;
+    for (auto& f : info_.share_target->params.files) {
+      file_names.push_back(f.name);
+      accepts.push_back(f.accept);
+    }
+    java_share_params_file_names =
+        base::android::ToJavaArrayOfStrings(env, file_names);
+    java_share_params_accepts =
+        base::android::ToJavaArrayOfStringArray(env, accepts);
   }
 
   Java_WebApkUpdateDataFetcher_onDataAvailable(
@@ -245,5 +265,7 @@
       java_icon_urls, info_.display, info_.orientation,
       OptionalSkColorToJavaColor(info_.theme_color),
       OptionalSkColorToJavaColor(info_.background_color), java_share_action,
-      java_share_params_title, java_share_params_text, java_share_params_url);
+      java_share_params_title, java_share_params_text, java_share_params_url,
+      java_share_params_is_method_post, java_share_params_is_enctype_multipart,
+      java_share_params_file_names, java_share_params_accepts);
 }
diff --git a/chrome/browser/android/webapk/webapk_update_manager.cc b/chrome/browser/android/webapk/webapk_update_manager.cc
index cbbf307..9bacd43 100644
--- a/chrome/browser/android/webapk/webapk_update_manager.cc
+++ b/chrome/browser/android/webapk/webapk_update_manager.cc
@@ -65,6 +65,10 @@
     const JavaParamRef<jstring>& java_share_target_param_title,
     const JavaParamRef<jstring>& java_share_target_param_text,
     const JavaParamRef<jstring>& java_share_target_param_url,
+    const jboolean java_share_target_param_is_method_post,
+    const jboolean java_share_target_param_is_enctype_multipart,
+    const JavaParamRef<jobjectArray>& java_share_target_param_file_names,
+    const JavaParamRef<jobjectArray>& java_share_target_param_accepts,
     const JavaParamRef<jstring>& java_web_manifest_url,
     const JavaParamRef<jstring>& java_webapk_package,
     jint java_webapk_version,
@@ -103,6 +107,31 @@
         ConvertJavaStringToUTF16(java_share_target_param_text);
     info.share_target->params.url =
         ConvertJavaStringToUTF16(java_share_target_param_url);
+    info.share_target->method =
+        java_share_target_param_is_method_post == JNI_TRUE ? ShareTarget::kPost
+                                                           : ShareTarget::kGet;
+
+    info.share_target->enctype =
+        java_share_target_param_is_enctype_multipart == JNI_TRUE
+            ? ShareTarget::kMultipart
+            : ShareTarget::kApplication;
+
+    std::vector<base::string16> fileNames;
+    base::android::AppendJavaStringArrayToStringVector(
+        env, java_share_target_param_file_names, &fileNames);
+
+    std::vector<std::vector<base::string16>> accepts;
+    base::android::Java2dStringArrayTo2dStringVector(
+        env, java_share_target_param_accepts, &accepts);
+
+    // The length of fileNames and accepts should always be the same, but here
+    // we just want to be safe.
+    for (size_t i = 0; i < std::min(fileNames.size(), accepts.size()); ++i) {
+      ShareTargetParamsFile file;
+      file.name = fileNames[i];
+      file.accept.swap(accepts[i]);
+      info.share_target->params.files.push_back(file);
+    }
   }
 
   base::android::AppendJavaStringArrayToStringVector(env, java_icon_urls,
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 4c1bbd7b..dac48ab 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -92,6 +92,7 @@
 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
 #include "extensions/browser/api/extensions_api_client.h"
 #include "extensions/browser/app_window/native_app_window.h"
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extensions_client.h"
@@ -3341,7 +3342,13 @@
                                                                 true);
 
   TestHelper("testPluginLoadInternalResource", "web_view/shim", NO_TEST_SERVER);
+  // Sanity check to ensure no GuestView was created.
+  for (auto* guest_wc : GetEmbedderWebContents()->GetInnerWebContents()) {
+    EXPECT_FALSE(extensions::MimeHandlerViewEmbedder::Get(
+        guest_wc->GetMainFrame()->GetFrameTreeNodeId()));
+  }
 }
+
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
 class WebViewCaptureTest : public WebViewTest {
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index c475b05..464ad92 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -346,18 +346,31 @@
     TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_REQUESTED);
 
   auto error = data.errors.empty() ? NO_ERROR_DETECTED : data.errors[0];
-  SetInstallableWebAppCheckResult(error == NO_ERROR_DETECTED
-                                      ? InstallableWebAppCheckResult::kYes
-                                      : InstallableWebAppCheckResult::kNo);
-
   if (error != NO_ERROR_DETECTED) {
     if (error == NO_MATCHING_SERVICE_WORKER)
       TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER);
 
+    SetInstallableWebAppCheckResult(InstallableWebAppCheckResult::kNo);
     Stop(error);
     return;
   }
 
+  if (CheckIfInstalled()) {
+    banners::TrackDisplayEvent(banners::DISPLAY_EVENT_INSTALLED_PREVIOUSLY);
+    SetInstallableWebAppCheckResult(InstallableWebAppCheckResult::kNo);
+    Stop(ALREADY_INSTALLED);
+    return;
+  }
+
+  if (manifest_.prefer_related_applications) {
+    SetInstallableWebAppCheckResult(
+        InstallableWebAppCheckResult::kByUserRequest);
+    Stop(PREFER_RELATED_APPLICATIONS);
+    return;
+  }
+
+  SetInstallableWebAppCheckResult(InstallableWebAppCheckResult::kPromotable);
+
   DCHECK(data.has_worker && data.valid_manifest);
   DCHECK(!data.primary_icon_url.is_empty());
   DCHECK(data.primary_icon);
@@ -365,11 +378,6 @@
   primary_icon_url_ = data.primary_icon_url;
   primary_icon_ = *data.primary_icon;
 
-  if (CheckIfInstalled()) {
-    banners::TrackDisplayEvent(banners::DISPLAY_EVENT_INSTALLED_PREVIOUSLY);
-    Stop(ALREADY_INSTALLED);
-    return;
-  }
 
   // If we triggered the installability check on page load, then it's possible
   // we don't have enough engagement yet. If that's the case, return here but
@@ -449,11 +457,6 @@
   return NO_ERROR_DETECTED;
 }
 
-bool AppBannerManager::IsInstallableWebApp() const {
-  return installable_web_app_check_result_ ==
-         InstallableWebAppCheckResult::kYes;
-}
-
 void AppBannerManager::SetInstallableWebAppCheckResult(
     InstallableWebAppCheckResult result) {
   if (installable_web_app_check_result_ == result)
@@ -464,15 +467,16 @@
   switch (result) {
     case InstallableWebAppCheckResult::kUnknown:
       break;
-    case InstallableWebAppCheckResult::kYes:
-      last_installable_web_app_scope_ = manifest_.scope;
-      DCHECK(!last_installable_web_app_scope_.is_empty());
+    case InstallableWebAppCheckResult::kPromotable:
+      last_promotable_web_app_scope_ = manifest_.scope;
+      DCHECK(!last_promotable_web_app_scope_.is_empty());
       install_animation_pending_ =
           AppBannerSettingsHelper::CanShowInstallTextAnimation(
-              web_contents(), last_installable_web_app_scope_);
+              web_contents(), last_promotable_web_app_scope_);
       break;
+    case InstallableWebAppCheckResult::kByUserRequest:
     case InstallableWebAppCheckResult::kNo:
-      last_installable_web_app_scope_ = GURL();
+      last_promotable_web_app_scope_ = GURL();
       install_animation_pending_ = false;
       break;
   }
@@ -641,28 +645,39 @@
 base::string16 AppBannerManager::GetInstallableWebAppName(
     content::WebContents* web_contents) {
   AppBannerManager* manager = FromWebContents(web_contents);
-  if (!manager || !manager->IsInstallableWebApp())
+  if (!manager)
     return base::string16();
-  return manager->GetAppName();
+  switch (manager->installable_web_app_check_result_) {
+    case InstallableWebAppCheckResult::kUnknown:
+    case InstallableWebAppCheckResult::kNo:
+      return base::string16();
+    case InstallableWebAppCheckResult::kByUserRequest:
+    case InstallableWebAppCheckResult::kPromotable:
+      return manager->GetAppName();
+  }
 }
 
-bool AppBannerManager::IsProbablyInstallableWebApp() const {
-  if (IsInstallableWebApp())
-    return true;
-  return installable_web_app_check_result_ ==
-             InstallableWebAppCheckResult::kUnknown &&
-         last_installable_web_app_scope_.is_valid() &&
-         base::StartsWith(web_contents()->GetLastCommittedURL().spec(),
-                          last_installable_web_app_scope_.spec(),
-                          base::CompareCase::SENSITIVE);
+bool AppBannerManager::IsProbablyPromotableWebApp() const {
+  switch (installable_web_app_check_result_) {
+    case InstallableWebAppCheckResult::kUnknown:
+      return last_promotable_web_app_scope_.is_valid() &&
+             base::StartsWith(web_contents()->GetLastCommittedURL().spec(),
+                              last_promotable_web_app_scope_.spec(),
+                              base::CompareCase::SENSITIVE);
+    case InstallableWebAppCheckResult::kNo:
+    case InstallableWebAppCheckResult::kByUserRequest:
+      return false;
+    case InstallableWebAppCheckResult::kPromotable:
+      return true;
+  }
 }
 
 bool AppBannerManager::MaybeConsumeInstallAnimation() {
-  DCHECK(IsProbablyInstallableWebApp());
+  DCHECK(IsProbablyPromotableWebApp());
   if (!install_animation_pending_)
     return false;
   AppBannerSettingsHelper::RecordInstallTextAnimationShown(
-      web_contents(), last_installable_web_app_scope_);
+      web_contents(), last_promotable_web_app_scope_);
   install_animation_pending_ = false;
   return true;
 }
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index c1d1e1e..b7ae097 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -105,9 +105,14 @@
     COMPLETE,
   };
 
-  // Installable describes whether a site satisifes the installablity
+  // Installable describes to what degree a site satisifes the installablity
   // requirements.
-  enum class InstallableWebAppCheckResult { kUnknown, kNo, kYes };
+  enum class InstallableWebAppCheckResult {
+    kUnknown,
+    kNo,
+    kByUserRequest,
+    kPromotable,
+  };
 
   // Retrieves the platform specific instance of AppBannerManager from
   // |web_contents|.
@@ -131,10 +136,10 @@
   static base::string16 GetInstallableWebAppName(
       content::WebContents* web_contents);
 
-  // Returns whether installability checks have passed (e.g. having a service
-  // worker fetch event) or have passed previously within the current manifest
-  // scope.
-  bool IsProbablyInstallableWebApp() const;
+  // Returns whether installability checks satisfy promotion requirements
+  // (e.g. having a service worker fetch event) or have passed previously within
+  // the current manifest scope.
+  bool IsProbablyPromotableWebApp() const;
 
   // Each successful installability check gets to show one animation prompt,
   // this returns and consumes the animation prompt if it is available.
@@ -167,9 +172,7 @@
   // GetAppIdentifier() must return a valid value for this method to work.
   bool CheckIfShouldShowBanner();
 
-  // Called when the current site is eligible to show a banner. Returns true if
-  // the banner should not be shown because the site is already installed, and
-  // false if the banner should be shown because the site is not yet installed.
+  // Returns whether the site is already installed as a web app.
   bool CheckIfInstalled();
 
   // Return a string identifying this app for metrics.
@@ -337,7 +340,6 @@
   // Returns a status code based on the current state, to log when terminating.
   InstallableStatusCode TerminationCode() const;
 
-  bool IsInstallableWebApp() const;
   void SetInstallableWebAppCheckResult(InstallableWebAppCheckResult result);
 
   // Fetches the data required to display a banner for the current page.
@@ -361,9 +363,9 @@
   bool install_animation_pending_;
   InstallableWebAppCheckResult installable_web_app_check_result_;
 
-  // The scope of the most recent installability check if successful otherwise
-  // invalid.
-  GURL last_installable_web_app_scope_;
+  // The scope of the most recent installability check that passes promotability
+  // requirements, otherwise invalid.
+  GURL last_promotable_web_app_scope_;
 
   base::ObserverList<Observer, true> observer_list_;
 
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index 9e77b2f..adc08ea9 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -468,4 +468,20 @@
                                 SHOWING_WEB_APP_BANNER, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, PreferRelatedApplications) {
+  std::unique_ptr<AppBannerManagerTest> manager(
+      CreateAppBannerManager(browser()));
+  base::HistogramTester histograms;
+
+  GURL test_url = embedded_test_server()->GetURL(
+      "/banners/manifest_test_page.html?manifest="
+      "manifest_prefer_related_apps_empty.json");
+  TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
+                                  false /* expected_will_show */,
+                                  State::COMPLETE);
+
+  histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
+                                PREFER_RELATED_APPLICATIONS, 1);
+}
+
 }  // namespace banners
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 5abd5845..90dceea 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -60,7 +60,7 @@
 #include "chrome/browser/component_updater/optimization_hints_component_installer.h"
 #include "chrome/browser/component_updater/origin_trials_component_installer.h"
 #include "chrome/browser/component_updater/pepper_flash_component_installer.h"
-#include "chrome/browser/component_updater/sth_set_component_installer.h"
+#include "chrome/browser/component_updater/sth_set_component_remover.h"
 #include "chrome/browser/component_updater/subresource_filter_component_installer.h"
 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
 #include "chrome/browser/defaults.h"
@@ -496,6 +496,15 @@
     // The CRLSet component previously resided in a different location: delete
     // the old file.
     component_updater::DeleteLegacyCRLSet(path);
+
+#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+    // Clean up previous STH sets that may have been installed. This is not
+    // done for:
+    // Android: Because STH sets were never used
+    // Chrome OS: On Chrome OS, this cleanup is delayed until user login.
+    component_updater::DeleteLegacySTHSet(path);
+#endif
+
 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
     // CRLSetFetcher attempts to load a CRL set from either the local disk or
     // network.
@@ -504,12 +513,6 @@
     component_updater::RegisterCRLSetComponent(cus, path);
 #endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
 
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
-    // Registration of the STH set fetcher here is not done for:
-    // Android: Because the story around CT on Mobile is not finalized yet.
-    // Chrome OS: On Chrome OS this registration is delayed until user login.
-    RegisterSTHSetComponent(cus, path);
-#endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
     RegisterOriginTrialsComponent(cus, path);
 
     RegisterFileTypePoliciesComponent(cus, path);
@@ -1813,15 +1816,14 @@
 
   PostBrowserStart();
 
-#if defined(OS_ANDROID)
-  DCHECK(!parameters().ui_task);
-#else
+  // The ui_task can be injected by tests to replace the main message loop.
+  // In that case we Run() it here, and set a flag to avoid running the main
+  // message loop later, as the test will do so as needed from the |ui_task|.
   if (parameters().ui_task) {
     parameters().ui_task->Run();
     delete parameters().ui_task;
     run_message_loop_ = false;
   }
-#endif
 
 #if defined(OS_WIN)
   // Clean up old user data directory and disk cache directory.
diff --git a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc
index c360b65..63254a4f 100644
--- a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc
+++ b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc
@@ -70,6 +70,14 @@
 
 }  // namespace
 
+struct ArcWallpaperService::WallpaperIdPair {
+  // ID of wallpaper image which can be obtaind by
+  // WallpaperResizer::GetImageId().
+  uint32_t image_id;
+  // ID of wallpaper generated in the container side.
+  int32_t android_id;
+};
+
 class ArcWallpaperService::DecodeRequest : public ImageDecoder::ImageRequest {
  public:
   DecodeRequest(ArcWallpaperService* service, int32_t android_id)
@@ -120,15 +128,26 @@
 ArcWallpaperService::ArcWallpaperService(content::BrowserContext* context,
                                          ArcBridgeService* bridge_service)
     : arc_bridge_service_(bridge_service),
-      decode_request_sender_(std::make_unique<DecodeRequestSenderImpl>()) {
+      decode_request_sender_(std::make_unique<DecodeRequestSenderImpl>()),
+      observer_binding_(this),
+      weak_ptr_factory_(this) {
   arc_bridge_service_->wallpaper()->SetHost(this);
+  arc_bridge_service_->wallpaper()->AddObserver(this);
 }
 
 ArcWallpaperService::~ArcWallpaperService() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  arc_bridge_service_->wallpaper()->RemoveObserver(this);
   arc_bridge_service_->wallpaper()->SetHost(nullptr);
 }
 
+void ArcWallpaperService::OnConnectionReady() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
+  observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+  WallpaperControllerClient::Get()->AddObserver(std::move(ptr_info));
+}
+
 void ArcWallpaperService::SetWallpaper(const std::vector<uint8_t>& data,
                                        int32_t wallpaper_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -156,12 +175,35 @@
 
 void ArcWallpaperService::GetWallpaper(GetWallpaperCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  gfx::ImageSkia image = WallpaperControllerClient::Get()->GetWallpaperImage();
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-      base::BindOnce(&EncodeImagePng, image), std::move(callback));
+  WallpaperControllerClient::Get()->GetWallpaperImage(
+      base::BindOnce(&ArcWallpaperService::OnGetWallpaperImageCallback,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void ArcWallpaperService::OnWallpaperChanged(uint32_t image_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  bool current_wallpaper_notified = false;
+  for (auto it = id_pairs_.begin(); it != id_pairs_.end();) {
+    int32_t const android_id = it->android_id;
+    if (it->image_id == image_id) {
+      current_wallpaper_notified = true;
+      it = id_pairs_.erase(it);
+      NotifyWallpaperChanged(android_id);
+    } else {
+      ++it;
+    }
+  }
+
+  if (!current_wallpaper_notified)
+    NotifyWallpaperChanged(-1);
+}
+
+void ArcWallpaperService::OnWallpaperColorsChanged(
+    const std::vector<SkColor>& prominent_colors) {}
+
+void ArcWallpaperService::OnWallpaperBlurChanged(bool blurred) {}
+
 void ArcWallpaperService::OnWallpaperDecoded(const gfx::ImageSkia& image,
                                              int32_t android_id) {
   const AccountId account_id =
@@ -169,20 +211,11 @@
   const std::string wallpaper_files_id =
       WallpaperControllerClient::Get()->GetFilesId(account_id);
 
-  const bool result = WallpaperControllerClient::Get()->SetThirdPartyWallpaper(
+  WallpaperControllerClient::Get()->SetThirdPartyWallpaper(
       account_id, wallpaper_files_id, kAndroidWallpaperFilename,
-      ash::WALLPAPER_LAYOUT_CENTER_CROPPED, image);
-
-  // Notify the Android side whether the request is going through or not.
-  if (result)
-    NotifyWallpaperChanged(android_id);
-  else
-    NotifyWallpaperChangedAndReset(android_id);
-
-  // TODO(crbug.com/618922): Register the wallpaper to Chrome OS wallpaper
-  // picker. Currently the new wallpaper does not appear there. The best way
-  // to make this happen seems to do the same things as wallpaper_api.cc and
-  // wallpaper_private_api.cc.
+      ash::WALLPAPER_LAYOUT_CENTER_CROPPED, image,
+      base::BindOnce(&ArcWallpaperService::OnSetThirdPartyWallpaperCallback,
+                     weak_ptr_factory_.GetWeakPtr(), android_id));
 }
 
 void ArcWallpaperService::NotifyWallpaperChanged(int android_id) {
@@ -203,4 +236,26 @@
   NotifyWallpaperChanged(-1);
 }
 
+void ArcWallpaperService::OnSetThirdPartyWallpaperCallback(int32_t android_id,
+                                                           bool allowed,
+                                                           uint32_t image_id) {
+  if (allowed)
+    id_pairs_.push_back({image_id, android_id});
+  else
+    NotifyWallpaperChangedAndReset(android_id);
+
+  // TODO(crbug.com/618922): Register the wallpaper to Chrome OS wallpaper
+  // picker. Currently the new wallpaper does not appear there. The best way
+  // to make this happen seems to do the same things as wallpaper_api.cc and
+  // wallpaper_private_api.cc.
+}
+
+void ArcWallpaperService::OnGetWallpaperImageCallback(
+    GetWallpaperCallback callback,
+    const gfx::ImageSkia& image) {
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&EncodeImagePng, image), std::move(callback));
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h
index 6f7e950..c6c8fe7 100644
--- a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h
+++ b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h
@@ -10,25 +10,28 @@
 #include <memory>
 #include <vector>
 
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/image_decoder.h"
 #include "components/arc/common/wallpaper.mojom.h"
+#include "components/arc/session/connection_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 
 namespace content {
 class BrowserContext;
 }  // namespace content
 
-namespace gfx {
-class ImageSkia;
-}  // namespace gfx
-
 namespace arc {
 
 class ArcBridgeService;
 
 // Lives on the UI thread.
-class ArcWallpaperService : public KeyedService, public mojom::WallpaperHost {
+class ArcWallpaperService : public KeyedService,
+                            public ConnectionObserver<mojom::WallpaperInstance>,
+                            public mojom::WallpaperHost,
+                            public ash::mojom::WallpaperObserver {
  public:
   // Returns singleton instance for the given BrowserContext,
   // or nullptr if the browser |context| is not allowed to use ARC.
@@ -39,12 +42,21 @@
                       ArcBridgeService* bridge_service);
   ~ArcWallpaperService() override;
 
+  // ConnectionObserver<mojom::WallpaperInstance> overrides.
+  void OnConnectionReady() override;
+
   // mojom::WallpaperHost overrides.
   void SetWallpaper(const std::vector<uint8_t>& data,
                     int32_t wallpaper_id) override;
   void SetDefaultWallpaper() override;
   void GetWallpaper(GetWallpaperCallback callback) override;
 
+  // ash::mojom::WallpaperObserver overrides.
+  void OnWallpaperChanged(uint32_t image_id) override;
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override;
+  void OnWallpaperBlurChanged(bool blurred) override;
+
   class DecodeRequestSender {
    public:
     virtual ~DecodeRequestSender();
@@ -63,6 +75,7 @@
   friend class TestApi;
   class AndroidIdStore;
   class DecodeRequest;
+  struct WallpaperIdPair;
 
   // Initiates a set wallpaper request to //ash.
   void OnWallpaperDecoded(const gfx::ImageSkia& image, int32_t android_id);
@@ -74,10 +87,27 @@
   // -1 to reset wallpaper cache at Android side.
   void NotifyWallpaperChangedAndReset(int android_id);
 
+  // If the wallpaper is allowed to be shown on screen, stores the |image_id|
+  // in order to track the wallpaper change later, otherwise notify the Android
+  // side immediately that the request is not going through.
+  void OnSetThirdPartyWallpaperCallback(int32_t android_id,
+                                        bool allowed,
+                                        uint32_t image_id);
+
+  // Initiates an encoding image request after getting the wallpaper image.
+  void OnGetWallpaperImageCallback(GetWallpaperCallback callback,
+                                   const gfx::ImageSkia& image);
+
   ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
   std::unique_ptr<DecodeRequest> decode_request_;
+  std::vector<WallpaperIdPair> id_pairs_;
   std::unique_ptr<DecodeRequestSender> decode_request_sender_;
 
+  // The binding this instance uses to implement ash::mojom::WallpaperObserver.
+  mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_;
+
+  base::WeakPtrFactory<ArcWallpaperService> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ArcWallpaperService);
 };
 
diff --git a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc
index 8b84c4e..7e53975 100644
--- a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service_unittest.cc
@@ -84,7 +84,8 @@
     // Wallpaper
     wallpaper_controller_client_ =
         std::make_unique<WallpaperControllerClient>();
-    wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+    wallpaper_controller_client_->InitForTesting(
+        test_wallpaper_controller_.CreateInterfacePtr());
 
     // Arc services
     arc_service_manager_.set_browser_context(&testing_profile_);
@@ -135,6 +136,7 @@
 TEST_F(ArcWallpaperServiceTest, SetDefaultWallpaper) {
   test_wallpaper_controller_.ClearCounts();
   service_->SetDefaultWallpaper();
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(1, test_wallpaper_controller_.set_default_wallpaper_count());
 }
 
@@ -143,6 +145,7 @@
       std::make_unique<SuccessDecodeRequestSender>());
   std::vector<uint8_t> bytes;
   service_->SetWallpaper(bytes, 10 /*wallpaper_id=*/);
+  wallpaper_controller_client_->FlushForTesting();
   ASSERT_EQ(1u, wallpaper_instance_->changed_ids().size());
   EXPECT_EQ(10, wallpaper_instance_->changed_ids()[0]);
 
@@ -150,6 +153,7 @@
       base::BindOnce([](std::vector<uint8_t>* out,
                         const std::vector<uint8_t>& bytes) { *out = bytes; },
                      &bytes));
+  wallpaper_controller_client_->FlushForTesting();
   content::RunAllTasksUntilIdle();
   ASSERT_NE(0u, bytes.size());
 }
@@ -159,6 +163,7 @@
       std::make_unique<FailureDecodeRequestSender>());
   std::vector<uint8_t> bytes;
   service_->SetWallpaper(bytes, 10 /*wallpaper_id=*/);
+  wallpaper_controller_client_->FlushForTesting();
 
   // For failure case, ArcWallpaperService reports that wallpaper is changed to
   // requested wallpaper (ID=10), then reports that the wallpaper is changed
diff --git a/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc b/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc
index 8017981..19c4370 100644
--- a/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc
+++ b/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc
@@ -6,7 +6,7 @@
 
 #include <vector>
 
-#include "ash/public/cpp/wallpaper_controller_observer.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/macros.h"
@@ -20,6 +20,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -120,22 +121,27 @@
   return true;
 }
 
-class TestWallpaperObserver : public ash::WallpaperControllerObserver {
+class TestWallpaperObserver : public ash::mojom::WallpaperObserver {
  public:
-  TestWallpaperObserver() {
-    WallpaperControllerClient::Get()->AddObserver(this);
+  TestWallpaperObserver() : finished_(false), observer_binding_(this) {
+    ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
+    observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+    WallpaperControllerClient::Get()->AddObserver(std::move(ptr_info));
   }
 
-  ~TestWallpaperObserver() override {
-    WallpaperControllerClient::Get()->RemoveObserver(this);
-  }
+  ~TestWallpaperObserver() override = default;
 
-  // ash::WallpaperControllerObserver:
-  void OnWallpaperChanged() override {
+  // ash::mojom::WallpaperObserver:
+  void OnWallpaperChanged(uint32_t image_id) override {
     finished_ = true;
     base::RunLoop::QuitCurrentWhenIdleDeprecated();
   }
 
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override {}
+
+  void OnWallpaperBlurChanged(bool blurred) override {}
+
   // Wait until the wallpaper update is completed.
   void WaitForWallpaperChanged() {
     while (!finished_)
@@ -145,7 +151,10 @@
   void Reset() { finished_ = false; }
 
  private:
-  bool finished_ = false;
+  bool finished_;
+
+  // The binding this instance uses to implement ash::mojom::WallpaperObserver.
+  mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_;
 
   DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserver);
 };
@@ -254,8 +263,13 @@
 
   // Verify the customized default wallpaper has replaced the built-in default
   // wallpaper.
-  gfx::ImageSkia image = WallpaperControllerClient::Get()->GetWallpaperImage();
-  EXPECT_TRUE(ImageIsNearColor(image, kCustomizedDefaultWallpaperColor));
+  base::RunLoop run_loop;
+  WallpaperControllerClient::Get()->GetWallpaperImage(
+      base::BindLambdaForTesting([&run_loop](const gfx::ImageSkia& image) {
+        run_loop.Quit();
+        EXPECT_TRUE(ImageIsNearColor(image, kCustomizedDefaultWallpaperColor));
+      }));
+  run_loop.Run();
   EXPECT_EQ(1U, num_attempts());
 }
 
@@ -281,8 +295,13 @@
 
   // Verify the customized default wallpaper has replaced the built-in default
   // wallpaper.
-  gfx::ImageSkia image = WallpaperControllerClient::Get()->GetWallpaperImage();
-  EXPECT_TRUE(ImageIsNearColor(image, kCustomizedDefaultWallpaperColor));
+  base::RunLoop run_loop;
+  WallpaperControllerClient::Get()->GetWallpaperImage(
+      base::BindLambdaForTesting([&run_loop](const gfx::ImageSkia& image) {
+        run_loop.Quit();
+        EXPECT_TRUE(ImageIsNearColor(image, kCustomizedDefaultWallpaperColor));
+      }));
+  run_loop.Run();
   EXPECT_EQ(2U, num_attempts());
 }
 
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
index 0e856811..bc5b9ce7 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
@@ -58,6 +58,8 @@
     wallpaper_private::SetCustomWallpaperLayout;
 namespace get_thumbnail = wallpaper_private::GetThumbnail;
 namespace save_thumbnail = wallpaper_private::SaveThumbnail;
+namespace get_offline_wallpaper_list =
+    wallpaper_private::GetOfflineWallpaperList;
 namespace record_wallpaper_uma = wallpaper_private::RecordWallpaperUMA;
 namespace get_collections_info = wallpaper_private::GetCollectionsInfo;
 namespace get_images_info = wallpaper_private::GetImagesInfo;
@@ -241,12 +243,20 @@
                   wallpaper_api_util::kCancelWallpaperMessage);
   dict->SetString("highResolutionSuffix", GetBackdropWallpaperSuffix());
 
-  auto info = WallpaperControllerClient::Get()->GetActiveUserWallpaperInfo();
-  dict->SetString("currentWallpaper", info.location);
-  dict->SetString("currentWallpaperLayout",
-                  wallpaper_api_util::GetLayoutString(info.layout));
+  WallpaperControllerClient::Get()->GetActiveUserWallpaperInfo(base::BindOnce(
+      &WallpaperPrivateGetStringsFunction::OnWallpaperInfoReturned, this,
+      std::move(dict)));
+  return RespondLater();
+}
 
-  return RespondNow(OneArgument(std::move(dict)));
+void WallpaperPrivateGetStringsFunction::OnWallpaperInfoReturned(
+    std::unique_ptr<base::DictionaryValue> dict,
+    const std::string& location,
+    ash::WallpaperLayout layout) {
+  dict->SetString("currentWallpaper", location);
+  dict->SetString("currentWallpaperLayout",
+                  wallpaper_api_util::GetLayoutString(layout));
+  Respond(OneArgument(std::move(dict)));
 }
 
 ExtensionFunction::ResponseAction
@@ -806,12 +816,20 @@
       get_current_wallpaper_thumbnail::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  auto image = WallpaperControllerClient::Get()->GetWallpaperImage();
-  gfx::Size thumbnail_size(params->thumbnail_width, params->thumbnail_height);
+  WallpaperControllerClient::Get()->GetWallpaperImage(base::BindOnce(
+      &WallpaperPrivateGetCurrentWallpaperThumbnailFunction::
+          OnWallpaperImageReturned,
+      this, gfx::Size(params->thumbnail_width, params->thumbnail_height)));
+  return RespondLater();
+}
+
+void WallpaperPrivateGetCurrentWallpaperThumbnailFunction::
+    OnWallpaperImageReturned(const gfx::Size& thumbnail_size,
+                             const gfx::ImageSkia& image) {
   image.EnsureRepsForSupportedScales();
   scoped_refptr<base::RefCountedBytes> thumbnail_data;
   GenerateThumbnail(image, thumbnail_size, &thumbnail_data);
-  return RespondNow(OneArgument(std::make_unique<Value>(
+  Respond(OneArgument(std::make_unique<Value>(
       Value::BlobStorage(thumbnail_data->front(),
                          thumbnail_data->front() + thumbnail_data->size()))));
 }
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.h b/chrome/browser/chromeos/extensions/wallpaper_private_api.h
index 5c2a757..50ddaee 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api.h
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.h
@@ -31,6 +31,12 @@
 
   // ExtensionFunction:
   ResponseAction Run() override;
+
+ private:
+  // Responds with the dictionary after getting the wallpaper info.
+  void OnWallpaperInfoReturned(std::unique_ptr<base::DictionaryValue> dict,
+                               const std::string& location,
+                               ash::WallpaperLayout layout);
 };
 
 // Check if sync themes setting is enabled.
@@ -411,6 +417,10 @@
   ResponseAction Run() override;
 
  private:
+  // Responds with the thumbnail data.
+  void OnWallpaperImageReturned(const gfx::Size& thumbnail_size,
+                                const gfx::ImageSkia& image);
+
   // WallpaperFunctionBase:
   void OnWallpaperDecoded(const gfx::ImageSkia& wallpaper) override;
 
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc
index 4f64fe0..5c051c99 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api_unittest.cc
@@ -71,7 +71,7 @@
   ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal());
   WallpaperControllerClient client;
   TestWallpaperController test_controller;
-  client.InitForTesting(&test_controller);
+  client.InitForTesting(test_controller.CreateInterfacePtr());
   fake_user_manager()->AddUser(AccountId::FromUserEmail(kTestAccount));
 
   {
@@ -81,6 +81,7 @@
         extensions::api_test_utils::RunFunction(function.get(), "[]", nullptr));
   }
 
+  client.FlushForTesting();
   // Expect SetDefaultWallpaper() to be called exactly once.
   EXPECT_EQ(1, test_controller.set_default_wallpaper_count());
 }
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
index 338b7919..c2a6cd3 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
@@ -82,7 +82,8 @@
     session_manager_ = std::make_unique<session_manager::SessionManager>();
     wallpaper_controller_client_ =
         std::make_unique<WallpaperControllerClient>();
-    wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+    wallpaper_controller_client_->InitForTesting(
+        test_wallpaper_controller_.CreateInterfacePtr());
   }
 
   void TearDown() override {
@@ -433,12 +434,14 @@
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
   session_manager_->SetSessionState(
       session_manager::SessionState::LOGIN_PRIMARY);
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(0, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(0,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
 
   ASSERT_TRUE(FinishResourcesComponentLoad(
       base::FilePath(kTestDemoModeResourcesMountPoint)));
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(1, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(0,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
@@ -457,11 +460,13 @@
       profile, new ChromeAppDelegate(true /* keep_alive */),
       screensaver_app.get());
   demo_session->OnAppWindowActivated(app_window);
+  wallpaper_controller_client_->FlushForTesting();
   // The splash screen is not removed until active session starts.
   EXPECT_EQ(1, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(0,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
   session_manager_->SetSessionState(session_manager::SessionState::ACTIVE);
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(1, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(1,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
@@ -484,12 +489,14 @@
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
   session_manager_->SetSessionState(
       session_manager::SessionState::LOGIN_PRIMARY);
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(0, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(0,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
 
   ASSERT_TRUE(FinishResourcesComponentLoad(
       base::FilePath(kTestDemoModeResourcesMountPoint)));
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(1, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(0,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
@@ -498,6 +505,7 @@
       static_cast<base::MockOneShotTimer*>(demo_session->GetTimerForTesting());
   ASSERT_TRUE(timer_ptr);
   timer_ptr->Fire();
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(1, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(1,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
@@ -517,11 +525,13 @@
       profile, new ChromeAppDelegate(true /* keep_alive */),
       screensaver_app.get());
   demo_session->OnAppWindowActivated(app_window);
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(1, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(1,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
   // Entering active session will not trigger splash screen removal anymore.
   session_manager_->SetSessionState(session_manager::SessionState::ACTIVE);
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(1, test_wallpaper_controller_.show_always_on_top_wallpaper_count());
   EXPECT_EQ(1,
             test_wallpaper_controller_.remove_always_on_top_wallpaper_count());
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index dfdedf6..9a1bdbcc 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -8,8 +8,8 @@
 #include "apps/test/app_window_waiter.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/keyboard/keyboard_switches.h"
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/public/interfaces/login_screen_test_api.test-mojom-test-utils.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/json/json_reader.h"
@@ -92,6 +92,7 @@
 #include "google_apis/gaia/gaia_switches.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "media/audio/test_audio_thread.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/audio/public/cpp/fake_system_info.h"
 #include "services/audio/public/cpp/sounds/audio_stream_handler.h"
@@ -2476,19 +2477,20 @@
 // Specialized test fixture for testing kiosk mode on the
 // hidden WebUI initialization flow for slow hardware.
 class KioskHiddenWebUITest : public KioskTest,
-                             public ash::WallpaperControllerObserver {
+                             public ash::mojom::WallpaperObserver {
  public:
-  KioskHiddenWebUITest() = default;
+  KioskHiddenWebUITest() : wallpaper_loaded_(false), observer_binding_(this) {}
 
-  // KioskTest:
   void SetUpOnMainThread() override {
     LoginDisplayHostWebUI::DisableRestrictiveProxyCheckForTest();
 
     KioskTest::SetUpOnMainThread();
-    WallpaperControllerClient::Get()->AddObserver(this);
+    ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
+    observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+    WallpaperControllerClient::Get()->AddObserver(std::move(ptr_info));
   }
+
   void TearDownOnMainThread() override {
-    WallpaperControllerClient::Get()->RemoveObserver(this);
     KioskTest::TearDownOnMainThread();
   }
 
@@ -2501,17 +2503,25 @@
 
   bool wallpaper_loaded() const { return wallpaper_loaded_; }
 
-  // ash::WallpaperControllerObserver:
-  void OnWallpaperChanged() override {
+  // ash::mojom::WallpaperObserver:
+  void OnWallpaperChanged(uint32_t image_id) override {
     wallpaper_loaded_ = true;
     if (runner_.get())
       runner_->Quit();
   }
 
- private:
-  bool wallpaper_loaded_ = false;
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override {}
+
+  void OnWallpaperBlurChanged(bool blurred) override {}
+
+  bool wallpaper_loaded_;
   scoped_refptr<content::MessageLoopRunner> runner_;
 
+ private:
+  // The binding this instance uses to implement ash::mojom::WallpaperObserver.
+  mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_;
+
   DISALLOW_COPY_AND_ASSIGN(KioskHiddenWebUITest);
 };
 
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 5f71b564..6b8b4e1 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -82,7 +82,7 @@
 #include "chrome/browser/chromeos/tether/tether_service.h"
 #include "chrome/browser/chromeos/tpm_firmware_update_notification.h"
 #include "chrome/browser/component_updater/crl_set_component_installer.h"
-#include "chrome/browser/component_updater/sth_set_component_installer.h"
+#include "chrome/browser/component_updater/sth_set_component_remover.h"
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/google/google_brand_chromeos.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
@@ -1852,8 +1852,8 @@
     const user_manager::User* user) {
   const std::string username_hash = user->username_hash();
   if (!username_hash.empty()) {
-    base::FilePath path;
-    path = ProfileHelper::GetProfilePathByUserIdHash(username_hash);
+    base::FilePath path =
+        ProfileHelper::GetProfilePathByUserIdHash(username_hash);
     component_updater::ComponentUpdateService* cus =
         g_browser_process->component_updater();
     if (cus)
@@ -1864,13 +1864,10 @@
 void UserSessionManager::InitializeCertificateTransparencyComponents(
     const user_manager::User* user) {
   const std::string username_hash = user->username_hash();
-  component_updater::ComponentUpdateService* cus =
-      g_browser_process->component_updater();
-  if (!username_hash.empty() && cus) {
-    const base::FilePath path =
+  if (!username_hash.empty()) {
+    base::FilePath path =
         ProfileHelper::GetProfilePathByUserIdHash(username_hash);
-    // STH set fetcher.
-    RegisterSTHSetComponent(cus, path);
+    component_updater::DeleteLegacySTHSet(path);
   }
 }
 
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h
index faf0f2c1..7aa0b09 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.h
+++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -209,8 +209,7 @@
   // Starts loading CRL set.
   void InitializeCRLSetFetcher(const user_manager::User* user);
 
-  // Starts loading CT-related components, which are the EV Certificates
-  // whitelist and the STHSet.
+  // Initializes Certificate Transparency-related components.
   void InitializeCertificateTransparencyComponents(
       const user_manager::User* user);
 
diff --git a/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc b/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc
index a1c2a45..8475ec9 100644
--- a/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc
+++ b/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc
@@ -103,6 +103,8 @@
   }
 
   void TearDown() override {
+    system_network_context_.reset();
+
     signin_ui_web_contents_.reset();
 
     signin_browser_context_.reset();
diff --git a/chrome/browser/chromeos/login/user_board_view_mojo.cc b/chrome/browser/chromeos/login/user_board_view_mojo.cc
index e206b19..fbe1b6a 100644
--- a/chrome/browser/chromeos/login/user_board_view_mojo.cc
+++ b/chrome/browser/chromeos/login/user_board_view_mojo.cc
@@ -104,11 +104,7 @@
   // warning banner message.
   // TODO(fukino): Remove ShowWarningMessage and related implementation along
   // with the migration screen once the transition to ext4 is compilete.
-  if (!message.empty()) {
-    LoginScreenClient::Get()->login_screen()->ShowWarningBanner(message);
-  } else {
-    LoginScreenClient::Get()->login_screen()->HideWarningBanner();
-  }
+  ash::LoginScreen::Get()->GetModel()->UpdateWarningMessage(message);
 }
 
 void UserBoardViewMojo::ShowUserPodCustomIcon(
diff --git a/chrome/browser/chromeos/login/users/user_manager_unittest.cc b/chrome/browser/chromeos/login/users/user_manager_unittest.cc
index 9830a82..9f73e62 100644
--- a/chrome/browser/chromeos/login/users/user_manager_unittest.cc
+++ b/chrome/browser/chromeos/login/users/user_manager_unittest.cc
@@ -83,7 +83,8 @@
 
     wallpaper_controller_client_ =
         std::make_unique<WallpaperControllerClient>();
-    wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+    wallpaper_controller_client_->InitForTesting(
+        test_wallpaper_controller_.CreateInterfacePtr());
   }
 
   void TearDown() override {
@@ -217,6 +218,7 @@
   EXPECT_EQ(1U, users->size());
   EXPECT_EQ((*users)[0]->GetAccountId(), owner_account_id_at_invalid_domain_);
   // Verify that the wallpaper is removed when user is removed.
+  wallpaper_controller_client_->FlushForTesting();
   EXPECT_EQ(2, test_wallpaper_controller_.remove_user_wallpaper_count());
 }
 
diff --git a/chrome/browser/chromeos/login/users/wallpaper_policy_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper_policy_browsertest.cc
index 3f7ad74..553f95ec 100644
--- a/chrome/browser/chromeos/login/users/wallpaper_policy_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper_policy_browsertest.cc
@@ -8,7 +8,7 @@
 #include <string>
 #include <vector>
 
-#include "ash/public/cpp/wallpaper_controller_observer.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
@@ -56,6 +56,7 @@
 #include "components/user_manager/user_names.h"
 #include "content/public/test/browser_test_utils.h"
 #include "crypto/rsa_private_key.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -129,7 +130,7 @@
 }  // namespace
 
 class WallpaperPolicyTest : public LoginManagerTest,
-                            public ash::WallpaperControllerObserver {
+                            public ash::mojom::WallpaperObserver {
  protected:
   WallpaperPolicyTest()
       : LoginManagerTest(true, true),
@@ -203,7 +204,9 @@
 
   void SetUpOnMainThread() override {
     LoginManagerTest::SetUpOnMainThread();
-    WallpaperControllerClient::Get()->AddObserver(this);
+    ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
+    observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+    WallpaperControllerClient::Get()->AddObserver(std::move(ptr_info));
 
     // Set up policy signing.
     user_policy_builders_[0] = GetUserPolicyBuilder(testUsers_[0]);
@@ -211,30 +214,45 @@
   }
 
   void TearDownOnMainThread() override {
-    WallpaperControllerClient::Get()->RemoveObserver(this);
     LoginManagerTest::TearDownOnMainThread();
   }
 
   // Obtain wallpaper image and return its average ARGB color.
   SkColor GetAverageWallpaperColor() {
     average_color_.reset();
-    auto image = WallpaperControllerClient::Get()->GetWallpaperImage();
+    WallpaperControllerClient::Get()->GetWallpaperImage(
+        base::BindOnce(&WallpaperPolicyTest::OnGetWallpaperImageCallback,
+                       weak_ptr_factory_.GetWeakPtr()));
+    while (!average_color_.has_value()) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+    return average_color_.value();
+  }
+
+  void OnGetWallpaperImageCallback(const gfx::ImageSkia& image) {
     const gfx::ImageSkiaRep& representation = image.GetRepresentation(1.0f);
     if (representation.is_null()) {
       ADD_FAILURE() << "No image representation.";
       average_color_ = SkColorSetARGB(0, 0, 0, 0);
     }
     average_color_ = ComputeAverageColor(representation.GetBitmap());
-    return average_color_.value();
+    if (run_loop_)
+      run_loop_->Quit();
   }
 
-  // ash::WallpaperControllerObserver:
-  void OnWallpaperChanged() override {
+  // ash::mojom::WallpaperObserver:
+  void OnWallpaperChanged(uint32_t image_id) override {
     ++wallpaper_change_count_;
     if (run_loop_)
       run_loop_->Quit();
   }
 
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override {}
+
+  void OnWallpaperBlurChanged(bool blurred) override {}
+
   // Runs the loop until wallpaper has changed to the expected color.
   void RunUntilWallpaperChangeToColor(const SkColor& expected_color) {
     while (expected_color != GetAverageWallpaperColor()) {
@@ -317,6 +335,10 @@
       &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
 
  private:
+  // The binding this instance uses to implement ash::mojom::WallpaperObserver.
+  mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_{
+      this};
+
   // The average ARGB color of the current wallpaper.
   base::Optional<SkColor> average_color_;
 
diff --git a/chrome/browser/component_updater/sth_set_component_installer.cc b/chrome/browser/component_updater/sth_set_component_installer.cc
deleted file mode 100644
index 2403050..0000000
--- a/chrome/browser/component_updater/sth_set_component_installer.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/component_updater/sth_set_component_installer.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/json_reader.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/no_destructor.h"
-#include "base/path_service.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/values.h"
-#include "base/version.h"
-#include "components/certificate_transparency/sth_observer.h"
-#include "components/component_updater/component_updater_paths.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/network_service_instance.h"
-#include "crypto/sha2.h"
-#include "net/cert/ct_log_response_parser.h"
-#include "net/cert/signed_tree_head.h"
-#include "services/network/network_service.h"
-#include "services/service_manager/public/cpp/service.h"
-
-using component_updater::ComponentUpdateService;
-
-namespace {
-const base::FilePath::CharType kSTHsDirName[] = FILE_PATH_LITERAL("sths");
-
-network::mojom::NetworkService* g_network_service_for_testing = nullptr;
-
-base::FilePath& GetInstallDir() {
-  static base::NoDestructor<base::FilePath> install_dir;
-  return *install_dir;
-}
-
-base::FilePath GetInstalledPath(const base::FilePath& base) {
-  return base.Append(FILE_PATH_LITERAL("_platform_specific"))
-      .Append(FILE_PATH_LITERAL("all"))
-      .Append(kSTHsDirName);
-}
-
-// Loads the STHs from |sths_path|, which contains a series of files that
-// are the in the form "[log_id].sth", where [log_id] is the hex-encoded ID
-// of the log, and the contents are JSON STH. Parsed STHs will be posted to
-// |callback| on |origin_task_runner|.
-void LoadSTHsFromDisk(
-    const base::FilePath& sths_path,
-    scoped_refptr<base::SequencedTaskRunner> origin_task_runner,
-    base::RepeatingCallback<void(const net::ct::SignedTreeHead&)> callback) {
-  base::FileEnumerator sth_file_enumerator(sths_path, false,
-                                           base::FileEnumerator::FILES,
-                                           FILE_PATH_LITERAL("*.sth"));
-  base::FilePath sth_file_path;
-
-  while (!(sth_file_path = sth_file_enumerator.Next()).empty()) {
-    DVLOG(1) << "Reading STH from file: " << sth_file_path.value();
-
-    const std::string log_id_hex =
-        sth_file_path.BaseName().RemoveExtension().MaybeAsASCII();
-    if (log_id_hex.empty()) {
-      DVLOG(1) << "Error extracting log_id from: "
-               << sth_file_path.BaseName().LossyDisplayName();
-      continue;
-    }
-
-    std::vector<uint8_t> decoding_output;
-    if (!base::HexStringToBytes(log_id_hex, &decoding_output)) {
-      DVLOG(1) << "Failed to decode Log ID: " << log_id_hex;
-      continue;
-    }
-
-    const std::string log_id(
-        reinterpret_cast<const char*>(decoding_output.data()),
-        decoding_output.size());
-
-    std::string json_sth;
-    {
-      base::ScopedBlockingCall scoped_blocking_call(
-          FROM_HERE, base::BlockingType::MAY_BLOCK);
-      if (!base::ReadFileToString(sth_file_path, &json_sth)) {
-        DVLOG(1) << "Failed reading from " << sth_file_path.value();
-        continue;
-      }
-    }
-
-    DVLOG(1) << "STH: Successfully read: " << json_sth;
-
-    int error_code = 0;
-    std::string error_message;
-    std::unique_ptr<base::Value> parsed_json =
-        base::JSONReader::ReadAndReturnErrorDeprecated(
-            json_sth, base::JSON_PARSE_RFC, &error_code, &error_message);
-
-    if (!parsed_json || error_code != base::JSONReader::JSON_NO_ERROR) {
-      DVLOG(1) << "STH loading failed: " << error_message << " for log: "
-               << base::HexEncode(log_id.data(), log_id.length());
-      continue;
-    }
-
-    DVLOG(1) << "STH parsing success for log: "
-             << base::HexEncode(log_id.data(), log_id.length());
-
-    net::ct::SignedTreeHead signed_tree_head;
-    if (!net::ct::FillSignedTreeHead(*parsed_json, &signed_tree_head)) {
-      LOG(ERROR) << "Failed to fill in signed tree head.";
-      continue;
-    }
-
-    // The log id is not a part of the response, fill in manually.
-    signed_tree_head.log_id = log_id;
-    origin_task_runner->PostTask(FROM_HERE,
-                                 base::BindOnce(callback, signed_tree_head));
-  }
-}
-
-// Indicates that a new STH has been loaded.
-void OnSTHLoaded(const net::ct::SignedTreeHead& sth) {
-  network::mojom::NetworkService* network_service =
-      g_network_service_for_testing ? g_network_service_for_testing
-                                    : content::GetNetworkService();
-  network_service->UpdateSignedTreeHead(sth);
-}
-
-}  // namespace
-
-namespace component_updater {
-
-// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
-// The extension id is: ojjgnpkioondelmggbekfhllhdaimnho
-const uint8_t kSthSetPublicKeySHA256[32] = {
-    0xe9, 0x96, 0xdf, 0xa8, 0xee, 0xd3, 0x4b, 0xc6, 0x61, 0x4a, 0x57,
-    0xbb, 0x73, 0x08, 0xcd, 0x7e, 0x51, 0x9b, 0xcc, 0x69, 0x08, 0x41,
-    0xe1, 0x96, 0x9f, 0x7c, 0xb1, 0x73, 0xef, 0x16, 0x80, 0x0a};
-
-const char kSTHSetFetcherManifestName[] = "Signed Tree Heads";
-
-STHSetComponentInstallerPolicy::STHSetComponentInstallerPolicy() {}
-
-STHSetComponentInstallerPolicy::~STHSetComponentInstallerPolicy() = default;
-
-// static
-void STHSetComponentInstallerPolicy::ReconfigureAfterNetworkRestart() {
-  if (!GetInstallDir().empty())
-    ConfigureNetworkService();
-}
-
-void STHSetComponentInstallerPolicy::SetNetworkServiceForTesting(
-    network::mojom::NetworkService* network_service) {
-  DCHECK(network_service);
-  g_network_service_for_testing = network_service;
-}
-
-bool STHSetComponentInstallerPolicy::
-    SupportsGroupPolicyEnabledComponentUpdates() const {
-  return false;
-}
-
-// Public data is delivered via this component, no need for encryption.
-bool STHSetComponentInstallerPolicy::RequiresNetworkEncryption() const {
-  return false;
-}
-
-update_client::CrxInstaller::Result
-STHSetComponentInstallerPolicy::OnCustomInstall(
-    const base::DictionaryValue& manifest,
-    const base::FilePath& install_dir) {
-  return update_client::CrxInstaller::Result(0);  // Nothing custom here.
-}
-
-void STHSetComponentInstallerPolicy::OnCustomUninstall() {}
-
-void STHSetComponentInstallerPolicy::ComponentReady(
-    const base::Version& version,
-    const base::FilePath& install_dir,
-    std::unique_ptr<base::DictionaryValue> manifest) {
-  GetInstallDir() = install_dir;
-  ConfigureNetworkService();
-}
-
-// Called during startup and installation before ComponentReady().
-bool STHSetComponentInstallerPolicy::VerifyInstallation(
-    const base::DictionaryValue& manifest,
-    const base::FilePath& install_dir) const {
-  return base::PathExists(GetInstalledPath(install_dir));
-}
-
-base::FilePath STHSetComponentInstallerPolicy::GetRelativeInstallDir() const {
-  return base::FilePath(FILE_PATH_LITERAL("CertificateTransparency"));
-}
-
-void STHSetComponentInstallerPolicy::GetHash(std::vector<uint8_t>* hash) const {
-  hash->assign(std::begin(kSthSetPublicKeySHA256),
-               std::end(kSthSetPublicKeySHA256));
-}
-
-std::string STHSetComponentInstallerPolicy::GetName() const {
-  return kSTHSetFetcherManifestName;
-}
-
-update_client::InstallerAttributes
-STHSetComponentInstallerPolicy::GetInstallerAttributes() const {
-  return update_client::InstallerAttributes();
-}
-
-std::vector<std::string> STHSetComponentInstallerPolicy::GetMimeTypes() const {
-  return std::vector<std::string>();
-}
-
-// static
-void STHSetComponentInstallerPolicy::ConfigureNetworkService() {
-  // Load and parse the STH JSON on a background task runner, then
-  // dispatch back to the current task runner with all of the successfully
-  // parsed results.
-  auto background_runner = base::CreateTaskRunnerWithTraits(
-      {base::TaskPriority::BEST_EFFORT, base::MayBlock()});
-  content::BrowserThread::PostAfterStartupTask(
-      FROM_HERE, background_runner,
-      base::BindOnce(&LoadSTHsFromDisk, GetInstalledPath(GetInstallDir()),
-                     base::SequencedTaskRunnerHandle::Get(),
-                     base::BindRepeating(OnSTHLoaded)));
-}
-
-void RegisterSTHSetComponent(ComponentUpdateService* cus,
-                             const base::FilePath& user_data_dir) {
-  DVLOG(1) << "Registering STH Set fetcher component.";
-  auto installer = base::MakeRefCounted<ComponentInstaller>(
-      std::make_unique<STHSetComponentInstallerPolicy>());
-  installer->Register(cus, base::Closure());
-}
-
-}  // namespace component_updater
diff --git a/chrome/browser/component_updater/sth_set_component_installer.h b/chrome/browser/component_updater/sth_set_component_installer.h
deleted file mode 100644
index 9d3b05c1..0000000
--- a/chrome/browser/component_updater/sth_set_component_installer.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_COMPONENT_UPDATER_STH_SET_COMPONENT_INSTALLER_H_
-#define CHROME_BROWSER_COMPONENT_UPDATER_STH_SET_COMPONENT_INSTALLER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/weak_ptr.h"
-#include "components/component_updater/component_installer.h"
-
-namespace network {
-namespace mojom {
-class NetworkService;
-}  // namespace mojom
-}  // namespace network
-
-namespace component_updater {
-
-class ComponentUpdateService;
-
-// Component for receiving Signed Tree Heads updates for Certificate
-// Transparency logs recognized in Chrome.
-// The STHs are in JSON format.
-// To identify the log each STH belongs to, the name of the file is
-// hex-encoded Log ID of the log that produced this STH.
-//
-// Notifications of each of the new STHs are sent to the
-// certificate_transparency::STHObserver, on the same task runner that this
-// object is created, so that it can take appropriate steps, including possible
-// persistence.
-class STHSetComponentInstallerPolicy : public ComponentInstallerPolicy {
- public:
-  STHSetComponentInstallerPolicy();
-  ~STHSetComponentInstallerPolicy() override;
-
-  // Update the STHs after a network process crash.
-  static void ReconfigureAfterNetworkRestart();
-
- private:
-  friend class STHSetComponentInstallerTest;
-  void SetNetworkServiceForTesting(
-      network::mojom::NetworkService* network_service);
-
-  // ComponentInstallerPolicy implementation.
-  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
-  bool RequiresNetworkEncryption() const override;
-  update_client::CrxInstaller::Result OnCustomInstall(
-      const base::DictionaryValue& manifest,
-      const base::FilePath& install_dir) override;
-  void OnCustomUninstall() override;
-  bool VerifyInstallation(const base::DictionaryValue& manifest,
-                          const base::FilePath& install_dir) const override;
-  void ComponentReady(const base::Version& version,
-                      const base::FilePath& install_dir,
-                      std::unique_ptr<base::DictionaryValue> manifest) override;
-  base::FilePath GetRelativeInstallDir() const override;
-  void GetHash(std::vector<uint8_t>* hash) const override;
-  std::string GetName() const override;
-  update_client::InstallerAttributes GetInstallerAttributes() const override;
-  std::vector<std::string> GetMimeTypes() const override;
-
-  static void ConfigureNetworkService();
-
-  DISALLOW_COPY_AND_ASSIGN(STHSetComponentInstallerPolicy);
-};
-
-void RegisterSTHSetComponent(ComponentUpdateService* cus,
-                             const base::FilePath& user_data_dir);
-
-}  // namespace component_updater
-
-#endif  // CHROME_BROWSER_COMPONENT_UPDATER_STH_SET_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/component_updater/sth_set_component_installer_unittest.cc b/chrome/browser/component_updater/sth_set_component_installer_unittest.cc
deleted file mode 100644
index 80911869..0000000
--- a/chrome/browser/component_updater/sth_set_component_installer_unittest.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/component_updater/sth_set_component_installer.h"
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "base/version.h"
-#include "components/certificate_transparency/sth_observer.h"
-#include "components/certificate_transparency/sth_reporter.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/test/ct_test_util.h"
-#include "services/data_decoder/public/cpp/testing_json_parser.h"
-#include "services/network/network_service.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-namespace component_updater {
-
-class StoringSTHObserver : public certificate_transparency::STHObserver {
- public:
-  void NewSTHObserved(const net::ct::SignedTreeHead& sth) override {
-    sths[sth.log_id] = sth;
-  }
-
-  std::map<std::string, net::ct::SignedTreeHead> sths;
-};
-
-class STHSetComponentInstallerTest : public PlatformTest {
- public:
-  STHSetComponentInstallerTest()
-      : network_service_(std::make_unique<network::NetworkService>(nullptr)) {
-    network_service_->sth_reporter()->RegisterObserver(&observer_);
-  }
-
-  ~STHSetComponentInstallerTest() override {
-    network_service_->sth_reporter()->UnregisterObserver(&observer_);
-  }
-
-  void SetUp() override {
-    PlatformTest::SetUp();
-
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-
-    policy_ = std::make_unique<STHSetComponentInstallerPolicy>();
-    policy_->SetNetworkServiceForTesting(network_service_.get());
-  }
-
-  void SimulateCrash() {
-    network_service_->sth_reporter()->UnregisterObserver(&observer_);
-    observer_.sths.clear();
-    network_service_.reset();
-    network_service_ = std::make_unique<network::NetworkService>(nullptr);
-    network_service_->sth_reporter()->RegisterObserver(&observer_);
-    policy_->SetNetworkServiceForTesting(network_service_.get());
-  }
-
-  void WriteSTHToFile(const std::string& sth_json,
-                      const base::FilePath& filename) {
-    ASSERT_EQ(static_cast<int32_t>(sth_json.length()),
-              base::WriteFile(filename, sth_json.data(), sth_json.length()));
-  }
-
-  base::FilePath GetSTHsDir() {
-    return temp_dir_.GetPath()
-        .Append(FILE_PATH_LITERAL("_platform_specific"))
-        .Append(FILE_PATH_LITERAL("all"))
-        .Append(FILE_PATH_LITERAL("sths"));
-  }
-
-  void CreateSTHsDir(const base::DictionaryValue& manifest,
-                     const base::FilePath& sths_dir) {
-    ASSERT_FALSE(policy_->VerifyInstallation(manifest, temp_dir_.GetPath()));
-    ASSERT_TRUE(base::CreateDirectory(sths_dir));
-  }
-
-  void LoadSTHs(const base::DictionaryValue& manifest,
-                const base::FilePath& sths_dir) {
-    ASSERT_TRUE(policy_->VerifyInstallation(manifest, temp_dir_.GetPath()));
-
-    const base::Version v("1.0");
-    policy_->ComponentReady(v, temp_dir_.GetPath(), manifest.CreateDeepCopy());
-    // Drain the RunLoop created by the TestBrowserThreadBundle
-    thread_bundle_.RunUntilIdle();
-  }
-
- protected:
-  content::TestBrowserThreadBundle thread_bundle_;
-
-  std::unique_ptr<network::NetworkService> network_service_;
-  StoringSTHObserver observer_;
-
-  base::ScopedTempDir temp_dir_;
-  std::unique_ptr<STHSetComponentInstallerPolicy> policy_;
-  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(STHSetComponentInstallerTest);
-};
-
-// Parses valid STH JSON in a file with valid hex encoding of log id.
-TEST_F(STHSetComponentInstallerTest, CanLoadAllSTHs) {
-  const base::DictionaryValue manifest;
-  const base::FilePath sths_dir(GetSTHsDir());
-  CreateSTHsDir(manifest, sths_dir);
-
-  const std::string good_sth_json = net::ct::GetSampleSTHAsJson();
-  const base::FilePath first_sth_file =
-      sths_dir.Append(FILE_PATH_LITERAL("616263.sth"));
-  WriteSTHToFile(good_sth_json, first_sth_file);
-
-  const base::FilePath second_sth_file =
-      sths_dir.Append(FILE_PATH_LITERAL("610064.sth"));
-  WriteSTHToFile(good_sth_json, second_sth_file);
-
-  LoadSTHs(manifest, sths_dir);
-
-  EXPECT_EQ(2u, observer_.sths.size());
-
-  const std::string first_log_id("abc");
-  ASSERT_TRUE(observer_.sths.find(first_log_id) != observer_.sths.end());
-  const net::ct::SignedTreeHead& first_sth(observer_.sths[first_log_id]);
-  EXPECT_EQ(21u, first_sth.tree_size);
-
-  const std::string second_log_id("a\00d", 3);
-  ASSERT_TRUE(observer_.sths.find(second_log_id) != observer_.sths.end());
-}
-
-// Does not notify of invalid STH JSON.
-TEST_F(STHSetComponentInstallerTest, DoesNotLoadInvalidJSON) {
-  const base::DictionaryValue manifest;
-  const base::FilePath sths_dir(GetSTHsDir());
-  CreateSTHsDir(manifest, sths_dir);
-
-  const base::FilePath invalid_sth =
-      sths_dir.Append(FILE_PATH_LITERAL("010101.sth"));
-  WriteSTHToFile(std::string("{invalid json}"), invalid_sth);
-
-  LoadSTHs(manifest, sths_dir);
-  EXPECT_EQ(0u, observer_.sths.size());
-}
-
-// Does not notify of valid JSON but in a file not hex-encoded log id.
-TEST_F(STHSetComponentInstallerTest,
-       DoesNotLoadValidJSONFromFileNotHexEncoded) {
-  const base::DictionaryValue manifest;
-  const base::FilePath sths_dir(GetSTHsDir());
-  CreateSTHsDir(manifest, sths_dir);
-
-  const base::FilePath not_hex_sth_file =
-      sths_dir.Append(FILE_PATH_LITERAL("nothex.sth"));
-  WriteSTHToFile(net::ct::GetSampleSTHAsJson(), not_hex_sth_file);
-
-  LoadSTHs(manifest, sths_dir);
-  EXPECT_EQ(0u, observer_.sths.size());
-}
-
-// Tests recovery after network process crashes.
-TEST_F(STHSetComponentInstallerTest, ReconfiguresAfterRestart) {
-  const base::DictionaryValue manifest;
-  const base::FilePath sths_dir(GetSTHsDir());
-  CreateSTHsDir(manifest, sths_dir);
-
-  const std::string good_sth_json = net::ct::GetSampleSTHAsJson();
-  const base::FilePath sth_file =
-      sths_dir.Append(FILE_PATH_LITERAL("616263.sth"));
-  WriteSTHToFile(good_sth_json, sth_file);
-
-  LoadSTHs(manifest, sths_dir);
-
-  const std::string log_id("abc");
-  ASSERT_TRUE(observer_.sths.find(log_id) != observer_.sths.end());
-  const net::ct::SignedTreeHead& sth(observer_.sths[log_id]);
-  EXPECT_EQ(21u, sth.tree_size);
-
-  // Simulate a Network Service crash
-  SimulateCrash();
-  STHSetComponentInstallerPolicy::ReconfigureAfterNetworkRestart();
-  thread_bundle_.RunUntilIdle();
-
-  ASSERT_TRUE(observer_.sths.find(log_id) != observer_.sths.end());
-}
-
-}  // namespace component_updater
diff --git a/chrome/browser/component_updater/sth_set_component_remover.cc b/chrome/browser/component_updater/sth_set_component_remover.cc
new file mode 100644
index 0000000..2e3791c
--- /dev/null
+++ b/chrome/browser/component_updater/sth_set_component_remover.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 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/component_updater/sth_set_component_remover.h"
+
+#include "base/bind.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/task/post_task.h"
+#include "base/version.h"
+
+namespace component_updater {
+
+namespace {
+
+void CleanupOnWorker(const base::FilePath& sth_directory) {
+  const base::FilePath base_dir = sth_directory.DirName();
+  base::FileEnumerator file_enumerator(base_dir, false,
+                                       base::FileEnumerator::DIRECTORIES);
+  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
+       path = file_enumerator.Next()) {
+    base::Version version(path.BaseName().MaybeAsASCII());
+
+    // Ignore folders that don't have valid version names. These folders are
+    // not managed by the component installer, so don't try to remove them.
+    if (!version.IsValid())
+      continue;
+
+    if (!base::DeleteFile(path, true)) {
+      DLOG(ERROR) << "Couldn't delete " << path.value();
+    }
+  }
+
+  if (base::IsDirectoryEmpty(base_dir)) {
+    if (!base::DeleteFile(base_dir, false)) {
+      DLOG(ERROR) << "Couldn't delete " << base_dir.value();
+    }
+  }
+}
+
+}  // namespace
+
+void DeleteLegacySTHSet(const base::FilePath& user_data_dir) {
+  base::PostTaskWithTraits(
+      FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+      base::BindOnce(&CleanupOnWorker, user_data_dir.Append(FILE_PATH_LITERAL(
+                                           "CertificateTransparency"))));
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/sth_set_component_remover.h b/chrome/browser/component_updater/sth_set_component_remover.h
new file mode 100644
index 0000000..51b028d
--- /dev/null
+++ b/chrome/browser/component_updater/sth_set_component_remover.h
@@ -0,0 +1,19 @@
+// Copyright 2019 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_COMPONENT_UPDATER_STH_SET_COMPONENT_REMOVER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_STH_SET_COMPONENT_REMOVER_H_
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace component_updater {
+
+// TODO(rsleevi): Remove in M86 or later.
+void DeleteLegacySTHSet(const base::FilePath& user_data_dir);
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_STH_SET_COMPONENT_REMOVER_H_
diff --git a/chrome/browser/download/download_open_prompt.cc b/chrome/browser/download/download_open_prompt.cc
index 19f0bc94..aac377f 100644
--- a/chrome/browser/download/download_open_prompt.cc
+++ b/chrome/browser/download/download_open_prompt.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/download/download_open_prompt.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -102,10 +105,11 @@
     const std::string& extension_name,
     const base::FilePath& file_path,
     DownloadOpenPrompt::OpenCallback open_callback) {
-  DownloadOpenConfirmationDialog* prompt = new DownloadOpenConfirmationDialog(
+  auto prompt = std::make_unique<DownloadOpenConfirmationDialog>(
       web_contents, extension_name, file_path, std::move(open_callback));
-  TabModalConfirmDialog::Create(prompt, web_contents);
-  return prompt;
+  DownloadOpenConfirmationDialog* prompt_observer = prompt.get();
+  TabModalConfirmDialog::Create(std::move(prompt), web_contents);
+  return prompt_observer;
 }
 
 void DownloadOpenPrompt::AcceptConfirmationDialogForTesting(
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
index fd6031c..62a4b0c 100644
--- a/chrome/browser/extensions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -544,7 +544,8 @@
         TestingBrowserProcess::GetGlobal());
     wallpaper_controller_client_ =
         std::make_unique<WallpaperControllerClient>();
-    wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+    wallpaper_controller_client_->InitForTesting(
+        test_wallpaper_controller_.CreateInterfacePtr());
     g_browser_process->local_state()->SetString(
         "PublicAccountPendingDataRemoval", user_email);
     user_manager::UserManager::Get()->UserLoggedIn(account_id, user_id_hash,
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_api_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_api_unittest.cc
index 3c29f4d..9cff1e3 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_api_unittest.cc
@@ -114,6 +114,12 @@
     browser()->tab_strip_model()->DetachWebContentsAt(0);
   browser_window_.reset();
   content::BrowserSideNavigationTearDown();
+
+  // Make sure the NetworkContext owned by SafeBrowsingService is destructed
+  // before the NetworkService object..
+  TestingBrowserProcess::GetGlobal()->safe_browsing_service()->ShutDown();
+  TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(nullptr);
+
   ExtensionServiceTestBase::TearDown();
 }
 
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
index 3c0e379..b9b3e4f 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -411,20 +411,8 @@
     const GURL& effective_site_url) {
   const Extension* extension = GetEnabledExtensions(browser_or_resource_context)
                                    ->GetExtensionOrAppByURL(effective_site_url);
-  if (!extension)
-    return false;
-
-  // Always isolate Chrome Web Store.
-  if (extension->id() == kWebStoreAppId)
-    return true;
-
-  // Extensions should be isolated, except for hosted apps. Isolating hosted
-  // apps is a good idea, but ought to be a separate knob.
-  if (extension->is_hosted_app())
-    return false;
-
   // Isolate all extensions.
-  return true;
+  return extension != nullptr;
 }
 
 // static
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index 65051af..5c903214 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -259,6 +259,11 @@
         browser_test_flags |=
             ExtensionBrowserTest::kFlagAllowOldManifestVersions;
       }
+      if (flags & kFlagRunAsServiceWorkerBasedExtension) {
+        browser_test_flags |=
+            ExtensionBrowserTest::kFlagRunAsServiceWorkerBasedExtension;
+      }
+
       extension = LoadExtensionWithFlags(extension_path, browser_test_flags);
     }
     if (!extension) {
diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h
index 23400bc4..8253bf58 100644
--- a/chrome/browser/extensions/extension_apitest.h
+++ b/chrome/browser/extensions/extension_apitest.h
@@ -61,6 +61,10 @@
     // Load the extension using //extensions/test/data/ as the root path instead
     // of loading from //chrome/test/data/extensions/api_test/.
     kFlagUseRootExtensionsDir = 1 << 7,
+
+    // Load the event page extension as a Service Worker based background
+    // extension.
+    kFlagRunAsServiceWorkerBasedExtension = 1 << 8,
   };
 
   ExtensionApiTest();
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index d6e9c59..0c4199a5 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -67,8 +68,12 @@
 #include "extensions/browser/notification_types.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/browser/uninstall_reason.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension_set.h"
+#include "extensions/common/file_test_util.h"
+#include "extensions/common/file_util.h"
 #include "extensions/common/switches.h"
+#include "extensions/common/value_builder.h"
 #include "net/url_request/url_request_file_job.h"
 
 #if defined(OS_CHROMEOS)
@@ -237,7 +242,12 @@
 
 const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
     const base::FilePath& path, int flags) {
-  return LoadExtensionWithInstallParam(path, flags, std::string());
+  base::FilePath extension_path = path;
+  if (flags & kFlagRunAsServiceWorkerBasedExtension) {
+    if (!CreateServiceWorkerBasedExtension(path, &extension_path))
+      return nullptr;
+  }
+  return LoadExtensionWithInstallParam(extension_path, flags, std::string());
 }
 
 const Extension* ExtensionBrowserTest::LoadExtensionWithInstallParam(
@@ -258,6 +268,123 @@
   return extension.get();
 }
 
+bool ExtensionBrowserTest::CreateServiceWorkerBasedExtension(
+    const base::FilePath& path,
+    base::FilePath* out_path) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  // This dir will contain all files for the Service Worker based extension.
+  base::FilePath temp_extension_container;
+  if (!base::CreateTemporaryDirInDir(temp_dir_.GetPath(),
+                                     base::FilePath::StringType(),
+                                     &temp_extension_container)) {
+    ADD_FAILURE() << "Could not create temporary dir for test under "
+                  << temp_dir_.GetPath();
+    return false;
+  }
+
+  // Copy all files from test dir to temp dir.
+  if (!base::CopyDirectory(path, temp_extension_container,
+                           true /* recursive */)) {
+    ADD_FAILURE() << path.value() << " could not be copied to "
+                  << temp_extension_container.value();
+    return false;
+  }
+
+  const base::FilePath extension_root =
+      temp_extension_container.Append(path.BaseName());
+
+  std::string error;
+  std::unique_ptr<base::DictionaryValue> manifest_dict =
+      file_util::LoadManifest(extension_root, &error);
+  if (!manifest_dict) {
+    ADD_FAILURE() << path.value() << " could not load manifest: " << error;
+    return false;
+  }
+
+  // Retrieve the value of the "background" key and verify that it is
+  // non-persistent and specifies JS files.
+  // Persistent background pages or background pages that specify HTML files
+  // are not supported.
+  base::Value* background_dict =
+      manifest_dict->FindKeyOfType("background", base::Value::Type::DICTIONARY);
+  if (!background_dict) {
+    ADD_FAILURE() << path.value()
+                  << " 'background' key not found in manifest.json";
+    return false;
+  }
+  {
+    base::Value* background_persistent = background_dict->FindKeyOfType(
+        "persistent", base::Value::Type::BOOLEAN);
+    if (!background_persistent || background_persistent->GetBool()) {
+      ADD_FAILURE() << path.value()
+                    << ": Only event pages can be loaded as SW extension.";
+      return false;
+    }
+  }
+  base::Value* background_scripts_list =
+      background_dict->FindKeyOfType("scripts", base::Value::Type::LIST);
+  if (!background_scripts_list) {
+    ADD_FAILURE() << path.value()
+                  << ": Only event pages with JS script(s) can be loaded "
+                     "as SW extension.";
+    return false;
+  }
+
+  // Number of JS scripts must be > 1.
+  base::Value::ListStorage& scripts_list = background_scripts_list->GetList();
+  if (scripts_list.size() < 1) {
+    ADD_FAILURE() << path.value()
+                  << ": Only event pages with JS script(s) can be loaded "
+                     " as SW extension.";
+    return false;
+  }
+
+  // Generate combined script as Service Worker script using importScripts().
+  constexpr const char kGeneratedSWFileName[] = "generated_service_worker__.js";
+
+  std::vector<std::string> script_filenames;
+  for (const base::Value& script : scripts_list)
+    script_filenames.push_back(base::StrCat({"'", script.GetString(), "'"}));
+
+  base::FilePath combined_script_filepath =
+      extension_root.AppendASCII(kGeneratedSWFileName);
+  // Collision with generated script filename.
+  if (base::PathExists(combined_script_filepath)) {
+    ADD_FAILURE() << combined_script_filepath.value()
+                  << " already exists, make sure " << path.value()
+                  << " does not contained file named " << kGeneratedSWFileName;
+    return false;
+  }
+  std::string generated_sw_script_content = base::StringPrintf(
+      "importScripts(%s);", base::JoinString(script_filenames, ",").c_str());
+  if (!file_test_util::WriteFile(combined_script_filepath,
+                                 generated_sw_script_content)) {
+    ADD_FAILURE() << "Could not write combined Service Worker script to: "
+                  << combined_script_filepath.value();
+    return false;
+  }
+
+  // Remove the existing background specification and replace it with a service
+  // worker.
+  background_dict->RemoveKey("persistent");
+  background_dict->RemoveKey("scripts");
+  background_dict->SetStringPath("service_worker", kGeneratedSWFileName);
+
+  // Write out manifest.json.
+  DictionaryBuilder manifest_builder(*manifest_dict);
+  std::string manifest_contents = manifest_builder.ToJSON();
+  base::FilePath manifest_path = extension_root.Append(kManifestFilename);
+  if (!file_test_util::WriteFile(manifest_path, manifest_contents)) {
+    ADD_FAILURE() << "Could not write manifest file to "
+                  << manifest_path.value();
+    return false;
+  }
+
+  *out_path = extension_root;
+  return true;
+}
+
 const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
     const base::FilePath& path,
     const base::FilePath::CharType* manifest_relative_path) {
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index cd66718..e892d781 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -62,6 +62,9 @@
     // Allow older manifest versions (typically these can't be loaded - we allow
     // them for testing).
     kFlagAllowOldManifestVersions = 1 << 3,
+
+    // Load the provided extension as Service Worker based extension.
+    kFlagRunAsServiceWorkerBasedExtension = 1 << 4,
   };
 
   ExtensionBrowserTest();
@@ -117,6 +120,16 @@
       int flags,
       const std::string& install_param);
 
+  // Converts an extension from |path| to a Service Worker based extension and
+  // returns true on success.
+  // If successful, |out_path| contains path of the converted extension.
+  //
+  // NOTE: The conversion works only for extensions with background.scripts and
+  // background.persistent = false; persistent background pages and
+  // background.page are not supported.
+  bool CreateServiceWorkerBasedExtension(const base::FilePath& path,
+                                         base::FilePath* out_path);
+
   // Loads unpacked extension from |path| with manifest |manifest_relative_path|
   // and imitates that it is a component extension.
   // |manifest_relative_path| is relative to |path|.
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index 504187e..99be1aa 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -1777,6 +1777,13 @@
   EXPECT_FALSE(ProcessManager::Get(profile())->HasServiceWorker(*worker_id));
 }
 
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBasedBackgroundTest,
+                       ChromeRuntimeOpenOptionsPage) {
+  ASSERT_TRUE(RunExtensionTestWithFlags("runtime/open_options_page",
+                                        kFlagRunAsServiceWorkerBasedExtension))
+      << message_;
+}
+
 // Tests that console messages logged by extension service workers, both via
 // the typical console.* methods and via our custom bindings console, are
 // passed through the normal ServiceWorker console messaging and are
diff --git a/chrome/browser/history/top_sites_factory.cc b/chrome/browser/history/top_sites_factory.cc
index d83305c..3839498 100644
--- a/chrome/browser/history/top_sites_factory.cc
+++ b/chrome/browser/history/top_sites_factory.cc
@@ -25,11 +25,14 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/locale_settings.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/grit/components_scaled_resources.h"
 #include "components/history/core/browser/history_constants.h"
 #include "components/history/core/browser/top_sites_impl.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/ntp_tiles/features.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
@@ -53,6 +56,14 @@
 #if !defined(OS_ANDROID)
 // Android does not use prepopulated pages.
 const RawPrepopulatedPage kRawPrepopulatedPages[] = {
+#if defined(GOOGLE_CHROME_BUILD)
+    {
+        IDS_NTP_DEFAULT_SEARCH_URL,
+        IDS_NTP_DEFAULT_SEARCH_TITLE,
+        IDS_ONBOARDING_WELCOME_SEARCH,
+        SkColorSetRGB(63, 132, 197),
+    },
+#endif
     {
         IDS_WEBSTORE_URL,
         IDS_EXTENSION_WEB_STORE_TITLE_SHORT,
@@ -73,6 +84,11 @@
     const RawPrepopulatedPage& page = kRawPrepopulatedPages[i];
     if (hide_web_store_icon && page.url_id == IDS_WEBSTORE_URL)
       continue;
+    if (page.url_id == IDS_NTP_DEFAULT_SEARCH_URL &&
+        !base::FeatureList::IsEnabled(ntp_tiles::kDefaultSearchShortcut)) {
+      continue;
+    }
+
     prepopulated_pages->push_back(history::PrepopulatedPage(
         GURL(l10n_util::GetStringUTF8(page.url_id)),
         l10n_util::GetStringUTF16(page.title_id), page.favicon_id, page.color));
diff --git a/chrome/browser/installable/installable_manager.cc b/chrome/browser/installable/installable_manager.cc
index c47554b3..e53ba98 100644
--- a/chrome/browser/installable/installable_manager.cc
+++ b/chrome/browser/installable/installable_manager.cc
@@ -577,11 +577,6 @@
     return false;
   }
 
-  if (manifest.prefer_related_applications) {
-    is_valid = false;
-    valid_manifest_->errors.push_back(PREFER_RELATED_APPLICATIONS);
-  }
-
   if (!manifest.start_url.is_valid()) {
     valid_manifest_->errors.push_back(START_URL_NOT_VALID);
     is_valid = false;
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc
index e78d7d9..53725cf 100644
--- a/chrome/browser/installable/installable_manager_browsertest.cc
+++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -634,8 +634,7 @@
     EXPECT_FALSE(tester->has_worker());
     EXPECT_EQ(
         std::vector<InstallableStatusCode>(
-            {PREFER_RELATED_APPLICATIONS, START_URL_NOT_VALID,
-             MANIFEST_MISSING_NAME_OR_SHORT_NAME,
+            {START_URL_NOT_VALID, MANIFEST_MISSING_NAME_OR_SHORT_NAME,
              MANIFEST_DISPLAY_NOT_SUPPORTED, MANIFEST_MISSING_SUITABLE_ICON}),
         tester->errors());
   }
@@ -1629,8 +1628,7 @@
   run_loop.Run();
 
   EXPECT_EQ(std::vector<InstallableStatusCode>(
-                {PREFER_RELATED_APPLICATIONS, START_URL_NOT_VALID,
-                 MANIFEST_MISSING_NAME_OR_SHORT_NAME,
+                {START_URL_NOT_VALID, MANIFEST_MISSING_NAME_OR_SHORT_NAME,
                  MANIFEST_DISPLAY_NOT_SUPPORTED, MANIFEST_MISSING_SUITABLE_ICON,
                  NO_URL_FOR_SERVICE_WORKER, NO_ACCEPTABLE_ICON}),
             tester->errors());
@@ -1652,8 +1650,7 @@
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
                        GetAllErrorsWithPlayAppManifest) {
   EXPECT_EQ(std::vector<std::string>(
-                {GetErrorMessage(PREFER_RELATED_APPLICATIONS),
-                 GetErrorMessage(START_URL_NOT_VALID),
+                {GetErrorMessage(START_URL_NOT_VALID),
                  GetErrorMessage(MANIFEST_MISSING_NAME_OR_SHORT_NAME),
                  GetErrorMessage(MANIFEST_DISPLAY_NOT_SUPPORTED),
                  GetErrorMessage(MANIFEST_MISSING_SUITABLE_ICON),
diff --git a/chrome/browser/installable/installable_manager_unittest.cc b/chrome/browser/installable/installable_manager_unittest.cc
index de3bfd8..4ae9bbb 100644
--- a/chrome/browser/installable/installable_manager_unittest.cc
+++ b/chrome/browser/installable/installable_manager_unittest.cc
@@ -213,11 +213,3 @@
   EXPECT_TRUE(IsManifestValid(manifest));
   EXPECT_EQ(NO_ERROR_DETECTED, GetErrorCode());
 }
-
-TEST_F(InstallableManagerUnitTest, PreferRelatedApplications) {
-  blink::Manifest manifest = GetValidManifest();
-
-  manifest.prefer_related_applications = true;
-  EXPECT_FALSE(IsManifestValid(manifest));
-  EXPECT_EQ(PREFER_RELATED_APPLICATIONS, GetErrorCode());
-}
diff --git a/chrome/browser/io_thread_browsertest.cc b/chrome/browser/io_thread_browsertest.cc
index 96a18ef3..43b2941 100644
--- a/chrome/browser/io_thread_browsertest.cc
+++ b/chrome/browser/io_thread_browsertest.cc
@@ -20,8 +20,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "components/certificate_transparency/features.h"
-#include "components/certificate_transparency/tree_state_tracker.h"
 #include "components/prefs/pref_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 183a377a..685c3bb 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/component_updater/crl_set_component_installer.h"
-#include "chrome/browser/component_updater/sth_set_component_installer.h"
 #include "chrome/browser/io_thread.h"
 #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
@@ -601,10 +600,6 @@
 
   // Asynchronously reapply the most recently received CRLSet (if any).
   component_updater::CRLSetPolicy::ReconfigureAfterNetworkRestart();
-
-  // Asynchronously reapply the most recently received STHs (if any).
-  component_updater::STHSetComponentInstallerPolicy::
-      ReconfigureAfterNetworkRestart();
 }
 
 void SystemNetworkContextManager::DisableQuic() {
@@ -691,7 +686,6 @@
     network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
     log_info->public_key = std::string(ct_log.log_key, ct_log.log_key_length);
     log_info->name = ct_log.log_name;
-    log_info->dns_api_endpoint = ct_log.log_dns_domain;
     network_context_params->ct_logs.push_back(std::move(log_info));
   }
 
diff --git a/chrome/browser/notifications/scheduler/BUILD.gn b/chrome/browser/notifications/scheduler/BUILD.gn
index 0a6d6b9a..d101031 100644
--- a/chrome/browser/notifications/scheduler/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/BUILD.gn
@@ -20,7 +20,6 @@
 
 source_set("public") {
   sources = [
-    "internal_types.h",
     "notification_background_task_scheduler.h",
     "notification_data.cc",
     "notification_data.h",
@@ -86,7 +85,6 @@
     "impression_types.h",
     "init_aware_scheduler.cc",
     "init_aware_scheduler.h",
-    "internal_types.h",
     "notification_entry.cc",
     "notification_entry.h",
     "notification_schedule_service_impl.cc",
@@ -143,3 +141,11 @@
     "//testing/gtest",
   ]
 }
+
+if (is_android) {
+  java_cpp_enum("jni_enums") {
+    sources = [
+      "notification_scheduler_types.h",
+    ]
+  }
+}
diff --git a/chrome/browser/notifications/scheduler/DEPS b/chrome/browser/notifications/scheduler/DEPS
new file mode 100644
index 0000000..c80012b5
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+jni",
+]
diff --git a/chrome/browser/notifications/scheduler/background_task_coordinator.h b/chrome/browser/notifications/scheduler/background_task_coordinator.h
index 19e622b..0487485 100644
--- a/chrome/browser/notifications/scheduler/background_task_coordinator.h
+++ b/chrome/browser/notifications/scheduler/background_task_coordinator.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/internal_types.h"
 #include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
 
 namespace base {
diff --git a/chrome/browser/notifications/scheduler/display_decider.h b/chrome/browser/notifications/scheduler/display_decider.h
index 72ee364..2e4187c6 100644
--- a/chrome/browser/notifications/scheduler/display_decider.h
+++ b/chrome/browser/notifications/scheduler/display_decider.h
@@ -13,7 +13,6 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/internal_types.h"
 #include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
 
 namespace notifications {
diff --git a/chrome/browser/notifications/scheduler/distribution_policy.h b/chrome/browser/notifications/scheduler/distribution_policy.h
index 25e1827..762ba338 100644
--- a/chrome/browser/notifications/scheduler/distribution_policy.h
+++ b/chrome/browser/notifications/scheduler/distribution_policy.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/internal_types.h"
+#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
 
 namespace notifications {
 
diff --git a/chrome/browser/notifications/scheduler/impression_types.h b/chrome/browser/notifications/scheduler/impression_types.h
index 85d1e838..607f0dd 100644
--- a/chrome/browser/notifications/scheduler/impression_types.h
+++ b/chrome/browser/notifications/scheduler/impression_types.h
@@ -11,7 +11,6 @@
 
 #include "base/optional.h"
 #include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/internal_types.h"
 #include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
 
 namespace notifications {
diff --git a/chrome/browser/notifications/scheduler/internal_types.h b/chrome/browser/notifications/scheduler/internal_types.h
deleted file mode 100644
index 0a994d6..0000000
--- a/chrome/browser/notifications/scheduler/internal_types.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_TYPES_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_TYPES_H_
-
-namespace notifications {
-
-// Enum to describe the time to process scheduled notification data.
-enum class SchedulerTaskTime {
-  // The system is started from normal user launch or other background
-  // tasks.
-  kUnknown = 0,
-
-  // Background task runs in the morning.
-  kMorning = 1,
-
-  // Background task runs in the evening.
-  kEvening = 2,
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h
index c59b3ad1..3c33cce5 100644
--- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h
+++ b/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/internal_types.h"
+#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
 
 namespace notifications {
 
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/notification_scheduler_types.h
index 12b7546..95decdff 100644
--- a/chrome/browser/notifications/scheduler/notification_scheduler_types.h
+++ b/chrome/browser/notifications/scheduler/notification_scheduler_types.h
@@ -7,6 +7,20 @@
 
 namespace notifications {
 
+// Enum to describe the time to process scheduled notification data.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.chrome.browser.notifications.scheduler)
+enum class SchedulerTaskTime {
+  // The system is started from normal user launch or other background
+  // tasks.
+  kUnknown = 0,
+  // Background task runs in the morning.
+  kMorning = 1,
+  // Background task runs in the evening.
+  kEvening = 2,
+};
+
 // The type of a list of clients using the notification scheduler system.
 enum class SchedulerClientType {
   // Test only values.
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 2e080f6..86d3ae5 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -147,7 +147,7 @@
 #include "chrome/browser/extensions/browser_context_keyed_service_factories.h"
 #include "chrome/browser/extensions/extension_management.h"
 #include "chrome/browser/ui/web_applications/web_app_metrics_factory.h"
-#include "chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.h"
+#include "chrome/browser/ui/web_applications/web_app_ui_service_factory.h"
 #include "chrome/browser/web_applications/web_app_provider_factory.h"
 #include "extensions/browser/api/networking_private/networking_private_delegate_factory.h"
 #include "extensions/browser/browser_context_keyed_service_factories.h"
@@ -400,7 +400,7 @@
 #endif
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   web_app::WebAppProviderFactory::GetInstance();
-  web_app::WebAppUiDelegateImplFactory::GetInstance();
+  web_app::WebAppUiServiceFactory::GetInstance();
   web_app::WebAppMetricsFactory::GetInstance();
 #endif
   WebDataServiceFactory::GetInstance();
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 7d9197c..a77de6f 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -47,7 +47,6 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/about_handler/about_protocol_handler.h"
-#include "components/certificate_transparency/tree_state_tracker.h"
 #include "components/content_settings/core/browser/content_settings_provider.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index fb54be05..2e164d3e 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -153,7 +153,8 @@
     cl->AppendSwitch(switches::kTestType);
     wallpaper_controller_client_ =
         std::make_unique<WallpaperControllerClient>();
-    wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+    wallpaper_controller_client_->InitForTesting(
+        test_wallpaper_controller_.CreateInterfacePtr());
 
     // Have to manually reset the session type in between test runs because
     // some tests log in users.
@@ -599,7 +600,8 @@
 
     wallpaper_controller_client_ =
         std::make_unique<WallpaperControllerClient>();
-    wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+    wallpaper_controller_client_->InitForTesting(
+        test_wallpaper_controller_.CreateInterfacePtr());
 
     // Have to manually reset the session type in between test runs because
     // RegisterUser() changes it.
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher.cc b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
index 9036c25..e34499b18 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
@@ -48,7 +48,14 @@
 // incrementally from 1.
 int64_t NewInt64ForLabelIdOrQueryId() {
   static int64_t id = 0;
-  return ++id;
+  // The id is shifted 13 bits so that the lower bits are reserved for counting
+  // multiple queries.
+  // We choose 13 so that the lower bits for counting multiple queries and
+  // higher bits for labeling queries are both unlikely to overflow. (lower bits
+  // only overflows when we have more than 8192 queries without labeling events;
+  // higher bits only overflow when we have more than 100 billion discards.
+  constexpr int kIdShiftBits = 13;
+  return (++id) << kIdShiftBits;
 }
 
 }  // namespace
@@ -103,8 +110,8 @@
     // Copy the replaced tab's stats.
     page_metrics_ = replaced_tab.page_metrics_;
 
-    // Record previous ukm_source_id from the |replaced_tab|.
-    previous_ukm_source_id_ = replaced_tab.ukm_source_id_;
+    // Recover the ukm_source_id from the |replaced_tab|.
+    ukm_source_id_ = replaced_tab.ukm_source_id_;
 
     // Copy the replaced label_id_.
     label_id_ = replaced_tab.label_id_;
@@ -161,10 +168,12 @@
     if (!tab.has_value())
       return;
 
-    // A new label_id_ is generated for this query.
+    // Update label_id_: a new label_id is generated for this query if the
+    // label_id_ is 0; otherwise the old label_id_ is incremented. This allows
+    // us to better pairing TabMetrics with ForegroundedOrClosed events offline.
     // The same label_id_ will be logged with ForegroundedOrClosed event later
     // on so that TabFeatures can be paired with ForegroundedOrClosed.
-    label_id_ = NewInt64ForLabelIdOrQueryId();
+    label_id_ = label_id_ ? label_id_ + 1 : NewInt64ForLabelIdOrQueryId();
 
     TabActivityWatcher::GetInstance()->tab_metrics_logger_->LogTabMetrics(
         ukm_source_id_, tab.value(), web_contents(), label_id_);
@@ -426,11 +435,8 @@
     metrics.total_tab_count = mru.total;
     metrics.label_id = label_id_;
 
-    const ukm::SourceId source_id = discarded_since_backgrounded_
-                                        ? previous_ukm_source_id_
-                                        : ukm_source_id_;
     TabActivityWatcher::GetInstance()
-        ->tab_metrics_logger_->LogForegroundedOrClosedMetrics(source_id,
+        ->tab_metrics_logger_->LogForegroundedOrClosedMetrics(ukm_source_id_,
                                                               metrics);
     // label_id_ is reset whenever a label is logged.
     // A new label_id_ is generated when a query happens inside
@@ -445,9 +451,6 @@
   // Updated when a navigation is finished.
   ukm::SourceId ukm_source_id_ = 0;
 
-  // Recorded when a WebContents is replaced by another.
-  ukm::SourceId previous_ukm_source_id_ = 0;
-
   // When the tab was created.
   base::TimeTicks creation_time_;
 
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
index d4ac8eb6..a8ebd87 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
@@ -56,6 +56,7 @@
 
 // These parameters don't affect logging.
 const bool kCheckNavigationSuccess = true;
+const int64_t kIdShift = 1 << 13;
 
 }  // namespace
 
@@ -381,6 +382,11 @@
 // Tests discarded tab is recorded correctly.
 IN_PROC_BROWSER_TEST_F(TabActivityWatcherUkmTest,
                        DiscardedTabGetsPreviousSourceId) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kTabRanker,
+      {{"number_of_oldest_tabs_to_log_with_TabRanker", "1"}});
+
   ukm::SourceId ukm_source_id_for_tab_0 = 0;
   ukm::SourceId ukm_source_id_for_tab_1 = 0;
 
@@ -407,6 +413,31 @@
       ->GetTabLifecycleUnitExternal(first_contents)
       ->DiscardTab();
 
+  // Call LogOldestNTabFeatures should log the oldest tab which is tab@0.
+  TabActivityWatcher::GetInstance()->LogOldestNTabFeatures();
+  {
+    SCOPED_TRACE("");
+    // tab feature of tab@0 should be logged correctly.
+    UkmMetricMap expected_tab_feature_values = kBasicMetricValues;
+    expected_tab_feature_values[TabManager_TabMetrics::kMRUIndexName] = 1;
+    expected_tab_feature_values
+        [TabManager_TabMetrics::kNumReactivationBeforeName] = 0;
+    expected_tab_feature_values[TabManager_TabMetrics::kTotalTabCountName] = 2;
+    expected_tab_feature_values[TabManager_TabMetrics::kWindowTabCountName] = 2;
+    expected_tab_feature_values[TabManager_TabMetrics::kLabelIdName] =
+        2 * kIdShift;
+    expected_tab_feature_values[TabManager_TabMetrics::kQueryIdName] =
+        1 * kIdShift;
+    expected_tab_feature_values
+        [TabManager_TabMetrics::kNavigationEntryCountName] = 2;
+
+    ukm_entry_checker_->ExpectNewEntry(kEntryName, test_urls_[0],
+                                       expected_tab_feature_values);
+    ExpectNewEntryWithSourceId(test_urls_[0], kEntryName, 2,
+                               expected_tab_feature_values,
+                               ukm_source_id_for_tab_0);
+  }
+
   // Switching to first tab logs a forgrounded event for test_urls_[0]
   // and a backgrounded event for test_urls_[1].
   browser()->tab_strip_model()->ActivateTabAt(
@@ -417,7 +448,7 @@
                                        kBasicMetricValues);
 
     ukm_source_id_for_tab_1 = ExpectNewEntryWithSourceId(
-        test_urls_[1], kEntryName, 2, kBasicMetricValues);
+        test_urls_[1], kEntryName, 3, kBasicMetricValues);
 
     ExpectNewEntryWithSourceId(
         test_urls_[0], kFOCEntryName, 1,
@@ -520,8 +551,10 @@
         [TabManager_TabMetrics::kNumReactivationBeforeName] = 0;
     expected_tab_feature_values[TabManager_TabMetrics::kTotalTabCountName] = 3;
     expected_tab_feature_values[TabManager_TabMetrics::kWindowTabCountName] = 3;
-    expected_tab_feature_values[TabManager_TabMetrics::kLabelIdName] = 2;
-    expected_tab_feature_values[TabManager_TabMetrics::kQueryIdName] = 1;
+    expected_tab_feature_values[TabManager_TabMetrics::kLabelIdName] =
+        2 * kIdShift;
+    expected_tab_feature_values[TabManager_TabMetrics::kQueryIdName] =
+        1 * kIdShift;
 
     ukm_entry_checker_->ExpectNewEntry(kEntryName, test_urls_[1],
                                        expected_tab_feature_values);
@@ -537,7 +570,8 @@
     ukm_entry_checker_->ExpectNewEntry(
         kFOCEntryName, test_urls_[1],
         {{TabManager_Background_ForegroundedOrClosed::kIsForegroundedName, 1},
-         {TabManager_Background_ForegroundedOrClosed::kLabelIdName, 2},
+         {TabManager_Background_ForegroundedOrClosed::kLabelIdName,
+          2 * kIdShift},
          {TabManager_Background_ForegroundedOrClosed::kIsDiscardedName, 0}});
   }
 
@@ -552,8 +586,10 @@
         [TabManager_TabMetrics::kNumReactivationBeforeName] = 0;
     expected_tab_feature_values[TabManager_TabMetrics::kTotalTabCountName] = 3;
     expected_tab_feature_values[TabManager_TabMetrics::kWindowTabCountName] = 3;
-    expected_tab_feature_values[TabManager_TabMetrics::kLabelIdName] = 4;
-    expected_tab_feature_values[TabManager_TabMetrics::kQueryIdName] = 3;
+    expected_tab_feature_values[TabManager_TabMetrics::kLabelIdName] =
+        4 * kIdShift;
+    expected_tab_feature_values[TabManager_TabMetrics::kQueryIdName] =
+        3 * kIdShift;
 
     ukm_entry_checker_->ExpectNewEntry(kEntryName, test_urls_[2],
                                        expected_tab_feature_values);
@@ -568,7 +604,8 @@
     ukm_entry_checker_->ExpectNewEntry(
         kFOCEntryName, test_urls_[2],
         {{TabManager_Background_ForegroundedOrClosed::kIsForegroundedName, 0},
-         {TabManager_Background_ForegroundedOrClosed::kLabelIdName, 4},
+         {TabManager_Background_ForegroundedOrClosed::kLabelIdName,
+          4 * kIdShift},
          {TabManager_Background_ForegroundedOrClosed::kIsDiscardedName, 0}});
 
     // Close Browser should log a ForegroundedOrClosed event for tab@0 with
@@ -601,8 +638,8 @@
     SCOPED_TRACE("");
     UkmMetricMap expected_metrics = kBasicMetricValues;
     expected_metrics[TabManager_TabMetrics::kNavigationEntryCountName] = 2;
-    expected_metrics[TabManager_TabMetrics::kLabelIdName] = 2;
-    expected_metrics[TabManager_TabMetrics::kQueryIdName] = 1;
+    expected_metrics[TabManager_TabMetrics::kLabelIdName] = 2 * kIdShift;
+    expected_metrics[TabManager_TabMetrics::kQueryIdName] = 1 * kIdShift;
     // tab feature of tab@0 should be logged correctly.
     ukm_entry_checker_->ExpectNewEntry(kEntryName, test_urls_[0],
                                        expected_metrics);
@@ -623,7 +660,8 @@
     UkmMetricMap expected_metrics = {
         {TabManager_Background_ForegroundedOrClosed::kIsForegroundedName, 1},
         {TabManager_Background_ForegroundedOrClosed::kIsDiscardedName, 1},
-        {TabManager_Background_ForegroundedOrClosed::kLabelIdName, 2}};
+        {TabManager_Background_ForegroundedOrClosed::kLabelIdName,
+         2 * kIdShift}};
 
     ukm_entry_checker_->ExpectNewEntry(kFOCEntryName, test_urls_[0],
                                        expected_metrics);
@@ -635,8 +673,8 @@
     SCOPED_TRACE("");
     // tab feature of tab@1 should be logged correctly.
     UkmMetricMap expected_metrics = kBasicMetricValues;
-    expected_metrics[TabManager_TabMetrics::kLabelIdName] = 4;
-    expected_metrics[TabManager_TabMetrics::kQueryIdName] = 3;
+    expected_metrics[TabManager_TabMetrics::kLabelIdName] = 4 * kIdShift;
+    expected_metrics[TabManager_TabMetrics::kQueryIdName] = 3 * kIdShift;
     ukm_entry_checker_->ExpectNewEntry(kEntryName, test_urls_[1],
                                        expected_metrics);
   }
@@ -654,10 +692,53 @@
     UkmMetricMap expected_metrics = {
         {TabManager_Background_ForegroundedOrClosed::kIsForegroundedName, 0},
         {TabManager_Background_ForegroundedOrClosed::kIsDiscardedName, 1},
-        {TabManager_Background_ForegroundedOrClosed::kLabelIdName, 4}};
+        {TabManager_Background_ForegroundedOrClosed::kLabelIdName,
+         4 * kIdShift}};
 
     ukm_entry_checker_->ExpectNewEntry(kFOCEntryName, test_urls_[1],
                                        expected_metrics);
   }
 }
+
+// Tests label_id is incremented if the LogOldestNTabFeatures is called second
+// times without logging the label first.
+IN_PROC_BROWSER_TEST_F(TabActivityWatcherUkmTest,
+                       TabsAlreadyHaveLabelIdGetIncrementalLabelIds) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kTabRanker,
+      {{"number_of_oldest_tabs_to_log_with_TabRanker", "1"},
+       {"disable_background_log_with_TabRanker", "true"}});
+
+  ui_test_utils::NavigateToURL(browser(), test_urls_[0]);
+  AddTabAtIndex(1, test_urls_[1], ui::PAGE_TRANSITION_LINK);
+  // No TabMetrics events are logged till now.
+  EXPECT_EQ(0u, ukm_entry_checker_->NumEntries(kEntryName));
+
+  // Log tab_0.
+  TabActivityWatcher::GetInstance()->LogOldestNTabFeatures();
+  {
+    SCOPED_TRACE("");
+    UkmMetricMap expected_metrics = kBasicMetricValues;
+    expected_metrics[TabManager_TabMetrics::kNavigationEntryCountName] = 2;
+    expected_metrics[TabManager_TabMetrics::kLabelIdName] = 2 * kIdShift;
+    expected_metrics[TabManager_TabMetrics::kQueryIdName] = 1 * kIdShift;
+    // tab feature of tab@0 should be logged correctly.
+    ukm_entry_checker_->ExpectNewEntry(kEntryName, test_urls_[0],
+                                       expected_metrics);
+  }
+
+  TabActivityWatcher::GetInstance()->LogOldestNTabFeatures();
+  {
+    SCOPED_TRACE("");
+    UkmMetricMap expected_metrics = kBasicMetricValues;
+    expected_metrics[TabManager_TabMetrics::kNavigationEntryCountName] = 2;
+    expected_metrics[TabManager_TabMetrics::kLabelIdName] = 2 * kIdShift + 1;
+    expected_metrics[TabManager_TabMetrics::kQueryIdName] = 3 * kIdShift;
+    // tab feature of tab@0 should be logged correctly.
+    ukm_entry_checker_->ExpectNewEntry(kEntryName, test_urls_[0],
+                                       expected_metrics);
+  }
+}
+
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
index a312a30..b852f6e 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
@@ -602,7 +602,9 @@
   tab_activity_simulator_.SwitchToTabAt(tab_strip_model, 1);
   {
     SCOPED_TRACE("");
-    ExpectNewEntry(kTestUrls[1], kBasicMetricValues);
+    // Replaced tab uses the orig source_id; so the metrics is logged to
+    // kTestUrls[0].
+    ExpectNewEntry(kTestUrls[0], kBasicMetricValues);
   }
 
   tab_strip_model->CloseAllTabs();
diff --git a/chrome/browser/resources/app_management/OWNERS b/chrome/browser/resources/app_management/OWNERS
index 90c732ec..54640cb 100644
--- a/chrome/browser/resources/app_management/OWNERS
+++ b/chrome/browser/resources/app_management/OWNERS
@@ -1,3 +1,5 @@
 calamity@chromium.org
 dominickn@chromium.org
 ericwilligers@chromium.org
+
+# COMPONENT: Platform>Apps>Foundation
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_client.js b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_client.js
index bf71b93..4cd2b14 100644
--- a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_client.js
+++ b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_client.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-const METHOD_LIST = ['logOut', 'getInstalledArcApps', 'uninstallArcApps'];
+const METHOD_LIST = ['logOut', 'getInstalledArcApps'];
 
 /**
  * Class that implements the client side of the AddSupervision postMessage API.
diff --git a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
index 49fcd5ac0..038d09af 100644
--- a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
+++ b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
@@ -5,7 +5,7 @@
 /**
  * The methods to expose to the client.
  */
-const METHOD_LIST = ['logOut', 'getInstalledArcApps', 'uninstallArcApps'];
+const METHOD_LIST = ['logOut', 'getInstalledArcApps'];
 
 /**
  * Class that implements the server side of the AddSupervision postMessage
@@ -31,7 +31,6 @@
     this.registerMethod('logOut', this.logOut.bind(this));
     this.registerMethod(
         'getInstalledArcApps', this.getInstalledArcApps.bind(this));
-    this.registerMethod('uninstallArcApps', this.uninstallArcApps.bind(this));
   }
 
   /**
@@ -53,14 +52,4 @@
   getInstalledArcApps(unused) {
     return this.proxy_.getInstalledArcApps();
   }
-
-  /**
-   * Uninstall the specified ARC apps.
-   * @param {!Array<!string>} apps List of app package names to uninstall.
-   * @return {Promise} a promise whose successful result indicates the apps were
-   *     uninstalled.
-   */
-  uninstallArcApps(apps) {
-    return this.proxy_.uninstallArcApps(apps);
-  }
 }
diff --git a/chrome/browser/resources/chromeos/add_supervision/webview-example.html b/chrome/browser/resources/chromeos/add_supervision/webview-example.html
index 400fe30..61d67a43 100644
--- a/chrome/browser/resources/chromeos/add_supervision/webview-example.html
+++ b/chrome/browser/resources/chromeos/add_supervision/webview-example.html
@@ -33,19 +33,6 @@
             console.error(err);
           })
        });
-
-      const uninstallArcAppsBtn = document.querySelector('#uninstall_arc_apps');
-
-      uninstallArcAppsBtn.addEventListener('click', () => {
-        api.uninstallArcApps(
-          ['com.netflix.mediaclient', 'com.google.android.videos'])
-          .then(() => {
-            console.log('uninstalled arc apps');
-          }, (error) => {
-            console.error(error.message);
-          });
-        });
-      });
   </script>
 </head>
 <body>
@@ -58,9 +45,6 @@
   <button id="get_arc_apps">Get ARC Apps</button>
   </div>
   <div>
-  <button id="uninstall_arc_apps">Uninstall ARC Apps</button>
-  </div>
-  <div>
   <input type="text" id="arc_apps"
          minlength="0" maxlength="128" size="128">
   </div>
diff --git a/chrome/browser/resources/chromeos/add_supervision/webview-example2.html b/chrome/browser/resources/chromeos/add_supervision/webview-example2.html
index b7d7202..6808e97f 100644
--- a/chrome/browser/resources/chromeos/add_supervision/webview-example2.html
+++ b/chrome/browser/resources/chromeos/add_supervision/webview-example2.html
@@ -33,19 +33,6 @@
             console.error(err);
           })
        });
-
-      const uninstallArcAppsBtn = document.querySelector('#uninstall_arc_apps');
-
-      uninstallArcAppsBtn.addEventListener('click', () => {
-        api.uninstallArcApps(
-          ['com.netflix.mediaclient', 'com.google.android.videos'])
-          .then(() => {
-            console.log('uninstalled arc apps');
-          }, (error) => {
-            console.error(error.message);
-          });
-        });
-      });
   </script>
 </head>
 <body>
@@ -58,9 +45,6 @@
   <button id="get_arc_apps">Get ARC Apps</button>
   </div>
   <div>
-  <button id="uninstall_arc_apps">Uninstall ARC Apps</button>
-  </div>
-  <div>
   <input type="text" id="arc_apps"
          minlength="0" maxlength="128" size="128">
   </div>
diff --git a/chrome/browser/resources/downloads/item.js b/chrome/browser/resources/downloads/item.js
index c318484..19ffb11 100644
--- a/chrome/browser/resources/downloads/item.js
+++ b/chrome/browser/resources/downloads/item.js
@@ -475,7 +475,6 @@
       });
       cr.toastManager.getInstance().showForStringPieces(pieces, true);
       this.mojoHandler_.remove(this.data.id);
-      this.fire('restore-focus-after-remove');
     },
 
     /** @private */
diff --git a/chrome/browser/resources/downloads/manager.js b/chrome/browser/resources/downloads/manager.js
index 084bc8e..f96ceed 100644
--- a/chrome/browser/resources/downloads/manager.js
+++ b/chrome/browser/resources/downloads/manager.js
@@ -60,19 +60,12 @@
       'itemsChanged_(items_.*)',
     ],
 
-    listeners: {
-      'restore-focus-after-remove': 'onRestoreFocusAfterRemove_',
-    },
-
     /** @private {downloads.mojom.PageCallbackRouter} */
     mojoEventTarget_: null,
 
     /** @private {downloads.mojom.PageHandlerInterface} */
     mojoHandler_: null,
 
-    /** @private {boolean} */
-    restoreFocusAfterRemove_: false,
-
     /** @private {?downloads.SearchService} */
     searchService_: null,
 
@@ -265,28 +258,10 @@
                            type: 'splice',
                            removed: removed,
                          }]);
-      if (this.restoreFocusAfterRemove_) {
-        this.restoreFocusAfterRemove_ = false;
-        const focusIndex = Math.min(this.items_.length - 1, index);
-        if (focusIndex >= 0) {
-          setTimeout(() => {
-            this.$.downloadsList.focusItem(focusIndex);
-            const item = getDeepActiveElement();
-            if (item) {
-              item.focusOnRemoveButton();
-            }
-          });
-        }
-      }
       this.onScroll_();
     },
 
     /** @private */
-    onRestoreFocusAfterRemove_: function() {
-      this.restoreFocusAfterRemove_ = true;
-    },
-
-    /** @private */
     onUndoClick_: function() {
       cr.toastManager.getInstance().hide();
       this.mojoHandler_.undo();
diff --git a/chrome/browser/resources/extensions/BUILD.gn b/chrome/browser/resources/extensions/BUILD.gn
index 8a3b44b..375e5e5 100644
--- a/chrome/browser/resources/extensions/BUILD.gn
+++ b/chrome/browser/resources/extensions/BUILD.gn
@@ -261,6 +261,7 @@
     "//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager",
     "//ui/webui/resources/js:assert",
     "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:load_time_data",
   ]
   externs_list = [ "$externs_path/developer_private.js" ]
 }
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log.html b/chrome/browser/resources/extensions/activity_log/activity_log.html
index 1f4abb9..ab435e7 100644
--- a/chrome/browser/resources/extensions/activity_log/activity_log.html
+++ b/chrome/browser/resources/extensions/activity_log/activity_log.html
@@ -27,10 +27,13 @@
         margin-inline-start: 8px;
       }
 
+      #closeButton {
+        margin-inline-end: 16px;
+      }
+
       #icon {
         height: 24px;
         margin-inline-end: 12px;
-        margin-inline-start: 16px;
         width: 24px;
       }
 
@@ -65,13 +68,15 @@
           <cr-icon-button class="icon-arrow-back no-overlap" id="closeButton"
               aria-label="$i18n{back}" on-click="onCloseButtonTap_">
           </cr-icon-button>
-          <img id="icon" src="[[extensionInfo.iconUrl]]"
-              alt$="[[appOrExtension(
-                  extensionInfo.type,
-                  '$i18nPolymer{appIcon}',
-                  '$i18nPolymer{extensionIcon}')]]">
+          <template is="dom-if" if="[[!extensionInfo.isPlaceholder]]">
+            <img id="icon" src="[[extensionInfo.iconUrl]]"
+                alt$="[[appOrExtension(
+                    extensionInfo.type,
+                    '$i18nPolymer{appIcon}',
+                    '$i18nPolymer{extensionIcon}')]]">
+          </template>
           <div id="activity-log-heading">
-            [[i18n('activityLogPageHeading', extensionInfo.name)]]
+            [[getActivityLogHeading_(extensionInfo)]]
           </div>
         </div>
         <cr-tabs selected="{{selectedSubpage_}}" tab-names="[[tabNames_]]">
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log.js b/chrome/browser/resources/extensions/activity_log/activity_log.js
index 4617810..8f29629 100644
--- a/chrome/browser/resources/extensions/activity_log/activity_log.js
+++ b/chrome/browser/resources/extensions/activity_log/activity_log.js
@@ -18,6 +18,17 @@
 cr.define('extensions', function() {
   'use strict';
 
+  /**
+   * A struct used as a placeholder for chrome.developerPrivate.ExtensionInfo
+   * for this component if the extensionId from the URL does not correspond to
+   * installed extension.
+   * @typedef {{
+   *   id: string,
+   *   isPlaceholder: boolean,
+   * }}
+   */
+  let ActivityLogExtensionPlaceholder;
+
   const ActivityLog = Polymer({
     is: 'extensions-activity-log',
 
@@ -30,7 +41,8 @@
     properties: {
       /**
        * The underlying ExtensionInfo for the details being displayed.
-       * @type {!chrome.developerPrivate.ExtensionInfo}
+       * @type {!chrome.developerPrivate.ExtensionInfo|
+       *        !extensions.ActivityLogExtensionPlaceholder}
        */
       extensionInfo: Object,
 
@@ -85,6 +97,17 @@
 
     /**
      * @private
+     * @return {string}
+     */
+    getActivityLogHeading_: function() {
+      const headingName = this.extensionInfo.isPlaceholder ?
+          this.i18n('missingOrUninstalledExtension') :
+          this.extensionInfo.name;
+      return this.i18n('activityLogPageHeading', headingName);
+    },
+
+    /**
+     * @private
      * @return {boolean}
      */
     isHistoryTabSelected_: function() {
@@ -123,12 +146,17 @@
 
     /** @private */
     onCloseButtonTap_: function() {
-      extensions.navigation.navigateTo(
-          {page: Page.DETAILS, extensionId: this.extensionInfo.id});
+      if (this.extensionInfo.isPlaceholder) {
+        extensions.navigation.navigateTo({page: Page.LIST});
+      } else {
+        extensions.navigation.navigateTo(
+            {page: Page.DETAILS, extensionId: this.extensionInfo.id});
+      }
     },
   });
 
   return {
     ActivityLog: ActivityLog,
+    ActivityLogExtensionPlaceholder: ActivityLogExtensionPlaceholder,
   };
 });
diff --git a/chrome/browser/resources/extensions/manager.js b/chrome/browser/resources/extensions/manager.js
index eda57f21..6095756 100644
--- a/chrome/browser/resources/extensions/manager.js
+++ b/chrome/browser/resources/extensions/manager.js
@@ -100,7 +100,8 @@
       /**
        * The item that provides some information about the current extension
        * for the activity log view subpage. See also errorPageItem_.
-       * @private {!chrome.developerPrivate.ExtensionInfo|undefined}
+       * @private {!chrome.developerPrivate.ExtensionInfo|undefined|
+       *           !extensions.ActivityLogExtensionPlaceholder}
        */
       activityLogItem_: Object,
 
@@ -407,6 +408,10 @@
           this.errorPageItem_ && this.errorPageItem_.id == item.id &&
           this.currentPage_.page == Page.ERRORS) {
         this.errorPageItem_ = item;
+      } else if (
+          this.activityLogItem_ && this.activityLogItem_.id == item.id &&
+          this.currentPage_.page == Page.ACTIVITY_LOG) {
+        this.activityLogItem_ = item;
       }
     },
 
@@ -465,12 +470,24 @@
       const fromPage = this.currentPage_ ? this.currentPage_.page : null;
       const toPage = newPage.page;
       let data;
+      let activityLogPlaceholder;
       if (newPage.extensionId) {
         data = this.getData_(newPage.extensionId);
         if (!data) {
-          // Attempting to view an invalid (removed?) app or extension ID.
-          extensions.navigation.replaceWith({page: Page.LIST});
-          return;
+          // Allow the user to navigate to the activity log page even if the
+          // extension ID is not valid. This enables the use case of seeing an
+          // extension's install-time activities by navigating to an extension's
+          // activity log page, then installing the extension.
+          if (this.showActivityLog && toPage == Page.ACTIVITY_LOG) {
+            activityLogPlaceholder = {
+              id: newPage.extensionId,
+              isPlaceholder: true,
+            };
+          } else {
+            // Attempting to view an invalid (removed?) app or extension ID.
+            extensions.navigation.replaceWith({page: Page.LIST});
+            return;
+          }
         }
       }
 
@@ -487,7 +504,7 @@
           return;
         }
 
-        this.activityLogItem_ = assert(data);
+        this.activityLogItem_ = data ? assert(data) : activityLogPlaceholder;
       }
 
       if (fromPage != toPage) {
diff --git a/chrome/browser/resources/feedback/css/feedback.css b/chrome/browser/resources/feedback/css/feedback.css
index 86371f0..cb000bd 100644
--- a/chrome/browser/resources/feedback/css/feedback.css
+++ b/chrome/browser/resources/feedback/css/feedback.css
@@ -144,6 +144,10 @@
   flex: auto;
 }
 
+.content #sys-info-container {
+  margin-inline-end: 130px;
+}
+
 .content #privacy-note {
   color: #969696;
   font-size: 12px;
diff --git a/chrome/browser/resources/feedback/html/default.html b/chrome/browser/resources/feedback/html/default.html
index e04a2cda..4443627 100644
--- a/chrome/browser/resources/feedback/html/default.html
+++ b/chrome/browser/resources/feedback/html/default.html
@@ -63,7 +63,7 @@
       <img id="screenshot-image">
     </div>
     <!-- System Information -->
-    <div class="checkbox-field-container">
+    <div id="sys-info-container" class="checkbox-field-container">
       <input id="sys-info-checkbox" type="checkbox" aria-labelledby="sys-info-label" checked>
       <label id="sys-info-label" i18n-values=".innerHTML:sys-info"></label>
     </div>
diff --git a/chrome/browser/resources/print_preview/ui/pages_settings.js b/chrome/browser/resources/print_preview/ui/pages_settings.js
index 64f6d0a..f46015b 100644
--- a/chrome/browser/resources/print_preview/ui/pages_settings.js
+++ b/chrome/browser/resources/print_preview/ui/pages_settings.js
@@ -38,19 +38,15 @@
   is: 'print-preview-pages-settings',
 
   behaviors: [
-    SettingsBehavior, print_preview.InputBehavior,
-    print_preview.SelectBehavior
+    SettingsBehavior, print_preview.InputBehavior, print_preview.SelectBehavior
   ],
 
   properties: {
     disabled: Boolean,
 
-    pageCount: Number,
-
-    /** @private {!Array<number>} */
-    allPagesArray_: {
-      type: Array,
-      computed: 'computeAllPagesArray_(pageCount)',
+    pageCount: {
+      type: Number,
+      observer: 'onPageCountChange_',
     },
 
     /** @private {boolean} */
@@ -90,7 +86,7 @@
     /** @private {!Array<{to: number, from: number}>} */
     rangesToPrint_: {
       type: Array,
-      computed: 'computeRangesToPrint_(pagesToPrint_, allPagesArray_)',
+      computed: 'computeRangesToPrint_(pagesToPrint_)',
     },
 
     /**
@@ -104,7 +100,7 @@
   },
 
   observers: [
-    'updatePagesToPrint_(inputString_, allPagesArray_)',
+    'updatePagesToPrint_(inputString_)',
     'onRangeChange_(errorState_, rangesToPrint_, settings.pages, ' +
         'settings.pagesPerSheet.value)',
   ],
@@ -153,8 +149,8 @@
   /** @private */
   onCollapseChanged_: function() {
     if (this.customSelected_) {
-        /** @type {!CrInputElement} */ (this.$.pageSettingsCustomInput)
-            .inputElement.focus();
+      /** @type {!CrInputElement} */ (this.$.pageSettingsCustomInput)
+          .inputElement.focus();
     }
   },
 
@@ -168,18 +164,6 @@
   },
 
   /**
-   * @return {!Array<number>}
-   * @private
-   */
-  computeAllPagesArray_: function() {
-    const array = new Array(this.pageCount);
-    for (let i = 0; i < array.length; i++) {
-      array[i] = i + 1;
-    }
-    return array;
-  },
-
-  /**
    * Updates pages to print and error state based on the validity and
    * current value of the input.
    * @private
@@ -187,7 +171,9 @@
   updatePagesToPrint_: function() {
     if (!this.customSelected_) {
       this.errorState_ = PagesInputErrorState.NO_ERROR;
-      this.pagesToPrint_ = this.allPagesArray_ || [];
+      this.pagesToPrint_ = this.pageCount ?
+          Array.from(new Array(this.pageCount).fill(0), (_, i) => i + 1) :
+          [];
       return;
     } else if (this.inputString_ === '') {
       this.errorState_ = PagesInputErrorState.EMPTY;
@@ -197,7 +183,7 @@
     const pages = [];
     const added = {};
     const ranges = this.inputString_.split(/,|\u3001/);
-    const maxPage = this.allPagesArray_.length;
+    const maxPage = this.pageCount;
     for (const range of ranges) {
       if (range == '') {
         this.errorState_ = PagesInputErrorState.INVALID_SYNTAX;
@@ -266,14 +252,13 @@
   },
 
   /**
-   * Updates ranges to print.
    * @return {!Array<{to: number, from: number}>}
    * @private
    */
   computeRangesToPrint_: function() {
     if (!this.pagesToPrint_ || this.pagesToPrint_.length == 0 ||
         this.pagesToPrint_[0] == -1 ||
-        this.pagesToPrint_.length == this.allPagesArray_.length) {
+        this.pagesToPrint_.length == this.pageCount) {
       return [];
     }
 
@@ -432,6 +417,28 @@
       this.resetString();
     }
     this.updatePagesToPrint_();
-  }
+  },
+
+  /**
+   * @param {number} current
+   * @param {number} previous
+   * @private
+   */
+  onPageCountChange_: function(current, previous) {
+    // Reset the custom input to the new "all pages" value if it is equal to the
+    // full page range and was either set automatically, or would become invalid
+    // due to the page count change.
+    const resetCustom = this.customSelected_ && !!this.pagesToPrint_ &&
+        this.pagesToPrint_.length === previous &&
+        (current < previous || !this.restoreLastInput_);
+
+    if (resetCustom) {
+      this.$$('cr-input').value = this.getAllPagesString_();
+      this.inputString_ = this.getAllPagesString_();
+      this.resetString();
+    } else {
+      this.updatePagesToPrint_();
+    }
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/internet_page/internet_config.js b/chrome/browser/resources/settings/internet_page/internet_config.js
index 64452cd..97cca74 100644
--- a/chrome/browser/resources/settings/internet_page/internet_config.js
+++ b/chrome/browser/resources/settings/internet_page/internet_config.js
@@ -122,6 +122,12 @@
    * @private
    */
   getDialogTitle_: function() {
+    // If no properties are available yet, wait until they are set as part of
+    // open().
+    if (!this.managedProperties_) {
+      return '';
+    }
+
     const name = /** @type {string} */ (
         CrOnc.getActiveValue(this.managedProperties_.Name));
     if (name && !this.showConnect) {
diff --git a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc
index b235300..4013bde 100644
--- a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc
+++ b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc
@@ -77,7 +77,13 @@
         std::make_unique<AndroidTelemetryService>(sb_service_.get(), profile());
   }
 
-  void TearDown() override {}
+  void TearDown() override {
+    // Make sure the NetworkContext owned by SafeBrowsingService is destructed
+    // before the NetworkService object..
+    browser_process_->safe_browsing_service()->ShutDown();
+    browser_process_->SetSafeBrowsingService(nullptr);
+    base::RunLoop().RunUntilIdle();
+  }
 
   bool CanSendPing(download::DownloadItem* item) {
     return telemetry_service_->CanSendPing(item);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 1f1a5314..3df0378 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2352,6 +2352,14 @@
       "//ui/events:dom_keycode_converter",
     ]
 
+    if (use_dbus) {
+      sources += [
+        "views/status_icons/status_icon_linux_dbus.cc",
+        "views/status_icons/status_icon_linux_dbus.h",
+      ]
+      defines += [ "USE_DBUS" ]
+    }
+
     if (use_x11) {
       sources += [
         "views/frame/browser_desktop_window_tree_host_x11.cc",
@@ -2367,6 +2375,8 @@
         "views/javascript_app_modal_dialog_views_x11.h",
         "views/javascript_app_modal_event_blocker_x11.cc",
         "views/javascript_app_modal_event_blocker_x11.h",
+        "views/status_icons/status_icon_linux_x11.cc",
+        "views/status_icons/status_icon_linux_x11.h",
       ]
       configs += [ "//build/config/linux:x11" ]
       deps += [
@@ -3348,6 +3358,7 @@
       "//ash/public/cpp/app_list/vector_icons",
       "//ash/resources/vector_icons",
       "//chrome/browser/ui:app_launch_event_logger_proto",
+      "//chrome/browser/ui/app_list/search/logging:search_ranking_event_proto",
       "//chrome/browser/ui/app_list/search/search_result_ranker:app_launch_predictor_proto",
       "//chrome/browser/ui/app_list/search/search_result_ranker:app_list_launch_recorder_proto",
       "//chrome/browser/ui/app_list/search/search_result_ranker:recurrence_ranker_proto",
@@ -3617,10 +3628,10 @@
       "web_applications/web_app_metrics.h",
       "web_applications/web_app_metrics_factory.cc",
       "web_applications/web_app_metrics_factory.h",
-      "web_applications/web_app_ui_delegate_impl.cc",
-      "web_applications/web_app_ui_delegate_impl.h",
-      "web_applications/web_app_ui_delegate_impl_factory.cc",
-      "web_applications/web_app_ui_delegate_impl_factory.h",
+      "web_applications/web_app_ui_service.cc",
+      "web_applications/web_app_ui_service.h",
+      "web_applications/web_app_ui_service_factory.cc",
+      "web_applications/web_app_ui_service_factory.h",
       "webui/extensions/extension_basic_info.cc",
       "webui/extensions/extension_basic_info.h",
       "webui/extensions/extension_icon_source.cc",
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils.cc b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
index fb619871..7e9a91a1 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_utils.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
@@ -639,12 +639,6 @@
   return app_info->package_name;
 }
 
-std::string ArcPackageNameToAppId(const std::string& package_name,
-                                  Profile* profile) {
-  ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
-  return arc_prefs->GetAppIdByPackageName(package_name);
-}
-
 bool IsArcAppSticky(const std::string& app_id, Profile* profile) {
   ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
   std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils.h b/chrome/browser/ui/app_list/arc/arc_app_utils.h
index 42babca..b53f570 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_utils.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_utils.h
@@ -189,11 +189,6 @@
 // be the AppID of an ARC app.
 std::string AppIdToArcPackageName(const std::string& app_id, Profile* profile);
 
-// Returns the AppID for the specified package_name, which must be the package
-// name of an ARC app.
-std::string ArcPackageNameToAppId(const std::string& package_name,
-                                  Profile* profile);
-
 // Returns true if the ARC app is sticky (not uninstallable). This function
 // will DCHECK if app_id isn't installed. This functionality should eventually
 // move to the App Service: (https://crbug.com/948408).
diff --git a/chrome/browser/ui/app_list/search/logging/BUILD.gn b/chrome/browser/ui/app_list/search/logging/BUILD.gn
new file mode 100644
index 0000000..d2fbed1
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/logging/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2019 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("//third_party/protobuf/proto_library.gni")
+
+proto_library("search_ranking_event_proto") {
+  sources = [
+    "search_ranking_event.proto",
+  ]
+}
diff --git a/chrome/browser/ui/app_list/search/logging/search_ranking_event.proto b/chrome/browser/ui/app_list/search/logging/search_ranking_event.proto
new file mode 100644
index 0000000..791ab28
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/logging/search_ranking_event.proto
@@ -0,0 +1,92 @@
+// Copyright 2019 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package app_list;
+
+// Some enums that will be used for both SearchRankingZeroStateEvent and
+// SearchRankingQueryEvent.
+enum CategoryID {
+  UNKNOWN_CATEGORY_ID = 0;
+  ANSWER_CARD = 1;
+  RECENT_QUERY = 2;
+  WEB_RESULT = 3;
+  BOOKMARK = 4;
+  RECENTLY_VISITED_URL = 5;
+  DRIVE_FILE = 6;
+  LOCAL_FILE = 7;
+}
+
+enum FileExtension {
+  UNKNOWN_FILE_EXTENSION = 0;
+  DOCS = 1;
+  SHEETS = 2;
+  SLIDES = 3;
+  FORMS = 4;
+  DRAWINGS = 5;
+}
+
+enum DayOfWeek {
+  SUN = 0;
+  MON = 1;
+  TUE = 2;
+  WED = 3;
+  THU = 4;
+  FRI = 5;
+  SAT = 6;
+}
+
+enum DeviceMode {
+  CLAMSHELL = 0;
+  TABLET = 1;
+}
+
+// SearchRankingZeroStateEvent contains features and related information for the
+// Chrome OS Search and Ranking project. These will be used for zero-state
+// recommendations.
+message SearchRankingZeroStateEvent {
+  // Contain features about a specific item.
+  message Features {
+    // Type of the item.
+    optional CategoryID category_id = 1;
+    // Extension of the file. Only available if category_id is DRIVE_FILE or
+    // LOCAL_FILE.
+    optional FileExtension file_extension = 2;
+    // Day of the week. Sunday is 0.
+    optional DayOfWeek day_of_week = 3;
+    // Position of the item. Topmost is position 1.
+    optional int32 position = 4;
+    // Last usage time as hours since midnight in the local time zone. For
+    // previous queries, it is the time the user clicked on it on the
+    // same Chrome device.
+    optional int32 last_usage_time = 5;
+    // Time since the item was last used/clicked. This is a duration timestamp
+    // and will be in seconds.
+    optional int32 time_since_last_use = 6;
+    // Number of clicks each hour as bucketed by the hour.
+    optional int32 num_clicks_each_hour = 7;
+    // Physical device mode, e.g. TABLET, CLAMSHELL.
+    optional DeviceMode device_mode = 8;
+    // Bucketed previous query length
+    optional int32 query_length = 9;
+  }
+
+  // Information related to a specific event.
+  message Event {
+    // Identifier used to associate all recommended items that were shown to the
+    // user.
+    optional int32 event_id = 1;
+    // Event time as hours since midnight in the local time zone. This is
+    // absolute timestamp and will be in hours.
+    optional int32 event_time = 2;
+    // Whether the item is clicked.
+    optional bool is_clicked = 3;
+  }
+
+  optional Features features = 1;
+  optional Event event = 2;
+}
diff --git a/chrome/browser/ui/apps/directory_access_confirmation_dialog.cc b/chrome/browser/ui/apps/directory_access_confirmation_dialog.cc
index d5299d6e..b31b1a2 100644
--- a/chrome/browser/ui/apps/directory_access_confirmation_dialog.cc
+++ b/chrome/browser/ui/apps/directory_access_confirmation_dialog.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h"
 
+#include <memory>
+
 #include "base/callback.h"
 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
@@ -93,7 +95,7 @@
                                              const base::Closure& on_accept,
                                              const base::Closure& on_cancel) {
   TabModalConfirmDialog::Create(
-      new DirectoryAccessConfirmationDialog(
+      std::make_unique<DirectoryAccessConfirmationDialog>(
           writable, app_name, web_contents, on_accept, on_cancel),
       web_contents);
 }
diff --git a/chrome/browser/ui/ash/assistant/device_actions.cc b/chrome/browser/ui/ash/assistant/device_actions.cc
index f9dc5db..006ba96c 100644
--- a/chrome/browser/ui/ash/assistant/device_actions.cc
+++ b/chrome/browser/ui/ash/assistant/device_actions.cc
@@ -81,9 +81,43 @@
                             kPackage, package_name.c_str(), kEndSuffix);
 }
 
+std::vector<AndroidAppInfoPtr> GetAppsInfo() {
+  std::vector<AndroidAppInfoPtr> android_apps_info;
+  auto* prefs = ArcAppListPrefs::Get(ProfileManager::GetActiveUserProfile());
+  if (!prefs) {
+    LOG(ERROR) << "ArcAppListPrefs is not available.";
+    return android_apps_info;
+  }
+  for (const auto& app_id : prefs->GetAppIds()) {
+    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
+    if (!app_info)
+      continue;
+    AndroidAppInfoPtr app_info_ptr =
+        chromeos::assistant::mojom::AndroidAppInfo::New();
+    app_info_ptr->package_name = app_info->package_name;
+    auto package = prefs->GetPackage(app_info->package_name);
+    if (package)
+      app_info_ptr->version = package->package_version;
+    app_info_ptr->localized_app_name = app_info->name;
+    app_info_ptr->intent = app_info->intent_uri;
+    android_apps_info.push_back(std::move(app_info_ptr));
+  }
+  return android_apps_info;
+}
+
+void NotifyAndroidAppListRefreshed(
+    mojo::InterfacePtrSet<chromeos::assistant::mojom::AppListEventSubscriber>
+        subscribers) {
+  std::vector<AndroidAppInfoPtr> android_apps_info = GetAppsInfo();
+
+  subscribers.ForAllPtrs([&android_apps_info](auto* ptr) {
+    ptr->OnAndroidAppListRefreshed(mojo::Clone(android_apps_info));
+  });
+}
+
 }  // namespace
 
-DeviceActions::DeviceActions() {}
+DeviceActions::DeviceActions() : scoped_prefs_observer_(this) {}
 
 DeviceActions::~DeviceActions() {
   bindings_.CloseAllBindings();
@@ -172,9 +206,8 @@
   std::move(callback).Run(!!app);
 }
 
-void DeviceActions::VerifyAndroidApp(
-    std::vector<chromeos::assistant::mojom::AndroidAppInfoPtr> apps_info,
-    VerifyAndroidAppCallback callback) {
+void DeviceActions::VerifyAndroidApp(std::vector<AndroidAppInfoPtr> apps_info,
+                                     VerifyAndroidAppCallback callback) {
   for (const auto& app_info : apps_info) {
     app_info->status = GetAndroidAppStatus(app_info->package_name);
   }
@@ -192,3 +225,32 @@
   // TODO(updowndota): Launch the intent in current active display.
   app->LaunchIntent(intent, display::kDefaultDisplayId);
 }
+
+void DeviceActions::AddAppListEventSubscriber(
+    chromeos::assistant::mojom::AppListEventSubscriberPtr subscriber) {
+  app_list_subscribers_.AddPtr(std::move(subscriber));
+  auto* prefs = ArcAppListPrefs::Get(ProfileManager::GetActiveUserProfile());
+  if (!prefs)
+    return;
+
+  if (!scoped_prefs_observer_.IsObserving(prefs)) {
+    scoped_prefs_observer_.Add(prefs);
+  }
+  if (prefs->package_list_initial_refreshed()) {
+    std::vector<AndroidAppInfoPtr> android_apps_info = GetAppsInfo();
+    subscriber->OnAndroidAppListRefreshed(mojo::Clone(android_apps_info));
+  }
+}
+
+void DeviceActions::OnPackageListInitialRefreshed() {
+  NotifyAndroidAppListRefreshed(app_list_subscribers_);
+}
+
+void DeviceActions::OnAppRegistered(const std::string& app_id,
+                                    const ArcAppListPrefs::AppInfo& app_info) {
+  NotifyAndroidAppListRefreshed(app_list_subscribers_);
+}
+
+void DeviceActions::OnAppRemoved(const std::string& id) {
+  NotifyAndroidAppListRefreshed(app_list_subscribers_);
+}
diff --git a/chrome/browser/ui/ash/assistant/device_actions.h b/chrome/browser/ui/ash/assistant/device_actions.h
index f6cca958..df934cc 100644
--- a/chrome/browser/ui/ash/assistant/device_actions.h
+++ b/chrome/browser/ui/ash/assistant/device_actions.h
@@ -5,10 +5,14 @@
 #ifndef CHROME_BROWSER_UI_ASH_ASSISTANT_DEVICE_ACTIONS_H_
 #define CHROME_BROWSER_UI_ASH_ASSISTANT_DEVICE_ACTIONS_H_
 
+#include "base/scoped_observer.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 
-class DeviceActions : public chromeos::assistant::mojom::DeviceActions {
+class DeviceActions : public chromeos::assistant::mojom::DeviceActions,
+                      public ArcAppListPrefs::Observer {
  public:
   DeviceActions();
   ~DeviceActions() override;
@@ -28,10 +32,21 @@
       std::vector<chromeos::assistant::mojom::AndroidAppInfoPtr> apps_info,
       VerifyAndroidAppCallback callback) override;
   void LaunchAndroidIntent(const std::string& intent) override;
+  void AddAppListEventSubscriber(
+      chromeos::assistant::mojom::AppListEventSubscriberPtr subscriber)
+      override;
 
  private:
-  mojo::BindingSet<chromeos::assistant::mojom::DeviceActions> bindings_;
+  // ArcAppListPrefs::Observer overrides.
+  void OnPackageListInitialRefreshed() override;
+  void OnAppRegistered(const std::string& app_id,
+                       const ArcAppListPrefs::AppInfo& app_info) override;
+  void OnAppRemoved(const std::string& id) override;
 
+  ScopedObserver<ArcAppListPrefs, DeviceActions> scoped_prefs_observer_;
+  mojo::BindingSet<chromeos::assistant::mojom::DeviceActions> bindings_;
+  mojo::InterfacePtrSet<chromeos::assistant::mojom::AppListEventSubscriber>
+      app_list_subscribers_;
   DISALLOW_COPY_AND_ASSIGN(DeviceActions);
 };
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index 1ed2b24..bd6aa73 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -1048,7 +1048,8 @@
     // Initialize WallpaperControllerClient.
     wallpaper_controller_client_ =
         std::make_unique<WallpaperControllerClient>();
-    wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+    wallpaper_controller_client_->InitForTesting(
+        test_wallpaper_controller_.CreateInterfacePtr());
 
     // AvatarMenu and multiple profiles works after user logged in.
     profile_manager()->SetLoggedIn(true);
diff --git a/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc
index 99292b8..4123c2a 100644
--- a/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc
@@ -278,7 +278,7 @@
 
   user_manager::ScopedUserManager user_manager_enabler_;
 
-  std::unique_ptr<::WallpaperControllerClient> wallpaper_controller_client_;
+  std::unique_ptr<WallpaperControllerClient> wallpaper_controller_client_;
 
   TestWallpaperController test_wallpaper_controller_;
 
@@ -315,9 +315,9 @@
       AccountId::FromUserEmail("A"));
   ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
       ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_DISABLED);
-  wallpaper_controller_client_ =
-      std::make_unique<::WallpaperControllerClient>();
-  wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
+  wallpaper_controller_client_ = std::make_unique<WallpaperControllerClient>();
+  wallpaper_controller_client_->InitForTesting(
+      test_wallpaper_controller_.CreateInterfacePtr());
 }
 
 void MultiProfileSupportTest::TearDown() {
diff --git a/chrome/browser/ui/ash/test_login_screen.cc b/chrome/browser/ui/ash/test_login_screen.cc
index 20d0d602..85de5f39 100644
--- a/chrome/browser/ui/ash/test_login_screen.cc
+++ b/chrome/browser/ui/ash/test_login_screen.cc
@@ -46,10 +46,6 @@
                                        const std::string& help_link_text,
                                        int32_t help_topic_id) {}
 
-void TestLoginScreen::ShowWarningBanner(const base::string16& message) {}
-
-void TestLoginScreen::HideWarningBanner() {}
-
 void TestLoginScreen::ClearErrors() {}
 
 void TestLoginScreen::SetAuthType(const AccountId& account_id,
diff --git a/chrome/browser/ui/ash/test_login_screen.h b/chrome/browser/ui/ash/test_login_screen.h
index e9995927..6e08eee70 100644
--- a/chrome/browser/ui/ash/test_login_screen.h
+++ b/chrome/browser/ui/ash/test_login_screen.h
@@ -35,8 +35,6 @@
                         const std::string& error_text,
                         const std::string& help_link_text,
                         int32_t help_topic_id) override;
-  void ShowWarningBanner(const base::string16& message) override;
-  void HideWarningBanner() override;
   void ClearErrors() override;
   void SetAuthType(const AccountId& account_id,
                    ::proximity_auth::mojom::AuthType auth_type,
diff --git a/chrome/browser/ui/ash/test_login_screen_model.cc b/chrome/browser/ui/ash/test_login_screen_model.cc
index df6cf8a..84e6a06 100644
--- a/chrome/browser/ui/ash/test_login_screen_model.cc
+++ b/chrome/browser/ui/ash/test_login_screen_model.cc
@@ -14,6 +14,8 @@
 void TestLoginScreenModel::ShowEasyUnlockIcon(
     const AccountId& account_id,
     const ash::EasyUnlockIconOptions& icon) {}
+void TestLoginScreenModel::UpdateWarningMessage(const base::string16& message) {
+}
 void TestLoginScreenModel::SetFingerprintState(const AccountId& account_id,
                                                ash::FingerprintState state) {}
 void TestLoginScreenModel::SetPublicSessionLocales(
diff --git a/chrome/browser/ui/ash/test_login_screen_model.h b/chrome/browser/ui/ash/test_login_screen_model.h
index a3a4730..9104f5b 100644
--- a/chrome/browser/ui/ash/test_login_screen_model.h
+++ b/chrome/browser/ui/ash/test_login_screen_model.h
@@ -21,6 +21,7 @@
                            ash::FingerprintState state) override;
   void ShowEasyUnlockIcon(const AccountId& user,
                           const ash::EasyUnlockIconOptions& icon) override;
+  void UpdateWarningMessage(const base::string16& message) override;
   void SetPublicSessionLocales(const AccountId& account_id,
                                const std::vector<ash::LocaleItem>& locales,
                                const std::string& default_locale,
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.cc b/chrome/browser/ui/ash/test_wallpaper_controller.cc
index 7d38d18..118e8e7 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.cc
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.cc
@@ -4,24 +4,32 @@
 
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
 
-#include "ash/public/cpp/wallpaper_controller_observer.h"
+constexpr uint32_t dummy_image_id = 1;
 
-TestWallpaperController::TestWallpaperController() = default;
+TestWallpaperController::TestWallpaperController() : binding_(this) {}
 
 TestWallpaperController::~TestWallpaperController() = default;
 
 void TestWallpaperController::ShowWallpaperImage(const gfx::ImageSkia& image) {
   current_wallpaper = image;
-  for (auto& observer : observers_)
-    observer.OnWallpaperChanged();
+  test_observers_.ForAllPtrs([](ash::mojom::WallpaperObserver* observer) {
+    observer->OnWallpaperChanged(dummy_image_id);
+  });
 }
 
 void TestWallpaperController::ClearCounts() {
   remove_user_wallpaper_count_ = 0;
 }
 
+ash::mojom::WallpaperControllerPtr
+TestWallpaperController::CreateInterfacePtr() {
+  ash::mojom::WallpaperControllerPtr ptr;
+  binding_.Bind(mojo::MakeRequest(&ptr));
+  return ptr;
+}
+
 void TestWallpaperController::Init(
-    ash::WallpaperControllerClient* client,
+    ash::mojom::WallpaperControllerClientPtr client,
     const base::FilePath& user_data_path,
     const base::FilePath& chromeos_wallpapers_path,
     const base::FilePath& chromeos_custom_wallpapers_path,
@@ -30,7 +38,7 @@
 }
 
 void TestWallpaperController::SetCustomWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     ash::WallpaperLayout layout,
@@ -40,16 +48,17 @@
 }
 
 void TestWallpaperController::SetOnlineWallpaperIfExists(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& url,
     ash::WallpaperLayout layout,
     bool preview_mode,
-    SetOnlineWallpaperIfExistsCallback callback) {
+    ash::mojom::WallpaperController::SetOnlineWallpaperIfExistsCallback
+        callback) {
   NOTIMPLEMENTED();
 }
 
 void TestWallpaperController::SetOnlineWallpaperFromData(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& image_data,
     const std::string& url,
     ash::WallpaperLayout layout,
@@ -59,7 +68,7 @@
 }
 
 void TestWallpaperController::SetDefaultWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     bool show_wallpaper) {
   ++set_default_wallpaper_count_;
@@ -72,7 +81,7 @@
 }
 
 void TestWallpaperController::SetPolicyWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     const std::string& data) {
   NOTIMPLEMENTED();
@@ -83,14 +92,15 @@
   NOTIMPLEMENTED();
 }
 
-bool TestWallpaperController::SetThirdPartyWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+void TestWallpaperController::SetThirdPartyWallpaper(
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     ash::WallpaperLayout layout,
-    const gfx::ImageSkia& image) {
+    const gfx::ImageSkia& image,
+    ash::mojom::WallpaperController::SetThirdPartyWallpaperCallback callback) {
+  std::move(callback).Run(true /*allowed=*/, dummy_image_id);
   ShowWallpaperImage(image);
-  return true;
 }
 
 void TestWallpaperController::ConfirmPreviewWallpaper() {
@@ -102,13 +112,13 @@
 }
 
 void TestWallpaperController::UpdateCustomWallpaperLayout(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     ash::WallpaperLayout layout) {
   NOTIMPLEMENTED();
 }
 
 void TestWallpaperController::ShowUserWallpaper(
-    const ash::WallpaperUserInfo& user_info) {
+    ash::mojom::WallpaperUserInfoPtr user_info) {
   NOTIMPLEMENTED();
 }
 
@@ -131,19 +141,19 @@
 }
 
 void TestWallpaperController::RemoveUserWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id) {
   ++remove_user_wallpaper_count_;
 }
 
 void TestWallpaperController::RemovePolicyWallpaper(
-    const ash::WallpaperUserInfo& user_info,
+    ash::mojom::WallpaperUserInfoPtr user_info,
     const std::string& wallpaper_files_id) {
   NOTIMPLEMENTED();
 }
 
 void TestWallpaperController::GetOfflineWallpaperList(
-    GetOfflineWallpaperListCallback callback) {
+    ash::mojom::WallpaperController::GetOfflineWallpaperListCallback callback) {
   NOTIMPLEMENTED();
 }
 
@@ -167,41 +177,41 @@
 }
 
 void TestWallpaperController::AddObserver(
-    ash::WallpaperControllerObserver* observer) {
-  observers_.AddObserver(observer);
+    ash::mojom::WallpaperObserverAssociatedPtrInfo observer) {
+  ash::mojom::WallpaperObserverAssociatedPtr observer_ptr;
+  observer_ptr.Bind(std::move(observer));
+  test_observers_.AddPtr(std::move(observer_ptr));
 }
 
-void TestWallpaperController::RemoveObserver(
-    ash::WallpaperControllerObserver* observer) {
-  observers_.RemoveObserver(observer);
+void TestWallpaperController::GetWallpaperImage(
+    ash::mojom::WallpaperController::GetWallpaperImageCallback callback) {
+  std::move(callback).Run(current_wallpaper);
 }
 
-gfx::ImageSkia TestWallpaperController::GetWallpaperImage() {
-  return current_wallpaper;
-}
-
-const std::vector<SkColor>& TestWallpaperController::GetWallpaperColors() {
+void TestWallpaperController::GetWallpaperColors(
+    ash::mojom::WallpaperController::GetWallpaperColorsCallback callback) {
   NOTIMPLEMENTED();
-  static std::vector<SkColor> kColors;
-  return kColors;
 }
 
-bool TestWallpaperController::IsWallpaperBlurred() {
+void TestWallpaperController::IsWallpaperBlurred(
+    ash::mojom::WallpaperController::IsWallpaperBlurredCallback callback) {
   NOTIMPLEMENTED();
-  return false;
 }
 
-bool TestWallpaperController::IsActiveUserWallpaperControlledByPolicy() {
+void TestWallpaperController::IsActiveUserWallpaperControlledByPolicy(
+    ash::mojom::WallpaperController::
+        IsActiveUserWallpaperControlledByPolicyCallback callback) {
   NOTIMPLEMENTED();
-  return false;
 }
 
-ash::WallpaperInfo TestWallpaperController::GetActiveUserWallpaperInfo() {
+void TestWallpaperController::GetActiveUserWallpaperInfo(
+    ash::mojom::WallpaperController::GetActiveUserWallpaperInfoCallback
+        callback) {
   NOTIMPLEMENTED();
-  return {};
 }
 
-bool TestWallpaperController::ShouldShowWallpaperSetting() {
+void TestWallpaperController::ShouldShowWallpaperSetting(
+    ash::mojom::WallpaperController::ShouldShowWallpaperSettingCallback
+        callback) {
   NOTIMPLEMENTED();
-  return false;
 }
diff --git a/chrome/browser/ui/ash/test_wallpaper_controller.h b/chrome/browser/ui/ash/test_wallpaper_controller.h
index 7eb6743..83b97fe0 100644
--- a/chrome/browser/ui/ash/test_wallpaper_controller.h
+++ b/chrome/browser/ui/ash/test_wallpaper_controller.h
@@ -5,16 +5,17 @@
 #ifndef CHROME_BROWSER_UI_ASH_TEST_WALLPAPER_CONTROLLER_H_
 #define CHROME_BROWSER_UI_ASH_TEST_WALLPAPER_CONTROLLER_H_
 
-#include "ash/public/cpp/wallpaper_controller.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/macros.h"
-#include "base/observer_list.h"
-#include "ui/gfx/image/image_skia.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 
 // Simulates WallpaperController in ash.
-class TestWallpaperController : public ash::WallpaperController {
+class TestWallpaperController : ash::mojom::WallpaperController {
  public:
   TestWallpaperController();
-  ~TestWallpaperController();
+
+  ~TestWallpaperController() override;
 
   // Simulates showing the wallpaper on screen by updating |current_wallpaper|
   // and notifying the observers.
@@ -36,76 +37,98 @@
     return remove_always_on_top_wallpaper_count_;
   }
 
-  // ash::WallpaperController:
-  void Init(ash::WallpaperControllerClient* client,
+  // Returns a mojo interface pointer bound to this object.
+  ash::mojom::WallpaperControllerPtr CreateInterfacePtr();
+
+  // ash::mojom::WallpaperController:
+  void Init(ash::mojom::WallpaperControllerClientPtr client,
             const base::FilePath& user_data_path,
             const base::FilePath& chromeos_wallpapers_path,
             const base::FilePath& chromeos_custom_wallpapers_path,
             const base::FilePath& device_policy_wallpaper_path) override;
-  void SetCustomWallpaper(const ash::WallpaperUserInfo& user_info,
+  void SetCustomWallpaper(ash::mojom::WallpaperUserInfoPtr user_info,
                           const std::string& wallpaper_files_id,
                           const std::string& file_name,
                           ash::WallpaperLayout layout,
                           const gfx::ImageSkia& image,
                           bool preview_mode) override;
   void SetOnlineWallpaperIfExists(
-      const ash::WallpaperUserInfo& user_info,
+      ash::mojom::WallpaperUserInfoPtr user_info,
       const std::string& url,
       ash::WallpaperLayout layout,
       bool preview_mode,
-      SetOnlineWallpaperIfExistsCallback callback) override;
+      ash::mojom::WallpaperController::SetOnlineWallpaperIfExistsCallback
+          callback) override;
   void SetOnlineWallpaperFromData(
-      const ash::WallpaperUserInfo& user_info,
+      ash::mojom::WallpaperUserInfoPtr user_info,
       const std::string& image_data,
       const std::string& url,
       ash::WallpaperLayout layout,
       bool preview_mode,
       SetOnlineWallpaperFromDataCallback callback) override;
-  void SetDefaultWallpaper(const ash::WallpaperUserInfo& user_info,
+  void SetDefaultWallpaper(ash::mojom::WallpaperUserInfoPtr user_info,
                            const std::string& wallpaper_files_id,
                            bool show_wallpaper) override;
   void SetCustomizedDefaultWallpaperPaths(
       const base::FilePath& customized_default_small_path,
       const base::FilePath& customized_default_large_path) override;
-  void SetPolicyWallpaper(const ash::WallpaperUserInfo& user_info,
+  void SetPolicyWallpaper(ash::mojom::WallpaperUserInfoPtr user_info,
                           const std::string& wallpaper_files_id,
                           const std::string& data) override;
   void SetDevicePolicyWallpaperPath(
       const base::FilePath& device_policy_wallpaper_path) override;
-  bool SetThirdPartyWallpaper(const ash::WallpaperUserInfo& user_info,
-                              const std::string& wallpaper_files_id,
-                              const std::string& file_name,
-                              ash::WallpaperLayout layout,
-                              const gfx::ImageSkia& image) override;
+  void SetThirdPartyWallpaper(
+      ash::mojom::WallpaperUserInfoPtr user_info,
+      const std::string& wallpaper_files_id,
+      const std::string& file_name,
+      ash::WallpaperLayout layout,
+      const gfx::ImageSkia& image,
+      ash::mojom::WallpaperController::SetThirdPartyWallpaperCallback callback)
+      override;
   void ConfirmPreviewWallpaper() override;
   void CancelPreviewWallpaper() override;
-  void UpdateCustomWallpaperLayout(const ash::WallpaperUserInfo& user_info,
+  void UpdateCustomWallpaperLayout(ash::mojom::WallpaperUserInfoPtr user_info,
                                    ash::WallpaperLayout layout) override;
-  void ShowUserWallpaper(const ash::WallpaperUserInfo& user_info) override;
+  void ShowUserWallpaper(ash::mojom::WallpaperUserInfoPtr user_info) override;
   void ShowSigninWallpaper() override;
   void ShowOneShotWallpaper(const gfx::ImageSkia& image) override;
   void ShowAlwaysOnTopWallpaper(const base::FilePath& image_path) override;
   void RemoveAlwaysOnTopWallpaper() override;
-  void RemoveUserWallpaper(const ash::WallpaperUserInfo& user_info,
+  void RemoveUserWallpaper(ash::mojom::WallpaperUserInfoPtr user_info,
                            const std::string& wallpaper_files_id) override;
-  void RemovePolicyWallpaper(const ash::WallpaperUserInfo& user_info,
+  void RemovePolicyWallpaper(ash::mojom::WallpaperUserInfoPtr user_info,
                              const std::string& wallpaper_files_id) override;
   void GetOfflineWallpaperList(
-      GetOfflineWallpaperListCallback callback) override;
+      ash::mojom::WallpaperController::GetOfflineWallpaperListCallback callback)
+      override;
   void SetAnimationDuration(base::TimeDelta animation_duration) override;
   void OpenWallpaperPickerIfAllowed() override;
   void MinimizeInactiveWindows(const std::string& user_id_hash) override;
   void RestoreMinimizedWindows(const std::string& user_id_hash) override;
-  void AddObserver(ash::WallpaperControllerObserver* observer) override;
-  void RemoveObserver(ash::WallpaperControllerObserver* observer) override;
-  gfx::ImageSkia GetWallpaperImage() override;
-  const std::vector<SkColor>& GetWallpaperColors() override;
-  bool IsWallpaperBlurred() override;
-  bool IsActiveUserWallpaperControlledByPolicy() override;
-  ash::WallpaperInfo GetActiveUserWallpaperInfo() override;
-  bool ShouldShowWallpaperSetting() override;
+  void AddObserver(
+      ash::mojom::WallpaperObserverAssociatedPtrInfo observer) override;
+  void GetWallpaperImage(
+      ash::mojom::WallpaperController::GetWallpaperImageCallback callback)
+      override;
+  void GetWallpaperColors(
+      ash::mojom::WallpaperController::GetWallpaperColorsCallback callback)
+      override;
+  void IsWallpaperBlurred(
+      ash::mojom::WallpaperController::IsWallpaperBlurredCallback callback)
+      override;
+  void IsActiveUserWallpaperControlledByPolicy(
+      ash::mojom::WallpaperController::
+          IsActiveUserWallpaperControlledByPolicyCallback callback) override;
+  void GetActiveUserWallpaperInfo(
+      ash::mojom::WallpaperController::GetActiveUserWallpaperInfoCallback
+          callback) override;
+  void ShouldShowWallpaperSetting(
+      ash::mojom::WallpaperController::ShouldShowWallpaperSettingCallback
+          callback) override;
 
  private:
+  mojo::Binding<ash::mojom::WallpaperController> binding_;
+
   bool was_client_set_ = false;
   int remove_user_wallpaper_count_ = 0;
   int set_default_wallpaper_count_ = 0;
@@ -113,7 +136,8 @@
   int show_always_on_top_wallpaper_count_ = 0;
   int remove_always_on_top_wallpaper_count_ = 0;
 
-  base::ObserverList<ash::WallpaperControllerObserver>::Unchecked observers_;
+  mojo::AssociatedInterfacePtrSet<ash::mojom::WallpaperObserver>
+      test_observers_;
 
   gfx::ImageSkia current_wallpaper;
 
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.cc b/chrome/browser/ui/ash/wallpaper_controller_client.cc
index fed413b..026caed 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 
-#include "ash/public/cpp/wallpaper_user_info.h"
+#include "ash/public/interfaces/constants.mojom.h"
 #include "base/bind.h"
 #include "base/hash/sha1.h"
 #include "base/path_service.h"
@@ -40,26 +40,27 @@
 
 WallpaperControllerClient* g_wallpaper_controller_client_instance = nullptr;
 
-// Creates a WallpaperUserInfo for the account id. Returns nullptr if user
-// manager cannot find the user.
-base::Optional<ash::WallpaperUserInfo> AccountIdToWallpaperUserInfo(
+// Creates a mojom::WallpaperUserInfo for the account id. Returns nullptr if
+// user manager cannot find the user.
+ash::mojom::WallpaperUserInfoPtr AccountIdToWallpaperUserInfo(
     const AccountId& account_id) {
   if (!account_id.is_valid()) {
     // |account_id| may be invalid in tests.
-    return base::nullopt;
+    return nullptr;
   }
   const user_manager::User* user =
       user_manager::UserManager::Get()->FindUser(account_id);
   if (!user)
-    return base::nullopt;
+    return nullptr;
 
-  ash::WallpaperUserInfo wallpaper_user_info;
-  wallpaper_user_info.account_id = account_id;
-  wallpaper_user_info.type = user->GetType();
-  wallpaper_user_info.is_ephemeral =
+  ash::mojom::WallpaperUserInfoPtr wallpaper_user_info =
+      ash::mojom::WallpaperUserInfo::New();
+  wallpaper_user_info->account_id = account_id;
+  wallpaper_user_info->type = user->GetType();
+  wallpaper_user_info->is_ephemeral =
       user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
           account_id);
-  wallpaper_user_info.has_gaia_account = user->HasGaiaAccount();
+  wallpaper_user_info->has_gaia_account = user->HasGaiaAccount();
 
   return wallpaper_user_info;
 }
@@ -133,7 +134,8 @@
 
 }  // namespace
 
-WallpaperControllerClient::WallpaperControllerClient() : weak_factory_(this) {
+WallpaperControllerClient::WallpaperControllerClient()
+    : binding_(this), weak_factory_(this) {
   local_state_ = g_browser_process->local_state();
   show_user_names_on_signin_subscription_ =
       chromeos::CrosSettings::Get()->AddSettingsObserver(
@@ -159,14 +161,16 @@
       base::BindRepeating(
           &WallpaperControllerClient::DeviceWallpaperImageFilePathChanged,
           weak_factory_.GetWeakPtr()));
-  wallpaper_controller_ = ash::WallpaperController::Get();
-  InitController();
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &wallpaper_controller_);
+  BindAndSetClient();
 }
 
 void WallpaperControllerClient::InitForTesting(
-    ash::WallpaperController* controller) {
-  wallpaper_controller_ = controller;
-  InitController();
+    ash::mojom::WallpaperControllerPtr controller) {
+  wallpaper_controller_ = std::move(controller);
+  BindAndSetClient();
 }
 
 // static
@@ -197,10 +201,11 @@
     ash::WallpaperLayout layout,
     const gfx::ImageSkia& image,
     bool preview_mode) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
-  wallpaper_controller_->SetCustomWallpaper(user_info.value(),
+  wallpaper_controller_->SetCustomWallpaper(std::move(user_info),
                                             wallpaper_files_id, file_name,
                                             layout, image, preview_mode);
 }
@@ -210,12 +215,14 @@
     const std::string& url,
     ash::WallpaperLayout layout,
     bool preview_mode,
-    ash::WallpaperController::SetOnlineWallpaperIfExistsCallback callback) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+    ash::mojom::WallpaperController::SetOnlineWallpaperIfExistsCallback
+        callback) {
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
   wallpaper_controller_->SetOnlineWallpaperIfExists(
-      user_info.value(), url, layout, preview_mode, std::move(callback));
+      std::move(user_info), url, layout, preview_mode, std::move(callback));
 }
 
 void WallpaperControllerClient::SetOnlineWallpaperFromData(
@@ -224,18 +231,21 @@
     const std::string& url,
     ash::WallpaperLayout layout,
     bool preview_mode,
-    ash::WallpaperController::SetOnlineWallpaperFromDataCallback callback) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+    ash::mojom::WallpaperController::SetOnlineWallpaperFromDataCallback
+        callback) {
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
   wallpaper_controller_->SetOnlineWallpaperFromData(
-      user_info.value(), image_data, url, layout, preview_mode,
+      std::move(user_info), image_data, url, layout, preview_mode,
       std::move(callback));
 }
 
 void WallpaperControllerClient::SetDefaultWallpaper(const AccountId& account_id,
                                                     bool show_wallpaper) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
 
@@ -251,7 +261,7 @@
   }
 
   wallpaper_controller_->SetDefaultWallpaper(
-      user_info.value(), GetFilesId(account_id), show_wallpaper);
+      std::move(user_info), GetFilesId(account_id), show_wallpaper);
 }
 
 void WallpaperControllerClient::SetCustomizedDefaultWallpaperPaths(
@@ -267,7 +277,8 @@
   if (!data)
     return;
 
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
 
@@ -280,21 +291,24 @@
     return;
   }
 
-  wallpaper_controller_->SetPolicyWallpaper(user_info.value(),
+  wallpaper_controller_->SetPolicyWallpaper(std::move(user_info),
                                             GetFilesId(account_id), *data);
 }
 
-bool WallpaperControllerClient::SetThirdPartyWallpaper(
+void WallpaperControllerClient::SetThirdPartyWallpaper(
     const AccountId& account_id,
     const std::string& wallpaper_files_id,
     const std::string& file_name,
     ash::WallpaperLayout layout,
-    const gfx::ImageSkia& image) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+    const gfx::ImageSkia& image,
+    ash::mojom::WallpaperController::SetThirdPartyWallpaperCallback callback) {
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
-    return false;
-  return wallpaper_controller_->SetThirdPartyWallpaper(
-      user_info.value(), wallpaper_files_id, file_name, layout, image);
+    return;
+  wallpaper_controller_->SetThirdPartyWallpaper(
+      std::move(user_info), wallpaper_files_id, file_name, layout, image,
+      std::move(callback));
 }
 
 void WallpaperControllerClient::ConfirmPreviewWallpaper() {
@@ -308,17 +322,20 @@
 void WallpaperControllerClient::UpdateCustomWallpaperLayout(
     const AccountId& account_id,
     ash::WallpaperLayout layout) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
-  wallpaper_controller_->UpdateCustomWallpaperLayout(user_info.value(), layout);
+  wallpaper_controller_->UpdateCustomWallpaperLayout(std::move(user_info),
+                                                     layout);
 }
 
 void WallpaperControllerClient::ShowUserWallpaper(const AccountId& account_id) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
-  wallpaper_controller_->ShowUserWallpaper(user_info.value());
+  wallpaper_controller_->ShowUserWallpaper(std::move(user_info));
 }
 
 void WallpaperControllerClient::ShowSigninWallpaper() {
@@ -336,7 +353,8 @@
 
 void WallpaperControllerClient::RemoveUserWallpaper(
     const AccountId& account_id) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
 
@@ -351,13 +369,14 @@
     return;
   }
 
-  wallpaper_controller_->RemoveUserWallpaper(user_info.value(),
+  wallpaper_controller_->RemoveUserWallpaper(std::move(user_info),
                                              GetFilesId(account_id));
 }
 
 void WallpaperControllerClient::RemovePolicyWallpaper(
     const AccountId& account_id) {
-  auto user_info = AccountIdToWallpaperUserInfo(account_id);
+  ash::mojom::WallpaperUserInfoPtr user_info =
+      AccountIdToWallpaperUserInfo(account_id);
   if (!user_info)
     return;
 
@@ -372,12 +391,12 @@
     return;
   }
 
-  wallpaper_controller_->RemovePolicyWallpaper(user_info.value(),
+  wallpaper_controller_->RemovePolicyWallpaper(std::move(user_info),
                                                GetFilesId(account_id));
 }
 
 void WallpaperControllerClient::GetOfflineWallpaperList(
-    ash::WallpaperController::GetOfflineWallpaperListCallback callback) {
+    ash::mojom::WallpaperController::GetOfflineWallpaperListCallback callback) {
   wallpaper_controller_->GetOfflineWallpaperList(std::move(callback));
 }
 
@@ -401,37 +420,42 @@
 }
 
 void WallpaperControllerClient::AddObserver(
-    ash::WallpaperControllerObserver* observer) {
-  wallpaper_controller_->AddObserver(observer);
+    ash::mojom::WallpaperObserverAssociatedPtrInfo observer) {
+  wallpaper_controller_->AddObserver(std::move(observer));
 }
 
-void WallpaperControllerClient::RemoveObserver(
-    ash::WallpaperControllerObserver* observer) {
-  wallpaper_controller_->RemoveObserver(observer);
+void WallpaperControllerClient::GetWallpaperImage(
+    ash::mojom::WallpaperController::GetWallpaperImageCallback callback) {
+  wallpaper_controller_->GetWallpaperImage(std::move(callback));
 }
 
-gfx::ImageSkia WallpaperControllerClient::GetWallpaperImage() {
-  return wallpaper_controller_->GetWallpaperImage();
+void WallpaperControllerClient::GetWallpaperColors(
+    ash::mojom::WallpaperController::GetWallpaperColorsCallback callback) {
+  wallpaper_controller_->GetWallpaperColors(std::move(callback));
 }
 
-const std::vector<SkColor>& WallpaperControllerClient::GetWallpaperColors() {
-  return wallpaper_controller_->GetWallpaperColors();
+void WallpaperControllerClient::IsWallpaperBlurred(
+    ash::mojom::WallpaperController::IsWallpaperBlurredCallback callback) {
+  wallpaper_controller_->IsWallpaperBlurred(std::move(callback));
 }
 
-bool WallpaperControllerClient::IsWallpaperBlurred() {
-  return wallpaper_controller_->IsWallpaperBlurred();
+void WallpaperControllerClient::IsActiveUserWallpaperControlledByPolicy(
+    ash::mojom::WallpaperController::
+        IsActiveUserWallpaperControlledByPolicyCallback callback) {
+  wallpaper_controller_->IsActiveUserWallpaperControlledByPolicy(
+      std::move(callback));
 }
 
-bool WallpaperControllerClient::IsActiveUserWallpaperControlledByPolicy() {
-  return wallpaper_controller_->IsActiveUserWallpaperControlledByPolicy();
+void WallpaperControllerClient::GetActiveUserWallpaperInfo(
+    ash::mojom::WallpaperController::GetActiveUserWallpaperInfoCallback
+        callback) {
+  wallpaper_controller_->GetActiveUserWallpaperInfo(std::move(callback));
 }
 
-ash::WallpaperInfo WallpaperControllerClient::GetActiveUserWallpaperInfo() {
-  return wallpaper_controller_->GetActiveUserWallpaperInfo();
-}
-
-bool WallpaperControllerClient::ShouldShowWallpaperSetting() {
-  return wallpaper_controller_->ShouldShowWallpaperSetting();
+void WallpaperControllerClient::ShouldShowWallpaperSetting(
+    ash::mojom::WallpaperController::ShouldShowWallpaperSettingCallback
+        callback) {
+  wallpaper_controller_->ShouldShowWallpaperSetting(std::move(callback));
 }
 
 void WallpaperControllerClient::DeviceWallpaperImageFilePathChanged() {
@@ -439,7 +463,14 @@
       GetDeviceWallpaperImageFilePath());
 }
 
-void WallpaperControllerClient::InitController() {
+void WallpaperControllerClient::FlushForTesting() {
+  wallpaper_controller_.FlushForTesting();
+}
+
+void WallpaperControllerClient::BindAndSetClient() {
+  ash::mojom::WallpaperControllerClientPtr client;
+  binding_.Bind(mojo::MakeRequest(&client));
+
   // Get the paths of wallpaper directories.
   base::FilePath user_data_path;
   CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &user_data_path));
@@ -453,9 +484,9 @@
   base::FilePath device_policy_wallpaper_path =
       GetDeviceWallpaperImageFilePath();
 
-  wallpaper_controller_->Init(this, user_data_path, chromeos_wallpapers_path,
-                              chromeos_custom_wallpapers_path,
-                              device_policy_wallpaper_path);
+  wallpaper_controller_->Init(
+      std::move(client), user_data_path, chromeos_wallpapers_path,
+      chromeos_custom_wallpapers_path, device_policy_wallpaper_path);
 }
 
 void WallpaperControllerClient::ShowWallpaperOnLoginScreen() {
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.h b/chrome/browser/ui/ash/wallpaper_controller_client.h
index 11d72c5..5d515b0 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client.h
+++ b/chrome/browser/ui/ash/wallpaper_controller_client.h
@@ -7,33 +7,32 @@
 
 #include <memory>
 
-#include "ash/public/cpp/wallpaper_controller.h"
-#include "ash/public/cpp/wallpaper_controller_client.h"
 #include "ash/public/cpp/wallpaper_types.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/macros.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "components/prefs/pref_change_registrar.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
-class AccountId;
-
-// Handles chrome-side wallpaper control alongside the ash-side controller.
-class WallpaperControllerClient : public ash::WallpaperControllerClient {
+// Handles method calls sent from ash to chrome. Also sends messages from chrome
+// to ash.
+class WallpaperControllerClient : public ash::mojom::WallpaperControllerClient {
  public:
   WallpaperControllerClient();
-  virtual ~WallpaperControllerClient();
+  ~WallpaperControllerClient() override;
 
   // Initializes and connects to ash.
   void Init();
 
-  // Tests can provide a mock interface for the ash controller.
-  void InitForTesting(ash::WallpaperController* controller);
+  // Tests can provide a mock mojo interface for the ash controller.
+  void InitForTesting(ash::mojom::WallpaperControllerPtr controller);
 
   static WallpaperControllerClient* Get();
 
   // Returns files identifier for the |account_id|.
   std::string GetFilesId(const AccountId& account_id) const;
 
-  // Wrappers around the ash::WallpaperController interface.
+  // Wrappers around the ash::mojom::WallpaperController interface.
   void SetCustomWallpaper(const AccountId& account_id,
                           const std::string& wallpaper_files_id,
                           const std::string& file_name,
@@ -45,25 +44,29 @@
       const std::string& url,
       ash::WallpaperLayout layout,
       bool preview_mode,
-      ash::WallpaperController::SetOnlineWallpaperIfExistsCallback callback);
+      ash::mojom::WallpaperController::SetOnlineWallpaperIfExistsCallback
+          callback);
   void SetOnlineWallpaperFromData(
       const AccountId& account_id,
       const std::string& image_data,
       const std::string& url,
       ash::WallpaperLayout layout,
       bool preview_mode,
-      ash::WallpaperController::SetOnlineWallpaperFromDataCallback callback);
+      ash::mojom::WallpaperController::SetOnlineWallpaperFromDataCallback
+          callback);
   void SetDefaultWallpaper(const AccountId& account_id, bool show_wallpaper);
   void SetCustomizedDefaultWallpaperPaths(
       const base::FilePath& customized_default_small_path,
       const base::FilePath& customized_default_large_path);
   void SetPolicyWallpaper(const AccountId& account_id,
                           std::unique_ptr<std::string> data);
-  bool SetThirdPartyWallpaper(const AccountId& account_id,
-                              const std::string& wallpaper_files_id,
-                              const std::string& file_name,
-                              ash::WallpaperLayout layout,
-                              const gfx::ImageSkia& image);
+  void SetThirdPartyWallpaper(
+      const AccountId& account_id,
+      const std::string& wallpaper_files_id,
+      const std::string& file_name,
+      ash::WallpaperLayout layout,
+      const gfx::ImageSkia& image,
+      ash::mojom::WallpaperController::SetThirdPartyWallpaperCallback callback);
   void ConfirmPreviewWallpaper();
   void CancelPreviewWallpaper();
   void UpdateCustomWallpaperLayout(const AccountId& account_id,
@@ -75,30 +78,42 @@
   void RemoveUserWallpaper(const AccountId& account_id);
   void RemovePolicyWallpaper(const AccountId& account_id);
   void GetOfflineWallpaperList(
-      ash::WallpaperController::GetOfflineWallpaperListCallback callback);
+      ash::mojom::WallpaperController::GetOfflineWallpaperListCallback
+          callback);
   void SetAnimationDuration(const base::TimeDelta& animation_duration);
   void OpenWallpaperPickerIfAllowed();
   void MinimizeInactiveWindows(const std::string& user_id_hash);
   void RestoreMinimizedWindows(const std::string& user_id_hash);
-  void AddObserver(ash::WallpaperControllerObserver* observer);
-  void RemoveObserver(ash::WallpaperControllerObserver* observer);
-  gfx::ImageSkia GetWallpaperImage();
-  const std::vector<SkColor>& GetWallpaperColors();
-  bool IsWallpaperBlurred();
-  bool IsActiveUserWallpaperControlledByPolicy();
-  ash::WallpaperInfo GetActiveUserWallpaperInfo();
-  bool ShouldShowWallpaperSetting();
+  void AddObserver(ash::mojom::WallpaperObserverAssociatedPtrInfo observer);
+  void GetWallpaperImage(
+      ash::mojom::WallpaperController::GetWallpaperImageCallback callback);
+  void GetWallpaperColors(
+      ash::mojom::WallpaperController::GetWallpaperColorsCallback callback);
+  void IsWallpaperBlurred(
+      ash::mojom::WallpaperController::IsWallpaperBlurredCallback callback);
+  void IsActiveUserWallpaperControlledByPolicy(
+      ash::mojom::WallpaperController::
+          IsActiveUserWallpaperControlledByPolicyCallback callback);
+  void GetActiveUserWallpaperInfo(
+      ash::mojom::WallpaperController::GetActiveUserWallpaperInfoCallback
+          callback);
+  void ShouldShowWallpaperSetting(
+      ash::mojom::WallpaperController::ShouldShowWallpaperSettingCallback
+          callback);
+
+  // Flushes the mojo pipe to ash.
+  void FlushForTesting();
 
  private:
-  // Initialize the controller for this client and some wallpaper directories.
-  void InitController();
+  // Binds this object to its mojo interface and sets it as the ash client.
+  void BindAndSetClient();
 
   // Shows the wallpaper of the first user in |UserManager::GetUsers|, or a
   // default signin wallpaper if there's no user. This ensures the wallpaper is
   // shown right after boot, regardless of when the login screen is available.
   void ShowWallpaperOnLoginScreen();
 
-  // ash::WallpaperControllerClient:
+  // ash::mojom::WallpaperControllerClient:
   void OpenWallpaperPicker() override;
   void OnReadyToSetWallpaper() override;
   void OnFirstWallpaperAnimationFinished() override;
@@ -111,7 +126,7 @@
   base::FilePath GetDeviceWallpaperImageFilePath();
 
   // WallpaperController interface in ash.
-  ash::WallpaperController* wallpaper_controller_;
+  ash::mojom::WallpaperControllerPtr wallpaper_controller_;
 
   PrefService* local_state_;
 
@@ -123,6 +138,9 @@
   std::unique_ptr<chromeos::CrosSettings::ObserverSubscription>
       show_user_names_on_signin_subscription_;
 
+  // Binds to the client interface.
+  mojo::Binding<ash::mojom::WallpaperControllerClient> binding_;
+
   base::WeakPtrFactory<WallpaperControllerClient> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(WallpaperControllerClient);
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_unittest.cc b/chrome/browser/ui/ash/wallpaper_controller_client_unittest.cc
index 4cfa016..14bea4c 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_unittest.cc
@@ -30,7 +30,8 @@
 TEST_F(WallpaperControllerClientTest, Construction) {
   WallpaperControllerClient client;
   TestWallpaperController controller;
-  client.InitForTesting(&controller);
+  client.InitForTesting(controller.CreateInterfacePtr());
+  client.FlushForTesting();
 
   // Singleton was initialized.
   EXPECT_EQ(&client, WallpaperControllerClient::Get());
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 5570973..10231fb1 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1551,8 +1551,8 @@
 }
 
 void Browser::ShowRepostFormWarningDialog(WebContents* source) {
-  TabModalConfirmDialog::Create(new RepostFormWarningController(source),
-                                source);
+  TabModalConfirmDialog::Create(
+      std::make_unique<RepostFormWarningController>(source), source);
 }
 
 bool Browser::ShouldCreateWebContents(
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc
index 7978ad48..798aafbf 100644
--- a/chrome/browser/ui/browser_command_controller_browsertest.cc
+++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -66,9 +66,10 @@
   // Showing constrained window should disable find.
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  MockTabModalConfirmDialogDelegate* delegate =
-      new MockTabModalConfirmDialogDelegate(web_contents, NULL);
-  TabModalConfirmDialog::Create(delegate, web_contents);
+  auto delegate = std::make_unique<MockTabModalConfirmDialogDelegate>(
+      web_contents, nullptr);
+  MockTabModalConfirmDialogDelegate* delegate_ptr = delegate.get();
+  TabModalConfirmDialog::Create(std::move(delegate), web_contents);
   EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FIND));
 
   // Switching to a new (unblocked) tab should reenable it.
@@ -80,7 +81,7 @@
   EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FIND));
 
   // Closing the constrained window should reenable it.
-  delegate->Cancel();
+  delegate_ptr->Cancel();
   content::RunAllPendingInMessageLoop();
   EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FIND));
 }
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index 2a64453..6aab086 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -36,6 +36,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/ntp_tiles/constants.h"
+#include "components/ntp_tiles/features.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/interstitial_page.h"
@@ -81,6 +82,16 @@
   return nullptr;
 }
 
+bool ContainsDefaultSearchTile(content::RenderFrameHost* iframe) {
+  int num_search_tiles;
+  EXPECT_TRUE(
+      instant_test_utils::GetIntFromJS(iframe,
+                                       "document.querySelectorAll(\".md-tile["
+                                       "href='https://google.com/']\").length",
+                                       &num_search_tiles));
+  return num_search_tiles == 1;
+}
+
 class LocalNTPTest : public InProcessBrowserTest {
  public:
   LocalNTPTest(const std::vector<base::Feature>& enabled_features,
@@ -844,4 +855,41 @@
   EXPECT_TRUE(search::IsInstantNTP(active_tab));
 }
 
+class LocalNTPNoSearchShortcutTest : public LocalNTPTest {
+ public:
+  LocalNTPNoSearchShortcutTest()
+      : LocalNTPTest({}, {ntp_tiles::kDefaultSearchShortcut}) {}
+};
+
+IN_PROC_BROWSER_TEST_F(LocalNTPNoSearchShortcutTest, SearchShortcutHidden) {
+  content::WebContents* active_tab =
+      local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
+
+  local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+
+  content::RenderFrameHost* iframe = GetIframe(active_tab, "mv-single");
+
+  EXPECT_FALSE(ContainsDefaultSearchTile(iframe));
+}
+
+#if defined(GOOGLE_CHROME_BUILD)
+class LocalNTPSearchShortcutTest : public LocalNTPTest {
+ public:
+  LocalNTPSearchShortcutTest()
+      : LocalNTPTest({ntp_tiles::kDefaultSearchShortcut}, {}) {}
+};
+
+IN_PROC_BROWSER_TEST_F(LocalNTPSearchShortcutTest, SearchShortcutShown) {
+  content::WebContents* active_tab =
+      local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
+  local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+
+  content::RenderFrameHost* iframe = GetIframe(active_tab, kMostVisitedIframe);
+
+  EXPECT_TRUE(ContainsDefaultSearchTile(iframe));
+}
+#endif
+
 }  // namespace
diff --git a/chrome/browser/ui/tab_modal_confirm_dialog.h b/chrome/browser/ui/tab_modal_confirm_dialog.h
index 9ecc4ba0..cbc0ea2 100644
--- a/chrome/browser/ui/tab_modal_confirm_dialog.h
+++ b/chrome/browser/ui/tab_modal_confirm_dialog.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_TAB_MODAL_CONFIRM_DIALOG_H_
 #define CHROME_BROWSER_UI_TAB_MODAL_CONFIRM_DIALOG_H_
 
+#include <memory>
+
 #include "build/build_config.h"
 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
 
@@ -17,8 +19,9 @@
  public:
   // Platform specific factory function. This function will automatically show
   // the dialog.
-  static TabModalConfirmDialog* Create(TabModalConfirmDialogDelegate* delegate,
-                                       content::WebContents* web_contents);
+  static TabModalConfirmDialog* Create(
+      std::unique_ptr<TabModalConfirmDialogDelegate> delegate,
+      content::WebContents* web_contents);
   // Accepts the dialog.
   virtual void AcceptTabModalDialog() = 0;
 
diff --git a/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc b/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc
index bb56ae9..cb6d14f 100644
--- a/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc
+++ b/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/tab_modal_confirm_dialog_browsertest.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
@@ -57,10 +60,12 @@
       closed_count_(0) {}
 
 void TabModalConfirmDialogTest::SetUpOnMainThread() {
-  delegate_ = new MockTabModalConfirmDialogDelegate(
+  auto delegate = std::make_unique<MockTabModalConfirmDialogDelegate>(
       browser()->tab_strip_model()->GetActiveWebContents(), this);
+  delegate_ = delegate.get();
   dialog_ = TabModalConfirmDialog::Create(
-      delegate_, browser()->tab_strip_model()->GetActiveWebContents());
+      std::move(delegate),
+      browser()->tab_strip_model()->GetActiveWebContents());
   content::RunAllPendingInMessageLoop();
 }
 
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index 4c4ff5b..31f6e8e 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -40,6 +40,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/payments/credit_card_save_manager.h"
+#include "components/autofill/core/browser/payments/credit_card_save_strike_database.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/test_event_waiter.h"
@@ -181,14 +182,13 @@
         ->set_url_loader_factory_for_testing(test_shared_loader_factory_);
 
     // Set up this class as the ObserverForTest implementation.
-    CreditCardSaveManager* credit_card_save_manager =
-        ContentAutofillDriver::GetForRenderFrameHost(
-            GetActiveWebContents()->GetMainFrame())
-            ->autofill_manager()
-            ->client()
-            ->GetFormDataImporter()
-            ->credit_card_save_manager_.get();
-    credit_card_save_manager->SetEventObserverForTesting(this);
+    credit_card_save_manager_ = ContentAutofillDriver::GetForRenderFrameHost(
+                                    GetActiveWebContents()->GetMainFrame())
+                                    ->autofill_manager()
+                                    ->client()
+                                    ->GetFormDataImporter()
+                                    ->credit_card_save_manager_.get();
+    credit_card_save_manager_->SetEventObserverForTesting(this);
 
     // Set up this class as the ObserverForTest implementation.
     AutofillHandler* autofill_handler =
@@ -731,6 +731,8 @@
 
   std::unique_ptr<ProfileSyncServiceHarness> harness_;
 
+  CreditCardSaveManager* credit_card_save_manager_ = nullptr;
+
  private:
   std::unique_ptr<autofill::EventWaiter<DialogEvent>> event_waiter_;
   std::unique_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
@@ -2587,9 +2589,11 @@
 
   bool controller_observer_set = false;
 
-  // Show and ignore the bubble kMaxStrikesToPreventPoppingUpOfferToSavePrompt
-  // times in order to accrue maximum strikes.
-  for (int i = 0; i < kMaxStrikesToPreventPoppingUpOfferToSavePrompt; i++) {
+  // Show and ignore the bubble enough times in order to accrue maximum strikes.
+  for (int i = 0;
+       i < credit_card_save_manager_->GetCreditCardSaveStrikeDatabase()
+               ->GetMaxStrikesLimit();
+       ++i) {
     // Submitting the form and having Payments decline offering to save should
     // show the local save bubble.
     // (Must wait for response from Payments before accessing the controller.)
@@ -2675,9 +2679,11 @@
   // Set up the Payments RPC.
   SetUploadDetailsRpcPaymentsAccepts();
 
-  // Show and ignore the bubble kMaxStrikesToPreventPoppingUpOfferToSavePrompt
-  // times in order to accrue maximum strikes.
-  for (int i = 0; i < kMaxStrikesToPreventPoppingUpOfferToSavePrompt; i++) {
+  // Show and ignore the bubble enough times in order to accrue maximum strikes.
+  for (int i = 0;
+       i < credit_card_save_manager_->GetCreditCardSaveStrikeDatabase()
+               ->GetMaxStrikesLimit();
+       ++i) {
     // Submitting the form should show the upload save bubble and legal footer.
     // (Must wait for response from Payments before accessing the controller.)
     ResetEventWaiterForSequence(
diff --git a/chrome/browser/ui/views/frame/browser_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
index f83f45e6..72d1c68 100644
--- a/chrome/browser/ui/views/frame/browser_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
@@ -317,12 +317,14 @@
                                window_title, base::CompareCase::SENSITIVE));
 
   content::WebContents* contents = browser_view()->GetActiveWebContents();
-  TestTabModalConfirmDialogDelegate* delegate =
-      new TestTabModalConfirmDialogDelegate(contents);
-  TabModalConfirmDialog::Create(delegate, contents);
-  EXPECT_EQ(browser_view()->GetAccessibleWindowTitle(), delegate->GetTitle());
+  auto delegate = std::make_unique<TestTabModalConfirmDialogDelegate>(contents);
+  TestTabModalConfirmDialogDelegate* delegate_observer = delegate.get();
+  TabModalConfirmDialog::Create(std::move(delegate), contents);
+  EXPECT_EQ(browser_view()->GetAccessibleWindowTitle(),
+            delegate_observer->GetTitle());
 
-  delegate->Close();
+  delegate_observer->Close();
+
   EXPECT_TRUE(base::StartsWith(browser_view()->GetAccessibleWindowTitle(),
                                window_title, base::CompareCase::SENSITIVE));
 }
@@ -349,9 +351,8 @@
             nullptr);
 
   content::WebContents* contents = browser_view()->GetActiveWebContents();
-  TestTabModalConfirmDialogDelegate* delegate =
-      new TestTabModalConfirmDialogDelegate(contents);
-  TabModalConfirmDialog::Create(delegate, contents);
+  auto delegate = std::make_unique<TestTabModalConfirmDialogDelegate>(contents);
+  TabModalConfirmDialog::Create(std::move(delegate), contents);
 
   // The tab modal dialog should be in the accessibility tree; everything else
   // should be hidden. So we expect an "OK" button and no reload button.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index 6081e7c6..724a180 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -615,13 +615,6 @@
 };
 
 TEST_P(OmniboxViewViewsClipboardTest, ClipboardCopyOrCutURL) {
-  // TODO(crbug.com/396477) Make this an interactive ui test so there isn't
-  // contention for the system clipboard.
-  // Force use of the system clipboard because this test checks for URL format
-  // on Linux, which is not supported by test clipboard.
-  ui::Clipboard::DestroyClipboardForCurrentThread();
-  ui::Clipboard::GetForCurrentThread();
-
   omnibox_view()->SelectAll(false);
   ASSERT_TRUE(omnibox_view()->IsSelectAll());
 
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 1aae1525..bdaeae717 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -35,12 +35,12 @@
   if (!manager)
     return false;
 
-  bool is_probably_installable = manager->IsProbablyInstallableWebApp();
+  bool is_probably_promotable = manager->IsProbablyPromotableWebApp();
   auto* tab_helper =
       web_app::WebAppTabHelperBase::FromWebContents(web_contents);
   bool is_installed = tab_helper && tab_helper->HasAssociatedApp();
 
-  bool show_install_button = is_probably_installable && !is_installed;
+  bool show_install_button = is_probably_promotable && !is_installed;
 
   if (show_install_button && manager->MaybeConsumeInstallAnimation())
     AnimateIn(base::nullopt);
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index d7c881d..4a3779b2 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -326,3 +326,20 @@
 IN_PROC_BROWSER_TEST_F(PwaInstallViewBrowserTest, BouncedInstallIgnored) {
   TestInstallBounce(base::TimeDelta::FromMinutes(70), 0);
 }
+
+// Omnibox install button shouldn't show for a PWA that prefers a related app
+// over the web app.
+IN_PROC_BROWSER_TEST_F(PwaInstallViewBrowserTest,
+                       SuppressForPreferredRelatedApp) {
+  NavigateToURL(
+      https_server_.GetURL("/banners/manifest_test_page.html?manifest="
+                           "manifest_prefer_related_apps_empty.json"));
+  ASSERT_TRUE(app_banner_manager_->WaitForInstallableCheck());
+  EXPECT_FALSE(pwa_install_view_->GetVisible());
+
+  // Site should still be considered installable even if it's not promoted in
+  // the omnibox.
+  EXPECT_TRUE(base::EqualsASCII(
+      banners::AppBannerManager::GetInstallableWebAppName(web_contents_),
+      "Manifest prefer related apps empty"));
+}
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
new file mode 100644
index 0000000..673aa9b
--- /dev/null
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 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/ui/views/status_icons/status_icon_linux_dbus.h"
+
+#include "base/logging.h"
+
+StatusIconLinuxDbus::StatusIconLinuxDbus(const gfx::ImageSkia& image,
+                                         const base::string16& tool_tip) {}
+
+StatusIconLinuxDbus::~StatusIconLinuxDbus() = default;
+
+void StatusIconLinuxDbus::SetImage(const gfx::ImageSkia& image) {
+  NOTIMPLEMENTED();
+}
+
+void StatusIconLinuxDbus::SetToolTip(const base::string16& tool_tip) {
+  NOTIMPLEMENTED();
+}
+
+void StatusIconLinuxDbus::UpdatePlatformContextMenu(ui::MenuModel* model) {
+  NOTIMPLEMENTED();
+}
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
new file mode 100644
index 0000000..ea18ea9
--- /dev/null
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
@@ -0,0 +1,28 @@
+// Copyright 2019 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_UI_VIEWS_STATUS_ICONS_STATUS_ICON_LINUX_DBUS_H_
+#define CHROME_BROWSER_UI_VIEWS_STATUS_ICONS_STATUS_ICON_LINUX_DBUS_H_
+
+#include "base/macros.h"
+#include "ui/views/linux_ui/status_icon_linux.h"
+
+// A status icon following the StatusNotifierItem specification.
+// https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/
+class StatusIconLinuxDbus : public views::StatusIconLinux {
+ public:
+  StatusIconLinuxDbus(const gfx::ImageSkia& image,
+                      const base::string16& tool_tip);
+  ~StatusIconLinuxDbus() override;
+
+  // StatusIcon:
+  void SetImage(const gfx::ImageSkia& image) override;
+  void SetToolTip(const base::string16& tool_tip) override;
+  void UpdatePlatformContextMenu(ui::MenuModel* model) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StatusIconLinuxDbus);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_STATUS_ICONS_STATUS_ICON_LINUX_DBUS_H_
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
index 4694a9a..b81d764 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
@@ -4,12 +4,24 @@
 
 #include "chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h"
 
+#include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
 #include "ui/views/linux_ui/linux_ui.h"
 
+#if defined(USE_DBUS)
+#include "chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h"
+#endif
+
+#if defined(USE_X11)
+#include "chrome/browser/ui/views/status_icons/status_icon_linux_x11.h"
+#endif
+
 namespace {
 
+constexpr base::Feature kEnableDbusAndX11StatusIcons{
+    "EnableDbusAndX11StatusIcons", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Prefix for app indicator ids
 const char kAppIndicatorIdPrefix[] = "chrome_app_indicator_";
 
@@ -58,6 +70,15 @@
 StatusIconLinuxWrapper::CreateWrappedStatusIcon(
     const gfx::ImageSkia& image,
     const base::string16& tool_tip) {
+  if (base::FeatureList::IsEnabled(kEnableDbusAndX11StatusIcons)) {
+#if defined(USE_DBUS)
+    return base::WrapUnique(new StatusIconLinuxWrapper(
+        std::make_unique<StatusIconLinuxDbus>(image, tool_tip)));
+#endif
+    // TODO(thomasanderson): Set up a mechanism for replacing |status_icon_|
+    // with a StatusIconLinuxX11 in case the system doesn't support DBus status
+    // icons, which we can only discover asynchronously.
+  }
   const views::LinuxUI* linux_ui = views::LinuxUI::instance();
   if (linux_ui) {
     auto status_icon =
@@ -72,7 +93,7 @@
 
 void StatusIconLinuxWrapper::UpdatePlatformContextMenu(
     StatusIconMenuModel* model) {
-  // If a menu already exists, remove ourself from its oberver list.
+  // If a menu already exists, remove ourself from its observer list.
   if (menu_model_)
     menu_model_->RemoveObserver(this);
 
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_x11.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_x11.cc
new file mode 100644
index 0000000..2256e37
--- /dev/null
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_x11.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 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/ui/views/status_icons/status_icon_linux_x11.h"
+
+#include "base/logging.h"
+
+StatusIconLinuxX11::StatusIconLinuxX11(const gfx::ImageSkia& image,
+                                       const base::string16& tool_tip) {}
+
+StatusIconLinuxX11::~StatusIconLinuxX11() = default;
+
+void StatusIconLinuxX11::SetImage(const gfx::ImageSkia& image) {
+  NOTIMPLEMENTED();
+}
+
+void StatusIconLinuxX11::SetToolTip(const base::string16& tool_tip) {
+  NOTIMPLEMENTED();
+}
+
+void StatusIconLinuxX11::UpdatePlatformContextMenu(ui::MenuModel* model) {
+  NOTIMPLEMENTED();
+}
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_x11.h b/chrome/browser/ui/views/status_icons/status_icon_linux_x11.h
new file mode 100644
index 0000000..bab7293f
--- /dev/null
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_x11.h
@@ -0,0 +1,28 @@
+// Copyright 2019 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_UI_VIEWS_STATUS_ICONS_STATUS_ICON_LINUX_X11_H_
+#define CHROME_BROWSER_UI_VIEWS_STATUS_ICONS_STATUS_ICON_LINUX_X11_H_
+
+#include "base/macros.h"
+#include "ui/views/linux_ui/status_icon_linux.h"
+
+// A status icon that uses the XEmbed protocol.
+// https://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
+class StatusIconLinuxX11 : public views::StatusIconLinux {
+ public:
+  StatusIconLinuxX11(const gfx::ImageSkia& image,
+                     const base::string16& tool_tip);
+  ~StatusIconLinuxX11() override;
+
+  // StatusIcon:
+  void SetImage(const gfx::ImageSkia& image) override;
+  void SetToolTip(const base::string16& tool_tip) override;
+  void UpdatePlatformContextMenu(ui::MenuModel* model) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StatusIconLinuxX11);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_STATUS_ICONS_STATUS_ICON_LINUX_X11_H_
diff --git a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
index fc16b97c..19470a2 100644
--- a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
+++ b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/views/tab_modal_confirm_dialog_views.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -21,25 +24,25 @@
 
 // static
 TabModalConfirmDialog* TabModalConfirmDialog::Create(
-    TabModalConfirmDialogDelegate* delegate,
+    std::unique_ptr<TabModalConfirmDialogDelegate> delegate,
     content::WebContents* web_contents) {
-  return new TabModalConfirmDialogViews(delegate, web_contents);
+  return new TabModalConfirmDialogViews(std::move(delegate), web_contents);
 }
 
 //////////////////////////////////////////////////////////////////////////////
 // TabModalConfirmDialogViews, constructor & destructor:
 
 TabModalConfirmDialogViews::TabModalConfirmDialogViews(
-    TabModalConfirmDialogDelegate* delegate,
+    std::unique_ptr<TabModalConfirmDialogDelegate> delegate,
     content::WebContents* web_contents)
-    : delegate_(delegate) {
-  views::MessageBoxView::InitParams init_params(delegate->GetDialogMessage());
+    : delegate_(std::move(delegate)) {
+  views::MessageBoxView::InitParams init_params(delegate_->GetDialogMessage());
   init_params.inter_row_vertical_spacing =
       ChromeLayoutProvider::Get()->GetDistanceMetric(
           views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
   message_box_view_ = new views::MessageBoxView(init_params);
 
-  base::string16 link_text(delegate->GetLinkText());
+  base::string16 link_text(delegate_->GetLinkText());
   if (!link_text.empty())
     message_box_view_->SetLink(link_text, this);
 
diff --git a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
index e664758..5b6e510 100644
--- a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
+++ b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_TAB_MODAL_CONFIRM_DIALOG_VIEWS_H_
 #define CHROME_BROWSER_UI_VIEWS_TAB_MODAL_CONFIRM_DIALOG_VIEWS_H_
 
+#include <memory>
+
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
@@ -30,8 +32,9 @@
                                    public views::DialogDelegate,
                                    public views::LinkListener {
  public:
-  TabModalConfirmDialogViews(TabModalConfirmDialogDelegate* delegate,
-                             content::WebContents* web_contents);
+  TabModalConfirmDialogViews(
+      std::unique_ptr<TabModalConfirmDialogDelegate> delegate,
+      content::WebContents* web_contents);
 
   // views::DialogDelegate:
   base::string16 GetWindowTitle() const override;
diff --git a/chrome/browser/ui/views/tabs/tab_animation.cc b/chrome/browser/ui/views/tabs/tab_animation.cc
index c80718c..d4e4e00 100644
--- a/chrome/browser/ui/views/tabs/tab_animation.cc
+++ b/chrome/browser/ui/views/tabs/tab_animation.cc
@@ -62,14 +62,6 @@
   duration_ = kZeroDuration;
 }
 
-void TabAnimation::CancelAnimation() {
-  TabAnimationState current_state = GetCurrentState();
-  initial_state_ = current_state;
-  target_state_ = current_state;
-  start_time_ = base::TimeTicks::Now();
-  duration_ = kZeroDuration;
-}
-
 void TabAnimation::NotifyCloseCompleted() {
   std::move(tab_removed_callback_).Run();
 }
diff --git a/chrome/browser/ui/views/tabs/tab_animation.h b/chrome/browser/ui/views/tabs/tab_animation.h
index 2f006af..6133e53 100644
--- a/chrome/browser/ui/views/tabs/tab_animation.h
+++ b/chrome/browser/ui/views/tabs/tab_animation.h
@@ -34,7 +34,6 @@
   void RetargetTo(TabAnimationState target_state);
 
   void CompleteAnimation();
-  void CancelAnimation();
 
   // Notifies the owner of the animated tab that the close animation
   // has completed and the tab can be cleaned up.
diff --git a/chrome/browser/ui/views/tabs/tab_animation_unittest.cc b/chrome/browser/ui/views/tabs/tab_animation_unittest.cc
index 85c4c8a..ce70460 100644
--- a/chrome/browser/ui/views/tabs/tab_animation_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_animation_unittest.cc
@@ -95,48 +95,6 @@
             PinnednessOf(animation.GetCurrentState()));
 }
 
-TEST_F(TabAnimationTest, CanceledAnimationStaysAtInitial) {
-  TabAnimationState initial_state = TabAnimationState::ForIdealTabState(
-      TabAnimationState::TabOpenness::kOpen,
-      TabAnimationState::TabPinnedness::kUnpinned,
-      TabAnimationState::TabActiveness::kInactive, 0);
-  TabAnimationState target_state =
-      initial_state.WithPinnedness(TabAnimationState::TabPinnedness::kPinned);
-  TabAnimation animation =
-      TabAnimation::ForStaticState(initial_state, base::BindOnce([]() {}));
-  animation.AnimateTo(target_state);
-
-  animation.CancelAnimation();
-
-  EXPECT_EQ(kZeroDuration, animation.GetTimeRemaining());
-  EXPECT_EQ(base::TimeDelta::FromMilliseconds(0), animation.GetTimeRemaining());
-  EXPECT_EQ(PinnednessOf(initial_state),
-            PinnednessOf(animation.GetCurrentState()));
-}
-
-TEST_F(TabAnimationTest, PartwayFinishedCanceledAnimationStaysPartwayFinished) {
-  TabAnimationState initial_state = TabAnimationState::ForIdealTabState(
-      TabAnimationState::TabOpenness::kOpen,
-      TabAnimationState::TabPinnedness::kUnpinned,
-      TabAnimationState::TabActiveness::kInactive, 0);
-  TabAnimationState target_state =
-      initial_state.WithPinnedness(TabAnimationState::TabPinnedness::kPinned);
-  TabAnimation animation =
-      TabAnimation::ForStaticState(initial_state, base::BindOnce([]() {}));
-  animation.AnimateTo(target_state);
-
-  env_.FastForwardBy(TabAnimation::kAnimationDuration / 2.0);
-
-  animation.CancelAnimation();
-
-  EXPECT_EQ(kZeroDuration, animation.GetTimeRemaining());
-  EXPECT_EQ(base::TimeDelta::FromMilliseconds(0), animation.GetTimeRemaining());
-  EXPECT_LT(PinnednessOf(initial_state),
-            PinnednessOf(animation.GetCurrentState()));
-  EXPECT_LT(PinnednessOf(animation.GetCurrentState()),
-            PinnednessOf(target_state));
-}
-
 TEST_F(TabAnimationTest, ReplacedAnimationRestartsDuration) {
   TabAnimationState initial_state = TabAnimationState::ForIdealTabState(
       TabAnimationState::TabOpenness::kOpen,
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 5f1a991..8b6453ec 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -2105,6 +2105,10 @@
 }
 
 void TabStrip::AnimateToIdealBounds() {
+  // bounds_animator_ and animator_ should not run concurrently.
+  // bounds_animator_ takes precedence, and can finish what animator_ started.
+  animator_->CompleteAnimationsWithoutDestroyingTabs();
+
   for (int i = 0; i < tab_count(); ++i) {
     // If the tab is being dragged manually, skip it.
     Tab* tab = tab_at(i);
diff --git a/chrome/browser/ui/views/tabs/tab_strip_animator.cc b/chrome/browser/ui/views/tabs/tab_strip_animator.cc
index 9f9f6c4f..39173902 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_animator.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_animator.cc
@@ -97,16 +97,13 @@
 }
 
 void TabStripAnimator::CompleteAnimations() {
-  for (size_t i = 0; i < animations_.size(); i++) {
-    animations_[i].CompleteAnimation();
-  }
+  CompleteAnimationsWithoutDestroyingTabs();
   RemoveClosedTabs();
-  timer_.Stop();
 }
 
-void TabStripAnimator::CancelAnimations() {
+void TabStripAnimator::CompleteAnimationsWithoutDestroyingTabs() {
   for (TabAnimation& animation : animations_) {
-    animation.CancelAnimation();
+    animation.CompleteAnimation();
   }
   timer_.Stop();
 }
diff --git a/chrome/browser/ui/views/tabs/tab_strip_animator.h b/chrome/browser/ui/views/tabs/tab_strip_animator.h
index 33b9907..ff065f4 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_animator.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_animator.h
@@ -58,10 +58,12 @@
 
   void CompleteAnimations();
 
-  // TODO(958173): Temporary method that aborts current animations, leaving
-  // tabs where they are. Use to hand off animation responsibilities from
-  // this animator to elsewhere without teleporting tabs.
-  void CancelAnimations();
+  // TODO(958173): Temporary method that completes running animations,
+  // without invoking the callback to destroy removed tabs. Use to hand
+  // off animation (and removed tab destruction) responsibilities from
+  // this animator to elsewhere without teleporting tabs or destroying
+  // the same tab more than once.
+  void CompleteAnimationsWithoutDestroyingTabs();
 
  private:
   void AnimateTabTo(int index, TabAnimationState target_state);
diff --git a/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc
index cd87784..d158fd5 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_animator_unittest.cc
@@ -160,20 +160,6 @@
   EXPECT_EQ(1.0f, OpennessOf(animator_.GetCurrentTabStates()[0]));
 }
 
-TEST_F(TabStripAnimatorTest, CancelAnimations) {
-  animator_.InsertTabAt(0, base::BindOnce([]() {}),
-                        TabAnimationState::TabActiveness::kActive,
-                        TabAnimationState::TabPinnedness::kUnpinned);
-  EXPECT_TRUE(animator_.IsAnimating());
-  EXPECT_EQ(1u, animator_.GetCurrentTabStates().size());
-  EXPECT_EQ(0.0f, OpennessOf(animator_.GetCurrentTabStates()[0]));
-
-  animator_.CancelAnimations();
-
-  EXPECT_FALSE(animator_.IsAnimating());
-  EXPECT_EQ(0.0f, OpennessOf(animator_.GetCurrentTabStates()[0]));
-}
-
 TEST_F(TabStripAnimatorTest, CompleteAnimationsRemovesClosedTabs) {
   TabClosedDetector second_tab;
   animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
@@ -200,7 +186,8 @@
   EXPECT_TRUE(second_tab.was_closed_);
 }
 
-TEST_F(TabStripAnimatorTest, CancelAnimationsDoesNotRemoveClosedTabs) {
+TEST_F(TabStripAnimatorTest,
+       CompleteAnimationsWithoutDestroyingTabsDoesNotRemoveClosedTabs) {
   TabClosedDetector second_tab;
   animator_.InsertTabAtNoAnimation(0, base::BindOnce([]() {}),
                                    TabAnimationState::TabActiveness::kActive,
@@ -219,7 +206,7 @@
   EXPECT_EQ(1.0f, OpennessOf(animator_.GetCurrentTabStates()[1]));
   EXPECT_FALSE(second_tab.was_closed_);
 
-  animator_.CancelAnimations();
+  animator_.CompleteAnimationsWithoutDestroyingTabs();
 
   EXPECT_FALSE(animator_.IsAnimating());
   EXPECT_EQ(2u, animator_.GetCurrentTabStates().size());
diff --git a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.cc b/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.cc
deleted file mode 100644
index 61ac027..0000000
--- a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 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/ui/web_applications/web_app_ui_delegate_impl_factory.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/web_applications/web_app_ui_delegate_impl.h"
-#include "chrome/browser/web_applications/components/web_app_utils.h"
-#include "chrome/browser/web_applications/web_app_provider_factory.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-
-namespace web_app {
-
-// static
-WebAppUiDelegateImpl* WebAppUiDelegateImplFactory::GetForProfile(
-    Profile* profile) {
-  return static_cast<WebAppUiDelegateImpl*>(
-      WebAppUiDelegateImplFactory::GetInstance()->GetServiceForBrowserContext(
-          profile, true /* create */));
-}
-
-// static
-WebAppUiDelegateImplFactory* WebAppUiDelegateImplFactory::GetInstance() {
-  return base::Singleton<WebAppUiDelegateImplFactory>::get();
-}
-
-WebAppUiDelegateImplFactory::WebAppUiDelegateImplFactory()
-    : BrowserContextKeyedServiceFactory(
-          "WebAppUiDelegate",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(WebAppProviderFactory::GetInstance());
-}
-
-WebAppUiDelegateImplFactory::~WebAppUiDelegateImplFactory() = default;
-
-KeyedService* WebAppUiDelegateImplFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  return new WebAppUiDelegateImpl(Profile::FromBrowserContext(context));
-}
-
-bool WebAppUiDelegateImplFactory::ServiceIsCreatedWithBrowserContext() const {
-  return true;
-}
-
-content::BrowserContext* WebAppUiDelegateImplFactory::GetBrowserContextToUse(
-    content::BrowserContext* context) const {
-  return GetBrowserContextForWebApps(context);
-}
-
-}  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.h b/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.h
deleted file mode 100644
index 100b4c40..0000000
--- a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2019 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_UI_WEB_APPLICATIONS_WEB_APP_UI_DELEGATE_IMPL_FACTORY_H_
-#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_DELEGATE_IMPL_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace content {
-class BrowserContext;
-}
-
-class Profile;
-
-namespace web_app {
-
-class WebAppUiDelegateImpl;
-
-// Singleton that owns all WebAppUiDelegateImplFactories and associated them
-// with Profile.
-class WebAppUiDelegateImplFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static WebAppUiDelegateImpl* GetForProfile(Profile* profile);
-
-  static WebAppUiDelegateImplFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<WebAppUiDelegateImplFactory>;
-
-  WebAppUiDelegateImplFactory();
-  ~WebAppUiDelegateImplFactory() override;
-
-  // BrowserContextKeyedServiceFactory
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(WebAppUiDelegateImplFactory);
-};
-
-}  // namespace web_app
-
-#endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_DELEGATE_IMPL_FACTORY_H_
diff --git a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl.cc b/chrome/browser/ui/web_applications/web_app_ui_service.cc
similarity index 71%
rename from chrome/browser/ui/web_applications/web_app_ui_delegate_impl.cc
rename to chrome/browser/ui/web_applications/web_app_ui_service.cc
index 5ef0980..256a7963 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/web_applications/web_app_ui_delegate_impl.h"
+#include "chrome/browser/ui/web_applications/web_app_ui_service.h"
 
 #include <utility>
 
@@ -10,18 +10,17 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
-#include "chrome/browser/ui/web_applications/web_app_ui_delegate_impl_factory.h"
+#include "chrome/browser/ui/web_applications/web_app_ui_service_factory.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 
 namespace web_app {
 
 // static
-WebAppUiDelegateImpl* WebAppUiDelegateImpl::Get(Profile* profile) {
-  return WebAppUiDelegateImplFactory::GetForProfile(profile);
+WebAppUiService* WebAppUiService::Get(Profile* profile) {
+  return WebAppUiServiceFactory::GetForProfile(profile);
 }
 
-WebAppUiDelegateImpl::WebAppUiDelegateImpl(Profile* profile)
-    : profile_(profile) {
+WebAppUiService::WebAppUiService(Profile* profile) : profile_(profile) {
   for (Browser* browser : *BrowserList::GetInstance()) {
     base::Optional<AppId> app_id = GetAppIdForBrowser(browser);
     if (!app_id.has_value())
@@ -34,14 +33,14 @@
   WebAppProvider::Get(profile_)->set_ui_delegate(this);
 }
 
-WebAppUiDelegateImpl::~WebAppUiDelegateImpl() = default;
+WebAppUiService::~WebAppUiService() = default;
 
-void WebAppUiDelegateImpl::Shutdown() {
+void WebAppUiService::Shutdown() {
   WebAppProvider::Get(profile_)->set_ui_delegate(nullptr);
   BrowserList::RemoveObserver(this);
 }
 
-size_t WebAppUiDelegateImpl::GetNumWindowsForApp(const AppId& app_id) {
+size_t WebAppUiService::GetNumWindowsForApp(const AppId& app_id) {
   auto it = num_windows_for_apps_map_.find(app_id);
   if (it == num_windows_for_apps_map_.end())
     return 0;
@@ -49,9 +48,8 @@
   return it->second;
 }
 
-void WebAppUiDelegateImpl::NotifyOnAllAppWindowsClosed(
-    const AppId& app_id,
-    base::OnceClosure callback) {
+void WebAppUiService::NotifyOnAllAppWindowsClosed(const AppId& app_id,
+                                                  base::OnceClosure callback) {
   const size_t num_windows_for_app = GetNumWindowsForApp(app_id);
   if (num_windows_for_app == 0) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
@@ -62,7 +60,7 @@
   windows_closed_requests_map_[app_id].push_back(std::move(callback));
 }
 
-void WebAppUiDelegateImpl::OnBrowserAdded(Browser* browser) {
+void WebAppUiService::OnBrowserAdded(Browser* browser) {
   base::Optional<AppId> app_id = GetAppIdForBrowser(browser);
   if (!app_id.has_value())
     return;
@@ -70,7 +68,7 @@
   ++num_windows_for_apps_map_[app_id.value()];
 }
 
-void WebAppUiDelegateImpl::OnBrowserRemoved(Browser* browser) {
+void WebAppUiService::OnBrowserRemoved(Browser* browser) {
   base::Optional<AppId> app_id_opt = GetAppIdForBrowser(browser);
   if (!app_id_opt.has_value())
     return;
@@ -94,8 +92,7 @@
   windows_closed_requests_map_.erase(app_id);
 }
 
-base::Optional<AppId> WebAppUiDelegateImpl::GetAppIdForBrowser(
-    Browser* browser) {
+base::Optional<AppId> WebAppUiService::GetAppIdForBrowser(Browser* browser) {
   if (browser->profile() != profile_)
     return base::nullopt;
 
diff --git a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl.h b/chrome/browser/ui/web_applications/web_app_ui_service.h
similarity index 65%
rename from chrome/browser/ui/web_applications/web_app_ui_delegate_impl.h
rename to chrome/browser/ui/web_applications/web_app_ui_service.h
index 7987502..e175429 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl.h
+++ b/chrome/browser/ui/web_applications/web_app_ui_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_DELEGATE_IMPL_H_
-#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_DELEGATE_IMPL_H_
+#ifndef CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_SERVICE_H_
+#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_SERVICE_H_
 
 #include <map>
 #include <vector>
@@ -20,14 +20,16 @@
 
 namespace web_app {
 
-class WebAppUiDelegateImpl : public KeyedService,
-                             public BrowserListObserver,
-                             public WebAppUiDelegate {
+// This KeyedService is a UI counterpart for WebAppProvider.
+// It tracks the open windows for each web app.
+class WebAppUiService : public KeyedService,
+                        public BrowserListObserver,
+                        public WebAppUiDelegate {
  public:
-  static WebAppUiDelegateImpl* Get(Profile* profile);
+  static WebAppUiService* Get(Profile* profile);
 
-  explicit WebAppUiDelegateImpl(Profile* profile);
-  ~WebAppUiDelegateImpl() override;
+  explicit WebAppUiService(Profile* profile);
+  ~WebAppUiService() override;
 
   // KeyedService
   void Shutdown() override;
@@ -49,9 +51,9 @@
   std::map<AppId, std::vector<base::OnceClosure>> windows_closed_requests_map_;
   std::map<AppId, size_t> num_windows_for_apps_map_;
 
-  DISALLOW_COPY_AND_ASSIGN(WebAppUiDelegateImpl);
+  DISALLOW_COPY_AND_ASSIGN(WebAppUiService);
 };
 
 }  // namespace web_app
 
-#endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_DELEGATE_IMPL_H_
+#endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_SERVICE_H_
diff --git a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_browsertest.cc b/chrome/browser/ui/web_applications/web_app_ui_service_browsertest.cc
similarity index 68%
rename from chrome/browser/ui/web_applications/web_app_ui_delegate_impl_browsertest.cc
rename to chrome/browser/ui/web_applications/web_app_ui_service_browsertest.cc
index ae7f3df7..f5974b33 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_delegate_impl_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_service_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/web_applications/web_app_ui_delegate_impl.h"
+#include "chrome/browser/ui/web_applications/web_app_ui_service.h"
 
 #include "base/barrier_closure.h"
 #include "base/test/bind_test_util.h"
@@ -62,7 +62,7 @@
 const GURL kFooUrl = GURL("https://foo.example");
 const GURL kBarUrl = GURL("https://bar.example");
 
-class WebAppUiDelegateImplBrowserTest : public InProcessBrowserTest {
+class WebAppUiServiceBrowserTest : public InProcessBrowserTest {
  protected:
   Profile* profile() { return browser()->profile(); }
 
@@ -78,28 +78,28 @@
     return extensions::browsertest_util::LaunchAppBrowser(profile(), app);
   }
 
-  WebAppUiDelegateImpl* ui_delegate() {
-    return WebAppUiDelegateImpl::Get(profile());
-  }
+  WebAppUiService* ui_service() { return WebAppUiService::Get(profile()); }
 };
 
-IN_PROC_BROWSER_TEST_F(WebAppUiDelegateImplBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAppUiServiceBrowserTest,
                        GetNumWindowsForApp_AppWindowsAdded) {
   // Should not crash.
-  auto& delegate = WebAppProvider::Get(browser()->profile())->ui_delegate();
-  auto* delegate_impl = WebAppUiDelegateImpl::Get(browser()->profile());
-  EXPECT_EQ(&delegate, delegate_impl);
+  auto& ui_delegate = WebAppProvider::Get(browser()->profile())->ui_delegate();
+  auto* ui_service = WebAppUiService::Get(browser()->profile());
+  EXPECT_EQ(&ui_delegate, ui_service);
 
   // Zero apps on start:
-  EXPECT_EQ(0u, delegate.GetNumWindowsForApp(AppId()));
+  EXPECT_EQ(0u, ui_service->GetNumWindowsForApp(AppId()));
 
   const auto* foo_app = InstallWebApp(kFooUrl);
   LaunchApp(foo_app);
+  EXPECT_EQ(1u, ui_service->GetNumWindowsForApp(foo_app->id()));
 
-  EXPECT_EQ(1u, delegate.GetNumWindowsForApp(foo_app->id()));
+  LaunchApp(foo_app);
+  EXPECT_EQ(2u, ui_service->GetNumWindowsForApp(foo_app->id()));
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppUiDelegateImplBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAppUiServiceBrowserTest,
                        GetNumWindowsForApp_AppWindowsRemoved) {
   const auto* foo_app = InstallWebApp(kFooUrl);
   auto* foo_window1 = LaunchApp(foo_app);
@@ -108,21 +108,21 @@
   const auto* bar_app = InstallWebApp(kBarUrl);
   LaunchApp(bar_app);
 
-  EXPECT_EQ(2u, ui_delegate()->GetNumWindowsForApp(foo_app->id()));
-  EXPECT_EQ(1u, ui_delegate()->GetNumWindowsForApp(bar_app->id()));
+  EXPECT_EQ(2u, ui_service()->GetNumWindowsForApp(foo_app->id()));
+  EXPECT_EQ(1u, ui_service()->GetNumWindowsForApp(bar_app->id()));
 
   CloseAndWait(foo_window1);
 
-  EXPECT_EQ(1u, ui_delegate()->GetNumWindowsForApp(foo_app->id()));
-  EXPECT_EQ(1u, ui_delegate()->GetNumWindowsForApp(bar_app->id()));
+  EXPECT_EQ(1u, ui_service()->GetNumWindowsForApp(foo_app->id()));
+  EXPECT_EQ(1u, ui_service()->GetNumWindowsForApp(bar_app->id()));
 
   CloseAndWait(foo_window2);
 
-  EXPECT_EQ(0u, ui_delegate()->GetNumWindowsForApp(foo_app->id()));
-  EXPECT_EQ(1u, ui_delegate()->GetNumWindowsForApp(bar_app->id()));
+  EXPECT_EQ(0u, ui_service()->GetNumWindowsForApp(foo_app->id()));
+  EXPECT_EQ(1u, ui_service()->GetNumWindowsForApp(bar_app->id()));
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppUiDelegateImplBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAppUiServiceBrowserTest,
                        NotifyOnAllAppWindowsClosed_NoOpenedWindows) {
   const auto* foo_app = InstallWebApp(kFooUrl);
   const auto* bar_app = InstallWebApp(kBarUrl);
@@ -130,14 +130,14 @@
 
   base::RunLoop run_loop;
   // Should return early; no windows for |foo_app|.
-  ui_delegate()->NotifyOnAllAppWindowsClosed(foo_app->id(),
-                                             run_loop.QuitClosure());
+  ui_service()->NotifyOnAllAppWindowsClosed(foo_app->id(),
+                                            run_loop.QuitClosure());
   run_loop.Run();
 }
 
 // Tests that the callback is correctly called when there is more than one
 // app window.
-IN_PROC_BROWSER_TEST_F(WebAppUiDelegateImplBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAppUiServiceBrowserTest,
                        NotifyOnAllAppWindowsClosed_MultipleOpenedWindows) {
   const auto* foo_app = InstallWebApp(kFooUrl);
   const auto* bar_app = InstallWebApp(kBarUrl);
@@ -151,11 +151,11 @@
 
     bool callback_ran = false;
     base::RunLoop run_loop;
-    ui_delegate()->NotifyOnAllAppWindowsClosed(
-        foo_app->id(), base::BindLambdaForTesting([&]() {
-          callback_ran = true;
-          run_loop.Quit();
-        }));
+    ui_service()->NotifyOnAllAppWindowsClosed(foo_app->id(),
+                                              base::BindLambdaForTesting([&]() {
+                                                callback_ran = true;
+                                                run_loop.Quit();
+                                              }));
 
     CloseAndWait(foo_window1);
     // The callback shouldn't have run yet because there is still one window
@@ -173,7 +173,7 @@
 
 // Tests that callbacks are correctly called when there is more than one
 // request.
-IN_PROC_BROWSER_TEST_F(WebAppUiDelegateImplBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAppUiServiceBrowserTest,
                        NotifyOnAllAppWindowsClosed_MultipleRequests) {
   const auto* foo_app = InstallWebApp(kFooUrl);
   const auto* bar_app = InstallWebApp(kBarUrl);
@@ -187,16 +187,16 @@
 
   base::RunLoop run_loop;
   auto barrier_closure = base::BarrierClosure(2, run_loop.QuitClosure());
-  ui_delegate()->NotifyOnAllAppWindowsClosed(foo_app->id(),
-                                             base::BindLambdaForTesting([&]() {
-                                               callback_ran1 = true;
-                                               barrier_closure.Run();
-                                             }));
-  ui_delegate()->NotifyOnAllAppWindowsClosed(foo_app->id(),
-                                             base::BindLambdaForTesting([&]() {
-                                               callback_ran2 = true;
-                                               barrier_closure.Run();
-                                             }));
+  ui_service()->NotifyOnAllAppWindowsClosed(foo_app->id(),
+                                            base::BindLambdaForTesting([&]() {
+                                              callback_ran1 = true;
+                                              barrier_closure.Run();
+                                            }));
+  ui_service()->NotifyOnAllAppWindowsClosed(foo_app->id(),
+                                            base::BindLambdaForTesting([&]() {
+                                              callback_ran2 = true;
+                                              barrier_closure.Run();
+                                            }));
 
   CloseAndWait(foo_window1);
   // The callback shouldn't have run yet because there is still one window
diff --git a/chrome/browser/ui/web_applications/web_app_ui_service_factory.cc b/chrome/browser/ui/web_applications/web_app_ui_service_factory.cc
new file mode 100644
index 0000000..1ef4395
--- /dev/null
+++ b/chrome/browser/ui/web_applications/web_app_ui_service_factory.cc
@@ -0,0 +1,50 @@
+// Copyright 2019 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/ui/web_applications/web_app_ui_service_factory.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/web_applications/web_app_ui_service.h"
+#include "chrome/browser/web_applications/components/web_app_utils.h"
+#include "chrome/browser/web_applications/web_app_provider_factory.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace web_app {
+
+// static
+WebAppUiService* WebAppUiServiceFactory::GetForProfile(Profile* profile) {
+  return static_cast<WebAppUiService*>(
+      WebAppUiServiceFactory::GetInstance()->GetServiceForBrowserContext(
+          profile, true /* create */));
+}
+
+// static
+WebAppUiServiceFactory* WebAppUiServiceFactory::GetInstance() {
+  return base::Singleton<WebAppUiServiceFactory>::get();
+}
+
+WebAppUiServiceFactory::WebAppUiServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "WebAppUiDelegate",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(WebAppProviderFactory::GetInstance());
+}
+
+WebAppUiServiceFactory::~WebAppUiServiceFactory() = default;
+
+KeyedService* WebAppUiServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new WebAppUiService(Profile::FromBrowserContext(context));
+}
+
+bool WebAppUiServiceFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+content::BrowserContext* WebAppUiServiceFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return GetBrowserContextForWebApps(context);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_ui_service_factory.h b/chrome/browser/ui/web_applications/web_app_ui_service_factory.h
new file mode 100644
index 0000000..1f5683c9
--- /dev/null
+++ b/chrome/browser/ui/web_applications/web_app_ui_service_factory.h
@@ -0,0 +1,48 @@
+// Copyright 2019 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_UI_WEB_APPLICATIONS_WEB_APP_UI_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}
+
+class Profile;
+
+namespace web_app {
+
+class WebAppUiService;
+
+// Singleton that owns all WebAppUiServices and associates them
+// with Profile.
+class WebAppUiServiceFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static WebAppUiService* GetForProfile(Profile* profile);
+
+  static WebAppUiServiceFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<WebAppUiServiceFactory>;
+
+  WebAppUiServiceFactory();
+  ~WebAppUiServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAppUiServiceFactory);
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_UI_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom
index 784434c..7d50324 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision.mojom
@@ -18,9 +18,6 @@
   // Returns an array of package names of installed ARC apps.
   GetInstalledArcApps() => (array<string> package_names);
 
-  // Uninstalls the ARC apps specified by the list of package names.
-  UninstallArcApps(array<string> package_names) => ();
-
   // Returns the oauth token to be passed to the server.
   GetOAuthToken() => (OAuthTokenFetchStatus status, string oauth_token);
 };
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
index 4e849e51..2bf9775a 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h"
 
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/stl_util.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -60,31 +62,6 @@
   std::move(callback).Run(installed_arc_apps);
 }
 
-void AddSupervisionHandler::UninstallArcApps(
-    const std::vector<std::string>& package_names,
-    UninstallArcAppsCallback callback) {
-  Profile* profile = Profile::FromWebUI(web_ui_);
-  apps::AppServiceProxy* proxy =
-      apps::AppServiceProxyFactory::GetForProfile(profile);
-
-  for (const std::string& package_name : package_names) {
-    proxy->AppRegistryCache().ForOneApp(
-        arc::ArcPackageNameToAppId(package_name, profile),
-        [proxy, package_names, profile](const apps::AppUpdate& update) {
-          // We don't include "sticky" ARC apps because they are
-          // system-required apps that should not be uninstalled.
-          // TODO(danan): check for stickyness via the App Service instead
-          // when that is available. (https://crbug.com/948408).
-          if (ShouldIncludeAppUpdate(update) &&
-              !arc::IsArcAppSticky(update.AppId(), profile)) {
-            proxy->Uninstall(update.AppId());
-          }
-        });
-  }
-
-  std::move(callback).Run();
-}
-
 void AddSupervisionHandler::GetOAuthToken(GetOAuthTokenCallback callback) {
   identity::ScopeSet scopes;
   scopes.insert(GaiaConstants::kKidFamilyOAuth2Scope);
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
index 6f58d04..d07c2886 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_handler.h
@@ -5,9 +5,6 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_ADD_SUPERVISION_ADD_SUPERVISION_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ADD_SUPERVISION_ADD_SUPERVISION_HANDLER_H_
 
-#include <string>
-#include <vector>
-
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -37,8 +34,6 @@
   // add_supervision::mojom::AddSupervisionHandler overrides:
   void LogOut(LogOutCallback callback) override;
   void GetInstalledArcApps(GetInstalledArcAppsCallback callback) override;
-  void UninstallArcApps(const std::vector<std::string>& apps,
-                        UninstallArcAppsCallback callback) override;
   void GetOAuthToken(GetOAuthTokenCallback callback) override;
 
  private:
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 71aabddc..eef88d33 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -233,6 +233,7 @@
       proxy_auth_dialog_reload_times_(kMaxGaiaReloadForProxyAuthDialog),
       gaia_screen_handler_(gaia_screen_handler),
       histogram_helper_(new ErrorScreensHistogramHelper("Signin")),
+      observer_binding_(this),
       weak_factory_(this) {
   DCHECK(network_state_informer_.get());
   DCHECK(error_screen_);
@@ -267,12 +268,12 @@
   tablet_mode_client->AddObserver(this);
   OnTabletModeToggled(tablet_mode_client->tablet_mode_enabled());
 
-  WallpaperControllerClient::Get()->AddObserver(this);
+  ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
+  observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+  WallpaperControllerClient::Get()->AddObserver(std::move(ptr_info));
 }
 
 SigninScreenHandler::~SigninScreenHandler() {
-  if (auto* wallpaper_controller_client = WallpaperControllerClient::Get())
-    wallpaper_controller_client->RemoveObserver(this);
   TabletModeClient::Get()->RemoveObserver(this);
   OobeUI* oobe_ui = GetOobeUI();
   if (oobe_ui && oobe_ui_observer_added_)
@@ -827,12 +828,14 @@
   }
 }
 
-void SigninScreenHandler::OnWallpaperColorsChanged() {
+void SigninScreenHandler::OnWallpaperChanged(uint32_t image_id) {}
+
+void SigninScreenHandler::OnWallpaperColorsChanged(
+    const std::vector<SkColor>& prominent_colors) {
   // Updates the color of the scrollable container on account picker screen,
   // based on wallpaper color extraction results.
-  auto colors = WallpaperControllerClient::Get()->GetWallpaperColors();
   SkColor dark_muted_color =
-      colors[static_cast<int>(ash::ColorProfileType::DARK_MUTED)];
+      prominent_colors[static_cast<int>(ash::ColorProfileType::DARK_MUTED)];
   if (dark_muted_color == ash::kInvalidWallpaperColor)
     dark_muted_color = ash::login_constants::kDefaultBaseColor;
 
@@ -848,10 +851,9 @@
          color_utils::SkColorToRgbaString(scroll_color));
 }
 
-void SigninScreenHandler::OnWallpaperBlurChanged() {
-  const bool show_pod_background =
-      !WallpaperControllerClient::Get()->IsWallpaperBlurred();
-  CallJS("login.AccountPickerScreen.togglePodBackground", show_pod_background);
+void SigninScreenHandler::OnWallpaperBlurChanged(bool blurred) {
+  CallJS("login.AccountPickerScreen.togglePodBackground",
+         !blurred /*show_pod_background=*/);
 }
 
 void SigninScreenHandler::ClearAndEnablePassword() {
@@ -1207,8 +1209,12 @@
 
   // The wallpaper may have been set before the instance is initialized, so make
   // sure the colors and blur state are updated.
-  OnWallpaperColorsChanged();
-  OnWallpaperBlurChanged();
+  WallpaperControllerClient::Get()->GetWallpaperColors(
+      base::BindOnce(&SigninScreenHandler::OnWallpaperColorsChanged,
+                     weak_factory_.GetWeakPtr()));
+  WallpaperControllerClient::Get()->IsWallpaperBlurred(
+      base::BindOnce(&SigninScreenHandler::OnWallpaperBlurChanged,
+                     weak_factory_.GetWeakPtr()));
 
   session_manager::SessionManager* session_manager =
       session_manager::SessionManager::Get();
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 48988d3..c2ab54e 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -10,7 +10,7 @@
 #include <set>
 #include <string>
 
-#include "ash/public/cpp/wallpaper_controller_observer.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
@@ -32,6 +32,7 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_ui.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "net/base/net_errors.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
@@ -185,7 +186,7 @@
       public input_method::ImeKeyboard::Observer,
       public TabletModeClientObserver,
       public OobeUI::Observer,
-      public ash::WallpaperControllerObserver {
+      public ash::mojom::WallpaperObserver {
  public:
   SigninScreenHandler(
       JSCallsContainer* js_calls_container,
@@ -224,9 +225,11 @@
                               OobeScreenId new_screen) override;
   void OnDestroyingOobeUI() override {}
 
-  // ash::WallpaperControllerObserver implementation:
-  void OnWallpaperColorsChanged() override;
-  void OnWallpaperBlurChanged() override;
+  // ash::mojom::WallpaperObserver implementation:
+  void OnWallpaperChanged(uint32_t image_id) override;
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override;
+  void OnWallpaperBlurChanged(bool blurred) override;
 
   void SetFocusPODCallbackForTesting(base::Closure callback);
 
@@ -503,6 +506,9 @@
 
   std::unique_ptr<AccountId> focused_pod_account_id_;
 
+  // The binding this instance uses to implement ash::mojom::WallpaperObserver.
+  mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_;
+
   base::WeakPtrFactory<SigninScreenHandler> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SigninScreenHandler);
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 05990cf..f9859030 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -245,6 +245,7 @@
     {"loadErrorErrorLabel", IDS_EXTENSIONS_LOAD_ERROR_ERROR_LABEL},
     {"loadErrorRetry", IDS_EXTENSIONS_LOAD_ERROR_RETRY},
     {"loadingActivities", IDS_EXTENSIONS_LOADING_ACTIVITIES},
+    {"missingOrUninstalledExtension", IDS_MISSING_OR_UNINSTALLED_EXTENSION},
     {"noActivities", IDS_EXTENSIONS_NO_ACTIVITIES},
     {"noErrorsToShow", IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE},
     {"runtimeHostsDialogInputError",
diff --git a/chrome/browser/ui/webui/settings/appearance_handler.cc b/chrome/browser/ui/webui/settings/appearance_handler.cc
index 435767b8b..ae92d56 100644
--- a/chrome/browser/ui/webui/settings/appearance_handler.cc
+++ b/chrome/browser/ui/webui/settings/appearance_handler.cc
@@ -71,16 +71,17 @@
 #if defined(OS_CHROMEOS)
 void AppearanceHandler::IsWallpaperSettingVisible(const base::ListValue* args) {
   CHECK_EQ(args->GetSize(), 1U);
-  bool result = WallpaperControllerClient::Get()->ShouldShowWallpaperSetting();
-  ResolveCallback(args->GetList()[0], result);
+  WallpaperControllerClient::Get()->ShouldShowWallpaperSetting(
+      base::Bind(&AppearanceHandler::ResolveCallback,
+                 weak_ptr_factory_.GetWeakPtr(), args->GetList()[0].Clone()));
 }
 
 void AppearanceHandler::IsWallpaperPolicyControlled(
     const base::ListValue* args) {
   CHECK_EQ(args->GetSize(), 1U);
-  bool result = WallpaperControllerClient::Get()
-                    ->IsActiveUserWallpaperControlledByPolicy();
-  ResolveCallback(args->GetList()[0], result);
+  WallpaperControllerClient::Get()->IsActiveUserWallpaperControlledByPolicy(
+      base::Bind(&AppearanceHandler::ResolveCallback,
+                 weak_ptr_factory_.GetWeakPtr(), args->GetList()[0].Clone()));
 }
 
 void AppearanceHandler::HandleOpenWallpaperManager(
diff --git a/chrome/browser/vr/service/xr_runtime_manager.cc b/chrome/browser/vr/service/xr_runtime_manager.cc
index 8729fea..c3eff313 100644
--- a/chrome/browser/vr/service/xr_runtime_manager.cc
+++ b/chrome/browser/vr/service/xr_runtime_manager.cc
@@ -52,6 +52,13 @@
 
 base::LazyInstance<base::ObserverList<XRRuntimeManagerObserver>>::Leaky
     g_xr_runtime_manager_observers;
+
+// Returns true if any of the AR-related features are enabled.
+bool AreArFeaturesEnabled() {
+  return base::FeatureList::IsEnabled(features::kWebXrHitTest) ||
+         base::FeatureList::IsEnabled(features::kWebXrPlaneDetection);
+}
+
 }  // namespace
 
 XRRuntimeManager::~XRRuntimeManager() {
@@ -64,15 +71,17 @@
     // Register VRDeviceProviders for the current platform
     ProviderList providers;
 
+    if (AreArFeaturesEnabled()) {
 #if defined(OS_ANDROID)
 #if BUILDFLAG(ENABLE_ARCORE)
-    if (base::FeatureList::IsEnabled(features::kWebXrHitTest)) {
       providers.emplace_back(device::ArCoreDeviceProviderFactory::Create());
+#endif  // BUILDFLAG(ENABLE_ARCORE)
+#endif  // defined(OS_ANDROID)
     }
-#endif
 
+#if defined(OS_ANDROID)
     providers.emplace_back(std::make_unique<device::GvrDeviceProvider>());
-#endif
+#endif  // defined(OS_ANDROID)
 
 #if BUILDFLAG(ENABLE_ISOLATED_XR_SERVICE)
     providers.emplace_back(std::make_unique<vr::IsolatedVRDeviceProvider>());
diff --git a/chrome/browser/vr/service/xr_session_request_consent_manager_impl.cc b/chrome/browser/vr/service/xr_session_request_consent_manager_impl.cc
index c194d9e5..d962f66 100644
--- a/chrome/browser/vr/service/xr_session_request_consent_manager_impl.cc
+++ b/chrome/browser/vr/service/xr_session_request_consent_manager_impl.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/vr/service/xr_session_request_consent_manager_impl.h"
 
+#include <memory>
 #include <utility>
 
 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
@@ -22,10 +23,10 @@
 XRSessionRequestConsentManagerImpl::ShowDialogAndGetConsent(
     content::WebContents* web_contents,
     base::OnceCallback<void(bool)> response_callback) {
-  auto* delegate = new XrSessionRequestConsentDialogDelegate(
+  auto delegate = std::make_unique<XrSessionRequestConsentDialogDelegate>(
       web_contents, std::move(response_callback));
   delegate->OnShowDialog();
-  return TabModalConfirmDialog::Create(delegate, web_contents);
+  return TabModalConfirmDialog::Create(std::move(delegate), web_contents);
 }
 
 }  // namespace vr
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index ac9a6b9..fedf2cf4 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -613,15 +613,18 @@
   // necessary here (see https://crbug.com/965747). For now, returning false
   // should take us to CreatePlugin after HTMLPlugInElement which is called
   // through HTMLPlugInElement::LoadPlugin code path.
-  if ((plugin_info->status != chrome::mojom::PluginStatus::kAllowed &&
-       plugin_info->status !=
-           chrome::mojom::PluginStatus::kPlayImportantContent) ||
-      !ChromeExtensionsRendererClient::MaybeCreateMimeHandlerView(
-          plugin_element, original_url, plugin_info->actual_mime_type,
-          plugin_info->plugin)) {
+  if (plugin_info->status != chrome::mojom::PluginStatus::kAllowed &&
+      plugin_info->status !=
+          chrome::mojom::PluginStatus::kPlayImportantContent) {
+    // We could get here when a MimeHandlerView is loaded inside a <webview>
+    // which is using permissions API (see WebViewPluginTests).
+    ChromeExtensionsRendererClient::DidBlockMimeHandlerViewForDisallowedPlugin(
+        plugin_element);
     return false;
   }
-  return true;
+  return ChromeExtensionsRendererClient::MaybeCreateMimeHandlerView(
+      plugin_element, original_url, plugin_info->actual_mime_type,
+      plugin_info->plugin);
 #else
   return false;
 #endif
@@ -821,17 +824,13 @@
           const Extension* extension =
               extensions::RendererExtensionRegistry::Get()
                   ->GetExtensionOrAppByURL(manifest_url);
-          if (!IsNaClAllowed(app_url, is_nacl_unrestricted, extension,
-                             &params)) {
+          if (!IsNativeNaClAllowed(app_url, is_nacl_unrestricted, extension)) {
             WebString error_message;
             if (is_nacl_mime_type) {
               error_message =
                   "Only unpacked extensions and apps installed from the Chrome "
                   "Web Store can load NaCl modules without enabling Native "
                   "Client in about:flags.";
-            } else if (is_pnacl_mime_type) {
-              error_message =
-                  "Portable Native Client must not be disabled in about:flags.";
             }
             frame->AddMessageToConsole(WebConsoleMessage(
                 blink::mojom::ConsoleMessageLevel::kError, error_message));
@@ -1052,12 +1051,10 @@
 
 #if BUILDFLAG(ENABLE_NACL)
 //  static
-bool ChromeContentRendererClient::IsNaClAllowed(
+bool ChromeContentRendererClient::IsNativeNaClAllowed(
     const GURL& app_url,
     bool is_nacl_unrestricted,
-    const Extension* extension,
-    WebPluginParams* params) {
-  // Temporarily allow these whitelisted apps to use NaCl.
+    const Extension* extension) {
   bool is_invoked_by_webstore_installed_extension = false;
   bool is_extension_unrestricted = false;
   bool is_extension_force_installed = false;
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index ac59181f..699c3da 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -258,12 +258,11 @@
   base::TimeTicks main_entry_time_;
 
 #if BUILDFLAG(ENABLE_NACL)
-  // Determines if a NaCl app is allowed, and modifies params to pass the app's
-  // permissions to the trusted NaCl plugin.
-  static bool IsNaClAllowed(const GURL& app_url,
-                            bool is_nacl_unrestricted,
-                            const extensions::Extension* extension,
-                            blink::WebPluginParams* params);
+  // Determines if a page/app/extension is allowed to run native (non-PNaCl)
+  // NaCl modules.
+  static bool IsNativeNaClAllowed(const GURL& app_url,
+                                  bool is_nacl_unrestricted,
+                                  const extensions::Extension* extension);
 #endif
 
   service_manager::Connector* GetConnector();
diff --git a/chrome/renderer/chrome_content_renderer_client_unittest.cc b/chrome/renderer/chrome_content_renderer_client_unittest.cc
index c6e91b4b..31c3966cd 100644
--- a/chrome/renderer/chrome_content_renderer_client_unittest.cc
+++ b/chrome/renderer/chrome_content_renderer_client_unittest.cc
@@ -152,103 +152,79 @@
 #if BUILDFLAG(ENABLE_NACL)
   // --enable-nacl allows all NaCl apps.
   {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(),
-        kNaClUnrestricted,
-        CreateExtension(kExtensionNotFromWebStore).get(),
-        &params));
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL(), kNaClUnrestricted,
+        CreateExtension(kExtensionNotFromWebStore).get()));
   }
   // Unpacked extensions are allowed without --enable-nacl.
   {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kExtensionUrl),
-        kNaClRestricted,
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL(kExtensionUrl), kNaClRestricted,
         CreateExtensionWithLocation(extensions::Manifest::UNPACKED,
-                                    kExtensionNotFromWebStore).get(),
-        &params));
+                                    kExtensionNotFromWebStore)
+            .get()));
   }
   // Component extensions are allowed without --enable-nacl.
   {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kExtensionUrl),
-        kNaClRestricted,
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL(kExtensionUrl), kNaClRestricted,
         CreateExtensionWithLocation(extensions::Manifest::COMPONENT,
-                                    kExtensionNotFromWebStore).get(),
-        &params));
+                                    kExtensionNotFromWebStore)
+            .get()));
   }
   {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kExtensionUrl),
-        kNaClRestricted,
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL(kExtensionUrl), kNaClRestricted,
         CreateExtensionWithLocation(extensions::Manifest::EXTERNAL_COMPONENT,
-                                    kExtensionNotFromWebStore).get(),
-        &params));
+                                    kExtensionNotFromWebStore)
+            .get()));
   }
   // Extensions that are force installed by policy are allowed without
   // --enable-nacl.
   {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kExtensionUrl),
-        kNaClRestricted,
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL(kExtensionUrl), kNaClRestricted,
         CreateExtensionWithLocation(extensions::Manifest::EXTERNAL_POLICY,
-                                    kExtensionNotFromWebStore).get(),
-        &params));
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kExtensionUrl),
-        kNaClRestricted,
+                                    kExtensionNotFromWebStore)
+            .get()));
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL(kExtensionUrl), kNaClRestricted,
         CreateExtensionWithLocation(
             extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD,
-            kExtensionNotFromWebStore).get(),
-        &params));
+            kExtensionNotFromWebStore)
+            .get()));
   }
   // CWS extensions are allowed without --enable-nacl if called from an
   // extension url.
   {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kExtensionUrl),
-        kNaClRestricted,
-        CreateExtension(kExtensionFromWebStore).get(),
-        &params));
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL(kExtensionUrl), kNaClRestricted,
+        CreateExtension(kExtensionFromWebStore).get()));
   }
   // Other URLs (including previously-whitelisted URLs) are blocked
   // without --enable-nacl.
   {
-    WebPluginParams params;
-    EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL("https://plus.google.com.evil.com/foo1"), kNaClRestricted, nullptr,
-        &params));
-    EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
+    EXPECT_FALSE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL("https://plus.google.com.evil.com/foo1"), kNaClRestricted,
+        nullptr));
+    EXPECT_FALSE(ChromeContentRendererClient::IsNativeNaClAllowed(
         GURL("https://talkgadget.google.com/hangouts/foo1"), kNaClRestricted,
-        nullptr, &params));
+        nullptr));
   }
   // Non chrome-extension:// URLs belonging to hosted apps are allowed for
   // webstore installed hosted apps.
   {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL("http://example.com/test.html"),
-        kNaClRestricted,
-        CreateHostedApp(kExtensionFromWebStore,
-                        "http://example.com/").get(),
-        &params));
-    EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL("http://example.com/test.html"),
-        kNaClRestricted,
-        CreateHostedApp(kExtensionNotFromWebStore,
-                        "http://example.com/").get(),
-        &params));
-    EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL("http://example.evil.com/test.html"),
-        kNaClRestricted,
-        CreateHostedApp(kExtensionNotFromWebStore,
-                        "http://example.com/").get(),
-        &params));
+    EXPECT_TRUE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL("http://example.com/test.html"), kNaClRestricted,
+        CreateHostedApp(kExtensionFromWebStore, "http://example.com/").get()));
+    EXPECT_FALSE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL("http://example.com/test.html"), kNaClRestricted,
+        CreateHostedApp(kExtensionNotFromWebStore, "http://example.com/")
+            .get()));
+    EXPECT_FALSE(ChromeContentRendererClient::IsNativeNaClAllowed(
+        GURL("http://example.evil.com/test.html"), kNaClRestricted,
+        CreateHostedApp(kExtensionNotFromWebStore, "http://example.com/")
+            .get()));
   }
 #endif  // BUILDFLAG(ENABLE_NACL)
 }
diff --git a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
index 486c5a65..2eae139 100644
--- a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
+++ b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
@@ -319,6 +319,16 @@
 }
 
 // static
+void ChromeExtensionsRendererClient::DidBlockMimeHandlerViewForDisallowedPlugin(
+    const blink::WebElement& plugin_element) {
+  extensions::MimeHandlerViewContainerManager::Get(
+      content::RenderFrame::FromWebFrame(
+          plugin_element.GetDocument().GetFrame()),
+      true /* create_if_does_not_exist */)
+      ->DidBlockMimeHandlerViewForDisallowedPlugin(plugin_element);
+}
+
+// static
 bool ChromeExtensionsRendererClient::MaybeCreateMimeHandlerView(
     const blink::WebElement& plugin_element,
     const GURL& resource_url,
diff --git a/chrome/renderer/extensions/chrome_extensions_renderer_client.h b/chrome/renderer/extensions/chrome_extensions_renderer_client.h
index 1612bbe1..13ff1500 100644
--- a/chrome/renderer/extensions/chrome_extensions_renderer_client.h
+++ b/chrome/renderer/extensions/chrome_extensions_renderer_client.h
@@ -93,6 +93,8 @@
       const content::WebPluginInfo& info,
       const std::string& mime_type,
       const GURL& original_url);
+  static void DidBlockMimeHandlerViewForDisallowedPlugin(
+      const blink::WebElement& plugin_element);
   static bool MaybeCreateMimeHandlerView(
       const blink::WebElement& plugin_element,
       const GURL& resource_url,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ef83da0..2e6cb58 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -445,6 +445,41 @@
   }
 }
 
+if (is_android) {
+  test("android_browsertests") {
+    configs += [ "//build/config:precompiled_headers" ]
+    defines = [
+      "HAS_OUT_OF_PROC_TEST_RUNNER",
+      "CHROME_VERSION_MAJOR=" + chrome_version_major,
+    ]
+    use_default_launcher = false
+
+    deps = [
+      ":test_support",
+      ":test_support_ui_android",
+      "//chrome/android:app_hooks_java",
+      "//chrome/android:chrome_java",
+    ]
+
+    sources = []
+  }
+
+  static_library("test_support_ui_android") {
+    configs += [ "//build/config:precompiled_headers" ]
+    testonly = true
+
+    sources = [
+      # TODO(crbug.com/961849): Write an AndroidBrowserTest base class and put
+      # it here.
+    ]
+    public_deps = []
+    deps = [
+      "//chrome/browser",
+      "//content/public/browser",
+    ]
+  }
+}
+
 if (!is_android) {
   static_library("browser_tests_runner") {
     testonly = true
@@ -466,129 +501,144 @@
   }
 
   test("browser_tests") {
-    sources = [
-      # TODO(jbudorick): Move tests here from other lists as Android support is
-      # implemented. See crbug.com/611756
-    ]
-
     configs += [ "//build/config:precompiled_headers" ]
-
-    deps = [
-      ":browser_tests_runner",
-      ":test_support",
-      "//base",
-      "//chrome/browser/profiling_host:profiling_browsertests",
-      "//components/cbor",
-      "//components/nacl/common:buildflags",
-      "//components/spellcheck:buildflags",
-      "//components/sync:test_support_model",
-      "//extensions/buildflags",
-      "//media:media_buildflags",
-      "//net:test_support",
-      "//ppapi/buildflags",
-      "//printing/buildflags",
-      "//rlz/buildflags",
-      "//services/strings",
-      "//third_party/blink/public:buildflags",
-    ]
-
-    data_deps = [
-      "//testing/buildbot/filters:browser_tests_filters",
-      "//tools/media_engagement_preload:generator",
-    ]
-
-    data = []
-
     defines = [
       "HAS_OUT_OF_PROC_TEST_RUNNER",
       "CHROME_VERSION_MAJOR=" + chrome_version_major,
     ]
 
-    if (is_win) {
-      data += [ "$root_out_dir/chrome_200_percent.pak" ]
-      deps += [ "//chrome/app:chrome_dll_resources" ]
-    }
-    if (is_chromeos) {
-      data += [
-        # TODO(GYP): figure out which of these things are
-        # actually needed and also which should be pulled in via
-        # data or data_deps and through which dependencies.
-        "//chrome/browser/chromeos/login/test/https_forwarder.py",
-        "//chrome/browser/resources/chromeos/wallpaper_manager/",
-        "//chrome/browser/resources/chromeos/zip_archiver/",
-        "//google_apis/test/",
-        "//chrome/browser/resources/chromeos/zip_archiver/test/",
-        "//chromeos/test/data/",
-        "//ui/file_manager/base/",
-        "//ui/file_manager/file_manager/",
-        "//ui/file_manager/gallery/",
-        "//ui/file_manager/image_loader/",
-        "//ui/file_manager/integration_tests/",
-        "//ui/file_manager/video_player",
-        "//third_party/polymer/v1_0/components-chromium/",
-        "$root_gen_dir/ui/file_manager/file_manager/",
-        "$root_out_dir/chromevox_test_data/",
-        "$root_out_dir/content_shell.pak",
-        "$root_out_dir/resources/chromeos/",
-      ]
+    deps = [
+      ":browser_tests_runner",
+      ":policy_testserver_pyproto",
+      ":test_support",
+      ":test_support_ui",
+      "//base",
+      "//base:i18n",
+      "//base/test:test_support",
+      "//chrome:browser_tests_pak",
+      "//chrome:packed_resources",
+      "//chrome:resources",
+      "//chrome:strings",
+      "//chrome/browser",
+      "//chrome/browser/devtools:test_support",
+      "//chrome/browser/profiling_host:profiling_browsertests",
+      "//chrome/browser/web_applications:browser_tests",
+      "//chrome/renderer",
+      "//chrome/services/removable_storage_writer:lib",
+      "//components/autofill/content/browser:risk_proto",
+      "//components/autofill/content/common:mojo_interfaces",
+      "//components/autofill/content/renderer:test_support",
+      "//components/captive_portal:test_support",
+      "//components/cbor",
+      "//components/data_reduction_proxy/core/browser:test_support",
+      "//components/dom_distiller/content/browser",
+      "//components/dom_distiller/content/renderer",
+      "//components/dom_distiller/core:test_support",
+      "//components/feature_engagement/test:test_support",
+      "//components/nacl/common:buildflags",
+      "//components/offline_items_collection/core/test_support",
+      "//components/optimization_guide:test_support",
+      "//components/policy:chrome_settings_proto_generated_compile",
+      "//components/resources",
+      "//components/safe_browsing/db:test_database_manager",
+      "//components/services/quarantine:test_support",
+      "//components/spellcheck:buildflags",
+      "//components/strings",
+      "//components/sync",
+      "//components/sync:test_support_model",
+      "//components/translate/core/common",
+      "//content/public/common:buildflags",
+      "//content/test:test_support",
+      "//crypto:platform",
+      "//crypto:test_support",
+      "//device/bluetooth:mocks",
+      "//extensions/buildflags",
+      "//google_apis:test_support",
+      "//media:media_buildflags",
+      "//media:test_support",
+      "//media/cast:test_support",
+      "//mojo/public/cpp/system",
+      "//net",
+      "//net:test_support",
+      "//net:test_support",
+      "//ppapi/buildflags",
+      "//printing/buildflags",
+      "//rlz/buildflags",
+      "//services/device/public/cpp:test_support",
+      "//services/device/public/mojom",
+      "//services/network/public/cpp",
+      "//services/service_manager/public/cpp",
+      "//services/strings",
+      "//skia",
+      "//testing/gmock",
+      "//testing/gtest",
+      "//testing/perf",
+      "//third_party/blink/public:blink_headers",
+      "//third_party/blink/public:buildflags",
+      "//third_party/cacheinvalidation",
+      "//third_party/icu",
+      "//third_party/leveldatabase",
+      "//third_party/webrtc_overrides",
+      "//third_party/widevine/cdm:buildflags",
+      "//third_party/widevine/cdm:headers",
+      "//ui/accessibility:test_support",
+      "//ui/base:test_support",
+      "//ui/base/clipboard:clipboard_test_support",
+      "//ui/compositor:test_support",
+      "//ui/native_theme:test_support",
+      "//ui/resources",
+      "//ui/web_dialogs:test_support",
+      "//v8",
+    ]
 
-      data_deps += [
-        "//chrome",
-        "//ash/keyboard/ui:resources",
-        "//ui/file_manager:unit_test_data",
-      ]
+    # Runtime dependencies
+    data_deps = [
+      "//ppapi:ppapi_tests",
+      "//ppapi:power_saver_test_plugin",
+      "//remoting/webapp:browser_test_resources",
+      "//remoting/webapp:unit_tests",
+      "//testing/buildbot/filters:browser_tests_filters",
+      "//third_party/mesa_headers",
+      "//third_party/widevine/cdm:widevine_test_license_server",
+      "//tools/media_engagement_preload:generator",
+    ]
 
-      if (use_dbus) {
-        deps += [ "//dbus:test_support" ]
-      }
+    data = [
+      "data/",
+      "//chrome/browser/policy/test/asn1der.py",
+      "//chrome/browser/policy/test/policy_testserver.py",
+      "//chrome/common/extensions/docs/examples/apps/calculator/",
+      "//chrome/third_party/mock4js/",
+      "//components/test/data/arc/",
+      "//components/test/data/autofill/",
+      "//components/test/data/payments/",
+      "//components/test/data/update_client/",
+      "//content/test/data/",
+      "//google_apis/test/",
+      "//media/test/data/",
+      "//net/tools/testserver/",
+      "//ppapi/tests/test_case.html",
+      "//ppapi/tests/test_case.html.mock-http-headers",
+      "//ppapi/tests/test_page.css",
+      "//ppapi/tests/test_page.css.mock-http-headers",
+      "//testing/test_env.py",
+      "//third_party/accessibility-audit/axs_testing.js",
+      "//third_party/chaijs/chai.js",
+      "//third_party/mocha/mocha.js",
+      "//third_party/polymer/v1_0/components-chromium/iron-test-helpers/mock-interactions.js",
+      "//third_party/pywebsocket/src/mod_pywebsocket/",
+      "//third_party/simplejson/",
+      "//third_party/tlslite/",
+      "//ui/webui/resources/",
+      "$root_out_dir/browser_tests.pak",
+      "$root_out_dir/test_case.html",
+      "$root_out_dir/test_case.html.mock-http-headers",
+      "$root_out_dir/test_page.css",
+      "$root_out_dir/test_page.css.mock-http-headers",
+      "$root_out_dir/test_url_loader_data/",
+    ]
 
-      if (enable_nacl) {
-        data_deps += [
-          "//components/nacl/loader:nacl_helper",
-          "//ppapi/native_client:irt",
-        ]
-
-        if (enable_nacl_nonsfi) {
-          data_deps += [ "//components/nacl/loader:helper_nonsfi" ]
-        }
-      }
-
-      deps += [
-        "//ash:test_support",
-        "//ash/keyboard/ui:test_support",
-        "//ash/public/interfaces:test_interfaces",
-        "//chrome/browser/chromeos:arc_test_support",
-        "//chrome/browser/chromeos:test_support",
-        "//chrome/browser/media/router:test_support",
-        "//chrome/browser/resources/chromeos/chromevox:browser_tests",
-        "//chrome/browser/resources/chromeos/login:browser_tests",
-        "//chrome/browser/resources/chromeos/select_to_speak:browser_tests",
-        "//chrome/browser/resources/chromeos/switch_access:browser_tests",
-        "//chrome/services/file_util/public/cpp:browser_tests",
-        "//chromeos:test_support",
-        "//chromeos/components/drivefs:test_support",
-        "//chromeos/dbus:test_support",
-        "//chromeos/dbus/session_manager",
-        "//components/arc:arc_test_support",
-        "//components/exo:test_support",
-        "//components/prefs",
-        "//components/user_manager:test_support",
-        "//content/public/common:feature_h264_with_openh264_ffmpeg",
-        "//mojo/core/embedder",
-        "//services/audio/public/cpp:test_support",
-        "//services/identity/public/cpp",
-        "//services/network/public/mojom",
-        "//services/preferences/public/cpp",
-        "//services/preferences/public/mojom",
-        "//services/service_manager/public/cpp",
-        "//services/ws/public/cpp/input_devices:test_support",
-        "//url",
-      ]
-    }
-
-    sources += [
-      # The list of sources which is only used by chrome browser tests on
-      # desktop platforms.
+    sources = [
       "../../apps/app_restore_service_browsertest.cc",
       "../../apps/load_and_launch_browsertest.cc",
       "../app/chrome_version.rc.version",
@@ -1053,7 +1103,7 @@
       "../browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc",
       "../browser/ui/views/webview_accessibility_browsertest.cc",
       "../browser/ui/web_applications/bookmark_app_browsertest.cc",
-      "../browser/ui/web_applications/web_app_ui_delegate_impl_browsertest.cc",
+      "../browser/ui/web_applications/web_app_ui_service_browsertest.cc",
       "../browser/ui/webauthn/authenticator_dialog_browsertest.cc",
       "../browser/ui/webui/bookmarks/bookmarks_browsertest.cc",
       "../browser/ui/webui/bookmarks/bookmarks_browsertest.h",
@@ -1139,72 +1189,76 @@
       "v8/wasm_trap_handler_browsertest.cc",
     ]
 
-    deps += [
-      ":policy_testserver_pyproto",
-      ":test_support_ui",
-      "//base:i18n",
-      "//base/test:test_support",
-      "//chrome:browser_tests_pak",
-      "//chrome:packed_resources",
-      "//chrome:resources",
-      "//chrome:strings",
-      "//chrome/browser",
-      "//chrome/browser/devtools:test_support",
-      "//chrome/browser/web_applications:browser_tests",
-      "//chrome/renderer",
-      "//chrome/services/removable_storage_writer:lib",
-      "//components/autofill/content/browser:risk_proto",
-      "//components/autofill/content/common:mojo_interfaces",
-      "//components/autofill/content/renderer:test_support",
-      "//components/captive_portal:test_support",
-      "//components/data_reduction_proxy/core/browser:test_support",
-      "//components/dom_distiller/content/browser",
-      "//components/dom_distiller/content/renderer",
-      "//components/dom_distiller/core:test_support",
-      "//components/feature_engagement/test:test_support",
-      "//components/offline_items_collection/core/test_support",
-      "//components/optimization_guide:test_support",
-      "//components/policy:chrome_settings_proto_generated_compile",
-      "//components/resources",
-      "//components/safe_browsing/db:test_database_manager",
-      "//components/services/quarantine:test_support",
-      "//components/strings",
-      "//components/sync",
-      "//components/translate/core/common",
-      "//content/public/common:buildflags",
-      "//content/test:test_support",
-      "//crypto:platform",
-      "//crypto:test_support",
-      "//device/bluetooth:mocks",
-      "//google_apis:test_support",
-      "//media:test_support",
-      "//media/cast:test_support",
-      "//mojo/public/cpp/system",
-      "//net",
-      "//net:test_support",
-      "//services/device/public/cpp:test_support",
-      "//services/device/public/mojom",
-      "//services/network/public/cpp",
-      "//services/service_manager/public/cpp",
-      "//skia",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//testing/perf",
-      "//third_party/cacheinvalidation",
-      "//third_party/icu",
-      "//third_party/leveldatabase",
-      "//third_party/webrtc_overrides",
-      "//third_party/widevine/cdm:buildflags",
-      "//third_party/widevine/cdm:headers",
-      "//ui/accessibility:test_support",
-      "//ui/base:test_support",
-      "//ui/base/clipboard:clipboard_test_support",
-      "//ui/compositor:test_support",
-      "//ui/native_theme:test_support",
-      "//ui/resources",
-      "//ui/web_dialogs:test_support",
-      "//v8",
-    ]
+    if (is_win) {
+      data += [ "$root_out_dir/chrome_200_percent.pak" ]
+      deps += [ "//chrome/app:chrome_dll_resources" ]
+    }
+
+    if (is_chromeos) {
+      data += [
+        # TODO(GYP): figure out which of these things are
+        # actually needed and also which should be pulled in via
+        # data or data_deps and through which dependencies.
+        "//chrome/browser/chromeos/login/test/https_forwarder.py",
+        "//chrome/browser/resources/chromeos/wallpaper_manager/",
+        "//chrome/browser/resources/chromeos/zip_archiver/",
+        "//chrome/browser/resources/chromeos/zip_archiver/test/",
+        "//chromeos/test/data/",
+        "//ui/file_manager/base/",
+        "//ui/file_manager/file_manager/",
+        "//ui/file_manager/gallery/",
+        "//ui/file_manager/image_loader/",
+        "//ui/file_manager/integration_tests/",
+        "//ui/file_manager/video_player",
+        "//third_party/polymer/v1_0/components-chromium/",
+        "$root_gen_dir/ui/file_manager/file_manager/",
+        "$root_out_dir/chromevox_test_data/",
+        "$root_out_dir/content_shell.pak",
+        "$root_out_dir/resources/chromeos/",
+      ]
+
+      data_deps += [
+        "//chrome",
+        "//ash/keyboard/ui:resources",
+        "//ui/file_manager:unit_test_data",
+      ]
+
+      if (use_dbus) {
+        deps += [ "//dbus:test_support" ]
+      }
+
+      deps += [
+        "//ash:test_support",
+        "//ash/keyboard/ui:test_support",
+        "//ash/public/interfaces:test_interfaces",
+        "//chrome/browser/chromeos:arc_test_support",
+        "//chrome/browser/chromeos:test_support",
+        "//chrome/browser/media/router:test_support",
+        "//chrome/browser/resources/chromeos/chromevox:browser_tests",
+        "//chrome/browser/resources/chromeos/login:browser_tests",
+        "//chrome/browser/resources/chromeos/select_to_speak:browser_tests",
+        "//chrome/browser/resources/chromeos/switch_access:browser_tests",
+        "//chrome/services/file_util/public/cpp:browser_tests",
+        "//chromeos:test_support",
+        "//chromeos/components/drivefs:test_support",
+        "//chromeos/dbus:test_support",
+        "//chromeos/dbus/session_manager",
+        "//components/arc:arc_test_support",
+        "//components/exo:test_support",
+        "//components/prefs",
+        "//components/user_manager:test_support",
+        "//content/public/common:feature_h264_with_openh264_ffmpeg",
+        "//mojo/core/embedder",
+        "//services/audio/public/cpp:test_support",
+        "//services/identity/public/cpp",
+        "//services/network/public/mojom",
+        "//services/preferences/public/cpp",
+        "//services/preferences/public/mojom",
+        "//services/service_manager/public/cpp",
+        "//services/ws/public/cpp/input_devices:test_support",
+        "//url",
+      ]
+    }
 
     if (include_js_tests) {
       deps += [
@@ -1217,51 +1271,6 @@
       deps += [ ":sync_integration_test_support" ]
     }
 
-    # Runtime dependencies
-    data_deps += [
-      "//ppapi:ppapi_tests",
-      "//ppapi:power_saver_test_plugin",
-      "//remoting/webapp:browser_test_resources",
-      "//remoting/webapp:unit_tests",
-      "//third_party/mesa_headers",
-      "//third_party/widevine/cdm:widevine_test_license_server",
-    ]
-
-    data += [
-      "data/",
-      "//chrome/browser/policy/test/asn1der.py",
-      "//chrome/browser/policy/test/policy_testserver.py",
-      "//chrome/common/extensions/docs/examples/apps/calculator/",
-      "//chrome/third_party/mock4js/",
-      "//components/test/data/arc/",
-      "//components/test/data/autofill/",
-      "//components/test/data/payments/",
-      "//components/test/data/update_client/",
-      "//content/test/data/",
-      "//google_apis/test/",
-      "//media/test/data/",
-      "//net/tools/testserver/",
-      "//ppapi/tests/test_case.html",
-      "//ppapi/tests/test_case.html.mock-http-headers",
-      "//ppapi/tests/test_page.css",
-      "//ppapi/tests/test_page.css.mock-http-headers",
-      "//testing/test_env.py",
-      "//third_party/accessibility-audit/axs_testing.js",
-      "//third_party/chaijs/chai.js",
-      "//third_party/mocha/mocha.js",
-      "//third_party/polymer/v1_0/components-chromium/iron-test-helpers/mock-interactions.js",
-      "//third_party/pywebsocket/src/mod_pywebsocket/",
-      "//third_party/simplejson/",
-      "//third_party/tlslite/",
-      "//ui/webui/resources/",
-      "$root_out_dir/browser_tests.pak",
-      "$root_out_dir/test_case.html",
-      "$root_out_dir/test_case.html.mock-http-headers",
-      "$root_out_dir/test_page.css",
-      "$root_out_dir/test_page.css.mock-http-headers",
-      "$root_out_dir/test_url_loader_data/",
-    ]
-
     if (!is_mac) {
       data += [
         "$root_out_dir/locales/",
@@ -1270,10 +1279,12 @@
         "$root_out_dir/resources.pak",
       ]
     }
+
     if (enable_captive_portal_detection) {
       sources +=
           [ "../browser/ssl/captive_portal_blocking_page_browsertest.cc" ]
     }
+
     if (enable_dice_support) {
       sources += [
         "../browser/signin/dice_browsertest.cc",
@@ -1313,7 +1324,14 @@
           "//third_party/liblouis/wasm/liblouis_wrapper_browsertest.cc",
         ]
         deps += [ "//chrome/browser/chromeos" ]
-        data_deps += [ "//third_party/liblouis:liblouis_test_data" ]
+        data_deps += [
+          "//third_party/liblouis:liblouis_test_data",
+          "//components/nacl/loader:nacl_helper",
+          "//ppapi/native_client:irt",
+        ]
+        if (enable_nacl_nonsfi) {
+          data_deps += [ "//components/nacl/loader:helper_nonsfi" ]
+        }
       } else if (is_linux || is_win) {
         sources += [
           "../browser/ui/views/ime/ime_warning_bubble_browsertest.cc",
@@ -2665,7 +2683,6 @@
     "../browser/component_updater/chrome_component_updater_configurator_unittest.cc",
     "../browser/component_updater/optimization_hints_component_installer_unittest.cc",
     "../browser/component_updater/origin_trials_component_installer_unittest.cc",
-    "../browser/component_updater/sth_set_component_installer_unittest.cc",
     "../browser/component_updater/subresource_filter_component_installer_unittest.cc",
     "../browser/component_updater/supervised_user_whitelist_installer_unittest.cc",
     "../browser/component_updater/sw_reporter_installer_win_unittest.cc",
@@ -4923,7 +4940,6 @@
       "//extensions/buildflags",
       "//skia",
       "//testing/gtest",
-      "//third_party/blink/public:blink_headers",
     ]
 
     if (enable_plugins) {
diff --git a/chrome/test/base/perf/performance_test.cc b/chrome/test/base/perf/performance_test.cc
index 8b7a76d..bacb39d 100644
--- a/chrome/test/base/perf/performance_test.cc
+++ b/chrome/test/base/perf/performance_test.cc
@@ -16,13 +16,12 @@
 #include "ui/gl/gl_switches.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/wallpaper_controller_observer.h"
 #include "ash/public/cpp/wallpaper_types.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "components/user_manager/user_names.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
-#include "ui/gfx/image/image_skia.h"
 #endif  // OS_CHROMEOS
 
 static const char kTraceDir[] = "trace-dir";
@@ -31,23 +30,31 @@
 
 #if defined(OS_CHROMEOS)
 // Watches if the wallpaper has been changed and runs a passed callback if so.
-class TestWallpaperObserver : public ash::WallpaperControllerObserver {
+class TestWallpaperObserver : public ash::mojom::WallpaperObserver {
  public:
   explicit TestWallpaperObserver(base::OnceClosure closure)
-      : closure_(std::move(closure)) {
-    WallpaperControllerClient::Get()->AddObserver(this);
+      : closure_(std::move(closure)), observer_binding_(this) {
+    ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
+    observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+    WallpaperControllerClient::Get()->AddObserver(std::move(ptr_info));
   }
 
-  ~TestWallpaperObserver() override {
-    WallpaperControllerClient::Get()->RemoveObserver(this);
-  }
+  ~TestWallpaperObserver() override = default;
 
-  // ash::WallpaperControllerObserver:
-  void OnWallpaperChanged() override { std::move(closure_).Run(); }
+  // ash::mojom::WallpaperObserver:
+  void OnWallpaperChanged(uint32_t image_id) override {
+    std::move(closure_).Run();
+  }
+  void OnWallpaperColorsChanged(
+      const std::vector<uint32_t>& prominent_colors) override {}
+  void OnWallpaperBlurChanged(bool blurred) override {}
 
  private:
   base::OnceClosure closure_;
 
+  // The binding this instance uses to implement ash::mojom::WallpaperObserver.
+  mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_;
+
   DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserver);
 };
 
diff --git a/chrome/test/data/banners/manifest_prefer_related_apps_empty.json b/chrome/test/data/banners/manifest_prefer_related_apps_empty.json
new file mode 100644
index 0000000..1ecc469
--- /dev/null
+++ b/chrome/test/data/banners/manifest_prefer_related_apps_empty.json
@@ -0,0 +1,14 @@
+{
+  "name": "Manifest prefer related apps empty",
+  "icons": [
+    {
+      "src": "/banners/image-512px.png",
+      "sizes": "512x512",
+      "type": "image/png"
+    }
+  ],
+  "scope": ".",
+  "start_url": ".",
+  "display": "standalone",
+  "prefer_related_applications": true
+}
diff --git a/chrome/test/data/extensions/api_test/runtime/open_options_page/manifest.json b/chrome/test/data/extensions/api_test/runtime/open_options_page/manifest.json
index 81f6f54..eaa1628 100644
--- a/chrome/test/data/extensions/api_test/runtime/open_options_page/manifest.json
+++ b/chrome/test/data/extensions/api_test/runtime/open_options_page/manifest.json
@@ -4,7 +4,8 @@
   "manifest_version": 2,
   "description": "Browser test for chrome.runtime.openOptionsPage, success case.",
   "background": {
-    "scripts": ["test.js"]
+    "scripts": ["test.js"],
+    "persistent": false
   },
   "options_ui": {
     "page": "options.html"
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index ddd9525..06a96eb 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -4831,7 +4831,7 @@
   },
 
   "BrowserSwitcherExternalGreylistUrl": {
-    "os": ["win", "linux", "mac"],
+    "os": [],
     "test_policy": {
       "BrowserSwitcherExternalGreylistUrl": "file:///C:/src/greylist.xml"
     },
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 90b35b7..b758293 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -108,6 +108,7 @@
       "settings/a11y/multidevice_a11y_test.js",
       "settings/a11y/multidevice_features_a11y_test.js",
       "settings/a11y/tts_subpage_a11y_test.js",
+      "settings/chromeos/os_settings_browsertest.js",
       "sys_internals/sys_internals_browsertest.js",
     ]
   } else {
diff --git a/chrome/test/data/webui/extensions/activity_log_test.js b/chrome/test/data/webui/extensions/activity_log_test.js
index 21b41cf..b7a0e34f 100644
--- a/chrome/test/data/webui/extensions/activity_log_test.js
+++ b/chrome/test/data/webui/extensions/activity_log_test.js
@@ -19,7 +19,8 @@
 
   /**
    * Backing extension info for the activity log.
-   * @type {chrome.developerPrivate.ExtensionInfo}
+   * @type {chrome.developerPrivate.ExtensionInfo|
+   *        extensions.ActivityLogExtensionPlaceholder}
    */
   let extensionInfo;
 
@@ -84,6 +85,22 @@
         currentPage, {page: Page.DETAILS, extensionId: EXTENSION_ID});
   });
 
+  test(
+      'clicking on back button for a placeholder page navigates to list view',
+      function() {
+        activityLog.extensionInfo = {id: EXTENSION_ID, isPlaceholder: true};
+
+        Polymer.dom.flush();
+
+        let currentPage = null;
+        extensions.navigation.addListener(newPage => {
+          currentPage = newPage;
+        });
+
+        activityLog.$$('#closeButton').click();
+        expectDeepEquals(currentPage, {page: Page.LIST});
+      });
+
   test('tab transitions', async () => {
     Polymer.dom.flush();
     // Default view should be the history view.
diff --git a/chrome/test/data/webui/extensions/manager_test.js b/chrome/test/data/webui/extensions/manager_test.js
index 3e09001..94f38ae56f 100644
--- a/chrome/test/data/webui/extensions/manager_test.js
+++ b/chrome/test/data/webui/extensions/manager_test.js
@@ -171,8 +171,9 @@
       extensions.navigation.navigateTo(
           {page: Page.ACTIVITY_LOG, extensionId: 'z'.repeat(32)});
       Polymer.dom.flush();
-      // Should be re-routed to the main page.
-      assertViewActive('extensions-item-list');
+      // Should also be on activity log page. See |changePage_| in manager.js
+      // for the use case.
+      assertViewActive('extensions-activity-log');
     });
   });
 
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.js b/chrome/test/data/webui/print_preview/pages_settings_test.js
index 8d3770e..5669cbc 100644
--- a/chrome/test/data/webui/print_preview/pages_settings_test.js
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.js
@@ -293,6 +293,17 @@
       // Input has been cleared.
       assertEquals('', input.value);
       validateState([1, 2, 3], [], '', false);
+      whenBlurred = test_util.eventToPromise('blur', input);
+      input.blur();
+
+      await whenBlurred;
+      assertEquals('1-3', input.value);
+
+      // Change the page count. Since the range was set automatically, this
+      // should reset it to the new set of all pages.
+      pagesSection.pageCount = 2;
+      validateState([1, 2], [], '', false);
+      assertEquals('1-2', input.value);
     });
 
     // Verifies that the input is never disabled when the validity of the
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
new file mode 100644
index 0000000..8b0619a
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -0,0 +1,70 @@
+// Copyright 2019 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.
+
+/** @fileoverview Tests for Chrome OS settings page. */
+
+/** @const {string} Path general Chrome browser settings. */
+const BROWSER_SETTINGS_PATH = '../';
+
+// Polymer BrowserTest fixture.
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
+
+GEN('#include "ash/public/cpp/ash_features.h"');
+GEN('#include "chrome/common/chrome_features.h"');
+GEN('#include "chromeos/constants/chromeos_features.h"');
+/**
+ * Generic text fixture for CrOS Polymer Settings elements to be overridden by
+ * individual element tests.
+ * @constructor
+ * @extends {PolymerTest}
+ */
+const OSSettingsBrowserTest = class extends PolymerTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://settings/chromeos/';
+  }
+
+  /** @override */
+  get featureList() {
+    return {enabled: ['chromeos::features::kSplitSettings']};
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + 'ensure_lazy_loaded.js',
+    ]);
+  }
+
+  /** @override */
+  setUp() {
+    super.setUp();
+    settings.ensureLazyLoaded();
+  }
+};
+
+/**
+ * Test fixture for the Smb Shares page.
+ * @constructor
+ * @extends {OSSettingsBrowserTest}
+ */
+OSSettingsSmbPageTest = class extends OSSettingsBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'os_downloads_page/smb_shares_page.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      'smb_shares_page_tests.js',
+    ]);
+  }
+};
+
+TEST_F('OSSettingsSmbPageTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/settings/smb_shares_page_tests.js b/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
similarity index 99%
rename from chrome/test/data/webui/settings/smb_shares_page_tests.js
rename to chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
index 0a9e3b86..5fdc0bf2 100644
--- a/chrome/test/data/webui/settings/smb_shares_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.js
@@ -192,5 +192,4 @@
     expectEquals(expectedSmbUrl, addDialog.mountUrl_);
     expectEquals(expectedSmbUrl, addDialog.mountUrl_);
   });
-
 });
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 1e5f191..9718a9f 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -2139,7 +2139,7 @@
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     'test_util.js',
     '../test_browser_proxy.js',
-    'smb_shares_page_tests.js',
+    'chromeos/smb_shares_page_tests.js',
   ]),
 };
 
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 186a2dbd..1d13162 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -31,6 +31,8 @@
       "crash_client.h",
       "crash_reporter.cc",
       "crash_reporter.h",
+      "installer.cc",
+      "installer.h",
       "patcher.cc",
       "patcher.h",
       "prefs.cc",
diff --git a/chrome/updater/DEPS b/chrome/updater/DEPS
index 843c0ac..5aa062e 100644
--- a/chrome/updater/DEPS
+++ b/chrome/updater/DEPS
@@ -1,11 +1,12 @@
 include_rules = [
   "+components/crash/core/common",
+  "+components/crx_file",
   "+components/prefs",
   "+components/services/patch",
   "+components/services/unzip",
   "+components/update_client",
   "+components/version_info",
   "+courgette",
-  "+third_party/zlib/google",
   "+third_party/crashpad",
+  "+third_party/zlib/google",
 ]
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc
new file mode 100644
index 0000000..8b2807a
--- /dev/null
+++ b/chrome/updater/installer.cc
@@ -0,0 +1,185 @@
+// Copyright 2019 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/updater/installer.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/util.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+
+namespace updater {
+
+namespace {
+
+// Version "0" corresponds to no installed version.
+const char kNullVersion[] = "0.0.0.0";
+
+}  // namespace
+
+Installer::InstallInfo::InstallInfo() : version(kNullVersion) {}
+Installer::InstallInfo::~InstallInfo() = default;
+
+Installer::Installer(const std::vector<uint8_t>& pk_hash)
+    : pk_hash_(pk_hash),
+      crx_id_(update_client::GetCrxIdFromPublicKeyHash(pk_hash)),
+      install_info_(std::make_unique<InstallInfo>()) {}
+
+Installer::~Installer() = default;
+
+update_client::CrxComponent Installer::MakeCrxComponent() {
+  update_client::CrxComponent component;
+  component.installer = scoped_refptr<Installer>(this);
+  component.requires_network_encryption = false;
+  component.crx_format_requirement =
+      crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF;
+  component.pk_hash = pk_hash_;
+  component.name = crx_id_;
+  component.version = install_info_->version;
+  component.fingerprint = install_info_->fingerprint;
+  return component;
+}
+
+void Installer::FindInstallOfApp() {
+  VLOG(1) << __func__ << " for " << crx_id_;
+
+  base::FilePath root_install_path;
+  if (!GetProductDataDirectory(&root_install_path)) {
+    install_info_ = std::make_unique<InstallInfo>();
+    return;
+  }
+  root_install_path = root_install_path.AppendASCII(crx_id_);
+  if (!base::PathExists(root_install_path)) {
+    install_info_ = std::make_unique<InstallInfo>();
+    return;
+  }
+
+  base::Version latest_version(kNullVersion);
+  base::FilePath latest_path;
+  std::vector<base::FilePath> older_paths;
+  base::FileEnumerator file_enumerator(root_install_path, false,
+                                       base::FileEnumerator::DIRECTORIES);
+  for (auto path = file_enumerator.Next(); !path.value().empty();
+       path = file_enumerator.Next()) {
+    const base::Version version(path.BaseName().MaybeAsASCII());
+
+    // Ignore folders that don't have valid version names.
+    if (!version.IsValid())
+      continue;
+
+    // The |version| not newer than the latest found version is marked for
+    // removal. |kNullVersion| is also removed.
+    if (version.CompareTo(latest_version) <= 0) {
+      older_paths.push_back(path);
+      continue;
+    }
+
+    // New valid |version| folder found.
+    if (!latest_path.empty())
+      older_paths.push_back(latest_path);
+
+    latest_version = version;
+    latest_path = path;
+  }
+
+  install_info_->version = latest_version;
+  install_info_->install_dir = latest_path;
+  install_info_->manifest = update_client::ReadManifest(latest_path);
+  base::ReadFileToString(latest_path.AppendASCII("manifest.fingerprint"),
+                         &install_info_->fingerprint);
+
+  for (const auto& older_path : older_paths)
+    base::DeleteFile(older_path, true);
+}
+
+Installer::Result Installer::InstallHelper(const base::FilePath& unpack_path) {
+  auto local_manifest = update_client::ReadManifest(unpack_path);
+  if (!local_manifest)
+    return Result(update_client::InstallError::BAD_MANIFEST);
+
+  std::string version_ascii;
+  local_manifest->GetStringASCII("version", &version_ascii);
+  const base::Version manifest_version(version_ascii);
+
+  VLOG(1) << "Installed version=" << install_info_->version.GetString()
+          << ", installing version=" << manifest_version.GetString();
+
+  if (!manifest_version.IsValid())
+    return Result(update_client::InstallError::INVALID_VERSION);
+
+  if (install_info_->version.CompareTo(manifest_version) > 0)
+    return Result(update_client::InstallError::VERSION_NOT_UPGRADED);
+
+  base::FilePath root_install_path;
+  if (!GetProductDataDirectory(&root_install_path))
+    return Result(update_client::InstallError::NO_DIR_COMPONENT_USER);
+
+  root_install_path = root_install_path.AppendASCII(crx_id_);
+  if (!base::CreateDirectory(root_install_path)) {
+    return Result(
+        static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) +
+        kCustomInstallErrorCreateAppInstallDirectory);
+  }
+
+  const auto versioned_install_path =
+      root_install_path.AppendASCII(manifest_version.GetString());
+  if (base::PathExists(versioned_install_path)) {
+    if (!base::DeleteFile(versioned_install_path, true))
+      return Result(update_client::InstallError::CLEAN_INSTALL_DIR_FAILED);
+  }
+
+  VLOG(1) << "Install_path=" << versioned_install_path.AsUTF8Unsafe();
+
+  if (!base::Move(unpack_path, versioned_install_path)) {
+    PLOG(ERROR) << "Move failed.";
+    base::DeleteFile(versioned_install_path, true);
+    return Result(update_client::InstallError::MOVE_FILES_ERROR);
+  }
+
+  DCHECK(!base::PathExists(unpack_path));
+  DCHECK(base::PathExists(versioned_install_path));
+
+  install_info_->manifest = std::move(local_manifest);
+  install_info_->version = manifest_version;
+  install_info_->install_dir = versioned_install_path;
+  base::ReadFileToString(
+      versioned_install_path.AppendASCII("manifest.fingerprint"),
+      &install_info_->fingerprint);
+
+  return Result(update_client::InstallError::NONE);
+}
+
+void Installer::OnUpdateError(int error) {
+  LOG(ERROR) << "updater error: " << error << " for " << crx_id_;
+}
+
+void Installer::Install(const base::FilePath& unpack_path,
+                        const std::string& public_key,
+                        Callback callback) {
+  std::unique_ptr<base::DictionaryValue> manifest;
+  base::Version version;
+  base::FilePath install_path;
+
+  const auto result = InstallHelper(unpack_path);
+  base::DeleteFile(unpack_path, true);
+  std::move(callback).Run(result);
+}
+
+bool Installer::GetInstalledFile(const std::string& file,
+                                 base::FilePath* installed_file) {
+  return false;
+}
+
+bool Installer::Uninstall() {
+  return false;
+}
+
+}  // namespace updater
diff --git a/chrome/updater/installer.h b/chrome/updater/installer.h
new file mode 100644
index 0000000..ef97e84
--- /dev/null
+++ b/chrome/updater/installer.h
@@ -0,0 +1,74 @@
+// Copyright 2019 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_UPDATER_INSTALLER_H_
+#define CHROME_UPDATER_INSTALLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "components/update_client/update_client.h"
+
+namespace updater {
+
+class Installer final : public update_client::CrxInstaller {
+ public:
+  struct InstallInfo {
+    InstallInfo();
+    ~InstallInfo();
+
+    base::FilePath install_dir;
+    base::Version version;
+    std::string fingerprint;
+    std::unique_ptr<base::DictionaryValue> manifest;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(InstallInfo);
+  };
+
+  explicit Installer(const std::vector<uint8_t>& pk_hash);
+
+  const std::string crx_id() const { return crx_id_; }
+
+  // Finds the highest version install of the app, and updates the install
+  // info for this installer instance.
+  void FindInstallOfApp();
+
+  // Returns a CrxComponent instance that describes the current install
+  // state of the app.
+  update_client::CrxComponent MakeCrxComponent();
+
+ private:
+  ~Installer() override;
+
+  // Overrides from update_client::CrxInstaller.
+  void OnUpdateError(int error) override;
+  void Install(const base::FilePath& unpack_path,
+               const std::string& public_key,
+               Callback callback) override;
+  bool GetInstalledFile(const std::string& file,
+                        base::FilePath* installed_file) override;
+  bool Uninstall() override;
+
+  Result InstallHelper(const base::FilePath& unpack_path);
+
+  const std::vector<uint8_t> pk_hash_;
+  const std::string crx_id_;
+  std::unique_ptr<InstallInfo> install_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(Installer);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_INSTALLER_H_
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index ef1a9d4..0aae5e93 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/updater.h"
 
+#include <stdint.h>
+
 #include <iterator>
 #include <memory>
 #include <string>
@@ -11,7 +13,7 @@
 #include <vector>
 
 #include "base/at_exit.h"
-#include "base/callback_forward.h"
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
@@ -30,6 +32,7 @@
 #include "chrome/updater/configurator.h"
 #include "chrome/updater/crash_client.h"
 #include "chrome/updater/crash_reporter.h"
+#include "chrome/updater/installer.h"
 #include "chrome/updater/updater_constants.h"
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util.h"
@@ -68,12 +71,16 @@
 
   // Overrides for update_client::UpdateClient::Observer.
   void OnEvent(Events event, const std::string& id) override {
-    update_client::CrxUpdateItem item;
-    update_client_->GetCrxUpdateState(id, &item);
+    update_client_->GetCrxUpdateState(id, &crx_update_item_);
+  }
+
+  const update_client::CrxUpdateItem& crx_update_item() const {
+    return crx_update_item_;
   }
 
  private:
   scoped_refptr<update_client::UpdateClient> update_client_;
+  update_client::CrxUpdateItem crx_update_item_;
   DISALLOW_COPY_AND_ASSIGN(Observer);
 };
 
@@ -100,11 +107,11 @@
       "process_type");
   crash_key_process_type.Set("updater");
 
-  if (CrashClient::GetInstance()->InitializeCrashReporting()) {
+  if (CrashClient::GetInstance()->InitializeCrashReporting())
     VLOG(1) << "Crash reporting initialized.";
-  } else {
+  else
     VLOG(1) << "Crash reporting is not available.";
-  }
+
   StartCrashReporter(UPDATER_VERSION_STRING);
 
   ThreadPoolStart();
@@ -138,12 +145,16 @@
     return *ptr;
   }
 
+  auto installer = base::MakeRefCounted<Installer>(
+      std::vector<uint8_t>(std::cbegin(mimo_hash), std::cend(mimo_hash)));
+  installer->FindInstallOfApp();
+  const auto component = installer->MakeCrxComponent();
+
   base::MessageLoopForUI message_loop;
   base::RunLoop runloop;
   DCHECK(base::ThreadTaskRunnerHandle::IsSet());
 
   auto config = base::MakeRefCounted<Configurator>();
-
   {
     base::ScopedDisallowBlocking no_blocking_allowed;
 
@@ -152,20 +163,17 @@
     Observer observer(update_client);
     update_client->AddObserver(&observer);
 
-    const std::vector<std::string> ids = {"mimojjlkmoijpicakmndhoigimigcmbb"};
+    const std::vector<std::string> ids = {installer->crx_id()};
     update_client->Update(
         ids,
         base::BindOnce(
-            [](const std::vector<std::string>& ids)
+            [](const update_client::CrxComponent& component,
+               const std::vector<std::string>& ids)
                 -> std::vector<base::Optional<update_client::CrxComponent>> {
-              update_client::CrxComponent component;
-              component.name = "mimo";
-              component.pk_hash.assign(std::begin(mimo_hash),
-                                       std::end(mimo_hash));
-              component.version = base::Version("0.0");
-              component.requires_network_encryption = false;
+              DCHECK_EQ(1u, ids.size());
               return {component};
-            }),
+            },
+            component),
         true,
         base::BindOnce(
             [](base::OnceClosure closure, update_client::Error error) {
@@ -176,6 +184,21 @@
 
     runloop.Run();
 
+    const auto& update_item = observer.crx_update_item();
+    switch (update_item.state) {
+      case update_client::ComponentState::kUpdated:
+        VLOG(1) << "Update success.";
+        break;
+      case update_client::ComponentState::kUpToDate:
+        VLOG(1) << "No updates.";
+        break;
+      case update_client::ComponentState::kUpdateError:
+        VLOG(1) << "Updater error: " << update_item.error_code << ".";
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
     update_client->RemoveObserver(&observer);
     update_client = nullptr;
   }
diff --git a/chrome/updater/updater_constants.h b/chrome/updater/updater_constants.h
index 4b3704b..235cddd 100644
--- a/chrome/updater/updater_constants.h
+++ b/chrome/updater/updater_constants.h
@@ -31,6 +31,11 @@
 extern const char kCrashUploadURL[];
 extern const char kCrashStagingUploadURL[];
 
+// Errors.
+//
+// The install directory for the application could not be created.
+const int kCustomInstallErrorCreateAppInstallDirectory = 0;
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_UPDATER_CONSTANTS_H_
diff --git a/chromecast/media/DEPS b/chromecast/media/DEPS
index 6eced6d..ffba57ad 100644
--- a/chromecast/media/DEPS
+++ b/chromecast/media/DEPS
@@ -5,6 +5,8 @@
   "+content/public/test/test_browser_thread.h",
   "+content/public/test/test_browser_thread_bundle.h",
   "+content/public/test/test_utils.h",
+  "+content/renderer/media/audio/audio_device_factory.h",
+  "+content/renderer/media/audio/audio_output_ipc_factory.h",
   "+media/audio",
   "+media/base",
   "+media/cdm",
diff --git a/chromecast/media/audio/BUILD.gn b/chromecast/media/audio/BUILD.gn
index 2e000eb..3b68287f 100644
--- a/chromecast/media/audio/BUILD.gn
+++ b/chromecast/media/audio/BUILD.gn
@@ -49,6 +49,21 @@
   configs += [ "//media/audio:platform_config" ]
 }
 
+if (is_android) {
+  cast_source_set("cast_audio_device_factory") {
+    sources = [
+      "cast_audio_device_factory.cc",
+      "cast_audio_device_factory.h",
+    ]
+
+    deps = [
+      "//base",
+      "//content/renderer:renderer",
+      "//media",
+    ]
+  }
+}
+
 cast_shared_library("libcast_external_audio_pipeline_1.0") {
   sources = [
     "external_audio_pipeline_dummy.cc",
diff --git a/chromecast/media/audio/cast_audio_device_factory.cc b/chromecast/media/audio/cast_audio_device_factory.cc
new file mode 100644
index 0000000..33e8f21
--- /dev/null
+++ b/chromecast/media/audio/cast_audio_device_factory.cc
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 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 "chromecast/media/audio/cast_audio_device_factory.h"
+
+#include <string>
+
+#include "content/renderer/media/audio/audio_output_ipc_factory.h"
+#include "media/audio/audio_output_device.h"
+#include "media/base/audio_capturer_source.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/audio_renderer_sink.h"
+#include "media/base/output_device_info.h"
+
+namespace chromecast {
+namespace media {
+
+class NonSwitchableAudioRendererSink
+    : public ::media::SwitchableAudioRendererSink {
+ public:
+  explicit NonSwitchableAudioRendererSink(
+      scoped_refptr<::media::AudioOutputDevice> output_device)
+      : output_device_(std::move(output_device)) {}
+
+  void Initialize(const ::media::AudioParameters& params,
+                  RenderCallback* callback) override {
+    output_device_->Initialize(params, callback);
+  }
+
+  void Start() override { output_device_->Start(); }
+
+  void Stop() override { output_device_->Stop(); }
+
+  void Pause() override { output_device_->Pause(); }
+
+  void Play() override { output_device_->Play(); }
+
+  bool SetVolume(double volume) override {
+    return output_device_->SetVolume(volume);
+  }
+
+  ::media::OutputDeviceInfo GetOutputDeviceInfo() override {
+    return output_device_->GetOutputDeviceInfo();
+  }
+
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override {
+    output_device_->GetOutputDeviceInfoAsync(std::move(info_cb));
+  }
+
+  bool IsOptimizedForHardwareParameters() override {
+    return output_device_->IsOptimizedForHardwareParameters();
+  }
+
+  bool CurrentThreadIsRenderingThread() override {
+    return output_device_->CurrentThreadIsRenderingThread();
+  }
+
+  void SwitchOutputDevice(const std::string& device_id,
+                          ::media::OutputDeviceStatusCB callback) override {
+    LOG(ERROR) << __func__ << " is not suported.";
+    std::move(callback).Run(::media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL);
+  }
+
+  void Flush() override { output_device_->Flush(); }
+
+ protected:
+  ~NonSwitchableAudioRendererSink() override = default;
+
+ private:
+  scoped_refptr<::media::AudioOutputDevice> output_device_;
+};
+
+scoped_refptr<::media::AudioOutputDevice> NewOutputDevice(
+    int render_frame_id,
+    const ::media::AudioSinkParameters& params,
+    base::TimeDelta auth_timeout) {
+  auto device = base::MakeRefCounted<::media::AudioOutputDevice>(
+      content::AudioOutputIPCFactory::get()->CreateAudioOutputIPC(
+          render_frame_id),
+      content::AudioOutputIPCFactory::get()->io_task_runner(), params,
+      auth_timeout);
+  device->RequestDeviceAuthorization();
+  return device;
+}
+
+CastAudioDeviceFactory::CastAudioDeviceFactory()
+    : content::AudioDeviceFactory() {
+  DVLOG(1) << "Register CastAudioDeviceFactory";
+}
+
+CastAudioDeviceFactory::~CastAudioDeviceFactory() {
+  DVLOG(1) << "Unregister CastAudioDeviceFactory";
+}
+
+scoped_refptr<::media::AudioRendererSink>
+CastAudioDeviceFactory::CreateFinalAudioRendererSink(
+    int render_frame_id,
+    const ::media::AudioSinkParameters& params,
+    base::TimeDelta auth_timeout) {
+  // Use default implementation.
+  return nullptr;
+}
+
+scoped_refptr<::media::AudioRendererSink>
+CastAudioDeviceFactory::CreateAudioRendererSink(
+    content::AudioDeviceFactory::SourceType source_type,
+    int render_frame_id,
+    const ::media::AudioSinkParameters& params) {
+  // Use default implementation.
+  return nullptr;
+}
+
+scoped_refptr<::media::SwitchableAudioRendererSink>
+CastAudioDeviceFactory::CreateSwitchableAudioRendererSink(
+    content::AudioDeviceFactory::SourceType source_type,
+    int render_frame_id,
+    const ::media::AudioSinkParameters& params) {
+  return base::MakeRefCounted<NonSwitchableAudioRendererSink>(NewOutputDevice(
+      render_frame_id, params, base::TimeDelta::FromSeconds(100)));
+}
+
+scoped_refptr<::media::AudioCapturerSource>
+CastAudioDeviceFactory::CreateAudioCapturerSource(
+    int render_frame_id,
+    const ::media::AudioSourceParameters& params) {
+  // Use default implementation.
+  return nullptr;
+}
+
+}  // namespace media
+}  // namespace chromecast
\ No newline at end of file
diff --git a/chromecast/media/audio/cast_audio_device_factory.h b/chromecast/media/audio/cast_audio_device_factory.h
new file mode 100644
index 0000000..2be9cdb4
--- /dev/null
+++ b/chromecast/media/audio/cast_audio_device_factory.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 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 CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_DEVICE_FACTORY_H_
+#define CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_DEVICE_FACTORY_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/renderer/media/audio/audio_device_factory.h"
+#include "media/audio/audio_sink_parameters.h"
+
+namespace media {
+class AudioCapturerSource;
+class AudioRendererSink;
+class SwitchableAudioRendererSink;
+}  // namespace media
+
+namespace chromecast {
+namespace media {
+
+class CastAudioDeviceFactory : public content::AudioDeviceFactory {
+ public:
+  CastAudioDeviceFactory();
+  ~CastAudioDeviceFactory() final;
+
+  scoped_refptr<::media::AudioRendererSink> CreateFinalAudioRendererSink(
+      int render_frame_id,
+      const ::media::AudioSinkParameters& params,
+      base::TimeDelta auth_timeout) override;
+
+  scoped_refptr<::media::AudioRendererSink> CreateAudioRendererSink(
+      content::AudioDeviceFactory::SourceType source_type,
+      int render_frame_id,
+      const ::media::AudioSinkParameters& params) override;
+
+  scoped_refptr<::media::SwitchableAudioRendererSink>
+  CreateSwitchableAudioRendererSink(
+      content::AudioDeviceFactory::SourceType source_type,
+      int render_frame_id,
+      const ::media::AudioSinkParameters& params) override;
+
+  scoped_refptr<::media::AudioCapturerSource> CreateAudioCapturerSource(
+      int render_frame_id,
+      const ::media::AudioSourceParameters& params) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastAudioDeviceFactory);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_DEVICE_FACTORY_H_
diff --git a/chromecast/media/audio/cast_audio_manager.cc b/chromecast/media/audio/cast_audio_manager.cc
index 6c9e272..25470c0 100644
--- a/chromecast/media/audio/cast_audio_manager.cc
+++ b/chromecast/media/audio/cast_audio_manager.cc
@@ -298,5 +298,16 @@
     return &mixer_service_connection_factory_;
 }
 
+#if defined(OS_ANDROID)
+::media::AudioOutputStream* CastAudioManager::MakeAudioOutputStreamProxy(
+    const ::media::AudioParameters& params,
+    const std::string& device_id) {
+  // Override to use MakeAudioOutputStream to prevent the audio output stream
+  // from closing during pause/stop.
+  return MakeAudioOutputStream(params, device_id,
+                               /*log_callback, not used*/ base::DoNothing());
+}
+#endif  // defined(OS_ANDROID)
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/audio/cast_audio_manager.h b/chromecast/media/audio/cast_audio_manager.h
index fbecbdf..dcac0c3 100644
--- a/chromecast/media/audio/cast_audio_manager.h
+++ b/chromecast/media/audio/cast_audio_manager.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
 #include "chromecast/media/audio/mixer_service/mixer_service_connection_factory.h"
 #include "media/audio/audio_manager_base.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -108,6 +109,12 @@
   virtual ::media::AudioOutputStream* MakeMixerOutputStream(
       const ::media::AudioParameters& params);
 
+#if defined(OS_ANDROID)
+  ::media::AudioOutputStream* MakeAudioOutputStreamProxy(
+      const ::media::AudioParameters& params,
+      const std::string& device_id) override;
+#endif
+
  private:
   friend class CastAudioMixer;
   friend class CastAudioManagerTest;
diff --git a/chromecast/media/audio/cast_audio_output_stream.cc b/chromecast/media/audio/cast_audio_output_stream.cc
index 91b9801..420b741 100644
--- a/chromecast/media/audio/cast_audio_output_stream.cc
+++ b/chromecast/media/audio/cast_audio_output_stream.cc
@@ -55,6 +55,7 @@
 constexpr base::TimeDelta kFadeTime = base::TimeDelta::FromMilliseconds(5);
 constexpr base::TimeDelta kMixerStartThreshold =
     base::TimeDelta::FromMilliseconds(60);
+constexpr base::TimeDelta kRenderBufferSize = base::TimeDelta::FromSeconds(4);
 }  // namespace
 
 namespace chromecast {
@@ -105,10 +106,18 @@
                   chromecast::mojom::MultiroomInfoPtr multiroom_info);
   void Start(AudioSourceCallback* source_callback);
   void Stop(base::WaitableEvent* finished);
+  void Flush(base::WaitableEvent* finished);
   void Close(base::OnceClosure closure);
   void SetVolume(double volume);
 
  private:
+  enum class CmaBackendState {
+    kUinitialized,
+    kStopped,
+    kPaused,
+    kStarted,
+  };
+
   void PushBuffer();
 
   // CmaBackend::Decoder::Delegate implementation:
@@ -126,6 +135,7 @@
   CmaBackendFactory* const cma_backend_factory_;
 
   AudioOutputState media_thread_state_;
+  CmaBackendState cma_backend_state_ = CmaBackendState::kUinitialized;
   ::media::AudioTimestampHelper timestamp_helper_;
   const base::TimeDelta buffer_duration_;
   std::unique_ptr<TaskRunnerImpl> cma_backend_task_runner_;
@@ -134,7 +144,8 @@
   base::OneShotTimer push_timer_;
   bool push_in_progress_;
   bool encountered_error_;
-  base::TimeTicks next_push_time_;
+  base::TimeTicks last_push_complete_time_;
+  base::TimeDelta render_buffer_size_estimate_ = kRenderBufferSize;
   CmaBackend::AudioDecoder* audio_decoder_;
   AudioSourceCallback* source_callback_;
 
@@ -154,7 +165,8 @@
       cma_backend_factory_(cma_backend_factory),
       media_thread_state_(kClosed),
       timestamp_helper_(audio_params_.sample_rate()),
-      buffer_duration_(audio_params_.GetBufferDuration()) {
+      buffer_duration_(audio_params_.GetBufferDuration()),
+      render_buffer_size_estimate_(kRenderBufferSize) {
   DETACH_FROM_THREAD(media_thread_checker_);
   DCHECK(audio_task_runner_);
   DCHECK(cma_backend_factory_);
@@ -222,6 +234,7 @@
     encountered_error_ = true;
     return;
   }
+  cma_backend_state_ = CmaBackendState::kStopped;
 
   audio_bus_ = ::media::AudioBus::Create(audio_params_);
   timestamp_helper_.SetBaseTimestamp(base::TimeDelta());
@@ -241,11 +254,19 @@
   }
 
   if (media_thread_state_ == kOpened) {
+    DCHECK(cma_backend_state_ == CmaBackendState::kPaused ||
+           cma_backend_state_ == CmaBackendState::kStopped);
+    if (cma_backend_state_ == CmaBackendState::kPaused) {
+      cma_backend_->Resume();
+    } else {
+      cma_backend_->Start(0);
+      render_buffer_size_estimate_ = kRenderBufferSize;
+    }
+    last_push_complete_time_ = base::TimeTicks::Now();
+    cma_backend_state_ = CmaBackendState::kStarted;
     media_thread_state_ = kStarted;
-    cma_backend_->Start(0);
   }
 
-  next_push_time_ = base::TimeTicks::Now();
   if (!push_in_progress_) {
     push_in_progress_ = true;
     PushBuffer();
@@ -256,8 +277,30 @@
   DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
   // Prevent further pushes to the audio buffer after stopping.
   push_timer_.Stop();
-  if (cma_backend_ && media_thread_state_ == kStarted) {
-    cma_backend_->Stop();
+  // Don't actually stop the backend.  Stop() gets called when the stream is
+  // paused.  We rely on Flush() to stop the backend.
+  if (cma_backend_) {
+    cma_backend_->Pause();
+    cma_backend_state_ = CmaBackendState::kPaused;
+  }
+  push_in_progress_ = false;
+  media_thread_state_ = kOpened;
+  source_callback_ = nullptr;
+  finished->Signal();
+}
+
+void CastAudioOutputStream::CmaWrapper::Flush(base::WaitableEvent* finished) {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  // Prevent further pushes to the audio buffer after stopping.
+  push_timer_.Stop();
+
+  if (cma_backend_ &&
+      (media_thread_state_ == kStarted || media_thread_state_ == kOpened)) {
+    if (cma_backend_state_ == CmaBackendState::kPaused ||
+        cma_backend_state_ == CmaBackendState::kStarted) {
+      cma_backend_->Stop();
+      cma_backend_state_ = CmaBackendState::kStopped;
+    }
   }
   push_in_progress_ = false;
   media_thread_state_ = kOpened;
@@ -270,8 +313,9 @@
   // Prevent further pushes to the audio buffer after stopping.
   push_timer_.Stop();
   // Only stop the backend if it was started.
-  if (cma_backend_ && media_thread_state_ == kStarted) {
+  if (cma_backend_ && cma_backend_state_ != CmaBackendState::kStopped) {
     cma_backend_->Stop();
+    cma_backend_state_ = CmaBackendState::kStopped;
   }
   push_in_progress_ = false;
   media_thread_state_ = kPendingClose;
@@ -308,19 +352,21 @@
 
   CmaBackend::AudioDecoder::RenderingDelay rendering_delay =
       audio_decoder_->GetRenderingDelay();
-  base::TimeDelta delay =
-      base::TimeDelta::FromMicroseconds(rendering_delay.delay_microseconds);
-  base::TimeTicks delay_timestamp =
-      base::TimeTicks() +
-      base::TimeDelta::FromMicroseconds(rendering_delay.timestamp_microseconds);
 
-  // The delay must be greater than zero, and if the timestamp is invalid, we
-  // cannot trust the current delay.
-  if (rendering_delay.timestamp_microseconds == kInvalidTimestamp ||
-      rendering_delay.delay_microseconds < 0) {
+  base::TimeDelta delay;
+  if (rendering_delay.delay_microseconds < 0) {
     delay = base::TimeDelta();
+  } else {
+    delay =
+        base::TimeDelta::FromMicroseconds(rendering_delay.delay_microseconds);
   }
 
+  // This isn't actually used by audio_renderer_impl
+  base::TimeTicks delay_timestamp = base::TimeTicks();
+  if (rendering_delay.timestamp_microseconds != kInvalidTimestamp) {
+    delay_timestamp += base::TimeDelta::FromMicroseconds(
+        rendering_delay.timestamp_microseconds);
+  }
   int frame_count =
       source_callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus_.get());
   DVLOG(3) << "frames_filled=" << frame_count << " with latency=" << delay;
@@ -357,9 +403,20 @@
 
   // Schedule next push buffer.
   const base::TimeTicks now = base::TimeTicks::Now();
-  next_push_time_ = std::max(now, next_push_time_ + buffer_duration_);
+  render_buffer_size_estimate_ -= buffer_duration_;
+  render_buffer_size_estimate_ += now - last_push_complete_time_;
+  last_push_complete_time_ = now;
 
-  base::TimeDelta delay = next_push_time_ - now;
+  base::TimeDelta delay;
+  if (render_buffer_size_estimate_ >= buffer_duration_) {
+    delay = base::TimeDelta::FromSeconds(0);
+  } else {
+    delay = buffer_duration_;
+  }
+
+  DVLOG(3) << "render_buffer_size_estimate_=" << render_buffer_size_estimate_
+           << " delay=" << delay << " buffer_duration_=" << buffer_duration_;
+
   push_timer_.Start(FROM_HERE, delay, this, &CmaWrapper::PushBuffer);
   push_in_progress_ = true;
 }
@@ -386,6 +443,7 @@
   void Stop();
   void Close(base::OnceClosure closure);
   void SetVolume(double volume);
+  void Flush();
 
   base::SingleThreadTaskRunner* io_task_runner() {
     return io_task_runner_.get();
@@ -484,6 +542,12 @@
   source_callback_ = nullptr;
 }
 
+void CastAudioOutputStream::MixerServiceWrapper::Flush() {
+  DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
+  // Nothing to do.
+  return;
+}
+
 void CastAudioOutputStream::MixerServiceWrapper::Close(
     base::OnceClosure closure) {
   DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
@@ -692,7 +756,21 @@
 void CastAudioOutputStream::Flush() {
   DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
   DVLOG(2) << this << ": " << __func__;
-  // TODO(alexleung): Implement in follow-up CL.
+
+  // |cma_wrapper_| and |mixer_service_wrapper_| cannot be both active.
+  DCHECK(!(cma_wrapper_ && mixer_service_wrapper_));
+
+  if (cma_wrapper_) {
+    // Make sure this is not on the same thread as CMA_WRAPPER to prevent
+    // deadlock.
+    DCHECK(!audio_manager_->media_task_runner()->BelongsToCurrentThread());
+
+    base::WaitableEvent finished;
+    POST_TO_CMA_WRAPPER(Flush, base::Unretained(&finished));
+    finished.Wait();
+  } else if (mixer_service_wrapper_) {
+    POST_TO_MIXER_SERVICE_WRAPPER(Flush);
+  }
 }
 
 void CastAudioOutputStream::SetVolume(double volume) {
diff --git a/chromecast/media/audio/cast_audio_output_stream.h b/chromecast/media/audio/cast_audio_output_stream.h
index c471664..4051cedc 100644
--- a/chromecast/media/audio/cast_audio_output_stream.h
+++ b/chromecast/media/audio/cast_audio_output_stream.h
@@ -12,6 +12,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
 #include "base/timer/timer.h"
+#include "build/build_config.h"
 #include "chromecast/base/task_runner_impl.h"
 #include "chromecast/common/mojom/multiroom.mojom.h"
 #include "chromecast/media/cma/backend/cma_backend.h"
diff --git a/chromecast/media/audio/cast_audio_output_stream_unittest.cc b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
index 3528131..31a9f05 100644
--- a/chromecast/media/audio/cast_audio_output_stream_unittest.cc
+++ b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
@@ -617,6 +617,10 @@
 
   stream->Stop();
   RunThreadsUntilIdle();
+  EXPECT_EQ(FakeCmaBackend::kStatePaused, cma_backend_->state());
+
+  stream->Flush();
+  RunThreadsUntilIdle();
   EXPECT_EQ(FakeCmaBackend::kStateStopped, cma_backend_->state());
 
   stream->Close();
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn
index 1b6e8ba5..21ee1ccc 100644
--- a/chromecast/renderer/BUILD.gn
+++ b/chromecast/renderer/BUILD.gn
@@ -84,6 +84,8 @@
       "//chromecast/common/mojom",
       "//mojo/public/cpp/bindings",
     ]
+  } else {
+    deps += [ "//chromecast/media/audio:cast_audio_device_factory" ]
   }
 
   if (enable_chromecast_extensions) {
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index ad26b8e1..23ee7f4 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/optional.h"
 #include "base/strings/string_number_conversions.h"
 #include "chromecast/base/bitstream_audio_codecs.h"
 #include "chromecast/base/chromecast_switches.h"
@@ -35,6 +36,7 @@
 #include "third_party/blink/public/web/web_view.h"
 
 #if defined(OS_ANDROID)
+#include "chromecast/media/audio/cast_audio_device_factory.h"
 #include "media/base/android/media_codec_util.h"
 #else
 #include "chromecast/renderer/memory_pressure_observer_impl.h"
@@ -59,6 +61,18 @@
 namespace chromecast {
 namespace shell {
 
+#if defined(OS_ANDROID)
+// Audio renderer algorithm maximum capacity.
+constexpr base::TimeDelta kAudioRendererMaxCapacity =
+    base::TimeDelta::FromSeconds(10);
+// Audio renderer algorithm starting capacity.  Configure large enough to
+// prevent underrun.
+constexpr base::TimeDelta kAudioRendererStartingCapacity =
+    base::TimeDelta::FromMilliseconds(5000);
+constexpr base::TimeDelta kAudioRendererStartingCapacityEncrypted =
+    base::TimeDelta::FromMilliseconds(5500);
+#endif  // defined(OS_ANDROID)
+
 CastContentRendererClient::CastContentRendererClient()
     : supported_profiles_(new media::SupportedCodecProfileLevelsMemo()),
       app_media_capabilities_observer_binding_(this),
@@ -71,6 +85,10 @@
   // instance, which caches the platform decoder supported state when it is
   // constructed.
   ::media::EnablePlatformDecoderSupport();
+
+  // Registers a custom content::AudioDeviceFactory
+  cast_audio_device_factory_ =
+      std::make_unique<media::CastAudioDeviceFactory>();
 #endif  // OS_ANDROID
 }
 
@@ -310,7 +328,16 @@
 base::Optional<::media::AudioRendererAlgorithmParameters>
 CastContentRendererClient::GetAudioRendererAlgorithmParameters(
     ::media::AudioParameters audio_parameters) {
+#if defined(OS_ANDROID)
+  ::media::AudioRendererAlgorithmParameters parameters;
+  parameters.max_capacity = kAudioRendererMaxCapacity;
+  parameters.starting_capacity = kAudioRendererStartingCapacity;
+  parameters.starting_capacity_for_encrypted =
+      kAudioRendererStartingCapacityEncrypted;
+  return base::Optional<::media::AudioRendererAlgorithmParameters>(parameters);
+#else
   return base::nullopt;
+#endif
 }
 
 }  // namespace shell
diff --git a/chromecast/renderer/cast_content_renderer_client.h b/chromecast/renderer/cast_content_renderer_client.h
index b45c074..27e5f38 100644
--- a/chromecast/renderer/cast_content_renderer_client.h
+++ b/chromecast/renderer/cast_content_renderer_client.h
@@ -32,6 +32,10 @@
 namespace media {
 class MediaCapsObserverImpl;
 class SupportedCodecProfileLevelsMemo;
+
+#if defined(OS_ANDROID)
+class CastAudioDeviceFactory;
+#endif  // defined(OS_ANDROID)
 }
 
 namespace shell {
@@ -106,6 +110,10 @@
       guest_view_container_dispatcher_;
 #endif
 
+#if defined(OS_ANDROID)
+  std::unique_ptr<media::CastAudioDeviceFactory> cast_audio_device_factory_;
+#endif
+
   int supported_bitstream_audio_codecs_;
 
   DISALLOW_COPY_AND_ASSIGN(CastContentRendererClient);
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 520040f..fd4b667 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12225.0.0
\ No newline at end of file
+12233.0.0
\ No newline at end of file
diff --git a/chromeos/network/proxy/ui_proxy_config_service.cc b/chromeos/network/proxy/ui_proxy_config_service.cc
index b154b87..c108736d 100644
--- a/chromeos/network/proxy/ui_proxy_config_service.cc
+++ b/chromeos/network/proxy/ui_proxy_config_service.cc
@@ -283,13 +283,20 @@
       NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
   if (!network)
     return false;
+  return ProxyModeForNetwork(network) == ProxyPrefs::MODE_FIXED_SERVERS;
+}
+
+ProxyPrefs::ProxyMode UIProxyConfigService::ProxyModeForNetwork(
+    const NetworkState* network) {
+  // TODO(919691): Include proxies set by an extension and per-user proxies.
   onc::ONCSource onc_source = onc::ONC_SOURCE_NONE;
   std::unique_ptr<ProxyConfigDictionary> proxy_dict =
       proxy_config::GetProxyConfigForNetwork(nullptr, local_state_prefs_,
                                              *network, &onc_source);
   ProxyPrefs::ProxyMode mode;
-  return (proxy_dict && proxy_dict->GetMode(&mode) &&
-          mode == ProxyPrefs::MODE_FIXED_SERVERS);
+  if (!proxy_dict || !proxy_dict->GetMode(&mode))
+    return ProxyPrefs::MODE_DIRECT;
+  return mode;
 }
 
 void UIProxyConfigService::OnPreferenceChanged(const std::string& pref_name) {
diff --git a/chromeos/network/proxy/ui_proxy_config_service.h b/chromeos/network/proxy/ui_proxy_config_service.h
index 830d6e43..f5bb6dbd 100644
--- a/chromeos/network/proxy/ui_proxy_config_service.h
+++ b/chromeos/network/proxy/ui_proxy_config_service.h
@@ -10,6 +10,7 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "components/prefs/pref_change_registrar.h"
+#include "components/proxy_config/proxy_prefs.h"
 
 class PrefService;
 
@@ -19,6 +20,8 @@
 
 namespace chromeos {
 
+class NetworkState;
+
 // This class provides an interface to the UI for getting a network proxy
 // configuration.
 // NOTE: This class must be rebuilt with the primary user's profile prefs when
@@ -57,6 +60,9 @@
   // with mode == MODE_FIXED_SERVERS.
   bool HasDefaultNetworkProxyConfigured();
 
+  // Returns the ProxyMode for |network| using |local_state_prefs_|
+  ProxyPrefs::ProxyMode ProxyModeForNetwork(const NetworkState* network);
+
  private:
   void OnPreferenceChanged(const std::string& pref_name);
 
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index ac21d8d..00fb5fe4 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -148,6 +148,7 @@
       service_(service),
       background_thread_("background thread"),
       media_controller_observer_binding_(this),
+      app_list_subscriber_binding_(this),
       weak_factory_(this) {
   background_thread_.Start();
   platform_api_ = std::make_unique<PlatformApiImpl>(
@@ -1030,6 +1031,13 @@
       service_->assistant_state()->hotword_enabled().value()) {
     assistant_settings_manager_->SyncSpeakerIdEnrollmentStatus();
   }
+
+  if (base::FeatureList::IsEnabled(assistant::features::kAssistantAppSupport)) {
+    mojom::AppListEventSubscriberPtr subscriber_ptr;
+    app_list_subscriber_binding_.Bind(mojo::MakeRequest(&subscriber_ptr));
+    service_->device_actions()->AddAppListEventSubscriber(
+        std::move(subscriber_ptr));
+  }
 }
 
 void AssistantManagerServiceImpl::HandleLaunchMediaIntentResponse(
@@ -1118,6 +1126,17 @@
     service_->assistant_alarm_timer_controller()->OnTimerSoundingFinished();
 }
 
+void AssistantManagerServiceImpl::OnAndroidAppListRefreshed(
+    std::vector<mojom::AndroidAppInfoPtr> apps_info) {
+  std::vector<action::AndroidAppInfo> android_apps_info;
+  for (const auto& app_info : apps_info) {
+    android_apps_info.push_back({app_info->package_name, app_info->version,
+                                 app_info->localized_app_name,
+                                 app_info->intent});
+  }
+  display_connection_->OnAndroidAppListRefreshed(android_apps_info);
+}
+
 void AssistantManagerServiceImpl::UpdateInternalOptions(
     assistant_client::AssistantManagerInternal* assistant_manager_internal) {
   // Build user agent string.
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index 0cf7e09..d774237 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -90,7 +90,8 @@
       public assistant_client::AssistantManagerDelegate,
       public assistant_client::DeviceStateListener,
       public assistant_client::MediaManager::Listener,
-      public media_session::mojom::MediaControllerObserver {
+      public media_session::mojom::MediaControllerObserver,
+      public mojom::AppListEventSubscriber {
  public:
   // |service| owns this class and must outlive this class.
   AssistantManagerServiceImpl(
@@ -184,6 +185,10 @@
   void OnTimerSoundingStarted() override;
   void OnTimerSoundingFinished() override;
 
+  // mojom::AppListEventSubscriber overrides:
+  void OnAndroidAppListRefreshed(
+      std::vector<mojom::AndroidAppInfoPtr> apps_info) override;
+
   void UpdateInternalOptions(
       assistant_client::AssistantManagerInternal* assistant_manager_internal);
 
@@ -337,6 +342,8 @@
 
   bool start_finished_ = false;
 
+  mojo::Binding<mojom::AppListEventSubscriber> app_list_subscriber_binding_;
+
   base::WeakPtrFactory<AssistantManagerServiceImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantManagerServiceImpl);
diff --git a/chromeos/services/assistant/public/mojom/assistant.mojom b/chromeos/services/assistant/public/mojom/assistant.mojom
index 5259a6b..d63927d 100644
--- a/chromeos/services/assistant/public/mojom/assistant.mojom
+++ b/chromeos/services/assistant/public/mojom/assistant.mojom
@@ -187,6 +187,15 @@
   // Launch Android intent. The intent is encoded as a URI string.
   // See Intent.toUri().
   LaunchAndroidIntent(string intent);
+
+  // Register App list event subscriber.
+  AddAppListEventSubscriber(AppListEventSubscriber subscriber);
+};
+
+// Subscribes to App list events.
+interface AppListEventSubscriber {
+  // Called when the android app list changed.
+  OnAndroidAppListRefreshed(array<AndroidAppInfo> apps_info);
 };
 
 // Enumeration of possible completions for an Assistant interaction.
diff --git a/chromeos/services/assistant/service_unittest.cc b/chromeos/services/assistant/service_unittest.cc
index 0014cd7..abdf27a 100644
--- a/chromeos/services/assistant/service_unittest.cc
+++ b/chromeos/services/assistant/service_unittest.cc
@@ -143,6 +143,9 @@
       std::vector<chromeos::assistant::mojom::AndroidAppInfoPtr> apps_info,
       VerifyAndroidAppCallback callback) override {}
   void LaunchAndroidIntent(const std::string& intent) override {}
+  void AddAppListEventSubscriber(
+      chromeos::assistant::mojom::AppListEventSubscriberPtr subscriber)
+      override {}
 
   mojo::Binding<mojom::DeviceActions> binding_;
 
diff --git a/chromeos/services/network_config/BUILD.gn b/chromeos/services/network_config/BUILD.gn
index b7e14024..0df128d 100644
--- a/chromeos/services/network_config/BUILD.gn
+++ b/chromeos/services/network_config/BUILD.gn
@@ -19,6 +19,7 @@
     "//chromeos/services/network_config/public/mojom",
     "//components/device_event_log",
     "//components/onc",
+    "//components/proxy_config",
     "//services/service_manager/public/cpp",
   ]
 }
diff --git a/chromeos/services/network_config/DEPS b/chromeos/services/network_config/DEPS
index 273d26b..dfcefe2 100644
--- a/chromeos/services/network_config/DEPS
+++ b/chromeos/services/network_config/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+components/onc",
+  "+components/proxy_config",
 ]
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index e18a9a84..59ce8e7 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -5,10 +5,12 @@
 #include "chromeos/services/network_config/cros_network_config.h"
 
 #include "chromeos/network/device_state.h"
+#include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "chromeos/network/onc/onc_translation_tables.h"
+#include "chromeos/network/proxy/ui_proxy_config_service.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h"
 #include "components/device_event_log/device_event_log.h"
@@ -143,6 +145,17 @@
   result->prohibited_by_policy = network->blocked_by_policy();
   result->source = mojom::ONCSource(network->onc_source());
 
+  // NetworkHandler and UIProxyConfigService may not exist in tests.
+  UIProxyConfigService* ui_proxy_config_service =
+      NetworkHandler::IsInitialized()
+          ? NetworkHandler::Get()->ui_proxy_config_service()
+          : nullptr;
+  result->proxy_mode =
+      ui_proxy_config_service
+          ? mojom::ProxyMode(
+                ui_proxy_config_service->ProxyModeForNetwork(network))
+          : mojom::ProxyMode::kDirect;
+
   const NetworkState::CaptivePortalProviderInfo* captive_portal_provider =
       network->captive_portal_provider();
   if (captive_portal_provider) {
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index 63ba877..9861b61 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -153,6 +153,7 @@
         ASSERT_TRUE(result->vpn);
         EXPECT_EQ(mojom::VPNType::kL2TPIPsec, result->vpn->type);
       }));
+  // TODO(919691): Test ProxyMode once UIProxyConfigService logic is improved.
 }
 
 TEST_F(CrosNetworkConfigTest, GetNetworkStateList) {
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.mojom b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
index 753c864..5691f92 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -46,15 +46,6 @@
   kUnavailable,
 };
 
-// The ONC source for policy or imported networks.
-enum ONCSource {
-  kUnknown,
-  kNone,
-  kUserImport,
-  kDevicePolicy,
-  kUserPolicy,
-};
-
 // The network technology type. NOTE: 'All' and 'Wireless' are only used by
 // FilterType for requesting groups of networks.
 enum NetworkType {
@@ -71,6 +62,28 @@
   kWiMAX,
 };
 
+// The ONC source for policy or imported networks.
+enum ONCSource {
+  kUnknown,
+  kNone,
+  kUserImport,
+  kDevicePolicy,
+  kUserPolicy,
+};
+
+enum ProxyMode {
+  // Direct connection to the network.
+  kDirect,
+  // Try to retrieve a PAC script from http://wpad/wpad.dat.
+  kAutoDetect,
+  // Try to retrieve a PAC script from kProxyPacURL.
+  kPacScript,
+  // Use a specified list of servers.
+  kFixedServers,
+  // Use the system's proxy settings.
+  kSystem,
+};
+
 // The security type for WiFi Ethernet networks.
 enum SecurityType {
   kNone,
@@ -189,6 +202,9 @@
   string name;
   // The relative priority of the network. Larger values have higher priority.
   int32 priority;
+  // The proxy mode affecting this network. Includes any settings that affect
+  // this network (i.e. global proxy settings are also considered).
+  ProxyMode proxy_mode;
   // True for visible networks that are blocked / disallowed by policy.
   bool prohibited_by_policy = false;
   ONCSource source;
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.typemap b/chromeos/services/network_config/public/mojom/cros_network_config.typemap
index ad3154c..2410ad27 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.typemap
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.typemap
@@ -7,6 +7,7 @@
 
 public_deps = [
   "//components/onc",
+  "//components/proxy_config",
 ]
 
 public_headers = [ "//components/onc/onc_constants.h" ]
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc
index 3d8b4a64..89895d0 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc
+++ b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.cc
@@ -53,4 +53,52 @@
   return false;
 }
 
+chromeos::network_config::mojom::ProxyMode
+EnumTraits<chromeos::network_config::mojom::ProxyMode,
+           ProxyPrefs::ProxyMode>::ToMojom(ProxyPrefs::ProxyMode input) {
+  switch (input) {
+    case ProxyPrefs::MODE_DIRECT:
+      return chromeos::network_config::mojom::ProxyMode::kDirect;
+    case ProxyPrefs::MODE_AUTO_DETECT:
+      return chromeos::network_config::mojom::ProxyMode::kAutoDetect;
+    case ProxyPrefs::MODE_PAC_SCRIPT:
+      return chromeos::network_config::mojom::ProxyMode::kPacScript;
+    case ProxyPrefs::MODE_FIXED_SERVERS:
+      return chromeos::network_config::mojom::ProxyMode::kFixedServers;
+    case ProxyPrefs::MODE_SYSTEM:
+      return chromeos::network_config::mojom::ProxyMode::kSystem;
+    case ProxyPrefs::kModeCount:
+      break;
+  }
+
+  NOTREACHED();
+  return chromeos::network_config::mojom::ProxyMode::kDirect;
+}
+
+bool EnumTraits<chromeos::network_config::mojom::ProxyMode,
+                ProxyPrefs::ProxyMode>::
+    FromMojom(chromeos::network_config::mojom::ProxyMode input,
+              ProxyPrefs::ProxyMode* out) {
+  switch (input) {
+    case chromeos::network_config::mojom::ProxyMode::kDirect:
+      *out = ProxyPrefs::MODE_DIRECT;
+      return true;
+    case chromeos::network_config::mojom::ProxyMode::kAutoDetect:
+      *out = ProxyPrefs::MODE_AUTO_DETECT;
+      return true;
+    case chromeos::network_config::mojom::ProxyMode::kPacScript:
+      *out = ProxyPrefs::MODE_PAC_SCRIPT;
+      return true;
+    case chromeos::network_config::mojom::ProxyMode::kFixedServers:
+      *out = ProxyPrefs::MODE_FIXED_SERVERS;
+      return true;
+    case chromeos::network_config::mojom::ProxyMode::kSystem:
+      *out = ProxyPrefs::MODE_SYSTEM;
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
 }  // namespace mojo
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h
index 0a860c221..0ae9185 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h
+++ b/chromeos/services/network_config/public/mojom/cros_network_config_mojom_traits.h
@@ -7,6 +7,7 @@
 
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "components/onc/onc_constants.h"
+#include "components/proxy_config/proxy_prefs.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 
 namespace mojo {
@@ -20,6 +21,16 @@
                         onc::ONCSource* out);
 };
 
+template <>
+class EnumTraits<chromeos::network_config::mojom::ProxyMode,
+                 ProxyPrefs::ProxyMode> {
+ public:
+  static chromeos::network_config::mojom::ProxyMode ToMojom(
+      ProxyPrefs::ProxyMode input);
+  static bool FromMojom(chromeos::network_config::mojom::ProxyMode input,
+                        ProxyPrefs::ProxyMode* out);
+};
+
 }  // namespace mojo
 
 #endif  // CHROMEOS_SERVICES_NETWORK_CONFIG_PUBLIC_MOJOM_CROS_NETWORK_CONFIG_MOJOM_TRAITS_H_
diff --git a/components/OWNERS b/components/OWNERS
index 00c717f..c3e984d 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -13,6 +13,7 @@
 per-file error_page_strings.grdp=file://components/error_page/OWNERS
 per-file management_strings.grdp=file://docs/privacy/OWNERS
 per-file ntp_snippets_strings.grdp=file://components/ntp_snippets/OWNERS
+per-file ntp_tiles_strings.grdp=file://components/ntp_tiles/OWNERS
 per-file omnibox_strings.grdp=file://components/omnibox/OWNERS
 per-file page_info_strings.grdp=file://chrome/browser/ui/page_info/OWNERS
 per-file password_manager_strings.grdp=file://components/password_manager/OWNERS
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc
index 251884d..56f860b 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -8,7 +8,8 @@
 #include <utility>
 
 #include "ash/public/cpp/new_window_delegate.h"
-#include "ash/public/cpp/wallpaper_controller.h"
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "base/memory/weak_ptr.h"
@@ -228,7 +229,11 @@
 void ArcIntentHelperBridge::OpenWallpaperPicker() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   RecordOpenType(ArcIntentHelperOpenType::WALLPAPER_PICKER);
-  ash::WallpaperController::Get()->OpenWallpaperPickerIfAllowed();
+  ash::mojom::WallpaperControllerPtr wallpaper_controller_ptr;
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &wallpaper_controller_ptr);
+  wallpaper_controller_ptr->OpenWallpaperPickerIfAllowed();
 }
 
 void ArcIntentHelperBridge::SetWallpaperDeprecated(
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.cc b/components/autofill/core/browser/payments/credit_card_save_manager.cc
index 3a8e590..72368d45 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -103,8 +103,10 @@
 
   // Query the Autofill StrikeDatabase on if we should pop up the
   // offer-to-save prompt for this card.
-  OnDidGetStrikesForLocalSave(GetCreditCardSaveStrikeDatabase()->GetStrikes(
-      base::UTF16ToUTF8(local_card_save_candidate_.LastFourDigits())));
+  show_save_prompt_ =
+      !GetCreditCardSaveStrikeDatabase()->IsMaxStrikesLimitReached(
+          base::UTF16ToUTF8(local_card_save_candidate_.LastFourDigits()));
+  OfferCardLocalSave();
 }
 
 void CreditCardSaveManager::AttemptToOfferCardUploadSave(
@@ -236,6 +238,12 @@
   // All required data is available, start the upload process.
   if (observer_for_testing_)
     observer_for_testing_->OnDecideToRequestUploadSave();
+
+  // Query the Autofill StrikeDatabase on if we should pop up the
+  // offer-to-save prompt for this card.
+  show_save_prompt_ =
+      !GetCreditCardSaveStrikeDatabase()->IsMaxStrikesLimitReached(
+          base::UTF16ToUTF8(upload_request_.card.LastFourDigits()));
   payments_client_->GetUploadDetails(
       send_only_country_in_addresses ? country_only_profiles
                                      : upload_request_.profiles,
@@ -245,10 +253,6 @@
                      weak_ptr_factory_.GetWeakPtr()),
       payments::kUploadCardBillableServiceNumber,
       payments::PaymentsClient::UploadCardSource::UPSTREAM_CHECKOUT_FLOW);
-  // Query the Autofill StrikeDatabase on if we should pop up the
-  // offer-to-save prompt for this card.
-  OnDidGetStrikesForUploadSave(GetCreditCardSaveStrikeDatabase()->GetStrikes(
-      base::UTF16ToUTF8(upload_request_.card.LastFourDigits())));
 }
 
 bool CreditCardSaveManager::IsCreditCardUploadEnabled() {
@@ -345,26 +349,6 @@
   return local_card_migration_strike_database_.get();
 }
 
-void CreditCardSaveManager::OnDidGetStrikesForLocalSave(const int num_strikes) {
-  show_save_prompt_ =
-      num_strikes < kMaxStrikesToPreventPoppingUpOfferToSavePrompt;
-
-  OfferCardLocalSave();
-}
-
-void CreditCardSaveManager::OnDidGetStrikesForUploadSave(
-    const int num_strikes) {
-  show_save_prompt_ =
-      num_strikes < kMaxStrikesToPreventPoppingUpOfferToSavePrompt;
-
-  // Only offer upload once both Payments and the Autofill
-  // StrikeDatabase have returned their decisions. Use population of
-  // |upload_request_.context_token| as an indicator of the Payments call
-  // returning successfully.
-  if (!upload_request_.context_token.empty())
-    OfferCardUploadSave();
-}
-
 void CreditCardSaveManager::OnDidGetUploadDetails(
     AutofillClient::PaymentsRpcResult result,
     const base::string16& context_token,
@@ -393,13 +377,7 @@
     }
     upload_request_.context_token = context_token;
     legal_message_ = base::DictionaryValue::From(std::move(legal_message));
-
-    // Only offer upload once both Payments and the Autofill
-    // StrikeDatabase have returned their decisions. Use presence of
-    // |show_save_prompt_| as an indicator of StrikeDatabase retrieving
-    // its data.
-    if (show_save_prompt_.has_value())
-      OfferCardUploadSave();
+    OfferCardUploadSave();
   } else {
     // If the upload details request failed and we *know* we have all possible
     // information (card number, expiration, cvc, name, and address), fall back
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.h b/components/autofill/core/browser/payments/credit_card_save_manager.h
index f28c34a..0de7ae9 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.h
@@ -143,6 +143,10 @@
   friend class TestCreditCardSaveManager;
   friend class SaveCardBubbleViewsFullFormBrowserTest;
   friend class SaveCardInfobarEGTestHelper;
+  FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTest,
+                           StrikeDatabase_Local_FullFlowTest);
+  FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTest,
+                           StrikeDatabase_Upload_FullFlowTest);
 
   // Returns the CreditCardSaveStrikeDatabase for |client_|.
   CreditCardSaveStrikeDatabase* GetCreditCardSaveStrikeDatabase();
@@ -150,14 +154,6 @@
   // Returns the GetLocalCardMigrationStrikeDatabase for |client_|.
   LocalCardMigrationStrikeDatabase* GetLocalCardMigrationStrikeDatabase();
 
-  // Sets |show_save_prompt| and moves forward with offering credit card local
-  // save.
-  void OnDidGetStrikesForLocalSave(const int num_strikes);
-
-  // Sets |show_save_prompt| and moves forward with offering credit card upload
-  // if Payments has also returned a success response.
-  void OnDidGetStrikesForUploadSave(const int num_strikes);
-
   // Returns the legal message retrieved from Payments. On failure or not
   // meeting Payments's conditions for upload, |legal_message| will contain
   // nullptr. |supported_card_bin_ranges| is a list of BIN prefix ranges which
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 9d02f4b5..df9e83ea 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1189,8 +1189,6 @@
                                           type.GetStorableType(), 1,
                                           app_locale_, &labels);
   }
-  suggestion_selection::PrepareSuggestions(formatter != nullptr, labels,
-                                           &unique_suggestions);
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
   if (use_improved_label_disambiguation && !unique_suggestions.empty()) {
@@ -1199,6 +1197,9 @@
   }
 #endif  // #if !defined(OS_ANDROID) && !defined(OS_IOS)
 
+  suggestion_selection::PrepareSuggestions(formatter != nullptr, labels,
+                                           &unique_suggestions, comparator);
+
   return unique_suggestions;
 }
 
diff --git a/components/autofill/core/browser/ui/suggestion_selection.cc b/components/autofill/core/browser/ui/suggestion_selection.cc
index 0e5ae85..3d3ef80fa 100644
--- a/components/autofill/core/browser/ui/suggestion_selection.cc
+++ b/components/autofill/core/browser/ui/suggestion_selection.cc
@@ -4,6 +4,7 @@
 #include "components/autofill/core/browser/ui/suggestion_selection.h"
 
 #include <string>
+#include <unordered_set>
 #include <vector>
 
 #include "base/strings/string_util.h"
@@ -247,18 +248,56 @@
 
 void PrepareSuggestions(bool add_profile_icon,
                         const std::vector<base::string16>& labels,
-                        std::vector<Suggestion>* suggestions) {
+                        std::vector<Suggestion>* suggestions,
+                        const AutofillProfileComparator& comparator) {
   DCHECK_EQ(suggestions->size(), labels.size());
 
+  // This set is used to determine whether duplicate Suggestions exist. For
+  // example, a Suggestion with the value "John" and the label "400 Oak Rd" has
+  // the normalized text "john400oakrd". This text can only be added to the set
+  // once.
+  std::unordered_set<base::string16> suggestion_text;
+  size_t index_to_add_suggestion = 0;
+
+  // Dedupes Suggestions to show in the dropdown once values and labels have
+  // been created. This is useful when LabelFormatters make Suggestions' labels.
+  //
+  // Suppose profile A has the data John, 400 Oak Rd, and (617) 544-7411 and
+  // profile B has the data John, 400 Oak Rd, (508) 957-5009. If a formatter
+  // puts only 400 Oak Rd in the label, then there will be two Suggestions with
+  // the normalized text "john400oakrd", and the Suggestion with the lower
+  // ranking should be discarded.
   for (size_t i = 0; i < labels.size(); ++i) {
-    (*suggestions)[i].additional_label = labels[i];
-    (*suggestions)[i].label = labels[i];
+    base::string16 label = labels[i];
+
+    bool text_inserted =
+        suggestion_text
+            .insert(comparator.NormalizeForComparison(
+                (*suggestions)[i].value + label,
+                autofill::AutofillProfileComparator::DISCARD_WHITESPACE))
+            .second;
+
+    if (text_inserted) {
+      if (index_to_add_suggestion != i) {
+        (*suggestions)[index_to_add_suggestion] = (*suggestions)[i];
+      }
+      // The given |suggestions| are already sorted from highest to lowest
+      // ranking. Suggestions with lower indices have a higher ranking and
+      // should be kept.
+      (*suggestions)[index_to_add_suggestion].additional_label = labels[i];
+      (*suggestions)[index_to_add_suggestion].label = labels[i];
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
-    if (add_profile_icon) {
-      (*suggestions)[i].icon = "accountBoxIcon";
-    }
+      if (add_profile_icon) {
+        (*suggestions)[index_to_add_suggestion].icon = "accountBoxIcon";
+      }
 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
+      ++index_to_add_suggestion;
+    }
+  }
+
+  if (index_to_add_suggestion < suggestions->size()) {
+    suggestions->resize(index_to_add_suggestion);
   }
 }
 
diff --git a/components/autofill/core/browser/ui/suggestion_selection.h b/components/autofill/core/browser/ui/suggestion_selection.h
index 0867fb9c..cac66719 100644
--- a/components/autofill/core/browser/ui/suggestion_selection.h
+++ b/components/autofill/core/browser/ui/suggestion_selection.h
@@ -65,12 +65,16 @@
     std::vector<AutofillProfile*>* profiles);
 
 // Prepares a collection of Suggestions to show to the user. Adds |labels| to
-// their corresponding |suggestions|. A label corresponds to the suggestion with
-// the same index. Adds an icon on desktop platforms when |add_profile_icon| is
-// true.
+// their corresponding |suggestions| and removes duplicates, if any. A label
+// corresponds to the suggestion with the same index. Adds an icon on desktop
+// platforms when |add_profile_icon| is true.
+//
+// NOTE: |suggestions| are assumed to have already been sorted from most to
+// least important.
 void PrepareSuggestions(bool add_profile_icon,
                         const std::vector<base::string16>& labels,
-                        std::vector<Suggestion>* suggestions);
+                        std::vector<Suggestion>* suggestions,
+                        const AutofillProfileComparator& comparator);
 
 }  // namespace suggestion_selection
 }  // namespace autofill
diff --git a/components/autofill/core/browser/ui/suggestion_selection_unittest.cc b/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
index 2691c07..783cc0c 100644
--- a/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
+++ b/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
@@ -519,5 +519,62 @@
   }
 }
 
+TEST_F(SuggestionSelectionTest,
+       PrepareSuggestions_DiscardDuplicateSuggestions) {
+  std::vector<Suggestion> suggestions{
+      Suggestion(base::ASCIIToUTF16("Jon Snow")),
+      Suggestion(base::ASCIIToUTF16("Jon Snow")),
+      Suggestion(base::ASCIIToUTF16("Jon Snow")),
+      Suggestion(base::ASCIIToUTF16("Jon Snow"))};
+
+  const std::vector<base::string16> labels{
+      base::ASCIIToUTF16("2 Beyond-the-Wall Rd"),
+      base::ASCIIToUTF16("1 Winterfell Ln"),
+      base::ASCIIToUTF16("2 Beyond-the-Wall Rd"),
+      base::ASCIIToUTF16("2 Beyond-the-Wall Rd.")};
+
+  PrepareSuggestions(/*add_profile_icon=*/false, labels, &suggestions,
+                     comparator_);
+
+  // Suggestions are sorted from highest to lowest rank, so check that
+  // duplicates with a lower rank are removed.
+  EXPECT_THAT(
+      suggestions,
+      ElementsAre(
+          AllOf(Field(&Suggestion::value, base::ASCIIToUTF16("Jon Snow")),
+                Field(&Suggestion::label,
+                      base::ASCIIToUTF16("2 Beyond-the-Wall Rd"))),
+          AllOf(Field(&Suggestion::value, base::ASCIIToUTF16("Jon Snow")),
+                Field(&Suggestion::label,
+                      base::ASCIIToUTF16("1 Winterfell Ln")))));
+}
+
+TEST_F(SuggestionSelectionTest,
+       PrepareSuggestions_KeepNonDuplicateSuggestions) {
+  std::vector<Suggestion> suggestions{
+      Suggestion(base::ASCIIToUTF16("Sansa")),
+      Suggestion(base::ASCIIToUTF16("Sansa")),
+      Suggestion(base::ASCIIToUTF16("Brienne"))};
+
+  const std::vector<base::string16> labels{
+      base::ASCIIToUTF16("1 Winterfell Ln"), base::ASCIIToUTF16(""),
+      base::ASCIIToUTF16("1 Winterfell Ln")};
+
+  PrepareSuggestions(/*add_profile_icon=*/false, labels, &suggestions,
+                     comparator_);
+
+  EXPECT_THAT(
+      suggestions,
+      ElementsAre(
+          AllOf(
+              Field(&Suggestion::value, base::ASCIIToUTF16("Sansa")),
+              Field(&Suggestion::label, base::ASCIIToUTF16("1 Winterfell Ln"))),
+          AllOf(Field(&Suggestion::value, base::ASCIIToUTF16("Sansa")),
+                Field(&Suggestion::label, base::ASCIIToUTF16(""))),
+          AllOf(Field(&Suggestion::value, base::ASCIIToUTF16("Brienne")),
+                Field(&Suggestion::label,
+                      base::ASCIIToUTF16("1 Winterfell Ln")))));
+}
+
 }  // namespace suggestion_selection
 }  // namespace autofill
diff --git a/components/autofill/core/common/autofill_constants.h b/components/autofill/core/common/autofill_constants.h
index 59314c26..5e3b1955 100644
--- a/components/autofill/core/common/autofill_constants.h
+++ b/components/autofill/core/common/autofill_constants.h
@@ -36,14 +36,6 @@
   IS_PASSWORD_FIELD = 1 << 1 /* input field is a password field */
 };
 
-// Autofill StrikeDatabase: Maximum strikes allowed for the credit card
-// save project. If the StrikeDatabase returns this many strikes for a
-// given card, it will not show the offer-to-save bubble on Desktop or infobar
-// on Android. On Desktop, however, the omnibox icon will still be available.
-// TODO(crbug.com/884817): Remove once StrikeDatabase v2 moves this constant to
-// its own credit card save policy.
-const int kMaxStrikesToPreventPoppingUpOfferToSavePrompt = 3;
-
 // Constants for the soft/hard deletion of Autofill data.
 constexpr base::TimeDelta kDisusedDataModelTimeDelta =
     base::TimeDelta::FromDays(180);
diff --git a/components/bookmarks/browser/bookmark_node_data.cc b/components/bookmarks/browser/bookmark_node_data.cc
index 6a5b0e8f5..6801c22 100644
--- a/components/bookmarks/browser/bookmark_node_data.cc
+++ b/components/bookmarks/browser/bookmark_node_data.cc
@@ -97,13 +97,14 @@
       LOG(WARNING) << "children_count failed bounds check";
       return false;
     }
+    // Note: do not preallocate the children vector. A pickle could be
+    // constructed to contain N nested Elements. By continually recursing on
+    // this ReadFromPickle function, the fast-fail logic is subverted. Each
+    // child would claim it contains M more children. The first (and only) child
+    // provided would claim M more children. We would allocate N * M Elements
+    // along the way, while only receiving N Elements.
     const size_t children_count =
         base::checked_cast<size_t>(children_count_tmp);
-    // Restrict vector preallocation to prevent OOM crashes on invalid (or
-    // malicious) pickles.
-    if (children_count > kMaxVectorPreallocateSize)
-      LOG(WARNING) << "children_count exceeds kMaxVectorPreallocateSize";
-    children.reserve(std::min(children_count, kMaxVectorPreallocateSize));
     for (size_t i = 0; i < children_count; ++i) {
       children.emplace_back();
       if (!children.back().ReadFromPickle(iterator))
diff --git a/components/certificate_transparency/BUILD.gn b/components/certificate_transparency/BUILD.gn
index 604177a7..3eedd649 100644
--- a/components/certificate_transparency/BUILD.gn
+++ b/components/certificate_transparency/BUILD.gn
@@ -10,20 +10,8 @@
     "chrome_require_ct_delegate.h",
     "ct_known_logs.cc",
     "ct_known_logs.h",
-    "features.cc",
-    "features.h",
-    "log_dns_client.cc",
-    "log_dns_client.h",
     "pref_names.cc",
     "pref_names.h",
-    "single_tree_tracker.cc",
-    "single_tree_tracker.h",
-    "sth_distributor.cc",
-    "sth_distributor.h",
-    "sth_observer.h",
-    "sth_reporter.h",
-    "tree_state_tracker.cc",
-    "tree_state_tracker.h",
   ]
 
   deps = [
@@ -44,12 +32,6 @@
     "chrome_ct_policy_enforcer_unittest.cc",
     "chrome_require_ct_delegate_unittest.cc",
     "ct_known_logs_unittest.cc",
-    "log_dns_client_unittest.cc",
-    "mock_log_dns_traffic.cc",
-    "mock_log_dns_traffic.h",
-    "single_tree_tracker_unittest.cc",
-    "sth_distributor_unittest.cc",
-    "tree_state_tracker_unittest.cc",
   ]
   deps = [
     ":certificate_transparency",
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.cc b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
index 7a2f2f71..43f3ae0 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "base/version.h"
@@ -109,6 +110,7 @@
     std::vector<std::string> operated_by_google_logs)
     : disqualified_logs_(disqualified_logs),
       operated_by_google_logs_(operated_by_google_logs),
+      clock_(base::DefaultClock::GetInstance()),
       log_list_date_(log_list_date) {}
 
 ChromeCTPolicyEnforcer::~ChromeCTPolicyEnforcer() {}
@@ -163,7 +165,7 @@
 
 bool ChromeCTPolicyEnforcer::IsLogDataTimely() const {
   // We consider built-in information to be timely for 10 weeks.
-  return (base::Time::Now() - log_list_date_).InDays() < 70 /* 10 weeks */;
+  return (clock_->Now() - log_list_date_).InDays() < 70 /* 10 weeks */;
 }
 
 // Evaluates against the policy specified at
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.h b/components/certificate_transparency/chrome_ct_policy_enforcer.h
index 989d3e6..053cbacd 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.h
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.h
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/time/clock.h"
 #include "base/time/time.h"
 #include "net/cert/ct_policy_enforcer.h"
 
@@ -42,6 +43,8 @@
       const net::ct::SCTList& verified_scts,
       const net::NetLogWithSource& net_log) override;
 
+  void SetClockForTesting(const base::Clock* clock) { clock_ = clock; }
+
  private:
   // Returns true if the log identified by |log_id| (the SHA-256 hash of the
   // log's DER-encoded SPKI) has been disqualified, and sets
@@ -68,6 +71,8 @@
   // List of SHA-256(SPKI) for logs operated by Google.
   std::vector<std::string> operated_by_google_logs_;
 
+  const base::Clock* clock_;
+
   // The time at which |disqualified_logs_| and |operated_by_google_logs_| were
   // generated.
   const base::Time log_list_date_;
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
index 49bf2ad..d072578 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/build_time.h"
 #include "base/stl_util.h"
+#include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
 #include "base/version.h"
 #include "components/certificate_transparency/ct_known_logs.h"
@@ -44,9 +45,10 @@
 class ChromeCTPolicyEnforcerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    policy_enforcer_.reset(
-        new ChromeCTPolicyEnforcer(base::GetBuildTime(), GetDisqualifiedLogs(),
-                                   GetLogsOperatedByGoogle()));
+    auto enforcer = std::make_unique<ChromeCTPolicyEnforcer>(
+        base::GetBuildTime(), GetDisqualifiedLogs(), GetLogsOperatedByGoogle());
+    enforcer->SetClockForTesting(&clock_);
+    policy_enforcer_.reset(enforcer.release());
 
     std::string der_test_cert(net::ct::GetDerEncodedX509Cert());
     chain_ = X509Certificate::CreateFromBytes(der_test_cert.data(),
@@ -54,6 +56,7 @@
     ASSERT_TRUE(chain_.get());
     google_log_id_ = std::string(kGoogleAviatorLogID, crypto::kSHA256Length);
     non_google_log_id_.assign(crypto::kSHA256Length, 1);
+    clock_.SetNow(base::Time::Now());
   }
 
   void FillListWithSCTsOfOrigin(
@@ -126,6 +129,7 @@
   }
 
  protected:
+  base::SimpleTestClock clock_;
   std::unique_ptr<net::CTPolicyEnforcer> policy_enforcer_;
   scoped_refptr<X509Certificate> chain_;
   std::string google_log_id_;
@@ -183,6 +187,22 @@
                                               NetLogWithSource()));
 }
 
+TEST_F(ChromeCTPolicyEnforcerTest, EnforcementDisabledByBinaryAge) {
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, &scts);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+
+  clock_.Advance(base::TimeDelta::FromDays(71));
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
 TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithEmbeddedSCTs) {
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
   SCTList scts;
diff --git a/components/certificate_transparency/features.cc b/components/certificate_transparency/features.cc
deleted file mode 100644
index 949ef35b..0000000
--- a/components/certificate_transparency/features.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/features.h"
-
-namespace certificate_transparency {
-
-// Enables or disables auditing Certificate Transparency logs over DNS.
-const base::Feature kCTLogAuditing = {"CertificateTransparencyLogAuditing",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/features.h b/components/certificate_transparency/features.h
deleted file mode 100644
index 835cdab9..0000000
--- a/components/certificate_transparency/features.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_FEATURES_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_FEATURES_H_
-
-#include "base/feature_list.h"
-
-namespace certificate_transparency {
-
-extern const base::Feature kCTLogAuditing;
-
-}  // namespace certificate_transparency
-
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_FEATURES_H_
diff --git a/components/certificate_transparency/log_dns_client.cc b/components/certificate_transparency/log_dns_client.cc
deleted file mode 100644
index 695c096..0000000
--- a/components/certificate_transparency/log_dns_client.cc
+++ /dev/null
@@ -1,612 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/log_dns_client.h"
-
-#include "base/big_endian.h"
-#include "base/bind.h"
-#include "base/format_macros.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/base32/base32.h"
-#include "crypto/sha2.h"
-#include "net/base/completion_once_callback.h"
-#include "net/base/sys_addrinfo.h"
-#include "net/cert/merkle_audit_proof.h"
-#include "net/dns/dns_client.h"
-#include "net/dns/dns_config.h"
-#include "net/dns/dns_response.h"
-#include "net/dns/dns_transaction.h"
-#include "net/dns/dns_util.h"
-#include "net/dns/public/dns_protocol.h"
-#include "net/dns/record_parsed.h"
-#include "net/dns/record_rdata.h"
-
-namespace certificate_transparency {
-
-namespace {
-
-void LogQueryDuration(net::Error error, const base::TimeDelta& duration) {
-  UMA_HISTOGRAM_MEDIUM_TIMES("Net.CertificateTransparency.DnsQueryDuration",
-                             duration);
-
-  if (error == net::OK) {
-    UMA_HISTOGRAM_MEDIUM_TIMES(
-        "Net.CertificateTransparency.DnsQueryDuration.Success", duration);
-  }
-}
-
-void LogQueryResult(const std::string& name,
-                    net::Error error,
-                    const net::DnsResponse* response) {
-  base::UmaHistogramSparse(
-      base::StrCat({"Net.CertificateTransparency.DnsQuery", name, "Error"}),
-      -error);
-
-  if (response) {
-    base::UmaHistogramSparse(
-        base::StrCat({"Net.CertificateTransparency.DnsQuery", name, "Rcode"}),
-        response->rcode());
-  }
-}
-
-// Returns an EDNS option that disables the client subnet extension, as
-// described in https://tools.ietf.org/html/rfc7871. This is to avoid the
-// privacy issues caused by this extension being enabled in recursive resolvers
-// used by this DNS client (see the "Privacy Note" in RFC7871).
-net::OptRecordRdata::Opt OptToDisableClientSubnetExtension() {
-  const uint16_t kClientSubnetExtensionCode = 8;
-  // https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml
-  const uint16_t kIanaAddressFamilyIpV4 = 1;
-
-  char buf[4];
-  base::BigEndianWriter writer(buf, base::size(buf));
-  // family - address is empty so the value of this is irrelevant, so long as
-  // it's valid (see https://tools.ietf.org/html/rfc7871#section-7.1.2).
-  writer.WriteU16(kIanaAddressFamilyIpV4);
-  // source prefix length - 0 to disable this extension.
-  writer.WriteU8(0);
-  // scope prefix length - must be 0 for queries.
-  writer.WriteU8(0);
-  // no address - don't want a client subnet in the query.
-
-  return net::OptRecordRdata::Opt(kClientSubnetExtensionCode,
-                                  base::StringPiece(buf, base::size(buf)));
-}
-
-// Parses the DNS response and extracts a single string from the TXT RDATA.
-// If the response is malformed, not a TXT record, or contains any number of
-// strings other than 1, this returns false and extracts nothing.
-// Otherwise, it returns true and the extracted string is assigned to |*txt|.
-bool ParseTxtResponse(const net::DnsResponse& response, std::string* txt) {
-  DCHECK(txt);
-
-  net::DnsRecordParser parser = response.Parser();
-  // We don't care about the creation time, since we're going to throw
-  // |parsed_record| away as soon as we've extracted the payload, so provide
-  // the "null" time.
-  auto parsed_record = net::RecordParsed::CreateFrom(&parser, base::Time());
-  if (!parsed_record)
-    return false;
-
-  auto* txt_record = parsed_record->rdata<net::TxtRecordRdata>();
-  if (!txt_record)
-    return false;
-
-  // The draft CT-over-DNS RFC says that there MUST be exactly one string in the
-  // TXT record.
-  if (txt_record->texts().size() != 1)
-    return false;
-
-  *txt = txt_record->texts().front();
-  return true;
-}
-
-// Extracts a leaf index value from a DNS response's TXT RDATA.
-// Returns true on success, false otherwise.
-bool ParseLeafIndex(const net::DnsResponse& response, uint64_t* index) {
-  DCHECK(index);
-
-  std::string index_str;
-  if (!ParseTxtResponse(response, &index_str))
-    return false;
-
-  return base::StringToUint64(index_str, index);
-}
-
-// Extracts audit proof nodes from a DNS response's TXT RDATA.
-// Returns true on success, false otherwise.
-// It will fail if there is not a whole number of nodes present > 0.
-// There must only be one string in the TXT RDATA.
-// The nodes will be appended to |proof->nodes|
-bool ParseAuditPath(const net::DnsResponse& response,
-                    net::ct::MerkleAuditProof* proof) {
-  DCHECK(proof);
-
-  std::string audit_path;
-  if (!ParseTxtResponse(response, &audit_path))
-    return false;
-  // If empty or not a multiple of the node size, it is considered invalid.
-  // It's important to consider empty audit paths as invalid, as otherwise an
-  // infinite loop could occur if the server consistently returned empty
-  // responses.
-  if (audit_path.empty() || audit_path.size() % crypto::kSHA256Length != 0)
-    return false;
-
-  for (size_t i = 0; i < audit_path.size(); i += crypto::kSHA256Length) {
-    proof->nodes.push_back(audit_path.substr(i, crypto::kSHA256Length));
-  }
-
-  return true;
-}
-
-}  // namespace
-
-// Encapsulates the state machine required to get an audit proof from a Merkle
-// leaf hash. This requires a DNS request to obtain the leaf index, then a
-// series of DNS requests to get the nodes of the proof.
-class AuditProofQueryImpl : public LogDnsClient::AuditProofQuery {
- public:
-  // The API contract of LogDnsClient requires that callers make sure the
-  // AuditProofQuery does not outlive LogDnsClient, so it's safe to leave
-  // ownership of |dns_client| with LogDnsClient.
-  AuditProofQueryImpl(net::DnsClient* dns_client,
-                      net::URLRequestContext* url_request_context,
-                      const std::string& domain_for_log,
-                      const net::NetLogWithSource& net_log);
-
-  ~AuditProofQueryImpl() override;
-
-  // Begins the process of getting an audit proof for the CT log entry with a
-  // leaf hash of |leaf_hash|. If |lookup_securely| is true, only secure DNS
-  // lookups will be performed, otherwise only insecure DNS lookups will be
-  // performed. The proof will be for a tree of size |tree_size|. If the proof
-  // cannot be obtained synchronously, net::ERR_IO_PENDING will be returned and
-  // |callback| will be invoked when the operation has completed asynchronously.
-  // If the operation is cancelled (by deleting the AuditProofQueryImpl),
-  // |cancellation_callback| will be invoked.
-  net::Error Start(std::string leaf_hash,
-                   bool lookup_securely,
-                   uint64_t tree_size,
-                   net::CompletionOnceCallback callback,
-                   base::OnceClosure cancellation_callback);
-
-  // Returns the proof that is being obtained by this query.
-  // It is only guaranteed to be populated once either Start() returns net::OK
-  // or the completion callback is invoked with net::OK.
-  const net::ct::MerkleAuditProof& GetProof() const override;
-
- private:
-  enum class State {
-    NONE,
-    REQUEST_LEAF_INDEX,
-    REQUEST_LEAF_INDEX_COMPLETE,
-    REQUEST_AUDIT_PROOF_NODES,
-    REQUEST_AUDIT_PROOF_NODES_COMPLETE,
-  };
-
-  net::Error DoLoop(net::Error result);
-
-  // When a DnsTransaction completes, store the response and resume the state
-  // machine. It is safe to store a pointer to |response| because |transaction|
-  // is kept alive in |current_dns_transaction_|.
-  void OnDnsTransactionComplete(net::DnsTransaction* transaction,
-                                int net_error,
-                                const net::DnsResponse* response,
-                                bool secure);
-
-  // Requests the leaf index for the CT log entry with |leaf_hash_|.
-  net::Error RequestLeafIndex();
-
-  // Stores the received leaf index in |proof_->leaf_index|.
-  // If successful, the audit proof nodes will be requested next.
-  net::Error RequestLeafIndexComplete(net::Error result);
-
-  // Requests the next batch of audit proof nodes from a CT log.
-  // The index of the first node required is determined by looking at how many
-  // nodes are already in |proof_->nodes|.
-  // The CT log may return up to 7 nodes - this is the maximum allowed by the
-  // CT-over-DNS draft RFC, as a TXT RDATA string can have a maximum length of
-  // 255 bytes and each node is 32 bytes long (a SHA-256 hash).
-  //
-  // The performance of this could be improved by sending all of the expected
-  // requests up front. Each response can contain a maximum of 7 audit path
-  // nodes, so for an audit proof of size 20, it could send 3 queries (for nodes
-  // 0-6, 7-13 and 14-19) immediately. Currently, it sends only the first and
-  // then, based on the number of nodes received, sends the next query.
-  // The complexity of the code would increase though, as it would need to
-  // detect gaps in the audit proof caused by the server not responding with the
-  // anticipated number of nodes. It would also undermine LogDnsClient's ability
-  // to rate-limit DNS requests.
-  net::Error RequestAuditProofNodes();
-
-  // Appends the received audit proof nodes to |proof_->nodes|.
-  // If any nodes are missing, another request will follow this one.
-  net::Error RequestAuditProofNodesComplete(net::Error result);
-
-  // Sends a TXT record request for the domain |qname|.
-  // Returns true if the request could be started.
-  // OnDnsTransactionComplete() will be invoked with the result of the request.
-  bool StartDnsTransaction(const std::string& qname);
-
-  // The next state that this query will enter.
-  State next_state_;
-  // The DNS domain of the CT log that is being queried.
-  std::string domain_for_log_;
-  // The Merkle leaf hash of the CT log entry an audit proof is required for.
-  std::string leaf_hash_;
-  // Whether the DNS request should be sent securely or insecurely.
-  bool lookup_securely_;
-  // The audit proof to populate.
-  net::ct::MerkleAuditProof proof_;
-  // The callback to invoke when the query is complete.
-  net::CompletionOnceCallback callback_;
-  // The callback to invoke when the query is cancelled.
-  base::OnceClosure cancellation_callback_;
-  // The DnsClient to use for sending DNS requests to the CT log.
-  net::DnsClient* dns_client_;
-  // The URLRequestContext to use for sending DoH requests to the CT log.
-  net::URLRequestContext* url_request_context_;
-  // The most recent DNS request. Null if no DNS requests have been made.
-  std::unique_ptr<net::DnsTransaction> current_dns_transaction_;
-  // The most recent DNS response. Only valid so long as the corresponding DNS
-  // request is stored in |current_dns_transaction_|.
-  const net::DnsResponse* last_dns_response_;
-  // The NetLog that DNS transactions will log to.
-  net::NetLogWithSource net_log_;
-  // The time that Start() was last called. Used to measure query duration.
-  base::TimeTicks start_time_;
-  // Produces WeakPtrs to |this| for binding callbacks.
-  base::WeakPtrFactory<AuditProofQueryImpl> weak_ptr_factory_;
-};
-
-AuditProofQueryImpl::AuditProofQueryImpl(
-    net::DnsClient* dns_client,
-    net::URLRequestContext* url_request_context,
-    const std::string& domain_for_log,
-    const net::NetLogWithSource& net_log)
-    : next_state_(State::NONE),
-      domain_for_log_(domain_for_log),
-      dns_client_(dns_client),
-      url_request_context_(url_request_context),
-      last_dns_response_(nullptr),
-      net_log_(net_log),
-      weak_ptr_factory_(this) {
-  DCHECK(dns_client_);
-  DCHECK(!domain_for_log_.empty());
-}
-
-AuditProofQueryImpl::~AuditProofQueryImpl() {
-  if (next_state_ != State::NONE)
-    std::move(cancellation_callback_).Run();
-}
-
-// |leaf_hash| is not a const-ref to allow callers to std::move that string into
-// the method, avoiding the need to make a copy.
-net::Error AuditProofQueryImpl::Start(std::string leaf_hash,
-                                      bool lookup_securely,
-                                      uint64_t tree_size,
-                                      net::CompletionOnceCallback callback,
-                                      base::OnceClosure cancellation_callback) {
-  // It should not already be in progress.
-  DCHECK_EQ(State::NONE, next_state_);
-  start_time_ = base::TimeTicks::Now();
-  proof_.tree_size = tree_size;
-  leaf_hash_ = std::move(leaf_hash);
-  lookup_securely_ = lookup_securely;
-  callback_ = std::move(callback);
-  cancellation_callback_ = std::move(cancellation_callback);
-  // The first step in the query is to request the leaf index corresponding to
-  // |leaf_hash| from the CT log.
-  next_state_ = State::REQUEST_LEAF_INDEX;
-  // Begin the state machine.
-  return DoLoop(net::OK);
-}
-
-const net::ct::MerkleAuditProof& AuditProofQueryImpl::GetProof() const {
-  return proof_;
-}
-
-net::Error AuditProofQueryImpl::DoLoop(net::Error result) {
-  CHECK_NE(State::NONE, next_state_);
-  State state;
-  do {
-    state = next_state_;
-    next_state_ = State::NONE;
-    switch (state) {
-      case State::REQUEST_LEAF_INDEX:
-        result = RequestLeafIndex();
-        break;
-      case State::REQUEST_LEAF_INDEX_COMPLETE:
-        result = RequestLeafIndexComplete(result);
-        if (result == net::OK)
-          LogQueryResult("LeafIndex", net::OK, last_dns_response_);
-        break;
-      case State::REQUEST_AUDIT_PROOF_NODES:
-        result = RequestAuditProofNodes();
-        break;
-      case State::REQUEST_AUDIT_PROOF_NODES_COMPLETE:
-        result = RequestAuditProofNodesComplete(result);
-        break;
-      case State::NONE:
-        NOTREACHED();
-        break;
-    }
-  } while (result != net::ERR_IO_PENDING && next_state_ != State::NONE);
-
-  if (result != net::ERR_IO_PENDING) {
-    // If the query is complete, log some metrics.
-    LogQueryDuration(result, base::TimeTicks::Now() - start_time_);
-    switch (state) {
-      case State::REQUEST_LEAF_INDEX:
-      case State::REQUEST_LEAF_INDEX_COMPLETE:
-        // An error must have occurred if the query completed in this state.
-        LogQueryResult("LeafIndex", result, last_dns_response_);
-        break;
-      case State::REQUEST_AUDIT_PROOF_NODES:
-      case State::REQUEST_AUDIT_PROOF_NODES_COMPLETE:
-        // The query may have completed successfully.
-        LogQueryResult("AuditProof", result, last_dns_response_);
-        break;
-      case State::NONE:
-        NOTREACHED();
-        break;
-    }
-  }
-
-  return result;
-}
-
-void AuditProofQueryImpl::OnDnsTransactionComplete(
-    net::DnsTransaction* transaction,
-    int net_error,
-    const net::DnsResponse* response,
-    bool secure) {
-  DCHECK_EQ(current_dns_transaction_.get(), transaction);
-  last_dns_response_ = response;
-  net::Error result = DoLoop(static_cast<net::Error>(net_error));
-
-  // If DoLoop() indicates that I/O is pending, don't invoke the completion
-  // callback. OnDnsTransactionComplete() will be invoked again once the I/O
-  // is complete, and can invoke the completion callback then if appropriate.
-  if (result != net::ERR_IO_PENDING) {
-    // The callback may delete this query (now that it has finished), so copy
-    // |callback_| before running it so that it is not deleted along with the
-    // query, mid-callback-execution (which would result in a crash).
-    std::move(callback_).Run(result);
-  }
-}
-
-net::Error AuditProofQueryImpl::RequestLeafIndex() {
-  std::string encoded_leaf_hash = base32::Base32Encode(
-      leaf_hash_, base32::Base32EncodePolicy::OMIT_PADDING);
-  DCHECK_EQ(encoded_leaf_hash.size(), 52u);
-
-  std::string qname = base::StringPrintf(
-      "%s.hash.%s.", encoded_leaf_hash.c_str(), domain_for_log_.c_str());
-
-  if (!StartDnsTransaction(qname)) {
-    return net::ERR_NAME_RESOLUTION_FAILED;
-  }
-
-  next_state_ = State::REQUEST_LEAF_INDEX_COMPLETE;
-  return net::ERR_IO_PENDING;
-}
-
-// Stores the received leaf index in |proof_->leaf_index|.
-// If successful, the audit proof nodes will be requested next.
-net::Error AuditProofQueryImpl::RequestLeafIndexComplete(net::Error result) {
-  if (result != net::OK) {
-    return result;
-  }
-
-  DCHECK(last_dns_response_);
-  if (!ParseLeafIndex(*last_dns_response_, &proof_.leaf_index)) {
-    return net::ERR_DNS_MALFORMED_RESPONSE;
-  }
-
-  // Reject leaf index if it is out-of-range.
-  // This indicates either:
-  // a) the wrong tree_size was provided.
-  // b) the wrong leaf hash was provided.
-  // c) there is a bug server-side.
-  // The first two are more likely, so return ERR_INVALID_ARGUMENT.
-  if (proof_.leaf_index >= proof_.tree_size) {
-    return net::ERR_INVALID_ARGUMENT;
-  }
-
-  next_state_ = State::REQUEST_AUDIT_PROOF_NODES;
-  return net::OK;
-}
-
-net::Error AuditProofQueryImpl::RequestAuditProofNodes() {
-  // Test pre-conditions (should be guaranteed by DNS response validation).
-  if (proof_.leaf_index >= proof_.tree_size ||
-      proof_.nodes.size() >= net::ct::CalculateAuditPathLength(
-                                 proof_.leaf_index, proof_.tree_size)) {
-    return net::ERR_UNEXPECTED;
-  }
-
-  std::string qname = base::StringPrintf(
-      "%zu.%" PRIu64 ".%" PRIu64 ".tree.%s.", proof_.nodes.size(),
-      proof_.leaf_index, proof_.tree_size, domain_for_log_.c_str());
-
-  if (!StartDnsTransaction(qname)) {
-    return net::ERR_NAME_RESOLUTION_FAILED;
-  }
-
-  next_state_ = State::REQUEST_AUDIT_PROOF_NODES_COMPLETE;
-  return net::ERR_IO_PENDING;
-}
-
-net::Error AuditProofQueryImpl::RequestAuditProofNodesComplete(
-    net::Error result) {
-  if (result != net::OK) {
-    return result;
-  }
-
-  const uint64_t audit_path_length =
-      net::ct::CalculateAuditPathLength(proof_.leaf_index, proof_.tree_size);
-
-  // The calculated |audit_path_length| can't ever be greater than 64, so
-  // deriving the amount of memory to reserve from the untrusted |leaf_index|
-  // is safe.
-  proof_.nodes.reserve(audit_path_length);
-
-  DCHECK(last_dns_response_);
-  if (!ParseAuditPath(*last_dns_response_, &proof_)) {
-    return net::ERR_DNS_MALFORMED_RESPONSE;
-  }
-
-  // Keep requesting more proof nodes until all of them are received.
-  if (proof_.nodes.size() < audit_path_length) {
-    next_state_ = State::REQUEST_AUDIT_PROOF_NODES;
-  }
-
-  return net::OK;
-}
-
-bool AuditProofQueryImpl::StartDnsTransaction(const std::string& qname) {
-  net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory();
-  if (!factory) {
-    return false;
-  }
-
-  last_dns_response_ = nullptr;
-  DCHECK(url_request_context_);
-  current_dns_transaction_ = factory->CreateTransaction(
-      qname, net::dns_protocol::kTypeTXT,
-      base::BindOnce(&AuditProofQueryImpl::OnDnsTransactionComplete,
-                     weak_ptr_factory_.GetWeakPtr()),
-      net_log_,
-      lookup_securely_ ? net::SecureDnsMode::SECURE : net::SecureDnsMode::OFF,
-      url_request_context_);
-
-  current_dns_transaction_->Start();
-  return true;
-}
-
-LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client,
-                           net::URLRequestContext* url_request_context,
-                           const net::NetLogWithSource& net_log,
-                           size_t max_in_flight_queries)
-    : dns_client_(std::move(dns_client)),
-      url_request_context_(url_request_context),
-      net_log_(net_log),
-      in_flight_queries_(0),
-      max_in_flight_queries_(max_in_flight_queries) {
-  CHECK(dns_client_);
-  net::NetworkChangeNotifier::AddDNSObserver(this);
-  UpdateDnsConfig();
-}
-
-LogDnsClient::~LogDnsClient() {
-  net::NetworkChangeNotifier::RemoveDNSObserver(this);
-}
-
-void LogDnsClient::OnDNSChanged() {
-  UpdateDnsConfig();
-}
-
-void LogDnsClient::OnInitialDNSConfigRead() {
-  UpdateDnsConfig();
-}
-
-void LogDnsClient::NotifyWhenNotThrottled(base::OnceClosure callback) {
-  DCHECK(HasMaxQueriesInFlight());
-  not_throttled_callbacks_.emplace_back(std::move(callback));
-}
-
-// |leaf_hash| is not a const-ref to allow callers to std::move that string into
-// the method, avoiding LogDnsClient::AuditProofQuery having to make a copy.
-net::Error LogDnsClient::QueryAuditProof(
-    base::StringPiece domain_for_log,
-    std::string leaf_hash,
-    bool lookup_securely,
-    uint64_t tree_size,
-    std::unique_ptr<AuditProofQuery>* out_query,
-    net::CompletionOnceCallback callback) {
-  DCHECK(out_query);
-
-  if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) {
-    return net::ERR_INVALID_ARGUMENT;
-  }
-
-  if (HasMaxQueriesInFlight()) {
-    return net::ERR_TEMPORARILY_THROTTLED;
-  }
-
-  auto* query = new AuditProofQueryImpl(dns_client_.get(), url_request_context_,
-                                        domain_for_log.as_string(), net_log_);
-  out_query->reset(query);
-
-  ++in_flight_queries_;
-
-  return query->Start(
-      std::move(leaf_hash), lookup_securely, tree_size,
-      base::BindOnce(&LogDnsClient::QueryAuditProofComplete,
-                     base::Unretained(this), std::move(callback)),
-      base::BindOnce(&LogDnsClient::QueryAuditProofCancelled,
-                     base::Unretained(this)));
-}
-
-void LogDnsClient::QueryAuditProofComplete(
-    net::CompletionOnceCallback completion_callback,
-    int net_error) {
-  --in_flight_queries_;
-
-  // Move the "not throttled" callbacks to a local variable, just in case one of
-  // the callbacks deletes this LogDnsClient.
-  std::list<base::OnceClosure> not_throttled_callbacks =
-      std::move(not_throttled_callbacks_);
-
-  std::move(completion_callback).Run(net_error);
-
-  // Notify interested parties that the next query will not be throttled.
-  for (auto& callback : not_throttled_callbacks) {
-    std::move(callback).Run();
-  }
-}
-
-void LogDnsClient::QueryAuditProofCancelled() {
-  --in_flight_queries_;
-
-  // Move not_throttled_callbacks_ to a local variable, just in case one of the
-  // callbacks deletes this LogDnsClient.
-  std::list<base::OnceClosure> not_throttled_callbacks =
-      std::move(not_throttled_callbacks_);
-
-  // Notify interested parties that the next query will not be throttled.
-  for (auto& callback : not_throttled_callbacks) {
-    std::move(callback).Run();
-  }
-}
-
-bool LogDnsClient::HasMaxQueriesInFlight() const {
-  return max_in_flight_queries_ != 0 &&
-         in_flight_queries_ >= max_in_flight_queries_;
-}
-
-void LogDnsClient::UpdateDnsConfig() {
-  net::DnsConfig config;
-  net::NetworkChangeNotifier::GetDnsConfig(&config);
-  if (config.IsValid())
-    dns_client_->SetConfig(config);
-
-  net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory();
-  if (factory) {
-    factory->AddEDNSOption(OptToDisableClientSubnetExtension());
-  }
-}
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/log_dns_client.h b/components/certificate_transparency/log_dns_client.h
deleted file mode 100644
index 091e9873..0000000
--- a/components/certificate_transparency/log_dns_client.h
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_LOG_DNS_CLIENT_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_LOG_DNS_CLIENT_H_
-
-#include <stdint.h>
-
-#include <list>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_piece.h"
-#include "net/base/completion_once_callback.h"
-#include "net/base/net_errors.h"
-#include "net/base/network_change_notifier.h"
-#include "net/log/net_log_with_source.h"
-#include "net/url_request/url_request_context.h"
-
-namespace net {
-class DnsClient;
-namespace ct {
-struct MerkleAuditProof;
-}  // namespace ct
-}  // namespace net
-
-namespace certificate_transparency {
-
-// Queries Certificate Transparency (CT) log servers via DNS.
-// All queries are performed asynchronously.
-// For more information, see
-// https://github.com/google/certificate-transparency-rfcs/blob/master/dns/draft-ct-over-dns.md.
-// It must be created and deleted on the same thread. It is not thread-safe.
-class LogDnsClient : public net::NetworkChangeNotifier::DNSObserver {
- public:
-  class AuditProofQuery {
-   public:
-    virtual ~AuditProofQuery() = default;
-    virtual const net::ct::MerkleAuditProof& GetProof() const = 0;
-  };
-
-  // Creates a log client that will take ownership of |dns_client| and use it
-  // to perform DNS queries. Queries will be logged to |net_log|.
-  // The |dns_client| does not need to be configured first - this will be done
-  // automatically as needed.
-  // A limit can be set on the number of concurrent DNS queries by providing a
-  // positive value for |max_concurrent_queries|. Queries that would exceed this
-  // limit will fail with net::TEMPORARILY_THROTTLED. Setting this to 0 will
-  // disable this limit.
-  LogDnsClient(std::unique_ptr<net::DnsClient> dns_client,
-               net::URLRequestContext* url_request_context,
-               const net::NetLogWithSource& net_log,
-               size_t max_concurrent_queries);
-  // Must be deleted on the same thread that it was created on.
-  ~LogDnsClient() override;
-
-  // Called by NetworkChangeNotifier when the DNS config changes.
-  // The DnsClient's config will be updated in response.
-  void OnDNSChanged() override;
-
-  // Called by NetworkChangeNotifier when the DNS config is first read.
-  // The DnsClient's config will be updated in response.
-  void OnInitialDNSConfigRead() override;
-
-  // Registers a callback to be invoked when the number of concurrent queries
-  // falls below the limit defined by |max_concurrent_queries| (passed to the
-  // constructor of LogDnsClient). This callback will fire once and then be
-  // unregistered. Should only be used if QueryAuditProof() returns
-  // net::ERR_TEMPORARILY_THROTTLED.
-  // The callback will be run on the same thread that created the LogDnsClient.
-  void NotifyWhenNotThrottled(base::OnceClosure callback);
-
-  // Queries a CT log to retrieve an audit proof for the leaf with |leaf_hash|.
-  // The log is identified by |domain_for_log|, which is the DNS name used as a
-  // suffix for all queries.
-  // The |leaf_hash| is the SHA-256 Merkle leaf hash (see RFC6962, section 2.1).
-  // The size of the CT log tree, for which the proof is requested, must be
-  // provided in |tree_size|.
-  // The field |lookup_securely| specifies whether DNS lookups should be
-  // performed securely or insecurely. This value should be set according to
-  // whether the hostname lookup was resolved securely or not in order to
-  // help achieve resolver consistency between the hostname and proof lookups.
-  // A handle to the query will be placed in |out_query|. The audit proof can be
-  // obtained from that once the query completes. Deleting this handle before
-  // the query completes will cancel it. It must not outlive the LogDnsClient.
-  // If the proof cannot be obtained synchronously, this method will return
-  // net::ERR_IO_PENDING and invoke |callback| once the query is complete.
-  // The callback will be run on the same thread that created the LogDnsClient.
-  // Returns:
-  // - net::OK if the query was successful.
-  // - net::ERR_IO_PENDING if the query was successfully started and is
-  //   continuing asynchronously.
-  // - net::ERR_TEMPORARILY_THROTTLED if the maximum number of concurrent
-  //   queries are already in progress. Try again later.
-  // - net::ERR_NAME_RESOLUTION_FAILED if DNS queries are not possible.
-  //   Check that the DnsConfig returned by NetworkChangeNotifier is valid.
-  // - net::ERR_INVALID_ARGUMENT if an argument is invalid, e.g. |leaf_hash| is
-  //   not a SHA-256 hash.
-  net::Error QueryAuditProof(base::StringPiece domain_for_log,
-                             std::string leaf_hash,
-                             bool lookup_securely,
-                             uint64_t tree_size,
-                             std::unique_ptr<AuditProofQuery>* out_query,
-                             net::CompletionOnceCallback callback);
-
- private:
-  // Invoked when an audit proof query completes.
-  // |callback| is the user-provided callback that should be notified.
-  // |net_error| is a net::Error indicating success or failure.
-  void QueryAuditProofComplete(net::CompletionOnceCallback callback,
-                               int net_error);
-
-  // Invoked when an audit proof query is cancelled.
-  void QueryAuditProofCancelled();
-
-  // Returns true if the maximum number of queries are currently in-flight.
-  // If the maximum number of in-flight queries is set to 0, this will always
-  // return false.
-  bool HasMaxQueriesInFlight() const;
-
-  // Updates the |dns_client_| config using NetworkChangeNotifier.
-  void UpdateDnsConfig();
-
-  // Used to perform DNS queries.
-  std::unique_ptr<net::DnsClient> dns_client_;
-  // Used to perform DoH queries.
-  net::URLRequestContext* url_request_context_;
-  // Passed to the DNS client for logging.
-  net::NetLogWithSource net_log_;
-  // The number of queries that are currently in-flight.
-  size_t in_flight_queries_;
-  // The maximum number of queries that can be in-flight at one time.
-  size_t max_in_flight_queries_;
-  // Callbacks to invoke when the number of in-flight queries is at its limit.
-  std::list<base::OnceClosure> not_throttled_callbacks_;
-
-  DISALLOW_COPY_AND_ASSIGN(LogDnsClient);
-};
-
-}  // namespace certificate_transparency
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_LOG_DNS_CLIENT_H_
diff --git a/components/certificate_transparency/log_dns_client_unittest.cc b/components/certificate_transparency/log_dns_client_unittest.cc
deleted file mode 100644
index 10d4a96f..0000000
--- a/components/certificate_transparency/log_dns_client_unittest.cc
+++ /dev/null
@@ -1,1101 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/log_dns_client.h"
-
-#include <memory>
-#include <numeric>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/format_macros.h"
-#include "base/run_loop.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/test/test_timeouts.h"
-#include "components/certificate_transparency/mock_log_dns_traffic.h"
-#include "crypto/sha2.h"
-#include "net/base/net_errors.h"
-#include "net/cert/merkle_audit_proof.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/dns/dns_client.h"
-#include "net/dns/dns_config.h"
-#include "net/dns/dns_test_util.h"
-#include "net/dns/public/dns_protocol.h"
-#include "net/log/net_log.h"
-#include "net/test/gtest_util.h"
-#include "net/url_request/url_request_filter.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace certificate_transparency {
-namespace {
-
-using net::test::IsError;
-using net::test::IsOk;
-using ::testing::AllOf;
-using ::testing::Eq;
-using ::testing::IsEmpty;
-using ::testing::Le;
-using ::testing::Not;
-using ::testing::NotNull;
-
-// Histogram names.
-const char kLeafIndexErrorHistogram[] =
-    "Net.CertificateTransparency.DnsQueryLeafIndexError";
-const char kLeafIndexRcodeHistogram[] =
-    "Net.CertificateTransparency.DnsQueryLeafIndexRcode";
-const char kAuditProofErrorHistogram[] =
-    "Net.CertificateTransparency.DnsQueryAuditProofError";
-const char kAuditProofRcodeHistogram[] =
-    "Net.CertificateTransparency.DnsQueryAuditProofRcode";
-
-// Sample Merkle leaf hashes.
-const char* const kLeafHashes[] = {
-    "\x1f\x25\xe1\xca\xba\x4f\xf9\xb8\x27\x24\x83\x0f\xca\x60\xe4\xc2\xbe\xa8"
-    "\xc3\xa9\x44\x1c\x27\xb0\xb4\x3e\x6a\x96\x94\xc7\xb8\x04",
-    "\x2c\x26\xb4\x6b\x68\xff\xc6\x8f\xf9\x9b\x45\x3c\x1d\x30\x41\x34\x13\x42"
-    "\x2d\x70\x64\x83\xbf\xa0\xf9\x8a\x5e\x88\x62\x66\xe7\xae",
-    "\xfc\xde\x2b\x2e\xdb\xa5\x6b\xf4\x08\x60\x1f\xb7\x21\xfe\x9b\x5c\x33\x8d"
-    "\x10\xee\x42\x9e\xa0\x4f\xae\x55\x11\xb6\x8f\xbf\x8f\xb9",
-};
-
-// DNS query names for looking up the leaf index associated with each hash in
-// |kLeafHashes|. Assumes the log domain is "ct.test".
-const char* const kLeafIndexQnames[] = {
-    "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
-    "FQTLI23I77DI76M3IU6B2MCBGQJUELLQMSB37IHZRJPIQYTG46XA.hash.ct.test.",
-    "7TPCWLW3UVV7ICDAD63SD7U3LQZY2EHOIKPKAT5OKUI3ND57R64Q.hash.ct.test.",
-};
-
-// Leaf indices and tree sizes for use with |kLeafHashes|.
-const uint64_t kLeafIndices[] = {0, 1, 2};
-const uint64_t kTreeSizes[] = {100, 10000, 1000000};
-
-// Only 7 audit proof nodes can fit into a DNS response, because they are sent
-// in a TXT RDATA string, which has a maximum size of 255 bytes, and each node
-// is a SHA-256 hash (32 bytes), i.e. (255 / 32) == 7.
-// This means audit proofs consisting of more than 7 nodes require multiple DNS
-// requests to retrieve.
-const size_t kMaxProofNodesPerDnsResponse = 7;
-
-// Returns an example Merkle audit proof containing |length| nodes.
-// The proof cannot be used for cryptographic purposes; it is merely a
-// placeholder.
-std::vector<std::string> GetSampleAuditProof(size_t length) {
-  std::vector<std::string> audit_proof(length);
-  // Makes each node of the audit proof different, so that tests are able to
-  // confirm that the audit proof is reconstructed in the correct order.
-  for (size_t i = 0; i < length; ++i) {
-    std::string node(crypto::kSHA256Length, '\0');
-    // Each node is 32 bytes, with each byte having a different value.
-    for (size_t j = 0; j < crypto::kSHA256Length; ++j) {
-      node[j] = static_cast<char>((-127 + i + j) % 128);
-    }
-    audit_proof[i].assign(std::move(node));
-  }
-
-  return audit_proof;
-}
-
-}  // namespace
-
-class LogDnsClientTest : public ::testing::TestWithParam<net::IoMode> {
-  void SetUp() override {
-    net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
-    filter->AddHostnameInterceptor(
-        "https", "mock.http",
-        std::make_unique<MockLogDnsTraffic::DohJobInterceptor>());
-  }
-
-  void TearDown() override {
-    net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
-    filter->ClearHandlers();
-  }
-
- protected:
-  LogDnsClientTest()
-      : network_change_notifier_(net::NetworkChangeNotifier::CreateMock()) {
-    mock_dns_.SetSocketReadMode(GetParam());
-    mock_dns_.InitializeDnsConfig();
-  }
-
-  std::unique_ptr<LogDnsClient> CreateLogDnsClient(
-      size_t max_concurrent_queries) {
-    return std::make_unique<LogDnsClient>(
-        mock_dns_.CreateDnsClient(), new net::TestURLRequestContext(),
-        net::NetLogWithSource(), max_concurrent_queries);
-  }
-
-  std::unique_ptr<LogDnsClient> CreateRuleBasedLogDnsClient(
-      net::MockDnsClientRuleList rules) {
-    return std::make_unique<LogDnsClient>(
-        std::make_unique<net::MockDnsClient>(net::DnsConfig(),
-                                             std::move(rules)),
-        new net::TestURLRequestContext(), net::NetLogWithSource(), 0);
-  }
-
-  // Convenience function for calling QueryAuditProof synchronously.
-  template <typename... Types>
-  net::Error QueryAuditProof(Types&&... args) {
-    std::unique_ptr<LogDnsClient> log_client = CreateLogDnsClient(0);
-    net::TestCompletionCallback callback;
-    const net::Error result = log_client->QueryAuditProof(
-        std::forward<Types>(args)..., callback.callback());
-
-    return result != net::ERR_IO_PENDING
-               ? result
-               : static_cast<net::Error>(callback.WaitForResult());
-  }
-
-  // This will be the NetworkChangeNotifier singleton for the duration of the
-  // test. It is accessed statically by LogDnsClient.
-  std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
-  // Queues and handles asynchronous DNS tasks. Indirectly used by LogDnsClient,
-  // the underlying net::DnsClient, and NetworkChangeNotifier.
-  base::test::ScopedTaskEnvironment task_environment_{
-      base::test::ScopedTaskEnvironment::MainThreadType::IO};
-  // Allows mock DNS sockets to be setup.
-  MockLogDnsTraffic mock_dns_;
-  // Tests that histograms are populated as expected.
-  base::HistogramTester histograms_;
-};
-
-TEST_P(LogDnsClientTest, QueryAuditProofReportsThatLogDomainDoesNotExist) {
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndErrorResponse(
-      kLeafIndexQnames[0], net::dns_protocol::kRcodeNXDOMAIN));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(
-      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
-                      kTreeSizes[0], &query),
-      IsError(net::ERR_NAME_NOT_RESOLVED));
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
-                                 -net::ERR_NAME_NOT_RESOLVED, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNXDOMAIN, 1);
-  histograms_.ExpectTotalCount(kAuditProofErrorHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsServerFailuresDuringLeafIndexRequests) {
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndErrorResponse(
-      kLeafIndexQnames[0], net::dns_protocol::kRcodeSERVFAIL));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(
-      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
-                      kTreeSizes[0], &query),
-      IsError(net::ERR_DNS_SERVER_FAILED));
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
-                                 -net::ERR_DNS_SERVER_FAILED, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeSERVFAIL, 1);
-  histograms_.ExpectTotalCount(kAuditProofErrorHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsServerRefusalsDuringLeafIndexRequests) {
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndErrorResponse(
-      kLeafIndexQnames[0], net::dns_protocol::kRcodeREFUSED));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(
-      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
-                      kTreeSizes[0], &query),
-      IsError(net::ERR_DNS_SERVER_FAILED));
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
-                                 -net::ERR_DNS_SERVER_FAILED, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeREFUSED, 1);
-  histograms_.ExpectTotalCount(kAuditProofErrorHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProofReportsMalformedLeafIndexResponse) {
-  const struct {
-    std::string name;
-    std::vector<base::StringPiece> txt_strings;
-  } tests[] = {{"contains no strings", {}},
-               {"contains more than one string", {"123456", "7"}},
-               {"is not numeric", {"foo"}},
-               {"is floating point", {"123456.0"}},
-               {"is empty"},
-               {"has non-numeric prefix", {"foo123456"}},
-               {"has non-numeric suffix", {"123456foo"}}};
-
-  for (auto test : tests) {
-    SCOPED_TRACE(test.name);
-    base::HistogramTester histograms;
-
-    ASSERT_TRUE(mock_dns_.ExpectRequestAndResponse(kLeafIndexQnames[0],
-                                                   test.txt_strings));
-
-    std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-    ASSERT_THAT(
-        QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
-                        kTreeSizes[0], &query),
-        IsError(net::ERR_DNS_MALFORMED_RESPONSE));
-    histograms.ExpectUniqueSample(kLeafIndexErrorHistogram,
-                                  -net::ERR_DNS_MALFORMED_RESPONSE, 1);
-    histograms.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                  net::dns_protocol::kRcodeNOERROR, 1);
-    histograms_.ExpectTotalCount(kAuditProofErrorHistogram, 0);
-    histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-  }
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProofReportsInvalidArgIfLogDomainIsEmpty) {
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("", kLeafHashes[0], false /* lookup_securely */,
-                              kTreeSizes[0], &query),
-              IsError(net::ERR_INVALID_ARGUMENT));
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProofReportsInvalidArgIfLeafHashIsInvalid) {
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", "foo", false /* lookup_securely */,
-                              kTreeSizes[0], &query),
-              IsError(net::ERR_INVALID_ARGUMENT));
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProofReportsInvalidArgIfLeafHashIsEmpty) {
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", "", false /* lookup_securely */,
-                              kTreeSizes[0], &query),
-              IsError(net::ERR_INVALID_ARGUMENT));
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsSocketErrorsDuringLeafIndexRequests) {
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndSocketError(
-      kLeafIndexQnames[0], net::ERR_CONNECTION_REFUSED));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(
-      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
-                      kTreeSizes[0], &query),
-      IsError(net::ERR_CONNECTION_REFUSED));
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
-                                 -net::ERR_CONNECTION_REFUSED, 1);
-  histograms_.ExpectTotalCount(kLeafIndexRcodeHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofErrorHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsTimeoutsDuringLeafIndexRequests) {
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndTimeout(kLeafIndexQnames[0]));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(
-      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
-                      kTreeSizes[0], &query),
-      IsError(net::ERR_DNS_TIMED_OUT));
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
-                                 -net::ERR_DNS_TIMED_OUT, 1);
-  histograms_.ExpectTotalCount(kLeafIndexRcodeHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofErrorHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProof) {
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
-
-  // Expect a leaf index query first, to map the leaf hash to a leaf index.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  // It takes a number of DNS requests to retrieve the entire |audit_proof|
-  // (see |kMaxProofNodesPerDnsResponse|).
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-
-    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
-        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
-  }
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsOk());
-  const net::ct::MerkleAuditProof& proof = query->GetProof();
-  EXPECT_THAT(proof.leaf_index, Eq(123456u));
-  EXPECT_THAT(proof.tree_size, Eq(999999u));
-  EXPECT_THAT(proof.nodes, Eq(audit_proof));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProofHandlesResponsesWithShortAuditPaths) {
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
-
-  // Expect a leaf index query first, to map the leaf hash to a leaf index.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  // Make some of the responses contain fewer proof nodes than they can hold.
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.123456.999999.tree.ct.test.", audit_proof.begin(),
-      audit_proof.begin() + 1));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "1.123456.999999.tree.ct.test.", audit_proof.begin() + 1,
-      audit_proof.begin() + 3));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "3.123456.999999.tree.ct.test.", audit_proof.begin() + 3,
-      audit_proof.begin() + 6));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "6.123456.999999.tree.ct.test.", audit_proof.begin() + 6,
-      audit_proof.begin() + 10));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "10.123456.999999.tree.ct.test.", audit_proof.begin() + 10,
-      audit_proof.begin() + 13));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "13.123456.999999.tree.ct.test.", audit_proof.begin() + 13,
-      audit_proof.end()));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsOk());
-  const net::ct::MerkleAuditProof& proof = query->GetProof();
-  EXPECT_THAT(proof.leaf_index, Eq(123456u));
-  EXPECT_THAT(proof.tree_size, Eq(999999u));
-  EXPECT_THAT(proof.nodes, Eq(audit_proof));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsThatAuditProofQnameDoesNotExist) {
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndErrorResponse(
-      "0.123456.999999.tree.ct.test.", net::dns_protocol::kRcodeNXDOMAIN));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_NAME_NOT_RESOLVED));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_NAME_NOT_RESOLVED, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNXDOMAIN, 1);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsServerFailuresDuringAuditProofRequests) {
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndErrorResponse(
-      "0.123456.999999.tree.ct.test.", net::dns_protocol::kRcodeSERVFAIL));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_SERVER_FAILED));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_SERVER_FAILED, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeSERVFAIL, 1);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsServerRefusalsDuringAuditProofRequests) {
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndErrorResponse(
-      "0.123456.999999.tree.ct.test.", net::dns_protocol::kRcodeREFUSED));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_SERVER_FAILED));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_SERVER_FAILED, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeREFUSED, 1);
-}
-
-TEST_P(
-    LogDnsClientTest,
-    QueryAuditProofReportsResponseMalformedIfProofNodesResponseContainsNoStrings) {
-  // Expect a leaf index query first, to map the leaf hash to a leaf index.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndResponse(
-      "0.123456.999999.tree.ct.test.", std::vector<base::StringPiece>()));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_MALFORMED_RESPONSE));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_MALFORMED_RESPONSE, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-}
-
-TEST_P(
-    LogDnsClientTest,
-    QueryAuditProofReportsResponseMalformedIfProofNodesResponseContainsMoreThanOneString) {
-  // The CT-over-DNS draft RFC states that the response will contain "exactly
-  // one character-string."
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(10);
-
-  std::string first_chunk_of_proof = std::accumulate(
-      audit_proof.begin(), audit_proof.begin() + 7, std::string());
-  std::string second_chunk_of_proof = std::accumulate(
-      audit_proof.begin() + 7, audit_proof.end(), std::string());
-
-  // Expect a leaf index query first, to map the leaf hash to a leaf index.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndResponse(
-      "0.123456.999999.tree.ct.test.",
-      {first_chunk_of_proof, second_chunk_of_proof}));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_MALFORMED_RESPONSE));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_MALFORMED_RESPONSE, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsResponseMalformedIfNodeTooShort) {
-  // node is shorter than a SHA-256 hash (31 vs 32 bytes)
-  const std::vector<std::string> audit_proof(1, std::string(31, 'a'));
-
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end()));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_MALFORMED_RESPONSE));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_MALFORMED_RESPONSE, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProofReportsResponseMalformedIfNodeTooLong) {
-  // node is longer than a SHA-256 hash (33 vs 32 bytes)
-  const std::vector<std::string> audit_proof(1, std::string(33, 'a'));
-
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end()));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_MALFORMED_RESPONSE));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_MALFORMED_RESPONSE, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-}
-
-TEST_P(LogDnsClientTest, QueryAuditProofReportsResponseMalformedIfEmpty) {
-  const std::vector<std::string> audit_proof;
-
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end()));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_MALFORMED_RESPONSE));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_MALFORMED_RESPONSE, 1);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsInvalidArgIfLeafIndexEqualToTreeSize) {
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 123456, &query),
-              IsError(net::ERR_INVALID_ARGUMENT));
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsInvalidArgIfLeafIndexGreaterThanTreeSize) {
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 999999));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 123456, &query),
-              IsError(net::ERR_INVALID_ARGUMENT));
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsSocketErrorsDuringAuditProofRequests) {
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndSocketError(
-      "0.123456.999999.tree.ct.test.", net::ERR_CONNECTION_REFUSED));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_CONNECTION_REFUSED));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_CONNECTION_REFUSED, 1);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest,
-       QueryAuditProofReportsTimeoutsDuringAuditProofRequests) {
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(
-      mock_dns_.ExpectRequestAndTimeout("0.123456.999999.tree.ct.test."));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
-                              false /* lookup_securely */, 999999, &query),
-              IsError(net::ERR_DNS_TIMED_OUT));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 1);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram,
-                                 -net::ERR_DNS_TIMED_OUT, 1);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest, AdoptsLatestDnsConfigIfValid) {
-  std::unique_ptr<net::DnsClient> tmp = mock_dns_.CreateDnsClient();
-  net::DnsClient* dns_client = tmp.get();
-  LogDnsClient log_client(std::move(tmp), new net::TestURLRequestContext(),
-                          net::NetLogWithSource(), 0);
-
-  // Get the current DNS config, modify it and broadcast the update.
-  net::DnsConfig config(*dns_client->GetConfig());
-  ASSERT_NE(123, config.attempts);
-  config.attempts = 123;
-  mock_dns_.SetDnsConfig(config);
-
-  // Let the DNS config change propogate.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(123, dns_client->GetConfig()->attempts);
-}
-
-TEST_P(LogDnsClientTest, IgnoresLatestDnsConfigIfInvalid) {
-  std::unique_ptr<net::DnsClient> tmp = mock_dns_.CreateDnsClient();
-  net::DnsClient* dns_client = tmp.get();
-  LogDnsClient log_client(std::move(tmp), new net::TestURLRequestContext(),
-                          net::NetLogWithSource(), 0);
-
-  // Get the current DNS config, modify it and broadcast the update.
-  net::DnsConfig config(*dns_client->GetConfig());
-  ASSERT_THAT(config.nameservers, Not(IsEmpty()));
-  config.nameservers.clear();  // Makes config invalid
-  mock_dns_.SetDnsConfig(config);
-
-  // Let the DNS config change propogate.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_THAT(dns_client->GetConfig()->nameservers, Not(IsEmpty()));
-}
-
-// Test that changes to the DNS config after starting a query are adopted and
-// that the query is not disrupted.
-TEST_P(LogDnsClientTest, AdoptsLatestDnsConfigMidQuery) {
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
-
-  // Expect a leaf index query first, to map the leaf hash to a leaf index.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  // It takes a number of DNS requests to retrieve the entire |audit_proof|
-  // (see |kMaxProofNodesPerDnsResponse|).
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-
-    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
-        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
-  }
-
-  std::unique_ptr<net::DnsClient> tmp = mock_dns_.CreateDnsClient();
-  net::DnsClient* dns_client = tmp.get();
-  LogDnsClient log_client(std::move(tmp), new net::TestURLRequestContext(),
-                          net::NetLogWithSource(), 0);
-
-  // Start query.
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  net::TestCompletionCallback callback;
-  ASSERT_THAT(log_client.QueryAuditProof("ct.test", kLeafHashes[0],
-                                         false /* lookup_securely */, 999999,
-                                         &query, callback.callback()),
-              IsError(net::ERR_IO_PENDING));
-
-  // Get the current DNS config, modify it and publish the update.
-  // The new config is distributed asynchronously via NetworkChangeNotifier.
-  net::DnsConfig config(*dns_client->GetConfig());
-  ASSERT_NE(123, config.attempts);
-  config.attempts = 123;
-  mock_dns_.SetDnsConfig(config);
-  // The new config is distributed asynchronously via NetworkChangeNotifier.
-  // Config change shouldn't have taken effect yet.
-  ASSERT_NE(123, dns_client->GetConfig()->attempts);
-
-  // Wait for the query to complete, then check that it was successful.
-  // The DNS config should be updated during this time.
-  ASSERT_THAT(callback.WaitForResult(), IsOk());
-  const net::ct::MerkleAuditProof& proof = query->GetProof();
-  EXPECT_THAT(proof.leaf_index, Eq(123456u));
-  EXPECT_THAT(proof.tree_size, Eq(999999u));
-  EXPECT_THAT(proof.nodes, Eq(audit_proof));
-
-  // Check that the DNS config change was adopted.
-  ASSERT_EQ(123, dns_client->GetConfig()->attempts);
-}
-
-TEST_P(LogDnsClientTest, CanPerformQueriesInParallel) {
-  // Check that 3 queries can be performed in parallel.
-  constexpr size_t kNumOfParallelQueries = 3;
-  ASSERT_THAT(kNumOfParallelQueries,
-              AllOf(Le(base::size(kLeafIndexQnames)),
-                    Le(base::size(kLeafIndices)), Le(base::size(kTreeSizes))))
-      << "Not enough test data for this many parallel queries";
-
-  std::unique_ptr<LogDnsClient> log_client =
-      CreateLogDnsClient(kNumOfParallelQueries);
-  net::TestCompletionCallback callbacks[kNumOfParallelQueries];
-
-  // Expect multiple leaf index requests.
-  for (size_t i = 0; i < kNumOfParallelQueries; ++i) {
-    ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[i],
-                                                            kLeafIndices[i]));
-  }
-
-  // Make each query require one more audit proof request than the last, by
-  // increasing the number of nodes in the audit proof by
-  // kMaxProofNodesPerDnsResponse for each query. This helps to test that
-  // parallel queries do not intefere with each other, e.g. one query causing
-  // another to end prematurely.
-  std::vector<std::string> audit_proofs[kNumOfParallelQueries];
-  for (size_t query_i = 0; query_i < kNumOfParallelQueries; ++query_i) {
-    const size_t dns_requests_required = query_i + 1;
-    audit_proofs[query_i] = GetSampleAuditProof(dns_requests_required *
-                                                kMaxProofNodesPerDnsResponse);
-  }
-  // The most DNS requests that are made by any of the above N queries is N.
-  const size_t kMaxDnsRequestsPerQuery = kNumOfParallelQueries;
-
-  // Setup expectations for up to N DNS requests per query performed.
-  // All of the queries will be started at the same time, so expect the DNS
-  // requests and responses to be interleaved.
-  // NB:
-  // Ideally, the tests wouldn't require that the DNS requests sent by the
-  // parallel queries are interleaved. However, the mock socket framework does
-  // not provide a way to express this.
-  for (size_t dns_req_i = 0; dns_req_i < kMaxDnsRequestsPerQuery; ++dns_req_i) {
-    for (size_t query_i = 0; query_i < kNumOfParallelQueries; ++query_i) {
-      const std::vector<std::string>& proof = audit_proofs[query_i];
-      // Closed-open range of |proof| nodes that are expected in this response.
-      const size_t start_node = dns_req_i * 7;
-      const size_t end_node =
-          std::min(start_node + kMaxProofNodesPerDnsResponse, proof.size());
-
-      // If there are any nodes left, expect another request and response.
-      if (start_node < end_node) {
-        ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-            base::StringPrintf("%zu.%" PRIu64 ".%" PRIu64 ".tree.ct.test.",
-                               start_node, kLeafIndices[query_i],
-                               kTreeSizes[query_i]),
-            proof.begin() + start_node, proof.begin() + end_node));
-      }
-    }
-  }
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> queries[kNumOfParallelQueries];
-
-  // Start the queries.
-  for (size_t i = 0; i < kNumOfParallelQueries; ++i) {
-    ASSERT_THAT(log_client->QueryAuditProof(
-                    "ct.test", kLeafHashes[i], false /* lookup_securely */,
-                    kTreeSizes[i], &queries[i], callbacks[i].callback()),
-                IsError(net::ERR_IO_PENDING))
-        << "query #" << i;
-  }
-
-  // Wait for each query to complete and check its results.
-  for (size_t i = 0; i < kNumOfParallelQueries; ++i) {
-    net::TestCompletionCallback& callback = callbacks[i];
-
-    SCOPED_TRACE(testing::Message() << "callbacks[" << i << "]");
-    EXPECT_THAT(callback.WaitForResult(), IsOk());
-    const net::ct::MerkleAuditProof& proof = queries[i]->GetProof();
-    EXPECT_THAT(proof.leaf_index, Eq(kLeafIndices[i]));
-    EXPECT_THAT(proof.tree_size, Eq(kTreeSizes[i]));
-    EXPECT_THAT(proof.nodes, Eq(audit_proofs[i]));
-  }
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 3);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 3);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram, -net::OK, 3);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 3);
-}
-
-TEST_P(LogDnsClientTest, CanBeThrottledToOneQueryAtATime) {
-  // Check that queries can be rate-limited to one at a time.
-  // The second query, initiated while the first is in progress, should fail.
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
-
-  // Expect the first query to send leaf index and audit proof requests, but the
-  // second should not due to throttling.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  // It takes a number of DNS requests to retrieve the entire |audit_proof|
-  // (see |kMaxProofNodesPerDnsResponse|).
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-
-    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
-        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
-  }
-
-  const size_t kMaxConcurrentQueries = 1;
-  std::unique_ptr<LogDnsClient> log_client =
-      CreateLogDnsClient(kMaxConcurrentQueries);
-
-  // Try to start the queries.
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query1;
-  net::TestCompletionCallback callback1;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
-                                          false /* lookup_securely */, 999999,
-                                          &query1, callback1.callback()),
-              IsError(net::ERR_IO_PENDING));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query2;
-  net::TestCompletionCallback callback2;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[1],
-                                          false /* lookup_securely */, 999999,
-                                          &query2, callback2.callback()),
-              IsError(net::ERR_TEMPORARILY_THROTTLED));
-
-  // Check that the first query succeeded.
-  EXPECT_THAT(callback1.WaitForResult(), IsOk());
-  const net::ct::MerkleAuditProof& proof1 = query1->GetProof();
-  EXPECT_THAT(proof1.leaf_index, Eq(123456u));
-  EXPECT_THAT(proof1.tree_size, Eq(999999u));
-  EXPECT_THAT(proof1.nodes, Eq(audit_proof));
-
-  // Try a third query, which should succeed now that the first is finished.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[2], 666));
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-
-    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-        base::StringPrintf("%zu.666.999999.tree.ct.test.", nodes_begin),
-        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
-  }
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query3;
-  net::TestCompletionCallback callback3;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[2],
-                                          false /* lookup_securely */, 999999,
-                                          &query3, callback3.callback()),
-              IsError(net::ERR_IO_PENDING));
-
-  // Check that the third query succeeded.
-  EXPECT_THAT(callback3.WaitForResult(), IsOk());
-  const net::ct::MerkleAuditProof& proof3 = query3->GetProof();
-  EXPECT_THAT(proof3.leaf_index, Eq(666u));
-  EXPECT_THAT(proof3.tree_size, Eq(999999u));
-  EXPECT_THAT(proof3.nodes, Eq(audit_proof));
-
-  histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 2);
-  histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 2);
-  histograms_.ExpectUniqueSample(kAuditProofErrorHistogram, -net::OK, 2);
-  histograms_.ExpectUniqueSample(kAuditProofRcodeHistogram,
-                                 net::dns_protocol::kRcodeNOERROR, 2);
-}
-
-TEST_P(LogDnsClientTest, NotifiesWhenNoLongerThrottled) {
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
-
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-
-    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
-        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
-  }
-
-  const size_t kMaxConcurrentQueries = 1;
-  std::unique_ptr<LogDnsClient> log_client =
-      CreateLogDnsClient(kMaxConcurrentQueries);
-
-  // Start a query.
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query1;
-  net::TestCompletionCallback query_callback1;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
-                                          false /* lookup_securely */, 999999,
-                                          &query1, query_callback1.callback()),
-              IsError(net::ERR_IO_PENDING));
-
-  net::TestClosure not_throttled_callback;
-  log_client->NotifyWhenNotThrottled(not_throttled_callback.closure());
-
-  ASSERT_THAT(query_callback1.WaitForResult(), IsOk());
-  not_throttled_callback.WaitForResult();
-
-  // Start another query to check |not_throttled_callback| doesn't fire again.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[1], 666));
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-
-    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-        base::StringPrintf("%zu.666.999999.tree.ct.test.", nodes_begin),
-        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
-  }
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query2;
-  net::TestCompletionCallback query_callback2;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[1],
-                                          false /* lookup_securely */, 999999,
-                                          &query2, query_callback2.callback()),
-              IsError(net::ERR_IO_PENDING));
-
-  // Give the query a chance to run.
-  ASSERT_THAT(query_callback2.WaitForResult(), IsOk());
-  // Give |not_throttled_callback| a chance to run - it shouldn't though.
-  base::RunLoop().RunUntilIdle();
-  ASSERT_FALSE(not_throttled_callback.have_result());
-}
-
-TEST_P(LogDnsClientTest, CanCancelQueries) {
-  const size_t kMaxConcurrentQueries = 1;
-  std::unique_ptr<LogDnsClient> log_client =
-      CreateLogDnsClient(kMaxConcurrentQueries);
-
-  // Expect the first request of the query to be sent, but not the rest because
-  // it'll be cancelled before it gets that far.
-  ASSERT_TRUE(
-      mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-
-  // Start query.
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  net::TestCompletionCallback callback;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
-                                          false /* lookup_securely */, 999999,
-                                          &query, callback.callback()),
-              IsError(net::ERR_IO_PENDING));
-
-  // Cancel the query.
-  query.reset();
-
-  // Give |callback| a chance to run - it shouldn't though.
-  base::RunLoop().RunUntilIdle();
-  ASSERT_FALSE(callback.have_result());
-
-  histograms_.ExpectTotalCount(kLeafIndexErrorHistogram, 0);
-  histograms_.ExpectTotalCount(kLeafIndexRcodeHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofErrorHistogram, 0);
-  histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
-}
-
-TEST_P(LogDnsClientTest, SecureDnsMode_Secure) {
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
-
-  net::MockDnsClientRuleList rules;
-  // Make leaf index queries for kLeafIndexQnames[0] successful only when
-  // lookup_securely is true.
-  rules.emplace_back(
-      kLeafIndexQnames[0], net::dns_protocol::kTypeTXT,
-      net::SecureDnsMode::SECURE,
-      net::MockDnsClientRule::CreateSecureResult(net::BuildTestDnsTextResponse(
-          kLeafIndexQnames[0],
-          std::vector<std::vector<std::string>>({{"123456"}}))),
-      false /* delay */);
-
-  // Add successful audit proof queries for lookup_securely true.
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-    rules.emplace_back(
-        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
-        net::dns_protocol::kTypeTXT, net::SecureDnsMode::SECURE,
-        net::MockDnsClientRule::CreateSecureResult(
-            net::BuildTestDnsTextResponse(
-                base::StringPrintf("%zu.123456.999999.tree.ct.test.",
-                                   nodes_begin),
-                {{std::accumulate(audit_proof.begin() + nodes_begin,
-                                  audit_proof.begin() + nodes_end,
-                                  std::string())}})),
-        false /* delay */
-    );
-  }
-
-  std::unique_ptr<LogDnsClient> log_client =
-      CreateRuleBasedLogDnsClient(std::move(rules));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  net::TestCompletionCallback callback1;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
-                                          false /* lookup_securely */, 999999,
-                                          &query, callback1.callback()),
-              IsError(net::ERR_IO_PENDING));
-  EXPECT_THAT(callback1.WaitForResult(), IsError(net::ERR_NAME_NOT_RESOLVED));
-
-  net::TestCompletionCallback callback2;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
-                                          true /* lookup_securely */, 999999,
-                                          &query, callback2.callback()),
-              IsError(net::ERR_IO_PENDING));
-  EXPECT_THAT(callback2.WaitForResult(), IsOk());
-}
-
-TEST_P(LogDnsClientTest, SecureDnsMode_Insecure) {
-  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
-
-  net::MockDnsClientRuleList rules;
-  // Make leaf index queries for kLeafIndexQnames[0] successful only when
-  // lookup_securely is false.
-  rules.emplace_back(
-      kLeafIndexQnames[0], net::dns_protocol::kTypeTXT, net::SecureDnsMode::OFF,
-      net::MockDnsClientRule::Result(net::BuildTestDnsTextResponse(
-          kLeafIndexQnames[0],
-          std::vector<std::vector<std::string>>({{"123456"}}))),
-      false /* delay */);
-
-  // Add successful audit proof queries for lookup_securely false.
-  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
-       nodes_begin += kMaxProofNodesPerDnsResponse) {
-    const size_t nodes_end = std::min(
-        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
-    rules.emplace_back(
-        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
-        net::dns_protocol::kTypeTXT, net::SecureDnsMode::OFF,
-        net::MockDnsClientRule::Result(net::BuildTestDnsTextResponse(
-            base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
-            {{std::accumulate(audit_proof.begin() + nodes_begin,
-                              audit_proof.begin() + nodes_end,
-                              std::string())}})),
-        false /* delay */
-    );
-  }
-
-  std::unique_ptr<LogDnsClient> log_client =
-      CreateRuleBasedLogDnsClient(std::move(rules));
-
-  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  net::TestCompletionCallback callback1;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
-                                          false /* lookup_securely */, 999999,
-                                          &query, callback1.callback()),
-              IsError(net::ERR_IO_PENDING));
-  EXPECT_THAT(callback1.WaitForResult(), IsOk());
-
-  net::TestCompletionCallback callback2;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
-                                          true /* lookup_securely */, 999999,
-                                          &query, callback2.callback()),
-              IsError(net::ERR_IO_PENDING));
-  EXPECT_THAT(callback2.WaitForResult(), IsError(net::ERR_NAME_NOT_RESOLVED));
-}
-
-INSTANTIATE_TEST_SUITE_P(ReadMode,
-                         LogDnsClientTest,
-                         ::testing::Values(net::IoMode::ASYNC,
-                                           net::IoMode::SYNCHRONOUS));
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/mock_log_dns_traffic.cc b/components/certificate_transparency/mock_log_dns_traffic.cc
deleted file mode 100644
index 3d0c0ea..0000000
--- a/components/certificate_transparency/mock_log_dns_traffic.cc
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/mock_log_dns_traffic.h"
-
-#include <algorithm>
-#include <numeric>
-#include <vector>
-
-#include "base/big_endian.h"
-#include "base/bind.h"
-#include "base/containers/span.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/sys_byteorder.h"
-#include "base/test/test_timeouts.h"
-#include "net/dns/dns_client.h"
-#include "net/dns/dns_config.h"
-#include "net/dns/dns_query.h"
-#include "net/dns/dns_util.h"
-#include "net/dns/public/dns_protocol.h"
-#include "net/dns/record_rdata.h"
-#include "net/socket/socket_test_util.h"
-#include "net/url_request/url_request_error_job.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace certificate_transparency {
-
-namespace {
-
-// This is used for the last mock socket response as a sentinel to prevent
-// trying to read more data than expected.
-const net::MockRead kNoMoreData(net::SYNCHRONOUS, net::ERR_UNEXPECTED, 2);
-
-// Necessary to expose SetDnsConfig for testing.
-class DnsChangeNotifier : public net::NetworkChangeNotifier {
- public:
-  static void SetDnsConfig(const net::DnsConfig& config) {
-    net::NetworkChangeNotifier::SetDnsConfig(config);
-  }
-};
-
-std::vector<char> AsVector(const net::IOBufferWithSize& buf) {
-  return std::vector<char>(buf.data(), buf.data() + buf.size());
-}
-
-// Always return min, to simplify testing.
-// This should result in the DNS query ID always being 0.
-int FakeRandInt(int min, int max) {
-  return min;
-}
-
-std::unique_ptr<net::DnsQuery> CreateDnsTxtQuery(base::StringPiece qname) {
-  std::string encoded_qname;
-  if (!net::DNSDomainFromDot(qname, &encoded_qname)) {
-    // qname is an invalid domain name.
-    return nullptr;
-  }
-
-  // Expect EDNS option that disables client subnet extension:
-  // https://tools.ietf.org/html/rfc7871
-  const uint16_t kClientSubnetExtensionCode = 8;
-  net::OptRecordRdata opt_rdata;
-  opt_rdata.AddOpt(net::OptRecordRdata::Opt(
-      kClientSubnetExtensionCode, base::StringPiece("\x00\x01\x00\x00", 4)));
-
-  const uint16_t kQueryId = 0;
-  return std::make_unique<net::DnsQuery>(
-      kQueryId, encoded_qname, net::dns_protocol::kTypeTXT, &opt_rdata);
-}
-
-bool CreateDnsTxtResponse(const net::DnsQuery& query,
-                          base::StringPiece answer,
-                          std::vector<char>* response) {
-  *response = AsVector(*query.io_buffer());
-
-  // Modify the header.
-  net::dns_protocol::Header* header =
-      reinterpret_cast<net::dns_protocol::Header*>(response->data());
-  header->ancount = base::HostToNet16(1);
-  header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse);
-
-  // The qname is at the start of the query section (just after the header).
-  const uint8_t qname_ptr = sizeof(*header);
-
-  // The answers section starts after the header and question section.
-  const size_t answers_section_offset =
-      sizeof(*header) + query.question().size();
-
-  // DNS answers section:
-  // 2 bytes - qname pointer
-  // 2 bytes - record type
-  // 2 bytes - record class
-  // 4 bytes - time-to-live
-  // 2 bytes - size of answer (N)
-  // N bytes - answer
-  // Total = 12 + N bytes
-  const size_t answers_section_size = 12 + answer.size();
-  constexpr uint32_t ttl = 86400;  // seconds
-
-  // Make space for the answers section.
-  response->insert(response->begin() + answers_section_offset,
-                   answers_section_size, 0);
-
-  // Write the answers section.
-  base::BigEndianWriter writer(response->data() + answers_section_offset,
-                               answers_section_size);
-  if (!writer.WriteU8(net::dns_protocol::kLabelPointer) ||
-      !writer.WriteU8(qname_ptr) ||
-      !writer.WriteU16(net::dns_protocol::kTypeTXT) ||
-      !writer.WriteU16(net::dns_protocol::kClassIN) || !writer.WriteU32(ttl) ||
-      !writer.WriteU16(answer.size()) ||
-      !writer.WriteBytes(answer.data(), answer.size())) {
-    return false;
-  }
-
-  if (writer.remaining() != 0) {
-    // Less than the expected amount of data was written.
-    return false;
-  }
-
-  return true;
-}
-
-bool CreateDnsErrorResponse(const net::DnsQuery& query,
-                            uint8_t rcode,
-                            std::vector<char>* response) {
-  *response = AsVector(*query.io_buffer());
-
-  // Modify the header
-  net::dns_protocol::Header* header =
-      reinterpret_cast<net::dns_protocol::Header*>(response->data());
-  header->ancount = base::HostToNet16(1);
-  header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse | rcode);
-  return true;
-}
-
-}  // namespace
-
-net::URLRequestJob* MockLogDnsTraffic::DohJobInterceptor::MaybeInterceptRequest(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate) const {
-  return new net::URLRequestErrorJob(request, network_delegate,
-                                     net::ERR_NOT_IMPLEMENTED);
-}
-
-// A container for all of the data needed for simulating a socket.
-// This is useful because Mock{Read,Write}, SequencedSocketData and
-// MockClientSocketFactory all do not take ownership of or copy their arguments,
-// so it is necessary to manage the lifetime of those arguments. Wrapping all
-// of that up in a single class simplifies this.
-class MockLogDnsTraffic::MockSocketData {
- public:
-  // A socket that expects one write and one read operation.
-  MockSocketData(const std::vector<char>& write, const std::vector<char>& read)
-      : expected_write_payload_(write),
-        expected_read_payload_(read),
-        expected_write_(net::SYNCHRONOUS,
-                        expected_write_payload_.data(),
-                        expected_write_payload_.size(),
-                        0),
-        expected_reads_{net::MockRead(net::ASYNC,
-                                      expected_read_payload_.data(),
-                                      expected_read_payload_.size(),
-                                      1),
-                        kNoMoreData},
-        socket_data_(expected_reads_, base::make_span(&expected_write_, 1)) {}
-
-  // A socket that expects one write and a read error.
-  MockSocketData(const std::vector<char>& write, net::Error error)
-      : expected_write_payload_(write),
-        expected_write_(net::SYNCHRONOUS,
-                        expected_write_payload_.data(),
-                        expected_write_payload_.size(),
-                        0),
-        expected_reads_{net::MockRead(net::ASYNC, error, 1), kNoMoreData},
-        socket_data_(expected_reads_, base::make_span(&expected_write_, 1)) {}
-
-  // A socket that expects one write and no response.
-  explicit MockSocketData(const std::vector<char>& write)
-      : expected_write_payload_(write),
-        expected_write_(net::SYNCHRONOUS,
-                        expected_write_payload_.data(),
-                        expected_write_payload_.size(),
-                        0),
-        expected_reads_{net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING, 1),
-                        kNoMoreData},
-        socket_data_(expected_reads_, base::make_span(&expected_write_, 1)) {}
-
-  ~MockSocketData() {}
-
-  void SetWriteMode(net::IoMode mode) { expected_write_.mode = mode; }
-  void SetReadMode(net::IoMode mode) { expected_reads_[0].mode = mode; }
-
-  void AddToFactory(net::MockClientSocketFactory* socket_factory) {
-    socket_factory->AddSocketDataProvider(&socket_data_);
-  }
-
- private:
-  // This class only supports one write and one read, so just need to store one
-  // payload each.
-  const std::vector<char> expected_write_payload_;
-  const std::vector<char> expected_read_payload_;
-
-  // Encapsulates the data that is expected to be written to a socket.
-  net::MockWrite expected_write_;
-
-  // Encapsulates the data/error that should be returned when reading from a
-  // socket. The second "expected" read is a sentinel (see |kNoMoreData|).
-  net::MockRead expected_reads_[2];
-
-  // Holds pointers to |expected_write_| and |expected_reads_|. This is what is
-  // added to net::MockClientSocketFactory to prepare a mock socket.
-  net::SequencedSocketData socket_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockSocketData);
-};
-
-MockLogDnsTraffic::MockLogDnsTraffic() : socket_read_mode_(net::ASYNC) {}
-
-MockLogDnsTraffic::~MockLogDnsTraffic() {}
-
-bool MockLogDnsTraffic::ExpectRequestAndErrorResponse(base::StringPiece qname,
-                                                      uint8_t rcode) {
-  std::unique_ptr<net::DnsQuery> query = CreateDnsTxtQuery(qname);
-  if (!query) {
-    return false;
-  }
-
-  std::vector<char> response;
-  if (!CreateDnsErrorResponse(*query, rcode, &response)) {
-    return false;
-  }
-
-  EmplaceMockSocketData(AsVector(*query->io_buffer()), response);
-  return true;
-}
-
-bool MockLogDnsTraffic::ExpectRequestAndSocketError(base::StringPiece qname,
-                                                    net::Error error) {
-  std::unique_ptr<net::DnsQuery> query = CreateDnsTxtQuery(qname);
-  if (!query) {
-    return false;
-  }
-
-  EmplaceMockSocketData(AsVector(*query->io_buffer()), error);
-  return true;
-}
-
-bool MockLogDnsTraffic::ExpectRequestAndTimeout(base::StringPiece qname) {
-  std::unique_ptr<net::DnsQuery> query = CreateDnsTxtQuery(qname);
-  if (!query) {
-    return false;
-  }
-
-  EmplaceMockSocketData(AsVector(*query->io_buffer()));
-
-  // Speed up timeout tests.
-  SetDnsTimeout(TestTimeouts::tiny_timeout());
-
-  return true;
-}
-
-bool MockLogDnsTraffic::ExpectRequestAndResponse(
-    base::StringPiece qname,
-    const std::vector<base::StringPiece>& txt_strings) {
-  std::string answer;
-  for (base::StringPiece str : txt_strings) {
-    // The size of the string must precede it. The size must fit into 1 byte.
-    answer.insert(answer.end(), base::checked_cast<uint8_t>(str.size()));
-    str.AppendToString(&answer);
-  }
-
-  std::unique_ptr<net::DnsQuery> query = CreateDnsTxtQuery(qname);
-  if (!query) {
-    return false;
-  }
-
-  std::vector<char> response;
-  if (!CreateDnsTxtResponse(*query, answer, &response)) {
-    return false;
-  }
-
-  EmplaceMockSocketData(AsVector(*query->io_buffer()), response);
-  return true;
-}
-
-bool MockLogDnsTraffic::ExpectLeafIndexRequestAndResponse(
-    base::StringPiece qname,
-    uint64_t leaf_index) {
-  return ExpectRequestAndResponse(qname, {base::NumberToString(leaf_index)});
-}
-
-bool MockLogDnsTraffic::ExpectAuditProofRequestAndResponse(
-    base::StringPiece qname,
-    std::vector<std::string>::const_iterator audit_path_start,
-    std::vector<std::string>::const_iterator audit_path_end) {
-  // Join nodes in the audit path into a single string.
-  std::string proof =
-      std::accumulate(audit_path_start, audit_path_end, std::string());
-
-  return ExpectRequestAndResponse(qname, {proof});
-}
-
-void MockLogDnsTraffic::InitializeDnsConfig() {
-  net::DnsConfig dns_config;
-  // Use an invalid nameserver address. This prevents the tests accidentally
-  // sending real DNS queries. The mock sockets don't care that the address
-  // is invalid.
-  dns_config.nameservers.push_back(net::IPEndPoint());
-  // Add a DoH server.
-  dns_config.dns_over_https_servers.push_back(
-      {"https://mock.http/dns-query{?dns}", true /* use_post */});
-  // Don't attempt retransmissions - just fail.
-  dns_config.attempts = 1;
-  // This ensures timeouts are long enough for memory tests.
-  dns_config.timeout = TestTimeouts::action_timeout();
-  // Simplify testing - don't require random numbers for the source port.
-  // This means our FakeRandInt function should only be called to get query
-  // IDs.
-  dns_config.randomize_ports = false;
-
-  DnsChangeNotifier::SetDnsConfig(dns_config);
-}
-
-void MockLogDnsTraffic::SetDnsConfig(const net::DnsConfig& config) {
-  DnsChangeNotifier::SetDnsConfig(config);
-}
-
-std::unique_ptr<net::DnsClient> MockLogDnsTraffic::CreateDnsClient() {
-  return net::DnsClient::CreateClientForTesting(nullptr, &socket_factory_,
-                                                base::Bind(&FakeRandInt));
-}
-
-template <typename... Args>
-void MockLogDnsTraffic::EmplaceMockSocketData(Args&&... args) {
-  mock_socket_data_.emplace_back(
-      new MockSocketData(std::forward<Args>(args)...));
-  mock_socket_data_.back()->SetReadMode(socket_read_mode_);
-  mock_socket_data_.back()->AddToFactory(&socket_factory_);
-}
-
-void MockLogDnsTraffic::SetDnsTimeout(const base::TimeDelta& timeout) {
-  net::DnsConfig dns_config;
-  DnsChangeNotifier::GetDnsConfig(&dns_config);
-  dns_config.timeout = timeout;
-  DnsChangeNotifier::SetDnsConfig(dns_config);
-}
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/mock_log_dns_traffic.h b/components/certificate_transparency/mock_log_dns_traffic.h
deleted file mode 100644
index d6cefb1d..0000000
--- a/components/certificate_transparency/mock_log_dns_traffic.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_MOCK_LOG_DNS_TRAFFIC_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_MOCK_LOG_DNS_TRAFFIC_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "net/dns/dns_client.h"
-#include "net/socket/socket_test_util.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_interceptor.h"
-
-namespace net {
-struct DnsConfig;
-}
-
-namespace certificate_transparency {
-
-// Mocks DNS requests and responses for a Certificate Transparency (CT) log.
-// This is implemented using mock sockets. Call the CreateDnsClient() method to
-// get a net::DnsClient wired up to these mock sockets.
-// The Expect*() methods must be called from within a GTest test case.
-//
-// Example Usage:
-// // net::DnsClient requires an I/O message loop for async operations.
-// base::MessageLoopForIO message_loop;
-//
-// // Create a mock NetworkChangeNotifier to propagate DNS config.
-// std::unique_ptr<net::NetworkChangeNotifier> net_change_notifier(
-//     net::NetworkChangeNotifier::CreateMock());
-//
-// MockLogDnsTraffic mock_dns;
-// mock_dns.InitializeDnsConfig();
-// // Use the Expect* methods to define expected DNS requests and responses.
-// mock_dns.ExpectLeafIndexRequestAndResponse(
-//     "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
-//     "123456");
-//
-// LogDnsClient log_client(mock_dns.CreateDnsClient(), ...);
-// log_client.QueryAuditProof("ct.test", ..., base::BindOnce(...));
-class MockLogDnsTraffic {
- public:
-  MockLogDnsTraffic();
-  ~MockLogDnsTraffic();
-
-  // Expect a CT DNS request for the domain |qname|.
-  // Such a request will receive a DNS response indicating that the error
-  // specified by |rcode| occurred. See RFC1035, Section 4.1.1 for |rcode|
-  // values.
-  // Returns false if any of the arguments are invalid.
-  WARN_UNUSED_RESULT
-  bool ExpectRequestAndErrorResponse(base::StringPiece qname, uint8_t rcode);
-
-  // Expect a CT DNS request for the domain |qname|.
-  // Such a request will trigger a socket error of type |error|.
-  // Returns false if any of the arguments are invalid.
-  WARN_UNUSED_RESULT
-  bool ExpectRequestAndSocketError(base::StringPiece qname, net::Error error);
-
-  // Expect a CT DNS request for the domain |qname|.
-  // Such a request will timeout.
-  // This will reduce the DNS timeout to minimize test duration.
-  // Returns false if |qname| is invalid.
-  WARN_UNUSED_RESULT
-  bool ExpectRequestAndTimeout(base::StringPiece qname);
-
-  // Expect a CT DNS request for the domain |qname|.
-  // Such a request will receive a DNS TXT response containing |txt_strings|.
-  // Returns false if any of the arguments are invalid.
-  WARN_UNUSED_RESULT
-  bool ExpectRequestAndResponse(
-      base::StringPiece qname,
-      const std::vector<base::StringPiece>& txt_strings);
-
-  // Expect a CT DNS request for the domain |qname|.
-  // Such a request will receive a DNS response containing |leaf_index|.
-  // A description of such a request and response can be seen here:
-  // https://github.com/google/certificate-transparency-rfcs/blob/c8844de6bd0b5d3d16bac79865e6edef533d760b/dns/draft-ct-over-dns.md#hash-query-hashquery
-  // Returns false if any of the arguments are invalid.
-  WARN_UNUSED_RESULT
-  bool ExpectLeafIndexRequestAndResponse(base::StringPiece qname,
-                                         uint64_t leaf_index);
-
-  // Expect a CT DNS request for the domain |qname|.
-  // Such a request will receive a DNS response containing the inclusion proof
-  // nodes between |audit_path_start| and |audit_path_end|.
-  // A description of such a request and response can be seen here:
-  // https://github.com/google/certificate-transparency-rfcs/blob/c8844de6bd0b5d3d16bac79865e6edef533d760b/dns/draft-ct-over-dns.md#tree-query-treequery
-  // Returns false if any of the arguments are invalid.
-  WARN_UNUSED_RESULT
-  bool ExpectAuditProofRequestAndResponse(
-      base::StringPiece qname,
-      std::vector<std::string>::const_iterator audit_path_start,
-      std::vector<std::string>::const_iterator audit_path_end);
-
-  // Sets the initial DNS config appropriate for testing.
-  // Requires that net::NetworkChangeNotifier is initialized first.
-  // The DNS config is propogated to NetworkChangeNotifier::DNSObservers
-  // asynchronously.
-  void InitializeDnsConfig();
-
-  // Sets the DNS config to |config|.
-  // Requires that net::NetworkChangeNotifier is initialized first.
-  // The DNS config is propogated to NetworkChangeNotifier::DNSObservers
-  // asynchronously.
-  void SetDnsConfig(const net::DnsConfig& config);
-
-  // Creates a DNS client that uses mock sockets.
-  // It is this DNS client that the expectations will be tested against.
-  std::unique_ptr<net::DnsClient> CreateDnsClient();
-
- private:
-  // Allows tests to change socket read mode. Only the LogDnsClient tests should
-  // need to do so, to ensure consistent behaviour regardless of mode.
-  friend class LogDnsClientTest;
-  friend class SingleTreeTrackerTest;
-
-  class DohJobInterceptor : public net::URLRequestInterceptor {
-   public:
-    DohJobInterceptor() {}
-
-    net::URLRequestJob* MaybeInterceptRequest(
-        net::URLRequest* request,
-        net::NetworkDelegate* network_delegate) const override;
-  };
-
-  class MockSocketData;
-
-  // Sets whether mock reads should complete synchronously or asynchronously.
-  // By default, they complete asynchronously.
-  void SetSocketReadMode(net::IoMode read_mode) {
-    socket_read_mode_ = read_mode;
-  }
-
-  // Constructs MockSocketData from |args| and adds it to |socket_factory_|.
-  template <typename... Args>
-  void EmplaceMockSocketData(Args&&... args);
-
-  // Sets the timeout used for DNS queries.
-  // Requires that net::NetworkChangeNotifier is initialized first.
-  // The new timeout is propogated to NetworkChangeNotifier::DNSObservers
-  // asynchronously.
-  void SetDnsTimeout(const base::TimeDelta& timeout);
-
-  // One MockSocketData for each socket that is created. This corresponds to one
-  // for each DNS request sent.
-  std::vector<std::unique_ptr<MockSocketData>> mock_socket_data_;
-  // Provides as many mock sockets as there are entries in |mock_socket_data_|.
-  net::MockClientSocketFactory socket_factory_;
-  // Controls whether mock socket reads are asynchronous.
-  net::IoMode socket_read_mode_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockLogDnsTraffic);
-};
-
-}  // namespace certificate_transparency
-
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_MOCK_LOG_DNS_TRAFFIC_H_
diff --git a/components/certificate_transparency/single_tree_tracker.cc b/components/certificate_transparency/single_tree_tracker.cc
deleted file mode 100644
index 09815d0f..0000000
--- a/components/certificate_transparency/single_tree_tracker.cc
+++ /dev/null
@@ -1,548 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/single_tree_tracker.h"
-
-#include <algorithm>
-#include <iterator>
-#include <list>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/values.h"
-#include "components/certificate_transparency/log_dns_client.h"
-#include "crypto/sha2.h"
-#include "net/base/hash_value.h"
-#include "net/base/net_errors.h"
-#include "net/cert/ct_log_verifier.h"
-#include "net/cert/merkle_audit_proof.h"
-#include "net/cert/merkle_tree_leaf.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/cert/x509_certificate.h"
-#include "net/dns/host_resolver.h"
-#include "net/log/net_log.h"
-
-using net::SHA256HashValue;
-using net::ct::MerkleAuditProof;
-using net::ct::MerkleTreeLeaf;
-using net::ct::SignedCertificateTimestamp;
-using net::ct::SignedTreeHead;
-
-// Overview of the process for auditing CT log entries
-//
-// In this file, obsered CT log entries are audited for inclusion in the CT log.
-// A pre-requirement for auditing a log entry is having a Signed Tree Head (STH)
-// from that log that is 24 hours (MMD period) after the timestamp in the SCT.
-// Log entries observed while the client has no STH from that log or an STH that
-// is too old start in the PENDING_NEWER_STH state.
-//
-// Once a fresh-enough STH is obtained, all entries that can be audited using
-// this STH move to the PENDING_INCLUSION_PROOF_REQUEST state.
-//
-// Requests for the entry index and inclusion proof are obtained using a
-// LogDnsClient instance - when an inclusion proof for an entry has been
-// successfully requested (e.g. it has not been throttled), it moves to the
-// INCLUSION_PROOF_REQUESTED state.
-//
-// Once the inclusion check is done, the entry is removed from
-// |pending_entries_|. If the inclusion check has been successful, the entry
-// is added to |checked_entries_|.
-
-namespace certificate_transparency {
-
-namespace {
-
-// Measure how often clients encounter very new SCTs, by measuring whether an
-// SCT can be checked for inclusion upon first observation.
-void LogCanBeCheckedForInclusionToUMA(
-    SCTCanBeCheckedForInclusion can_be_checked) {
-  UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.CanInclusionCheckSCT",
-                            can_be_checked, SCT_CAN_BE_CHECKED_MAX);
-}
-
-// Enum indicating the outcome of an inclusion check for a particular log
-// entry.
-//
-// Note: The numeric values are used within a histogram and should not change
-// or be re-assigned.
-enum LogEntryInclusionCheckResult {
-  // Inclusion check succeeded: Proof obtained and validated successfully.
-  GOT_VALID_INCLUSION_PROOF = 0,
-
-  // Could not get an inclusion proof.
-  FAILED_GETTING_INCLUSION_PROOF = 1,
-
-  // An inclusion proof was obtained but it is invalid.
-  GOT_INVALID_INCLUSION_PROOF = 2,
-
-  // The SCT could not be audited because the client's DNS configuration
-  // is faulty.
-  DNS_QUERY_NOT_POSSIBLE = 3,
-
-  LOG_ENTRY_INCLUSION_CHECK_RESULT_MAX
-};
-
-void LogInclusionCheckResult(LogEntryInclusionCheckResult result) {
-  UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.InclusionCheckResult",
-                            result, LOG_ENTRY_INCLUSION_CHECK_RESULT_MAX);
-}
-
-// Calculate the leaf hash of the the entry in the log represented by
-// the given |cert| and |sct|. If leaf hash calculation succeeds returns
-// true, false otherwise.
-bool GetLogEntryLeafHash(const net::X509Certificate* cert,
-                         const SignedCertificateTimestamp* sct,
-                         SHA256HashValue* leaf_hash) {
-  MerkleTreeLeaf leaf;
-  if (!GetMerkleTreeLeaf(cert, sct, &leaf))
-    return false;
-
-  std::string leaf_hash_str;
-  if (!HashMerkleTreeLeaf(leaf, &leaf_hash_str))
-    return false;
-
-  memcpy(leaf_hash->data, leaf_hash_str.data(), crypto::kSHA256Length);
-  return true;
-}
-
-// Audit state of a log entry.
-enum AuditState {
-  // Entry cannot be audited because a newer STH is needed.
-  PENDING_NEWER_STH,
-  // A leaf index has been obtained and the entry is now pending request
-  // of an inclusion proof.
-  PENDING_INCLUSION_PROOF_REQUEST,
-  // An inclusion proof for this entry has been requested from the log.
-  INCLUSION_PROOF_REQUESTED
-};
-
-// Maximal size of the checked entries cache.
-size_t kCheckedEntriesCacheSize = 100;
-
-// Maximal size of the pending entries queue.
-size_t kPendingEntriesQueueSize = 100;
-
-// Maximum Merge Delay - logs can have individual MMD, but all known logs
-// currently have 24 hours MMD and Chrome's CT policy requires an MMD
-// that's no greater than that. For simplicity, use 24 hours for all logs.
-constexpr base::TimeDelta kMaximumMergeDelay = base::TimeDelta::FromHours(24);
-
-// The log MUST incorporate the a certificate in the tree within the Maximum
-// Merge Delay, so an entry can be audited once the timestamp from the SCT +
-// MMD has passed.
-// Returns true if the timestamp from the STH is newer than SCT timestamp + MMD.
-bool IsSCTReadyForAudit(base::Time sth_timestamp, base::Time sct_timestamp) {
-  return sct_timestamp + kMaximumMergeDelay < sth_timestamp;
-}
-
-base::Value NetLogEntryAuditingEventCallback(
-    const SHA256HashValue* log_entry,
-    base::StringPiece log_id,
-    bool success,
-    net::NetLogCaptureMode capture_mode) {
-  base::DictionaryValue dict;
-
-  dict.SetString("log_entry",
-                 base::HexEncode(log_entry->data, crypto::kSHA256Length));
-  dict.SetString("log_id", base::HexEncode(log_id.data(), log_id.size()));
-  dict.SetBoolean("success", success);
-
-  return std::move(dict);
-}
-
-}  // namespace
-
-// The entry that is being audited.
-struct SingleTreeTracker::EntryToAudit {
-  base::Time sct_timestamp;
-  SHA256HashValue leaf_hash;
-  bool lookup_securely;
-
-  explicit EntryToAudit(base::Time timestamp, bool lookup_securely)
-      : sct_timestamp(timestamp), lookup_securely(lookup_securely) {}
-};
-
-// State of a log entry: its audit state and information necessary to
-// validate an inclusion proof. Gets updated as the entry transitions
-// between the different audit states.
-struct SingleTreeTracker::EntryAuditState {
-  // Current phase of inclusion check.
-  AuditState state;
-
-  // The audit proof query performed by LogDnsClient.
-  // It is null unless a query has been started.
-  std::unique_ptr<LogDnsClient::AuditProofQuery> audit_proof_query;
-
-  // The root hash of the tree for which an inclusion proof was requested.
-  // The root hash is needed after the inclusion proof is fetched for validating
-  // the inclusion proof (each inclusion proof is valid for one particular leaf,
-  // denoted by the leaf index, in exactly one particular tree, denoted by the
-  // tree size in the proof).
-  // To avoid having to re-fetch the inclusion proof if a newer STH is provided
-  // to the SingleTreeTracker, the size of the original tree for which the
-  // inclusion proof was requested is stored in |proof| and the root hash
-  // in |root_hash|.
-  std::string root_hash;
-
-  explicit EntryAuditState(AuditState state) : state(state) {}
-};
-
-class SingleTreeTracker::NetworkObserver
-    : public net::NetworkChangeNotifier::NetworkChangeObserver {
- public:
-  explicit NetworkObserver(SingleTreeTracker* parent) : parent_(parent) {
-    net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
-  }
-
-  ~NetworkObserver() override {
-    net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
-  }
-
-  // net::NetworkChangeNotifier::NetworkChangeObserver implementation.
-  void OnNetworkChanged(
-      net::NetworkChangeNotifier::ConnectionType type) override {
-    parent_->ResetPendingQueue();
-  }
-
- private:
-  SingleTreeTracker* parent_;
-};
-
-// Orders entries by the SCT timestamp. In case of tie, which is very unlikely
-// as it requires two SCTs issued from a log at exactly the same time, order
-// by leaf hash. There should never be multiple entries that are identical
-// apart from the |lookup_securely| field, so this field can be excluded from
-// the comparator.
-bool SingleTreeTracker::OrderByTimestamp::operator()(
-    const EntryToAudit& lhs,
-    const EntryToAudit& rhs) const {
-  return std::tie(lhs.sct_timestamp, lhs.leaf_hash) <
-         std::tie(rhs.sct_timestamp, rhs.leaf_hash);
-}
-
-SingleTreeTracker::SingleTreeTracker(
-    scoped_refptr<const net::CTLogVerifier> ct_log,
-    LogDnsClient* dns_client,
-    net::HostResolver* host_resolver,
-    net::NetLog* net_log)
-    : ct_log_(std::move(ct_log)),
-      checked_entries_(kCheckedEntriesCacheSize),
-      dns_client_(dns_client),
-      host_resolver_(host_resolver),
-      net_log_(net::NetLogWithSource::Make(
-          net_log,
-          net::NetLogSourceType::CT_TREE_STATE_TRACKER)),
-      network_observer_(new NetworkObserver(this)),
-      weak_factory_(this) {
-  memory_pressure_listener_.reset(new base::MemoryPressureListener(base::Bind(
-      &SingleTreeTracker::OnMemoryPressure, base::Unretained(this))));
-}
-
-SingleTreeTracker::~SingleTreeTracker() {
-  ResetPendingQueue();
-}
-
-void SingleTreeTracker::OnSCTVerified(base::StringPiece hostname,
-                                      net::X509Certificate* cert,
-                                      const SignedCertificateTimestamp* sct) {
-  DCHECK_EQ(ct_log_->key_id(), sct->log_id);
-
-  // Check that a DNS lookup for hostname has already occurred (i.e. the DNS
-  // resolver already knows that the user has been accessing that host). If not,
-  // the DNS resolver may not know that the user has been accessing that host,
-  // but performing an inclusion check would reveal that information so abort to
-  // preserve the user's privacy.
-  //
-  // It's ok to do this now, even though the inclusion check may not happen for
-  // some time, because SingleTreeTracker will discard the SCT if the network
-  // changes.
-  bool secure;
-  if (!WasLookedUpOverDNS(hostname, &secure)) {
-    LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_NO_DNS_LOOKUP);
-    return;
-  }
-
-  EntryToAudit entry(sct->timestamp, secure /* lookup_securely */);
-  if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) {
-    LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_INVALID_LEAF_HASH);
-    return;
-  }
-
-  // Avoid queueing multiple instances of the same entry, ignoring the value of
-  // the |lookup_securely| field.
-  switch (GetAuditedEntryInclusionStatus(entry, nullptr)) {
-    case SCT_NOT_OBSERVED:
-      // No need to record UMA, will be done below.
-      break;
-    case SCT_INCLUDED_IN_LOG:
-      LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_ALREADY_CHECKED);
-      return;
-    default:
-      // Already pending, either due to a newer STH or in the queue.
-      LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_ALREADY_PENDING_CHECK);
-      return;
-  }
-
-  if (pending_entries_.size() >= kPendingEntriesQueueSize) {
-    // Queue is full - cannot audit SCT.
-    LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_QUEUE_FULL);
-    return;
-  }
-
-  // If there isn't a valid STH or the STH is not fresh enough to check
-  // inclusion against, store the SCT for future checking and return.
-  if (verified_sth_.timestamp.is_null() ||
-      !IsSCTReadyForAudit(verified_sth_.timestamp, entry.sct_timestamp)) {
-    pending_entries_.insert(
-        std::make_pair(std::move(entry), EntryAuditState(PENDING_NEWER_STH)));
-
-    if (verified_sth_.timestamp.is_null()) {
-      LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED);
-    } else {
-      LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED);
-    }
-
-    return;
-  }
-
-  LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED);
-  pending_entries_.insert(std::make_pair(
-      std::move(entry), EntryAuditState(PENDING_INCLUSION_PROOF_REQUEST)));
-
-  ProcessPendingEntries();
-}
-
-void SingleTreeTracker::NewSTHObserved(const SignedTreeHead& sth) {
-  DCHECK_EQ(ct_log_->key_id(), sth.log_id);
-
-  if (!ct_log_->VerifySignedTreeHead(sth)) {
-    // Sanity check the STH; the caller should have done this
-    // already, but being paranoid here.
-    // NOTE(eranm): Right now there's no way to get rid of this check here
-    // as this is the first object in the chain that has an instance of
-    // a CTLogVerifier to verify the STH.
-    return;
-  }
-
-  // In order to avoid updating |verified_sth_| to an older STH in case
-  // an older STH is observed, check that either the observed STH is for
-  // a larger tree size or that it is for the same tree size but has
-  // a newer timestamp.
-  const bool sths_for_same_tree = verified_sth_.tree_size == sth.tree_size;
-  const bool received_sth_is_for_larger_tree =
-      (verified_sth_.tree_size < sth.tree_size);
-  const bool received_sth_is_newer = (sth.timestamp > verified_sth_.timestamp);
-
-  if (!verified_sth_.timestamp.is_null() && !received_sth_is_for_larger_tree &&
-      !(sths_for_same_tree && received_sth_is_newer)) {
-    // Observed an old STH - do nothing.
-    return;
-  }
-
-  verified_sth_ = sth;
-
-  // Find the first entry in the PENDING_NEWER_STH state - entries
-  // before that should be pending leaf index / inclusion proof, no
-  // reason to inspect them.
-  auto auditable_entries_begin = std::find_if(
-      pending_entries_.begin(), pending_entries_.end(),
-      [](std::pair<const EntryToAudit&, const EntryAuditState&> value) {
-        return value.second.state == PENDING_NEWER_STH;
-      });
-
-  // Find where to stop - this is the first entry whose timestamp + MMD
-  // is greater than the STH's timestamp.
-  auto auditable_entries_end = std::lower_bound(
-      auditable_entries_begin, pending_entries_.end(), sth.timestamp,
-      [](std::pair<const EntryToAudit&, const EntryAuditState&> value,
-         base::Time sth_timestamp) {
-        return IsSCTReadyForAudit(sth_timestamp, value.first.sct_timestamp);
-      });
-
-  // Update the state of all entries that can now be checked for inclusion.
-  for (auto curr_entry = auditable_entries_begin;
-       curr_entry != auditable_entries_end; ++curr_entry) {
-    DCHECK_EQ(curr_entry->second.state, PENDING_NEWER_STH);
-    curr_entry->second.state = PENDING_INCLUSION_PROOF_REQUEST;
-  }
-
-  if (auditable_entries_begin == auditable_entries_end)
-    return;
-
-  ProcessPendingEntries();
-}
-
-void SingleTreeTracker::ResetPendingQueue() {
-  // Move entries out of pending_entries_ prior to deleting them, in case any
-  // have inclusion checks in progress. Cancelling those checks would invoke the
-  // cancellation callback (ProcessPendingEntries()), which would attempt to
-  // access pending_entries_ while it was in the process of being deleted.
-  std::map<EntryToAudit, EntryAuditState, OrderByTimestamp> pending_entries;
-  pending_entries_.swap(pending_entries);
-}
-
-SingleTreeTracker::SCTInclusionStatus
-SingleTreeTracker::GetLogEntryInclusionStatusForTesting(
-    net::X509Certificate* cert,
-    const SignedCertificateTimestamp* sct,
-    bool* pending_lookup_securely) {
-  EntryToAudit entry(sct->timestamp, false /* lookup_securely */);
-  if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash))
-    return SCT_NOT_OBSERVED;
-  return GetAuditedEntryInclusionStatus(entry, pending_lookup_securely);
-}
-
-void SingleTreeTracker::ProcessPendingEntries() {
-  for (auto it = pending_entries_.begin(); it != pending_entries_.end(); ++it) {
-    if (it->second.state != PENDING_INCLUSION_PROOF_REQUEST) {
-      continue;
-    }
-
-    it->second.root_hash =
-        std::string(verified_sth_.sha256_root_hash, crypto::kSHA256Length);
-
-    std::string leaf_hash(
-        reinterpret_cast<const char*>(it->first.leaf_hash.data),
-        crypto::kSHA256Length);
-    net::Error result = dns_client_->QueryAuditProof(
-        ct_log_->dns_domain(), leaf_hash, it->first.lookup_securely,
-        verified_sth_.tree_size, &(it->second.audit_proof_query),
-        base::BindOnce(&SingleTreeTracker::OnAuditProofObtained,
-                       base::Unretained(this), it->first));
-    // Handling proofs returned synchronously is not implemeted.
-    DCHECK_NE(result, net::OK);
-    if (result == net::ERR_IO_PENDING) {
-      // Successfully requested an inclusion proof - change entry state
-      // and continue to the next one.
-      it->second.state = INCLUSION_PROOF_REQUESTED;
-    } else if (result == net::ERR_TEMPORARILY_THROTTLED) {
-      // Need to use a weak pointer here, as this callback could be triggered
-      // when the SingleTreeTracker is deleted (and pending queries are
-      // cancelled).
-      dns_client_->NotifyWhenNotThrottled(
-          base::BindOnce(&SingleTreeTracker::ProcessPendingEntries,
-                         weak_factory_.GetWeakPtr()));
-      // Exit the loop since all subsequent calls to QueryAuditProof
-      // will be throttled.
-      break;
-    } else if (result == net::ERR_NAME_RESOLUTION_FAILED) {
-      LogInclusionCheckResult(DNS_QUERY_NOT_POSSIBLE);
-      LogAuditResultToNetLog(it->first, false);
-      // Lookup failed due to bad DNS configuration, erase the entry and
-      // continue to the next one.
-      it = pending_entries_.erase(it);
-      // Break here if it's the last entry to avoid |it| being incremented
-      // by the for loop.
-      if (it == pending_entries_.end())
-        break;
-    } else {
-      // BUG: an invalid argument was provided or an unexpected error
-      // was returned from LogDnsClient.
-      DCHECK_EQ(result, net::ERR_INVALID_ARGUMENT);
-      NOTREACHED();
-    }
-  }
-}
-
-SingleTreeTracker::SCTInclusionStatus
-SingleTreeTracker::GetAuditedEntryInclusionStatus(
-    const EntryToAudit& entry,
-    bool* pending_lookup_securely) {
-  const auto checked_entries_iterator = checked_entries_.Get(entry.leaf_hash);
-  if (checked_entries_iterator != checked_entries_.end()) {
-    return SCT_INCLUDED_IN_LOG;
-  }
-
-  auto pending_iterator = pending_entries_.find(entry);
-  if (pending_iterator == pending_entries_.end()) {
-    return SCT_NOT_OBSERVED;
-  }
-  // Found match in |pending_entries_|, so set |pending_lookup_securely|.
-  if (pending_lookup_securely)
-    *pending_lookup_securely = pending_iterator->first.lookup_securely;
-  switch (pending_iterator->second.state) {
-    case PENDING_NEWER_STH:
-      return SCT_PENDING_NEWER_STH;
-    case PENDING_INCLUSION_PROOF_REQUEST:
-    case INCLUSION_PROOF_REQUESTED:
-      return SCT_PENDING_INCLUSION_CHECK;
-  }
-
-  NOTREACHED();
-  return SCT_NOT_OBSERVED;
-}
-
-void SingleTreeTracker::OnAuditProofObtained(const EntryToAudit& entry,
-                                             int net_error) {
-  auto it = pending_entries_.find(entry);
-  // The entry may not be present if it was evacuated due to low memory
-  // pressure.
-  if (it == pending_entries_.end())
-    return;
-
-  DCHECK_EQ(it->second.state, INCLUSION_PROOF_REQUESTED);
-
-  if (net_error != net::OK) {
-    // TODO(eranm): Should failures be cached? For now, they are not.
-    LogInclusionCheckResult(FAILED_GETTING_INCLUSION_PROOF);
-    LogAuditResultToNetLog(entry, false);
-    pending_entries_.erase(it);
-    return;
-  }
-
-  std::string leaf_hash(reinterpret_cast<const char*>(entry.leaf_hash.data),
-                        crypto::kSHA256Length);
-
-  bool verified =
-      ct_log_->VerifyAuditProof(it->second.audit_proof_query->GetProof(),
-                                it->second.root_hash, leaf_hash);
-  LogAuditResultToNetLog(entry, verified);
-
-  if (!verified) {
-    LogInclusionCheckResult(GOT_INVALID_INCLUSION_PROOF);
-  } else {
-    LogInclusionCheckResult(GOT_VALID_INCLUSION_PROOF);
-    checked_entries_.Put(entry.leaf_hash, EntryAuditResult());
-  }
-
-  pending_entries_.erase(it);
-}
-
-void SingleTreeTracker::OnMemoryPressure(
-    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
-  switch (memory_pressure_level) {
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
-      break;
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
-      ResetPendingQueue();
-      // Fall through to clearing the other cache.
-      FALLTHROUGH;
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
-      checked_entries_.Clear();
-      break;
-  }
-}
-
-void SingleTreeTracker::LogAuditResultToNetLog(const EntryToAudit& entry,
-                                               bool success) {
-  net::NetLogParametersCallback net_log_callback =
-      base::Bind(&NetLogEntryAuditingEventCallback, &entry.leaf_hash,
-                 ct_log_->key_id(), success);
-
-  net_log_.AddEvent(net::NetLogEventType::CT_LOG_ENTRY_AUDITED,
-                    net_log_callback);
-}
-
-bool SingleTreeTracker::WasLookedUpOverDNS(base::StringPiece hostname,
-                                           bool* secure) const {
-  net::HostCache::Entry::Source source;
-  net::HostCache::EntryStaleness staleness;
-  return host_resolver_->HasCached(hostname, &source, &staleness, secure) &&
-         source == net::HostCache::Entry::SOURCE_DNS &&
-         staleness.network_changes == 0;
-}
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/single_tree_tracker.h b/components/certificate_transparency/single_tree_tracker.h
deleted file mode 100644
index 1beffdb2..0000000
--- a/components/certificate_transparency/single_tree_tracker.h
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_SINGLE_TREE_TRACKER_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_SINGLE_TREE_TRACKER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/containers/mru_cache.h"
-#include "base/memory/memory_pressure_monitor.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "net/base/hash_value.h"
-#include "net/base/network_change_notifier.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/dns/host_cache.h"
-#include "net/log/net_log_with_source.h"
-
-namespace net {
-
-class CTLogVerifier;
-class HostResolver;
-class X509Certificate;
-
-namespace ct {
-
-struct MerkleAuditProof;
-struct SignedCertificateTimestamp;
-
-}  // namespace ct
-
-}  // namespace net
-
-namespace certificate_transparency {
-
-class LogDnsClient;
-
-// Enum indicating whether an SCT can be checked for inclusion and if not,
-// the reason it cannot.
-//
-// Note: The numeric values are used within a histogram and should not change
-// or be re-assigned.
-enum SCTCanBeCheckedForInclusion {
-  // If the SingleTreeTracker does not have a valid STH, then a valid STH is
-  // first required to evaluate whether the SCT can be checked for inclusion
-  // or not.
-  VALID_STH_REQUIRED = 0,
-
-  // If the STH does not cover the SCT (the timestamp in the SCT is greater than
-  // MMD + timestamp in the STH), then a newer STH is needed.
-  NEWER_STH_REQUIRED = 1,
-
-  // When an SCT is observed, if the SingleTreeTracker instance has a valid STH
-  // and the STH covers the SCT (the timestamp in the SCT is less than MMD +
-  // timestamp in the STH), then it can be checked for inclusion.
-  CAN_BE_CHECKED = 2,
-
-  // This SCT was not audited because the queue of pending entries was
-  // full.
-  NOT_AUDITED_QUEUE_FULL = 3,
-
-  // This SCT was not audited because no DNS lookup was done when first
-  // visiting the website that supplied it. It could compromise the user's
-  // privacy to do an inclusion check over DNS in this scenario.
-  NOT_AUDITED_NO_DNS_LOOKUP = 4,
-
-  // This SCT was not audited, because it was recently checked and the cached
-  // inclusion result can be used.
-  NOT_AUDITED_ALREADY_CHECKED = 5,
-
-  // This SCT is already pending an audit check, and thus can be
-  // de-duplicated.
-  NOT_AUDITED_ALREADY_PENDING_CHECK = 6,
-
-  // This SCT was not audited, as an Entry Leaf Hash could not be calculated.
-  NOT_AUDITED_INVALID_LEAF_HASH = 7,
-
-  SCT_CAN_BE_CHECKED_MAX
-};
-
-// Tracks the state of an individual Certificate Transparency Log's Merkle Tree.
-// A CT Log constantly issues Signed Tree Heads, for which every older STH must
-// be incorporated into the current/newer STH. As new certificates are logged,
-// new SCTs are produced, and eventually, those SCTs are incorporated into the
-// log and a new STH is produced, with there being an inclusion proof between
-// the SCTs and the new STH, and a consistency proof between the old STH and the
-// new STH.
-// This class receives STHs provided by/observed by the embedder, with the
-// assumption that STHs have been checked for consistency already. As SCTs are
-// observed, their status is checked against the latest STH to ensure they were
-// properly logged. If an SCT is newer than the latest STH, then this class
-// verifies that when an STH is observed that should have incorporated those
-// SCTs, the SCTs (and their corresponding entries) are present in the log.
-//
-// To accomplish this, this class needs to be notified of when new SCTs are
-// observed and when new STHs are observed. Once both SCTs and at least one
-// STH have been provided, the status for a given SCT can be queried by
-// calling GetLogEntryInclusionCheck.
-class SingleTreeTracker {
- public:
-  enum SCTInclusionStatus {
-    // SCT was not observed by this class and is not currently pending
-    // inclusion check. As there's no evidence the SCT this status relates
-    // to is verified (it was never observed via OnSCTVerified), nothing
-    // is done with it.
-    SCT_NOT_OBSERVED,
-
-    // SCT was observed but the STH known to this class is not old
-    // enough to check for inclusion, so a newer STH is needed first.
-    SCT_PENDING_NEWER_STH,
-
-    // SCT is known and there's a new-enough STH to check inclusion against.
-    // It's in the process of being checked for inclusion.
-    SCT_PENDING_INCLUSION_CHECK,
-
-    // Inclusion check succeeded.
-    SCT_INCLUDED_IN_LOG,
-  };
-
-  // Tracks new STHs and SCTs received that were issued by |ct_log|.
-  // The |dns_client| will be used to obtain inclusion proofs for these SCTs,
-  // where possible. It is not optional.
-  // The |host_resolver| will be used to ensure that DNS requests performed to
-  // obtain inclusion proofs do not compromise the user's privacy. It is not
-  // optional. It is assumed that it caches DNS lookups.
-  SingleTreeTracker(scoped_refptr<const net::CTLogVerifier> ct_log,
-                    LogDnsClient* dns_client,
-                    net::HostResolver* host_resolver,
-                    net::NetLog* net_log);
-  ~SingleTreeTracker();
-
-  // Enqueues |sct| for later inclusion checking of the given |cert|, so long as
-  // both of the following are true:
-  // a) The latest STH known for this log is older than |sct.timestamp| +
-  //    Maximum Merge Delay.
-  // b) The |hostname| for which this certificate was issued has previously been
-  //    resolved to an IP address using a DNS lookup, and the network has not
-  //    changed since. This ensures that performing an inclusion check over DNS
-  //    will not leak information to the DNS resolver.
-  // Should only be called with SCTs issued by the log this instance tracks.
-  // Hostname may be an IP literal, but a DNS lookup will not have been
-  // performed in this case so inclusion checking will not be performed.
-  // TODO(eranm): Make sure not to perform any synchronous, blocking operation
-  // here as this callback is invoked during certificate validation.
-  void OnSCTVerified(base::StringPiece hostname,
-                     net::X509Certificate* cert,
-                     const net::ct::SignedCertificateTimestamp* sct);
-
-  // After verification of the signature over the |sth|, uses this
-  // STH for future inclusion checks.
-  // Must only be called for STHs issued by the log this instance tracks.
-  void NewSTHObserved(const net::ct::SignedTreeHead& sth);
-
-  // Returns the status of a given log entry that is assembled from |cert| and
-  // |sct|. If |cert| and |sct| were not previously observed, |sct| is not an
-  // SCT for |cert| or |sct| is not for this log, and SCT_NOT_OBSERVED will be
-  // returned. If the assembled entry is pending, |pending_lookup_securely| will
-  // be set to the value of the pending match's |lookup_securely| field.
-  SCTInclusionStatus GetLogEntryInclusionStatusForTesting(
-      net::X509Certificate* cert,
-      const net::ct::SignedCertificateTimestamp* sct,
-      bool* pending_lookup_securely = nullptr);
-
- private:
-  struct EntryToAudit;
-  struct EntryAuditState;
-  struct EntryAuditResult {};
-  class NetworkObserver;
-  friend class NetworkObserver;
-
-  // Less-than comparator that sorts EntryToAudits based on the SCT timestamp,
-  // with smaller (older) SCTs appearing less than larger (newer) SCTs.
-  struct OrderByTimestamp {
-    bool operator()(const EntryToAudit& lhs, const EntryToAudit& rhs) const;
-  };
-
-  // Requests an inclusion proof for each of the entries in |pending_entries_|
-  // until throttled by the LogDnsClient.
-  void ProcessPendingEntries();
-
-  // Returns the inclusion status of the given |entry|, similar to
-  // GetLogEntryInclusionStatus(). The |entry| is an internal representation of
-  // a certificate + SCT combination, and the |lookup_securely| value in |entry|
-  // is ignored. If |entry| has a pending status, |pending_lookup_securely| will
-  // be set to the value fo the pending match's |lookup_securely| field.
-  SCTInclusionStatus GetAuditedEntryInclusionStatus(
-      const EntryToAudit& entry,
-      bool* pending_lookup_securely);
-
-  // Processes the result of obtaining an audit proof for |entry|.
-  // * If an audit proof was successfully obtained and validated,
-  //   updates |checked_entries_| so that future calls to
-  //   GetLogEntryInclusionStatus() will indicate the entry's
-  //   inclusion.
-  // * If there was a failure to obtain or validate an inclusion
-  //   proof, removes |entry| from the queue of entries to validate.
-  //   Future calls to GetLogEntryInclusionStatus() will indicate the entry
-  //   has not been observed.
-  void OnAuditProofObtained(const EntryToAudit& entry, int net_error);
-
-  // Discards all entries pending inclusion check on network change.
-  // That is done to prevent the client looking up inclusion proofs for
-  // certificates received from one network, on another network, thus
-  // leaking state between networks.
-  void ResetPendingQueue();
-
-  // Clears entries to reduce memory overhead.
-  void OnMemoryPressure(
-      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
-
-  void LogAuditResultToNetLog(const EntryToAudit& entry, bool success);
-
-  // Returns true if |hostname| has previously been looked up using DNS, and the
-  // network has not changed since.
-  bool WasLookedUpOverDNS(base::StringPiece hostname, bool* secure) const;
-
-  // Holds the latest STH fetched and verified for this log.
-  net::ct::SignedTreeHead verified_sth_;
-
-  // The log being tracked.
-  scoped_refptr<const net::CTLogVerifier> ct_log_;
-
-  // Log entries waiting to be checked for inclusion, or being checked for
-  // inclusion, and their state.
-  std::map<EntryToAudit, EntryAuditState, OrderByTimestamp> pending_entries_;
-
-  // A cache of leaf hashes identifying entries which were checked for
-  // inclusion (the key is the Leaf Hash of the log entry).
-  // NOTE: The current implementation does not cache failures, so the presence
-  // of an entry in |checked_entries_| indicates success.
-  // To extend support for caching failures, a success indicator should be
-  // added to the EntryAuditResult struct.
-  base::MRUCache<net::SHA256HashValue, EntryAuditResult> checked_entries_;
-
-  LogDnsClient* dns_client_;
-
-  net::HostResolver* host_resolver_;
-
-  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
-
-  net::NetLogWithSource net_log_;
-
-  std::unique_ptr<NetworkObserver> network_observer_;
-
-  base::WeakPtrFactory<SingleTreeTracker> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(SingleTreeTracker);
-};
-
-}  // namespace certificate_transparency
-
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_SINGLE_TREE_TRACKER_H_
diff --git a/components/certificate_transparency/single_tree_tracker_unittest.cc b/components/certificate_transparency/single_tree_tracker_unittest.cc
deleted file mode 100644
index 247d456..0000000
--- a/components/certificate_transparency/single_tree_tracker_unittest.cc
+++ /dev/null
@@ -1,1049 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/single_tree_tracker.h"
-
-#include <string>
-#include <utility>
-
-#include <memory>
-
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/base32/base32.h"
-#include "components/certificate_transparency/log_dns_client.h"
-#include "components/certificate_transparency/mock_log_dns_traffic.h"
-#include "crypto/sha2.h"
-#include "net/base/network_change_notifier.h"
-#include "net/cert/ct_log_verifier.h"
-#include "net/cert/ct_serialization.h"
-#include "net/cert/merkle_tree_leaf.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/cert/x509_certificate.h"
-#include "net/dns/dns_client.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/log/net_log.h"
-#include "net/log/test_net_log.h"
-#include "net/log/test_net_log_util.h"
-#include "net/test/ct_test_util.h"
-#include "net/url_request/url_request_filter.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::ct::SignedCertificateTimestamp;
-using net::ct::SignedTreeHead;
-using net::ct::GetSampleSignedTreeHead;
-using net::ct::GetTestPublicKeyId;
-using net::ct::GetTestPublicKey;
-using net::ct::kSthRootHashLength;
-using net::ct::GetX509CertSCT;
-
-namespace certificate_transparency {
-
-namespace {
-
-const char kHostname[] = "example.test";
-const char kCanCheckForInclusionHistogramName[] =
-    "Net.CertificateTransparency.CanInclusionCheckSCT";
-const char kInclusionCheckResultHistogramName[] =
-    "Net.CertificateTransparency.InclusionCheckResult";
-
-const char kDNSRequestSuffix[] = "dns.example.com";
-
-// These tests use a 0 time-to-live for HostCache entries, so all entries will
-// be stale. This is fine because SingleTreeTracker considers stale entries to
-// still be evidence that a DNS lookup was performed for a given hostname.
-// Ignoring stale entries could be exploited, as an attacker could set their
-// website's DNS record to have a very short TTL in order to avoid having
-// inclusion checks performed on the SCTs they provide.
-constexpr base::TimeDelta kZeroTTL;
-
-constexpr base::TimeDelta kMoreThanMMD = base::TimeDelta::FromHours(25);
-
-bool GetOldSignedTreeHead(SignedTreeHead* sth) {
-  sth->version = SignedTreeHead::V1;
-  sth->timestamp = base::Time::UnixEpoch() +
-                   base::TimeDelta::FromMilliseconds(INT64_C(1348589665525));
-  sth->tree_size = 12u;
-
-  const uint8_t kOldSTHRootHash[] = {
-      0x18, 0x04, 0x1b, 0xd4, 0x66, 0x50, 0x83, 0x00, 0x1f, 0xba, 0x8c,
-      0x54, 0x11, 0xd2, 0xd7, 0x48, 0xe8, 0xab, 0xbf, 0xdc, 0xdf, 0xd9,
-      0x21, 0x8c, 0xb0, 0x2b, 0x68, 0xa7, 0x8e, 0x7d, 0x4c, 0x23};
-  memcpy(sth->sha256_root_hash, kOldSTHRootHash, kSthRootHashLength);
-
-  sth->log_id = GetTestPublicKeyId();
-
-  const uint8_t kOldSTHSignatureData[] = {
-      0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x15, 0x7b, 0x23,
-      0x42, 0xa2, 0x5f, 0x88, 0xc9, 0x0b, 0x30, 0xa6, 0xb4, 0x49, 0x50,
-      0xb3, 0xab, 0xf5, 0x25, 0xfe, 0x27, 0xf0, 0x3f, 0x9a, 0xbf, 0xc1,
-      0x16, 0x5a, 0x7a, 0xc0, 0x62, 0x2b, 0xbb, 0x02, 0x21, 0x00, 0xe6,
-      0x57, 0xa3, 0xfe, 0xfc, 0x5a, 0x82, 0x9b, 0x29, 0x46, 0x15, 0x1d,
-      0xbc, 0xfd, 0x9e, 0x87, 0x7f, 0xd0, 0x00, 0x5d, 0x62, 0x4f, 0x9a,
-      0x1a, 0x9f, 0x20, 0x79, 0xd0, 0xc1, 0x34, 0x2e, 0x08};
-  base::StringPiece sp(reinterpret_cast<const char*>(kOldSTHSignatureData),
-                       sizeof(kOldSTHSignatureData));
-  return DecodeDigitallySigned(&sp, &(sth->signature)) && sp.empty();
-}
-
-scoped_refptr<SignedCertificateTimestamp> GetSCT() {
-  scoped_refptr<SignedCertificateTimestamp> sct;
-
-  // TODO(eranm): Move setting of the origin field to ct_test_util.cc
-  GetX509CertSCT(&sct);
-  sct->origin = SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE;
-  return sct;
-}
-
-std::string LeafHash(const net::X509Certificate* cert,
-                     const SignedCertificateTimestamp* sct) {
-  net::ct::MerkleTreeLeaf leaf;
-  if (!GetMerkleTreeLeaf(cert, sct, &leaf))
-    return std::string();
-
-  std::string leaf_hash;
-  if (!HashMerkleTreeLeaf(leaf, &leaf_hash))
-    return std::string();
-
-  return leaf_hash;
-}
-
-std::string Base32LeafHash(const net::X509Certificate* cert,
-                           const SignedCertificateTimestamp* sct) {
-  std::string leaf_hash = LeafHash(cert, sct);
-  if (leaf_hash.empty())
-    return std::string();
-
-  return base32::Base32Encode(leaf_hash,
-                              base32::Base32EncodePolicy::OMIT_PADDING);
-}
-
-// Fills in |sth| for a tree of size 2, where the root hash is a hash of
-// the test SCT (from GetX509CertSCT) and another entry,
-// whose hash is '0a' 32 times.
-bool GetSignedTreeHeadForTreeOfSize2(SignedTreeHead* sth) {
-  sth->version = SignedTreeHead::V1;
-  // Timestamp is after the timestamp of the test SCT (GetX509CertSCT)
-  // to indicate it can be audited using this STH.
-  sth->timestamp = base::Time::UnixEpoch() +
-                   base::TimeDelta::FromMilliseconds(INT64_C(1365354256089));
-  sth->tree_size = 2;
-  // Root hash is:
-  // HASH (0x01 || HASH(log entry made of test SCT) || HASH(0x0a * 32))
-  // The proof provided by FillVectorWithValidAuditProofForTreeOfSize2 would
-  // validate with this root hash for the log entry made of the test SCT +
-  // cert.
-  const uint8_t kRootHash[] = {0x16, 0x80, 0xbd, 0x5a, 0x1b, 0xc1, 0xb6, 0xcf,
-                               0x1b, 0x7e, 0x77, 0x41, 0xeb, 0xed, 0x86, 0x8b,
-                               0x73, 0x81, 0x87, 0xf5, 0xab, 0x93, 0x6d, 0xb2,
-                               0x0a, 0x79, 0x0d, 0x9e, 0x40, 0x55, 0xc3, 0xe6};
-  memcpy(sth->sha256_root_hash, reinterpret_cast<const char*>(kRootHash),
-         kSthRootHashLength);
-
-  sth->log_id = GetTestPublicKeyId();
-
-  // valid signature over the STH, using the test log key at:
-  // https://github.com/google/certificate-transparency/blob/master/test/testdata/ct-server-key.pem
-  const uint8_t kTreeHeadSignatureData[] = {
-      0x04, 0x03, 0x00, 0x46, 0x30, 0x44, 0x02, 0x20, 0x25, 0xa1, 0x9d,
-      0x7b, 0xf6, 0xe6, 0xfc, 0x47, 0xa7, 0x2d, 0xef, 0x6b, 0xf4, 0x84,
-      0x71, 0xb7, 0x7b, 0x7e, 0xd4, 0x4c, 0x7a, 0x5c, 0x4f, 0x9a, 0xb7,
-      0x04, 0x71, 0x6e, 0xd0, 0xa8, 0x0f, 0x53, 0x02, 0x20, 0x27, 0xe5,
-      0xed, 0x7d, 0xc3, 0x5d, 0x4c, 0xf0, 0x67, 0x35, 0x5d, 0x8a, 0x10,
-      0xae, 0x25, 0x87, 0x1a, 0xef, 0xea, 0xd2, 0xf7, 0xe3, 0x73, 0x2f,
-      0x07, 0xb3, 0x4b, 0xea, 0x5b, 0xdd, 0x81, 0x2d};
-
-  base::StringPiece sp(reinterpret_cast<const char*>(kTreeHeadSignatureData),
-                       sizeof(kTreeHeadSignatureData));
-  return DecodeDigitallySigned(&sp, &sth->signature);
-}
-
-void FillVectorWithValidAuditProofForTreeOfSize2(
-    std::vector<std::string>* out_proof) {
-  std::string node(crypto::kSHA256Length, '\0');
-  for (size_t i = 0; i < crypto::kSHA256Length; ++i) {
-    node[i] = static_cast<char>(0x0a);
-  }
-  out_proof->push_back(node);
-}
-
-void AddCacheEntry(net::HostCache* cache,
-                   const std::string& hostname,
-                   bool secure,
-                   net::HostCache::Entry::Source source,
-                   base::TimeDelta ttl) {
-  auto key = net::HostCache::Key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0);
-  key.secure = secure;
-  cache->Set(key, net::HostCache::Entry(net::OK, net::AddressList(), source),
-             base::TimeTicks::Now(), ttl);
-}
-
-}  // namespace
-
-class SingleTreeTrackerTest : public ::testing::Test {
-  void SetUp() override {
-    log_ = net::CTLogVerifier::Create(GetTestPublicKey(), "testlog",
-                                      kDNSRequestSuffix);
-
-    ASSERT_TRUE(log_);
-    ASSERT_EQ(log_->key_id(), GetTestPublicKeyId());
-
-    const std::string der_test_cert(net::ct::GetDerEncodedX509Cert());
-    chain_ = net::X509Certificate::CreateFromBytes(der_test_cert.data(),
-                                                   der_test_cert.length());
-    ASSERT_TRUE(chain_.get());
-    GetX509CertSCT(&cert_sct_);
-    cert_sct_->origin = SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE;
-
-    net_change_notifier_ =
-        base::WrapUnique(net::NetworkChangeNotifier::CreateMock());
-    mock_dns_.InitializeDnsConfig();
-
-    net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
-    filter->AddHostnameInterceptor(
-        "https", "mock.http",
-        std::make_unique<MockLogDnsTraffic::DohJobInterceptor>());
-  }
-
-  void TearDown() override {
-    net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
-    filter->ClearHandlers();
-  }
-
- protected:
-  void CreateTreeTracker() {
-    log_dns_client_ = std::make_unique<LogDnsClient>(
-        mock_dns_.CreateDnsClient(), new net::TestURLRequestContext(),
-        net_log_with_source_, 1);
-
-    tree_tracker_ = std::make_unique<SingleTreeTracker>(
-        log_, log_dns_client_.get(), &host_resolver_, &net_log_);
-  }
-
-  void CreateTreeTrackerWithDefaultDnsExpectation() {
-    // Default to throttling requests as it means observed log entries will
-    // be frozen in a pending state, simplifying testing of the
-    // SingleTreeTracker.
-    ASSERT_TRUE(ExpectLeafIndexRequestAndThrottle(chain_, cert_sct_));
-    CreateTreeTracker();
-  }
-
-  // Configured the |mock_dns_| to expect a request for the leaf index
-  // and have th mock DNS client throttle it.
-  bool ExpectLeafIndexRequestAndThrottle(
-      const scoped_refptr<net::X509Certificate>& chain,
-      const scoped_refptr<SignedCertificateTimestamp>& sct) {
-    return mock_dns_.ExpectRequestAndSocketError(
-        Base32LeafHash(chain.get(), sct.get()) + ".hash." + kDNSRequestSuffix,
-        net::Error::ERR_TEMPORARILY_THROTTLED);
-  }
-
-  bool MatchAuditingResultInNetLog(net::TestNetLog& net_log,
-                                   std::string expected_leaf_hash,
-                                   bool expected_success) {
-    net::TestNetLogEntry::List entries;
-
-    net_log.GetEntries(&entries);
-    if (entries.size() == 0)
-      return false;
-
-    size_t pos = net::ExpectLogContainsSomewhere(
-        entries, 0, net::NetLogEventType::CT_LOG_ENTRY_AUDITED,
-        net::NetLogEventPhase::NONE);
-
-    const net::TestNetLogEntry& logged_entry = entries[pos];
-
-    std::string logged_log_id, logged_leaf_hash;
-    if (!logged_entry.GetStringValue("log_id", &logged_log_id) ||
-        !logged_entry.GetStringValue("log_entry", &logged_leaf_hash))
-      return false;
-
-    if (base::HexEncode(GetTestPublicKeyId().data(),
-                        GetTestPublicKeyId().size()) != logged_log_id)
-      return false;
-
-    if (base::HexEncode(expected_leaf_hash.data(), expected_leaf_hash.size()) !=
-        logged_leaf_hash)
-      return false;
-
-    bool logged_success;
-    if (!logged_entry.GetBooleanValue("success", &logged_success))
-      return false;
-
-    return logged_success == expected_success;
-  }
-
-  base::test::ScopedTaskEnvironment task_environment_{
-      base::test::ScopedTaskEnvironment::MainThreadType::IO};
-  MockLogDnsTraffic mock_dns_;
-  scoped_refptr<const net::CTLogVerifier> log_;
-  std::unique_ptr<net::NetworkChangeNotifier> net_change_notifier_;
-  std::unique_ptr<LogDnsClient> log_dns_client_;
-  net::MockCachingHostResolver host_resolver_;
-  std::unique_ptr<SingleTreeTracker> tree_tracker_;
-  scoped_refptr<net::X509Certificate> chain_;
-  scoped_refptr<SignedCertificateTimestamp> cert_sct_;
-  net::TestNetLog net_log_;
-  net::NetLogWithSource net_log_with_source_;
-};
-
-// Test that an SCT is discarded if the HostResolver cache does not indicate
-// that the hostname lookup was done using DNS. To perform an inclusion check
-// in this case could compromise privacy, as the DNS resolver would learn that
-// the user had visited that host.
-TEST_F(SingleTreeTrackerTest, DiscardsSCTWhenHostnameNotLookedUpUsingDNS) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_UNKNOWN, kZeroTTL);
-
-  base::HistogramTester histograms;
-  // Provide an STH to the tree_tracker_.
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  // Make sure the SCT status is the same as if there's no STH for this log.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // The status for this SCT should still be 'not observed'.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Exactly one value should be logged, indicating that the SCT could not be
-  // checked for inclusion because of no prior DNS lookup for this hostname.
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 4, 1);
-
-  // Nothing should be logged in the result histogram or net log since an
-  // inclusion check wasn't performed.
-  histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that an SCT is discarded if the hostname it was obtained from is an IP
-// literal. To perform an inclusion check in this case could compromise privacy,
-// as the DNS resolver would learn that the user had visited that host.
-TEST_F(SingleTreeTrackerTest, DiscardsSCTWhenHostnameIsIPLiteral) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-
-  base::HistogramTester histograms;
-  // Provide an STH to the tree_tracker_.
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  // Make sure the SCT status is the same as if there's no STH for this log.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  tree_tracker_->OnSCTVerified("::1", chain_.get(), cert_sct_.get());
-
-  // The status for this SCT should still be 'not observed'.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Exactly one value should be logged, indicating that the SCT could not be
-  // checked for inclusion because of no prior DNS lookup for this hostname
-  // (because it's an IP literal).
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 4, 1);
-
-  // Nothing should be logged in the result histogram or net log since an
-  // inclusion check wasn't performed.
-  histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that an SCT is discarded if the network has changed since the hostname
-// lookup was performed. To perform an inclusion check in this case could
-// compromise privacy, as the current DNS resolver would learn that the user had
-// visited that host (it would not already know this already because the
-// hostname lookup was performed on a different network, using a different DNS
-// resolver).
-TEST_F(SingleTreeTrackerTest,
-       DiscardsSCTWhenHostnameLookedUpUsingDNSOnDiffNetwork) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  // Simulate network change.
-  host_resolver_.GetHostCache()->Invalidate();
-
-  base::HistogramTester histograms;
-  // Provide an STH to the tree_tracker_.
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  // Make sure the SCT status is the same as if there's no STH for this log.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // The status for this SCT should still be 'not observed'.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Exactly one value should be logged, indicating that the SCT could not be
-  // checked for inclusion because of no prior DNS lookup for this hostname on
-  // the current network.
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 4, 1);
-
-  // Nothing should be logged in the result histogram or net log since an
-  // inclusion check wasn't performed.
-  histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-TEST_F(SingleTreeTrackerTest, EntriesIndistinguishedBySecurity) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, true /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  base::HistogramTester histograms;
-  // Provide an STH to the tree_tracker_.
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  // Make sure the SCT status is the same as if there's no STH for this log.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // The status for this SCT should be 'pending inclusion check' with
-  // |pending_lookup_securely| set to true since the cache check will return the
-  // secure key.
-  bool pending_lookup_securely;
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get(), &pending_lookup_securely));
-  EXPECT_TRUE(pending_lookup_securely);
-
-  // Exactly one value should be logged, indicating the SCT can be checked for
-  // inclusion, as |tree_tracker_| did have a valid STH when it was notified
-  // of a new SCT.
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 2, 1);
-
-  // Simulate network change.
-  host_resolver_.GetHostCache()->Invalidate();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // The status for this SCT should still be 'pending inclusion check' with
-  // |pending_lookup_securely| set to true.
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get(), &pending_lookup_securely));
-  EXPECT_TRUE(pending_lookup_securely);
-
-  // Another value should be logged, indicating that there is already a
-  // pending audit check for this SCT.
-  histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 6, 1);
-  // Nothing should be logged in the result histogram or net log since an
-  // inclusion check wasn't performed.
-  histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that an SCT is classified as pending for a newer STH if the
-// SingleTreeTracker has not seen any STHs so far.
-TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  base::HistogramTester histograms;
-  // First make sure the SCT has not been observed at all.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // Since no STH was provided to the tree_tracker_ the status should be that
-  // the SCT is pending a newer STH.
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Expect logging of a value indicating a valid STH is required.
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 0, 1);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that an SCT is classified as pending an inclusion check if the
-// SingleTreeTracker has a fresh-enough STH to check inclusion against.
-TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  base::HistogramTester histograms;
-  // Provide an STH to the tree_tracker_.
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  // Make sure the SCT status is the same as if there's no STH for
-  // this log.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // The status for this SCT should be 'pending inclusion check' since the STH
-  // provided at the beginning of the test is newer than the SCT.
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Exactly one value should be logged, indicating the SCT can be checked for
-  // inclusion, as |tree_tracker_| did have a valid STH when it was notified
-  // of a new SCT.
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 2, 1);
-  // Nothing should be logged in the result histogram since inclusion check
-  // didn't finish.
-  histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that the SingleTreeTracker correctly queues verified SCTs for inclusion
-// checking such that, upon receiving a fresh STH, it changes the SCT's status
-// from pending newer STH to pending inclusion check.
-TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  base::HistogramTester histograms;
-  // Report an observed SCT and make sure it's in the pending newer STH
-  // state.
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
-
-  // Provide with a fresh STH
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  // Test that its status has changed.
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-  // Check that no additional UMA was logged for this case as the histogram is
-  // only supposed to measure the state of newly-observed SCTs, not pending
-  // ones.
-  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that the SingleTreeTracker does not change an SCT's status if an STH
-// from the log it was issued by is observed, but that STH is too old to check
-// inclusion against.
-TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  // Notify of an SCT and make sure it's in the 'pending newer STH' state.
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Provide an old STH for the same log.
-  SignedTreeHead sth;
-  GetOldSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  // Make sure the SCT's state hasn't changed.
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that the SingleTreeTracker correctly logs that an SCT is pending a new
-// STH, when it has a valid STH,  but the observed SCT is not covered by the
-// STH.
-TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) {
-  CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  base::HistogramTester histograms;
-  // Provide an old STH for the same log.
-  SignedTreeHead sth;
-  GetOldSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0);
-
-  // Notify of an SCT and make sure it's in the 'pending newer STH' state.
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // Exactly one value should be logged, indicating the SCT cannot be checked
-  // for inclusion as the STH is too old.
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 1, 1);
-  EXPECT_EQ(0u, net_log_.GetSize());
-}
-
-// Test that an entry transitions to the "not found" state if the LogDnsClient
-// fails to get a leaf index.
-TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterLeafIndexFetchFailure) {
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndSocketError(
-      Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
-          kDNSRequestSuffix,
-      net::Error::ERR_FAILED));
-
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Provide with a fresh STH
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-  // There should have been one NetLog event, logged with failure.
-  EXPECT_TRUE(MatchAuditingResultInNetLog(
-      net_log_, LeafHash(chain_.get(), cert_sct_.get()), false));
-}
-
-// Test that an entry transitions to the "not found" state if the LogDnsClient
-// succeeds to get a leaf index but fails to get an inclusion proof.
-TEST_F(SingleTreeTrackerTest, TestEntryNotPendingAfterInclusionCheckFailure) {
-  // Return 12 as the index of this leaf.
-  ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
-      Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
-          kDNSRequestSuffix,
-      12));
-  // Expect a request for an inclusion proof for leaf #12 in a tree of size
-  // 21, which is the size of the tree in the STH returned by
-  // GetSampleSignedTreeHead.
-  ASSERT_TRUE(mock_dns_.ExpectRequestAndSocketError(
-      std::string("0.12.21.tree.") + kDNSRequestSuffix,
-      net::Error::ERR_FAILED));
-
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Provide with a fresh STH
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-  // There should have been one NetLog event, logged with failure.
-  EXPECT_TRUE(MatchAuditingResultInNetLog(
-      net_log_, LeafHash(chain_.get(), cert_sct_.get()), false));
-}
-
-// Test that an entry transitions to the "included" state if the LogDnsClient
-// succeeds to get a leaf index and an inclusion proof.
-TEST_F(SingleTreeTrackerTest, TestEntryIncludedAfterInclusionCheckSuccess) {
-  std::vector<std::string> audit_proof;
-  FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
-
-  // Return 0 as the index for this leaf, so the proof provided
-  // later on would verify.
-  ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
-      Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
-          kDNSRequestSuffix,
-      0));
-  // The STH (later on) is for a tree of size 2 and the entry has index 0
-  // in the tree, so expect an inclusion proof for entry 0 in a tree
-  // of size 2 (0.0.2).
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      std::string("0.0.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
-      audit_proof.begin() + 1));
-
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Provide with a fresh STH, which is for a tree of size 2.
-  SignedTreeHead sth;
-  ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
-  ASSERT_TRUE(log_->VerifySignedTreeHead(sth));
-
-  tree_tracker_->NewSTHObserved(sth);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(SingleTreeTracker::SCT_INCLUDED_IN_LOG,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-  // There should have been one NetLog event, with success logged.
-  EXPECT_TRUE(MatchAuditingResultInNetLog(
-      net_log_, LeafHash(chain_.get(), cert_sct_.get()), true));
-}
-
-// Tests that inclusion checks are aborted and SCTs discarded if under critical
-// memory pressure.
-TEST_F(SingleTreeTrackerTest,
-       TestInclusionCheckCancelledIfUnderMemoryPressure) {
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Provide with a fresh STH, which is for a tree of size 2.
-  SignedTreeHead sth;
-  ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
-  ASSERT_TRUE(log_->VerifySignedTreeHead(sth));
-
-  // Make the first event that is processed a critical memory pressure
-  // notification. This should be handled before the response to the first DNS
-  // request, so no requests after the first one should be sent (the leaf index
-  // request).
-  base::MemoryPressureListener::NotifyMemoryPressure(
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
-
-  ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
-      Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
-          kDNSRequestSuffix,
-      0));
-
-  tree_tracker_->NewSTHObserved(sth);
-  base::RunLoop().RunUntilIdle();
-
-  // Expect the SCT to have been discarded.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-}
-
-// Test that pending entries transition states correctly according to the
-// STHs provided:
-// * Start without an STH.
-// * Add a collection of entries with mixed timestamps (i.e. SCTs not added
-//   in the order of their timestamps).
-// * Provide an STH that covers some of the entries, test these are audited.
-// * Provide another STH that covers more of the entries, test that the entries
-//   already audited are not audited again and that those that need to be
-//   audited are audited, while those that are not covered by that STH are
-//   not audited.
-TEST_F(SingleTreeTrackerTest, TestMultipleEntriesTransitionStateCorrectly) {
-  SignedTreeHead old_sth;
-  GetOldSignedTreeHead(&old_sth);
-
-  SignedTreeHead new_sth;
-  GetSampleSignedTreeHead(&new_sth);
-
-  base::TimeDelta kLessThanMMD = base::TimeDelta::FromHours(23);
-
-  // Assert the gap between the two timestamps is big enough so that
-  // all assumptions below on which SCT can be audited with the
-  // new STH are true.
-  ASSERT_LT(old_sth.timestamp + (kMoreThanMMD * 2), new_sth.timestamp);
-
-  // Oldest SCT - auditable by the old and new STHs.
-  scoped_refptr<SignedCertificateTimestamp> oldest_sct(GetSCT());
-  oldest_sct->timestamp = old_sth.timestamp - kMoreThanMMD;
-
-  // SCT that's older than the old STH's timestamp but by less than the MMD,
-  // so not auditable by old STH.
-  scoped_refptr<SignedCertificateTimestamp> not_auditable_by_old_sth_sct(
-      GetSCT());
-  not_auditable_by_old_sth_sct->timestamp = old_sth.timestamp - kLessThanMMD;
-
-  // SCT that's newer than the old STH's timestamp so is only auditable by
-  // the new STH.
-  scoped_refptr<SignedCertificateTimestamp> newer_than_old_sth_sct(GetSCT());
-  newer_than_old_sth_sct->timestamp = old_sth.timestamp + kLessThanMMD;
-
-  // SCT that's older than the new STH's timestamp but by less than the MMD,
-  // so isn't auditable by the new STH.
-  scoped_refptr<SignedCertificateTimestamp> not_auditable_by_new_sth_sct(
-      GetSCT());
-  not_auditable_by_new_sth_sct->timestamp = new_sth.timestamp - kLessThanMMD;
-
-  // SCT that's newer than the new STH's timestamp so isn't auditable by the
-  // the new STH.
-  scoped_refptr<SignedCertificateTimestamp> newer_than_new_sth_sct(GetSCT());
-  newer_than_new_sth_sct->timestamp = new_sth.timestamp + kLessThanMMD;
-
-  // Set up DNS expectations based on inclusion proof request order.
-  ASSERT_TRUE(ExpectLeafIndexRequestAndThrottle(chain_, oldest_sct));
-  ASSERT_TRUE(
-      ExpectLeafIndexRequestAndThrottle(chain_, not_auditable_by_old_sth_sct));
-  ASSERT_TRUE(
-      ExpectLeafIndexRequestAndThrottle(chain_, newer_than_old_sth_sct));
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  // Add SCTs in mixed order.
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(),
-                               newer_than_new_sth_sct.get());
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), oldest_sct.get());
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(),
-                               not_auditable_by_new_sth_sct.get());
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(),
-                               newer_than_old_sth_sct.get());
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(),
-                               not_auditable_by_old_sth_sct.get());
-
-  // Ensure all are in the PENDING_NEWER_STH state.
-  for (const auto& sct :
-       {oldest_sct, not_auditable_by_old_sth_sct, newer_than_old_sth_sct,
-        not_auditable_by_new_sth_sct, newer_than_new_sth_sct}) {
-    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
-                                                                  sct.get()))
-        << "SCT age: " << sct->timestamp;
-  }
-
-  // Provide the old STH, ensure only the oldest one is auditable.
-  tree_tracker_->NewSTHObserved(old_sth);
-  // Ensure all but the oldest are in the PENDING_NEWER_STH state.
-  ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), oldest_sct.get()));
-
-  for (const auto& sct :
-       {not_auditable_by_old_sth_sct, newer_than_old_sth_sct,
-        not_auditable_by_new_sth_sct, newer_than_new_sth_sct}) {
-    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
-                                                                  sct.get()))
-        << "SCT age: " << sct->timestamp;
-  }
-
-  // Provide the newer one, ensure two more are auditable but the
-  // rest aren't.
-  tree_tracker_->NewSTHObserved(new_sth);
-
-  for (const auto& sct :
-       {not_auditable_by_old_sth_sct, newer_than_old_sth_sct}) {
-    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
-                                                                  sct.get()))
-        << "SCT age: " << sct->timestamp;
-  }
-
-  for (const auto& sct :
-       {not_auditable_by_new_sth_sct, newer_than_new_sth_sct}) {
-    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
-              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
-                                                                  sct.get()))
-        << "SCT age: " << sct->timestamp;
-  }
-}
-
-// Test that if a request for an entry is throttled, it remains in a
-// pending state.
-
-// Test that if several entries are throttled, when the LogDnsClient notifies
-// of un-throttling all entries are handled.
-TEST_F(SingleTreeTrackerTest, TestThrottledEntryGetsHandledAfterUnthrottling) {
-  std::vector<std::string> audit_proof;
-  FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
-
-  ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
-      Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
-          kDNSRequestSuffix,
-      0));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      std::string("0.0.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
-      audit_proof.begin() + 1));
-
-  scoped_refptr<SignedCertificateTimestamp> second_sct(GetSCT());
-  second_sct->timestamp -= base::TimeDelta::FromHours(1);
-
-  // Process request for |second_sct|
-  ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
-      Base32LeafHash(chain_.get(), second_sct.get()) + ".hash." +
-          kDNSRequestSuffix,
-      1));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      std::string("0.1.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
-      audit_proof.begin() + 1));
-
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  SignedTreeHead sth;
-  ASSERT_TRUE(GetSignedTreeHeadForTreeOfSize2(&sth));
-  tree_tracker_->NewSTHObserved(sth);
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), second_sct.get());
-
-  // Both entries should be in the pending state, the first because the
-  // LogDnsClient did not invoke the callback yet, the second one because
-  // the LogDnsClient is "busy" with the first entry and so would throttle.
-  ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-  ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), second_sct.get()));
-
-  // Process pending DNS queries so later assertions are on handling
-  // of the entries based on replies received.
-  base::RunLoop().RunUntilIdle();
-
-  // Check that the first sct is included in the log.
-  ASSERT_EQ(SingleTreeTracker::SCT_INCLUDED_IN_LOG,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Check that the second SCT got an invalid proof and is not included, rather
-  // than being in the pending state.
-  ASSERT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), second_sct.get()));
-}
-
-// Test that proof fetching failure due to DNS config errors is handled
-// correctly:
-//   (1) Entry removed from pending queue.
-//   (2) UMA logged
-TEST_F(SingleTreeTrackerTest,
-       TestProofLookupDueToBadDNSConfigHandledCorrectly) {
-  base::HistogramTester histograms;
-  // Provide an STH to the tree_tracker_.
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-
-  // Clear existing DNS configuration, so that the DnsClient created
-  // by the MockLogDnsTraffic has no valid DnsConfig.
-  net_change_notifier_.reset();
-  net_change_notifier_ =
-      base::WrapUnique(net::NetworkChangeNotifier::CreateMock());
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  tree_tracker_->NewSTHObserved(sth);
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-
-  // Make sure the SCT status indicates the entry has been removed from
-  // the SingleTreeTracker's internal queue as the DNS lookup failed
-  // synchronously.
-  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatusForTesting(
-                chain_.get(), cert_sct_.get()));
-
-  // Exactly one value should be logged, indicating the SCT can be checked for
-  // inclusion, as |tree_tracker_| did have a valid STH when it was notified
-  // of a new SCT.
-  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 2, 1);
-  // Failure due to DNS configuration should be logged in the result histogram.
-  histograms.ExpectUniqueSample(kInclusionCheckResultHistogramName, 3, 1);
-}
-
-// Test that entries are no longer pending after a network state
-// change.
-TEST_F(SingleTreeTrackerTest, DiscardsPendingEntriesAfterNetworkChange) {
-  // Setup expectations for 2 SCTs to pass inclusion checking.
-  // However, the first should be cancelled half way through (when the network
-  // change occurs) and the second should be throttled (and then cancelled) so,
-  // by the end of test, neither should actually have passed the checks.
-  std::vector<std::string> audit_proof;
-  FillVectorWithValidAuditProofForTreeOfSize2(&audit_proof);
-
-  ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
-      Base32LeafHash(chain_.get(), cert_sct_.get()) + ".hash." +
-          kDNSRequestSuffix,
-      0));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      std::string("0.0.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
-      audit_proof.begin() + 1));
-
-  scoped_refptr<SignedCertificateTimestamp> second_sct(GetSCT());
-  second_sct->timestamp -= base::TimeDelta::FromHours(1);
-
-  ASSERT_TRUE(mock_dns_.ExpectLeafIndexRequestAndResponse(
-      Base32LeafHash(chain_.get(), second_sct.get()) + ".hash." +
-          kDNSRequestSuffix,
-      1));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      std::string("0.1.2.tree.") + kDNSRequestSuffix, audit_proof.begin(),
-      audit_proof.begin() + 1));
-
-  CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
-                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
-
-  // Provide an STH to the tree_tracker_.
-  SignedTreeHead sth;
-  GetSignedTreeHeadForTreeOfSize2(&sth);
-  tree_tracker_->NewSTHObserved(sth);
-
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), second_sct.get());
-
-  for (auto sct : {cert_sct_, second_sct}) {
-    EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
-                                                                  sct.get()));
-  }
-
-  net_change_notifier_->NotifyObserversOfNetworkChangeForTests(
-      net::NetworkChangeNotifier::CONNECTION_UNKNOWN);
-  base::RunLoop().RunUntilIdle();
-
-  for (auto sct : {cert_sct_, second_sct}) {
-    EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
-                                                                  sct.get()));
-  }
-}
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/sth_distributor.cc b/components/certificate_transparency/sth_distributor.cc
deleted file mode 100644
index 3382095..0000000
--- a/components/certificate_transparency/sth_distributor.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/sth_distributor.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "net/cert/signed_tree_head.h"
-
-namespace {
-const uint8_t kPilotLogID[] = {0xa4, 0xb9, 0x09, 0x90, 0xb4, 0x18, 0x58, 0x14,
-                               0x87, 0xbb, 0x13, 0xa2, 0xcc, 0x67, 0x70, 0x0a,
-                               0x3c, 0x35, 0x98, 0x04, 0xf9, 0x1b, 0xdf, 0xb8,
-                               0xe3, 0x77, 0xcd, 0x0e, 0xc8, 0x0d, 0xdc, 0x10};
-}
-
-namespace certificate_transparency {
-
-STHDistributor::STHDistributor()
-    : observer_list_(base::ObserverListPolicy::EXISTING_ONLY) {}
-
-STHDistributor::~STHDistributor() = default;
-
-void STHDistributor::NewSTHObserved(const net::ct::SignedTreeHead& sth) {
-  auto it = std::find_if(observed_sths_.begin(), observed_sths_.end(),
-                         [&sth](const net::ct::SignedTreeHead& other) {
-                           return sth.log_id == other.log_id;
-                         });
-
-  if (it == observed_sths_.end())
-    observed_sths_.push_back(sth);
-  else
-    *it = sth;
-
-  for (auto& observer : observer_list_)
-    observer.NewSTHObserved(sth);
-
-  if (sth.log_id.compare(0, sth.log_id.size(),
-                         reinterpret_cast<const char*>(kPilotLogID),
-                         sizeof(kPilotLogID)) != 0)
-    return;
-
-  const base::TimeDelta sth_age = base::Time::Now() - sth.timestamp;
-  UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertificateTransparency.PilotSTHAge", sth_age,
-                             base::TimeDelta::FromHours(1),
-                             base::TimeDelta::FromDays(4), 100);
-}
-
-void STHDistributor::RegisterObserver(STHObserver* observer) {
-  observer_list_.AddObserver(observer);
-  // Make a local copy, because notifying the |observer| of a
-  // new STH may result in this class being notified of a
-  // (different) new STH, thus invalidating the iterator.
-  std::vector<net::ct::SignedTreeHead> local_sths(observed_sths_);
-
-  for (const auto& sth : local_sths)
-    observer->NewSTHObserved(sth);
-}
-
-void STHDistributor::UnregisterObserver(STHObserver* observer) {
-  observer_list_.RemoveObserver(observer);
-}
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/sth_distributor.h b/components/certificate_transparency/sth_distributor.h
deleted file mode 100644
index db96a5d..0000000
--- a/components/certificate_transparency/sth_distributor.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_DISTRIBUTOR_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_DISTRIBUTOR_H_
-
-#include <vector>
-
-#include "base/observer_list.h"
-#include "components/certificate_transparency/sth_observer.h"
-#include "components/certificate_transparency/sth_reporter.h"
-#include "net/base/net_export.h"
-
-namespace net {
-namespace ct {
-struct SignedTreeHead;
-}  // namespace ct
-}  // namespace net
-
-namespace certificate_transparency {
-
-// A proxy for delegating new STH notifications to all registered
-// observers.
-// For each |observer| registered with RegisterObserver, the
-// NewSTHObserved method will be called whenever the STHDistributor's
-// NewSTHObserved method is invoked.
-class STHDistributor : public STHObserver, public STHReporter {
- public:
-  STHDistributor();
-  ~STHDistributor() override;
-
-  // STHObserver implementation.
-  void NewSTHObserved(const net::ct::SignedTreeHead& sth) override;
-
-  // STHReporter implementation
-  // Registers |observer| for new STH notifications. On registration,
-  // the |observer| will be notified of the latest STH for each log tha the
-  // STHDistributor has observed.
-  void RegisterObserver(STHObserver* observer) override;
-
-  // Unregisters |observer|, which must have been previously
-  // registered via RegisterObserver()
-  void UnregisterObserver(STHObserver* observer) override;
-
- private:
-  // STHs from logs, one for each log.
-  std::vector<net::ct::SignedTreeHead> observed_sths_;
-
-  // The observers for new STH notifications.
-  base::ObserverList<STHObserver, true>::Unchecked observer_list_;
-};
-
-}  // namespace certificate_transparency
-
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_DISTRIBUTOR_H_
diff --git a/components/certificate_transparency/sth_distributor_unittest.cc b/components/certificate_transparency/sth_distributor_unittest.cc
deleted file mode 100644
index d4ade2e..0000000
--- a/components/certificate_transparency/sth_distributor_unittest.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/sth_distributor.h"
-
-#include <map>
-#include <string>
-
-#include "base/test/metrics/histogram_tester.h"
-#include "components/certificate_transparency/sth_observer.h"
-#include "crypto/sha2.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/test/ct_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace certificate_transparency {
-
-namespace {
-
-// An STHObserver implementation that simply stores all
-// observed STHs, keyed by log ID.
-class StoringSTHObserver : public STHObserver {
- public:
-  void NewSTHObserved(const net::ct::SignedTreeHead& sth) override {
-    sths[sth.log_id] = sth;
-  }
-
-  std::map<std::string, net::ct::SignedTreeHead> sths;
-};
-
-class STHDistributorTest : public ::testing::Test {
- public:
-  STHDistributorTest() = default;
-
-  void SetUp() override {
-    ASSERT_TRUE(GetSampleSignedTreeHead(&sample_sth_));
-    sample_sth_.log_id = net::ct::GetTestPublicKeyId();
-  }
-
- protected:
-  STHDistributor distributor_;
-  net::ct::SignedTreeHead sample_sth_;
-};
-
-// Test that when a new observer is registered, the STHDistributor notifies it
-// of all the observed STHs it received so far.
-// This test makes sure that all observed STHs are reported to the observer.
-TEST_F(STHDistributorTest, NotifiesOfExistingSTHs) {
-  // Create an STH that differs from the |sample_sth_| by belonging to a
-  // different log.
-  const std::string other_log = "another log";
-  net::ct::SignedTreeHead second_sth(sample_sth_);
-  second_sth.log_id = other_log;
-
-  // Notify |distributor_| of both STHs.
-  distributor_.NewSTHObserved(sample_sth_);
-  distributor_.NewSTHObserved(second_sth);
-
-  StoringSTHObserver observer;
-  distributor_.RegisterObserver(&observer);
-
-  // Check that two STHs from different logs received prior to observer
-  // registration were reported to the observer once registered.
-  EXPECT_EQ(2u, observer.sths.size());
-  EXPECT_EQ(1u, observer.sths.count(other_log));
-  distributor_.UnregisterObserver(&observer);
-}
-
-// Test that histograms are properly recorded for the STH age when an STH
-// from Google's Pilot log is observed.
-TEST_F(STHDistributorTest, LogsUMAForPilotSTH) {
-  const char kPilotSTHAgeHistogram[] =
-      "Net.CertificateTransparency.PilotSTHAge";
-  base::HistogramTester histograms;
-  histograms.ExpectTotalCount(kPilotSTHAgeHistogram, 0);
-
-  const uint8_t kPilotLogID[] = {
-      0xa4, 0xb9, 0x09, 0x90, 0xb4, 0x18, 0x58, 0x14, 0x87, 0xbb, 0x13,
-      0xa2, 0xcc, 0x67, 0x70, 0x0a, 0x3c, 0x35, 0x98, 0x04, 0xf9, 0x1b,
-      0xdf, 0xb8, 0xe3, 0x77, 0xcd, 0x0e, 0xc8, 0x0d, 0xdc, 0x10};
-  sample_sth_.log_id = std::string(reinterpret_cast<const char*>(kPilotLogID),
-                                   crypto::kSHA256Length);
-
-  distributor_.NewSTHObserved(sample_sth_);
-  histograms.ExpectTotalCount(kPilotSTHAgeHistogram, 1);
-}
-
-// Test that the STHDistributor updates, rather than accumulates, STHs
-// coming from the same log.
-// This is tested by notifying the STHDistributor of an STH, modifying that
-// STH, notifying the STHDistributor of the modified STH, then registering
-// an observer which should get notified only once, with the modified STH.
-TEST_F(STHDistributorTest, UpdatesObservedSTHData) {
-  // Observe an initial STH
-  StoringSTHObserver observer;
-  distributor_.RegisterObserver(&observer);
-
-  distributor_.NewSTHObserved(sample_sth_);
-
-  EXPECT_EQ(1u, observer.sths.size());
-  EXPECT_EQ(sample_sth_, observer.sths[net::ct::GetTestPublicKeyId()]);
-
-  // Observe a new STH. "new" simply means that it is a more recently observed
-  // SignedTreeHead for the given log ID, not necessarily that it's newer
-  // chronologically (the timestamp) or the log state (the tree size).
-  // To make sure the more recently observed SignedTreeHead is returned, just
-  // modify some fields.
-  net::ct::SignedTreeHead new_sth = sample_sth_;
-  new_sth.tree_size++;
-  new_sth.timestamp -= base::TimeDelta::FromSeconds(3);
-
-  distributor_.NewSTHObserved(new_sth);
-  // The STH should have been broadcast to existing observers.
-  EXPECT_EQ(1u, observer.sths.size());
-  EXPECT_NE(sample_sth_, observer.sths[net::ct::GetTestPublicKeyId()]);
-  EXPECT_EQ(new_sth, observer.sths[net::ct::GetTestPublicKeyId()]);
-
-  // Registering a new observer should only receive the most recently observed
-  // STH.
-  StoringSTHObserver new_observer;
-  distributor_.RegisterObserver(&new_observer);
-  EXPECT_EQ(1u, new_observer.sths.size());
-  EXPECT_NE(sample_sth_, new_observer.sths[net::ct::GetTestPublicKeyId()]);
-  EXPECT_EQ(new_sth, new_observer.sths[net::ct::GetTestPublicKeyId()]);
-
-  distributor_.UnregisterObserver(&new_observer);
-  distributor_.UnregisterObserver(&observer);
-}
-
-}  // namespace
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/sth_observer.h b/components/certificate_transparency/sth_observer.h
deleted file mode 100644
index 62742ea5..0000000
--- a/components/certificate_transparency/sth_observer.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_OBSERVER_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_OBSERVER_H_
-
-#include <set>
-
-namespace net {
-namespace ct {
-struct SignedTreeHead;
-}  // namespace ct
-}  // namespace net
-
-namespace certificate_transparency {
-
-// Interface for receiving notifications of new STHs observed.
-class STHObserver {
- public:
-  virtual ~STHObserver() {}
-
-  // Called with a new |sth| when one is observed.
-  virtual void NewSTHObserved(const net::ct::SignedTreeHead& sth) = 0;
-};
-
-}  // namespace certificate_transparency
-
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_OBSERVER_H_
diff --git a/components/certificate_transparency/sth_reporter.h b/components/certificate_transparency/sth_reporter.h
deleted file mode 100644
index c4eea96..0000000
--- a/components/certificate_transparency/sth_reporter.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_REPORTER_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_REPORTER_H_
-
-#include <set>
-
-namespace certificate_transparency {
-
-class STHObserver;
-
-// Interface for registering/unregistering observers.
-class STHReporter {
- public:
-  virtual ~STHReporter() {}
-
-  virtual void RegisterObserver(STHObserver* observer) = 0;
-  virtual void UnregisterObserver(STHObserver* observer) = 0;
-};
-
-}  // namespace certificate_transparency
-
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_STH_REPORTER_H_
diff --git a/components/certificate_transparency/tree_state_tracker.cc b/components/certificate_transparency/tree_state_tracker.cc
deleted file mode 100644
index 3b87858..0000000
--- a/components/certificate_transparency/tree_state_tracker.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2015 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/certificate_transparency/tree_state_tracker.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/feature_list.h"
-#include "components/certificate_transparency/features.h"
-#include "components/certificate_transparency/log_dns_client.h"
-#include "components/certificate_transparency/single_tree_tracker.h"
-#include "net/base/network_change_notifier.h"
-#include "net/cert/ct_log_verifier.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/cert/x509_certificate.h"
-#include "net/dns/dns_client.h"
-#include "net/log/net_log.h"
-
-using net::X509Certificate;
-using net::CTLogVerifier;
-using net::ct::SignedCertificateTimestamp;
-using net::ct::SignedTreeHead;
-
-namespace {
-const size_t kMaxConcurrentDnsQueries = 1;
-}
-
-namespace certificate_transparency {
-
-TreeStateTracker::TreeStateTracker(
-    std::vector<scoped_refptr<const CTLogVerifier>> ct_logs,
-    net::HostResolver* host_resolver,
-    net::URLRequestContext* url_request_context,
-    net::NetLog* net_log) {
-  std::unique_ptr<net::DnsClient> dns_client =
-      net::DnsClient::CreateClient(net_log);
-  dns_client_ = std::make_unique<LogDnsClient>(
-      std::move(dns_client), url_request_context,
-      net::NetLogWithSource::Make(net_log,
-                                  net::NetLogSourceType::CT_TREE_STATE_TRACKER),
-      kMaxConcurrentDnsQueries);
-
-  for (const auto& log : ct_logs) {
-    tree_trackers_[log->key_id()].reset(
-        new SingleTreeTracker(log, dns_client_.get(), host_resolver, net_log));
-  }
-}
-
-TreeStateTracker::~TreeStateTracker() {}
-
-void TreeStateTracker::OnSCTVerified(base::StringPiece hostname,
-                                     X509Certificate* cert,
-                                     const SignedCertificateTimestamp* sct) {
-  auto it = tree_trackers_.find(sct->log_id);
-  // Ignore if the SCT is from an unknown log.
-  if (it == tree_trackers_.end())
-    return;
-
-  it->second->OnSCTVerified(hostname, cert, sct);
-}
-
-void TreeStateTracker::NewSTHObserved(const SignedTreeHead& sth) {
-  auto it = tree_trackers_.find(sth.log_id);
-  // Is the STH from a known log? Since STHs can be provided from external
-  // sources for logs not yet recognized by this client, return, rather than
-  // DCHECK.
-  if (it == tree_trackers_.end())
-    return;
-
-  it->second->NewSTHObserved(sth);
-}
-
-}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/tree_state_tracker.h b/components/certificate_transparency/tree_state_tracker.h
deleted file mode 100644
index 97a22ad..0000000
--- a/components/certificate_transparency/tree_state_tracker.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_TREE_STATE_TRACKER_H_
-#define COMPONENTS_CERTIFICATE_TRANSPARENCY_TREE_STATE_TRACKER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/memory/ref_counted.h"
-#include "components/certificate_transparency/sth_observer.h"
-#include "net/cert/ct_verifier.h"
-#include "net/url_request/url_request_context.h"
-
-namespace net {
-class NetLog;
-class CTLogVerifier;
-class HostResolver;
-class X509Certificate;
-
-namespace ct {
-struct SignedCertificateTimestamp;
-struct SignedTreeHead;
-}  // namespace ct
-
-}  // namespace net
-
-namespace certificate_transparency {
-class LogDnsClient;
-class SingleTreeTracker;
-
-// This class receives notifications of new Signed Tree Heads (STHs) and
-// verified Signed Certificate Timestamps (SCTs) and delegates them to
-// the SingleTreeTracker tracking the CT log they relate to.
-// TODO(eranm): Export the inclusion check status of SCTs+Certs so it can
-// be used in the DevTools Security panel, for example. See
-// https://crbug.com/506227#c22.
-class TreeStateTracker : public net::CTVerifier::Observer, public STHObserver {
- public:
-  // Tracks the state of the logs provided in |ct_logs|. An instance of this
-  // class only tracks the logs provided in the constructor. The implementation
-  // is based on the assumption that the list of recognized logs does not change
-  // during the object's life time.
-  // Observed STHs from logs not in this list will be simply ignored.
-  TreeStateTracker(std::vector<scoped_refptr<const net::CTLogVerifier>> ct_logs,
-                   net::HostResolver* host_resolver,
-                   net::URLRequestContext* url_request_context,
-                   net::NetLog* net_log);
-  ~TreeStateTracker() override;
-
-  // net::ct::CTVerifier::Observer implementation.
-  // Delegates to the tree tracker corresponding to the log that issued the SCT.
-  void OnSCTVerified(base::StringPiece hostname,
-                     net::X509Certificate* cert,
-                     const net::ct::SignedCertificateTimestamp* sct) override;
-
-  // STHObserver implementation.
-  // Delegates to the tree tracker corresponding to the log that issued the STH.
-  void NewSTHObserved(const net::ct::SignedTreeHead& sth) override;
-
- private:
-  // A Log DNS client for fetching inclusion proof and leaf indices from
-  // DNS front-end of CT logs.
-  // Shared between all SingleTreeTrackers, for rate-limiting across all
-  // trackers. Must be deleted after the tree trackers.
-  std::unique_ptr<LogDnsClient> dns_client_;
-
-  // Holds the SingleTreeTracker for each log
-  std::map<std::string, std::unique_ptr<SingleTreeTracker>> tree_trackers_;
-
-  DISALLOW_COPY_AND_ASSIGN(TreeStateTracker);
-};
-
-}  // namespace certificate_transparency
-
-#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_TREE_STATE_TRACKER_H_
diff --git a/components/certificate_transparency/tree_state_tracker_unittest.cc b/components/certificate_transparency/tree_state_tracker_unittest.cc
deleted file mode 100644
index 4dac773..0000000
--- a/components/certificate_transparency/tree_state_tracker_unittest.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/certificate_transparency/tree_state_tracker.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/certificate_transparency/features.h"
-#include "net/base/net_errors.h"
-#include "net/cert/ct_log_verifier.h"
-#include "net/cert/ct_serialization.h"
-#include "net/cert/merkle_tree_leaf.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/cert/x509_certificate.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/log/net_log.h"
-#include "net/log/test_net_log.h"
-#include "net/test/ct_test_util.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::ct::SignedCertificateTimestamp;
-using net::ct::SignedTreeHead;
-using net::ct::GetSampleSignedTreeHead;
-using net::ct::GetTestPublicKeyId;
-using net::ct::GetTestPublicKey;
-using net::ct::kSthRootHashLength;
-using net::ct::GetX509CertSCT;
-
-constexpr char kHostname[] = "example.test";
-constexpr base::TimeDelta kZeroTTL;
-
-namespace certificate_transparency {
-
-class TreeStateTrackerTest : public ::testing::Test {
-  void SetUp() override {
-    log_ = net::CTLogVerifier::Create(GetTestPublicKey(), "testlog",
-                                      "unresolvable.invalid");
-
-    ASSERT_TRUE(log_);
-    ASSERT_EQ(log_->key_id(), GetTestPublicKeyId());
-
-    const std::string der_test_cert(net::ct::GetDerEncodedX509Cert());
-    chain_ = net::X509Certificate::CreateFromBytes(der_test_cert.data(),
-                                                   der_test_cert.length());
-    ASSERT_TRUE(chain_.get());
-    GetX509CertSCT(&cert_sct_);
-    cert_sct_->origin = SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE;
-  }
-
- protected:
-  base::test::ScopedTaskEnvironment task_environment_{
-      base::test::ScopedTaskEnvironment::MainThreadType::IO};
-  scoped_refptr<const net::CTLogVerifier> log_;
-  net::MockCachingHostResolver host_resolver_;
-  std::unique_ptr<TreeStateTracker> tree_tracker_;
-  scoped_refptr<net::X509Certificate> chain_;
-  scoped_refptr<SignedCertificateTimestamp> cert_sct_;
-  net::TestNetLog net_log_;
-};
-
-// Test that a new STH & SCT are delegated correctly to a
-// SingleTreeTracker instance created by the TreeStateTracker.
-// This is verified by looking for a single event on the net_log_
-// passed into the TreeStateTracker c'tor.
-TEST_F(TreeStateTrackerTest, TestDelegatesCorrectly) {
-  std::vector<scoped_refptr<const net::CTLogVerifier>> verifiers;
-  verifiers.push_back(log_);
-
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(kCTLogAuditing);
-
-  tree_tracker_ = std::make_unique<TreeStateTracker>(
-      verifiers, &host_resolver_, new net::TestURLRequestContext(), &net_log_);
-
-  // Add a cache entry for kHostname that indicates it was looked up over DNS.
-  // SingleTreeTracker requires this before it will request an inclusion proof,
-  // as otherwise it would reveal to the DNS resolver which server a user
-  // visited. If the server was already looked up via DNS though, that that
-  // information is already known to the DNS resolver so there is then no harm.
-  host_resolver_.GetHostCache()->Set(
-      net::HostCache::Key(kHostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0),
-      net::HostCache::Entry(net::OK, net::AddressList(),
-                            net::HostCache::Entry::SOURCE_DNS),
-      base::TimeTicks::Now(), kZeroTTL);
-
-  SignedTreeHead sth;
-  GetSampleSignedTreeHead(&sth);
-  ASSERT_EQ(log_->key_id(), sth.log_id);
-  tree_tracker_->NewSTHObserved(sth);
-
-  ASSERT_EQ(log_->key_id(), cert_sct_->log_id);
-  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  base::RunLoop().RunUntilIdle();
-
-  net::ct::MerkleTreeLeaf leaf;
-  ASSERT_TRUE(GetMerkleTreeLeaf(chain_.get(), cert_sct_.get(), &leaf));
-
-  std::string leaf_hash;
-  ASSERT_TRUE(HashMerkleTreeLeaf(leaf, &leaf_hash));
-  // There should be one NetLog event.
-  EXPECT_EQ(1u, net_log_.GetSize());
-}
-
-}  // namespace certificate_transparency
diff --git a/components/component_updater/component_installer.cc b/components/component_updater/component_installer.cc
index 7f30edc8..2cf32b9c 100644
--- a/components/component_updater/component_installer.cc
+++ b/components/component_updater/component_installer.cc
@@ -176,13 +176,6 @@
   current_version_ = version;
   current_install_dir_ = install_path;
 
-  // Invoke |ComponentReady| on the main thread, then after this task has
-  // completed, post a task to call the lamda below using the task scheduler.
-  // The task scheduler PostTaskAndReply call requires the caller to run on
-  // a sequence. This code is not running on a sequence, therefore, there
-  // are two tasks posted to the main thread runner, to ensure that
-  // the |callback| is invoked by the task scheduler after |ComponentReady| has
-  // returned.
   main_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&ComponentInstaller::ComponentReady, this,
                                 std::move(manifest)));
diff --git a/components/components_strings.grd b/components/components_strings.grd
index b334ee32..f35232385 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -208,6 +208,7 @@
       <part file="login_dialog_strings.grdp" />
       <part file="new_or_sad_tab_strings.grdp" />
       <part file="ntp_snippets_strings.grdp" />
+      <part file="ntp_tiles_strings.grdp" />
       <part file="omnibox_strings.grdp" />
       <part file="page_info_strings.grdp" />
       <part file="password_manager_strings.grdp" />
diff --git a/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureController.java b/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureController.java
index 8de95c9..118b4fb 100644
--- a/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureController.java
+++ b/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureController.java
@@ -34,6 +34,16 @@
     public abstract boolean shouldStartCapture();
 
     /**
+     * Clear all ContentCapture data associated with Chrome.
+     */
+    public void clearAllContentCaptureData() {}
+
+    /**
+     * Clear ContentCapture data for specific URLs.
+     */
+    public void clearContentCaptureDataForURLs(String[] urlsToDelete) {}
+
+    /**
      * Invoked by native side to pull the whitelist, the subclass should implement this and set
      * the whitelist by call setWhiteList.
      */
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index 083de9c2..5845e48 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -306,10 +306,7 @@
           jinclude_subdomains,
           base::Time::UnixEpoch() +
               base::TimeDelta::FromMilliseconds(jexpiration_time)));
-  size_t hash_count = env->GetArrayLength(jhashes);
-  for (size_t i = 0; i < hash_count; ++i) {
-    ScopedJavaLocalRef<jbyteArray> bytes_array(
-        env, static_cast<jbyteArray>(env->GetObjectArrayElement(jhashes, i)));
+  for (auto bytes_array : jhashes.ReadElements<jbyteArray>()) {
     static_assert(std::is_pod<net::SHA256HashValue>::value,
                   "net::SHA256HashValue is not POD");
     static_assert(sizeof(net::SHA256HashValue) * CHAR_BIT == 256,
diff --git a/components/cronet/stale_host_resolver.cc b/components/cronet/stale_host_resolver.cc
index e2ccbaa1..68d1ea7 100644
--- a/components/cronet/stale_host_resolver.cc
+++ b/components/cronet/stale_host_resolver.cc
@@ -455,14 +455,6 @@
       optional_parameters.value_or(ResolveHostParameters()), tick_clock_);
 }
 
-bool StaleHostResolver::HasCached(base::StringPiece hostname,
-                                  net::HostCache::Entry::Source* source_out,
-                                  net::HostCache::EntryStaleness* stale_out,
-                                  bool* secure_out) const {
-  return inner_resolver_->HasCached(hostname, source_out, stale_out,
-                                    secure_out);
-}
-
 net::HostCache* StaleHostResolver::GetHostCache() {
   return inner_resolver_->GetHostCache();
 }
diff --git a/components/cronet/stale_host_resolver.h b/components/cronet/stale_host_resolver.h
index fa122002..568e1949 100644
--- a/components/cronet/stale_host_resolver.h
+++ b/components/cronet/stale_host_resolver.h
@@ -85,10 +85,6 @@
   // The remaining public methods pass through to the inner resolver:
 
   net::HostCache* GetHostCache() override;
-  bool HasCached(base::StringPiece hostname,
-                 net::HostCache::Entry::Source* source_out,
-                 net::HostCache::EntryStaleness* stale_out,
-                 bool* secure_out) const override;
   std::unique_ptr<base::Value> GetDnsConfigAsValue() const override;
   void SetRequestContext(net::URLRequestContext* request_context) override;
 
diff --git a/components/download/internal/common/android/download_collection_bridge.cc b/components/download/internal/common/android/download_collection_bridge.cc
index c1c5601..ae075552 100644
--- a/components/download/internal/common/android/download_collection_bridge.cc
+++ b/components/download/internal/common/android/download_collection_bridge.cc
@@ -154,10 +154,7 @@
     std::move(cb).Run(std::move(result));
     return;
   }
-  jsize count = env->GetArrayLength(jdisplay_infos.obj());
-  for (jsize i = 0; i < count; ++i) {
-    base::android::ScopedJavaLocalRef<jobject> jdisplay_info(
-        env, env->GetObjectArrayElement(jdisplay_infos.obj(), i));
+  for (auto jdisplay_info : jdisplay_infos.ReadElements<jobject>()) {
     ScopedJavaLocalRef<jstring> juri =
         Java_DisplayNameInfo_getDownloadUri(env, jdisplay_info);
     ScopedJavaLocalRef<jstring> jdisplay_name =
diff --git a/components/download/internal/common/download_file_impl.cc b/components/download/internal/common/download_file_impl.cc
index b56fb8f..13f52e03 100644
--- a/components/download/internal/common/download_file_impl.cc
+++ b/components/download/internal/common/download_file_impl.cc
@@ -4,6 +4,7 @@
 
 #include "components/download/public/common/download_file_impl.h"
 
+#include <algorithm>
 #include <string>
 #include <utility>
 
@@ -58,13 +59,24 @@
 DownloadFileImpl::SourceStream::SourceStream(
     int64_t offset,
     int64_t length,
+    int64_t starting_file_write_offset,
     std::unique_ptr<InputStream> stream)
     : offset_(offset),
       length_(length),
+      starting_file_write_offset_(starting_file_write_offset),
+      bytes_read_(0),
       bytes_written_(0),
       finished_(false),
       index_(0u),
-      input_stream_(std::move(stream)) {}
+      input_stream_(std::move(stream)) {
+  CHECK_LE(offset_, starting_file_write_offset_);
+  CHECK_GE(offset_, 0);
+  DCHECK(length <= 0 || length >= starting_file_write_offset - offset)
+      << "Not enough for content validation. offset = " << offset
+      << ", length = " << length
+      << " , starting_file_write_offset = " << starting_file_write_offset
+      << ".";
+}
 
 DownloadFileImpl::SourceStream::~SourceStream() = default;
 
@@ -72,19 +84,25 @@
   input_stream_->Initialize();
 }
 
-void DownloadFileImpl::SourceStream::OnWriteBytesToDisk(int64_t bytes_write) {
-  bytes_written_ += bytes_write;
+void DownloadFileImpl::SourceStream::OnBytesConsumed(int64_t bytes_read,
+                                                     int64_t bytes_written) {
+  CHECK_GE(bytes_read, bytes_written);
+  bytes_read_ += bytes_read;
+  bytes_written_ += bytes_written;
 }
 
 void DownloadFileImpl::SourceStream::TruncateLengthWithWrittenDataBlock(
-    int64_t offset,
+    int64_t received_slice_offset,
     int64_t bytes_written) {
   DCHECK_GT(bytes_written, 0);
   if (length_ == kNoBytesToWrite)
     return;
 
-  if (offset <= offset_) {
-    if (offset + bytes_written > offset_) {
+  if (received_slice_offset <= starting_file_write_offset_) {
+    // If validation has completed, mark the stream as finished if the file
+    // write position already has data.
+    if (received_slice_offset + bytes_written > starting_file_write_offset_ &&
+        GetRemainingBytesToValidate() == 0) {
       length_ = kNoBytesToWrite;
       finished_ = true;
     }
@@ -92,8 +110,12 @@
   }
 
   if (length_ == DownloadSaveInfo::kLengthFullContent ||
-      length_ > offset - offset_) {
-    length_ = offset - offset_;
+      (length_ > received_slice_offset - offset_ &&
+       length_ > starting_file_write_offset_ - offset_)) {
+    // Stream length should always include the validation data, unless the
+    // response is too short.
+    length_ =
+        std::max(received_slice_offset, starting_file_write_offset_) - offset_;
   }
 }
 
@@ -123,6 +145,11 @@
   return input_stream_->Read(data, length);
 }
 
+size_t DownloadFileImpl::SourceStream::GetRemainingBytesToValidate() {
+  int64_t bytes_remaining = starting_file_write_offset_ - offset_ - bytes_read_;
+  return bytes_remaining < 0 ? 0 : bytes_remaining;
+}
+
 DownloadFileImpl::DownloadFileImpl(
     std::unique_ptr<DownloadSaveInfo> save_info,
     const base::FilePath& default_download_directory,
@@ -149,7 +176,8 @@
                                     download_id);
 
   source_streams_[save_info_->offset] = std::make_unique<SourceStream>(
-      save_info_->offset, save_info_->length, std::move(stream));
+      save_info_->offset, save_info_->length,
+      save_info_->GetStartingFileWriteOffset(), std::move(stream));
 
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
@@ -184,7 +212,7 @@
       bytes_so_far += received_slice.received_bytes;
     }
   } else {
-    bytes_so_far = save_info_->offset;
+    bytes_so_far = save_info_->GetStartingFileWriteOffset();
   }
   int64_t bytes_wasted = 0;
   DownloadInterruptReason reason = file_.Initialize(
@@ -227,7 +255,7 @@
   }
   DCHECK(source_streams_.find(offset) == source_streams_.end());
   source_streams_[offset] =
-      std::make_unique<SourceStream>(offset, length, std::move(stream));
+      std::make_unique<SourceStream>(offset, length, offset, std::move(stream));
   OnSourceStreamAdded(source_streams_[offset].get());
 }
 
@@ -244,46 +272,73 @@
     RegisterAndActivateStream(source_stream);
 }
 
-DownloadInterruptReason DownloadFileImpl::WriteDataToFile(int64_t offset,
-                                                          const char* data,
-                                                          size_t data_len) {
+DownloadInterruptReason DownloadFileImpl::ValidateAndWriteDataToFile(
+    int64_t offset,
+    const char* data,
+    size_t bytes_to_validate,
+    size_t bytes_to_write) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  WillWriteToDisk(data_len);
-  return file_.WriteDataToFile(offset, data, data_len);
+  // Check if some of the data is for validation purpose.
+  if (bytes_to_validate > 0 &&
+      !file_.ValidateDataInFile(offset, data, bytes_to_validate)) {
+    return DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH;
+  }
+  // If there is no data to write, just return DOWNLOAD_INTERRUPT_REASON_NONE
+  // and read the next chunk.
+  if (bytes_to_write <= 0)
+    return DOWNLOAD_INTERRUPT_REASON_NONE;
+  // Write the remaining data to disk.
+  WillWriteToDisk(bytes_to_write);
+  return file_.WriteDataToFile(offset + bytes_to_validate,
+                               data + bytes_to_validate, bytes_to_write);
 }
 
 bool DownloadFileImpl::CalculateBytesToWrite(SourceStream* source_stream,
                                              size_t bytes_available_to_write,
+                                             size_t* bytes_to_validate,
                                              size_t* bytes_to_write) {
+  *bytes_to_validate = 0;
   if (source_stream->length() == kNoBytesToWrite) {
     *bytes_to_write = 0;
     return true;
   }
 
+  // First calculate the number of bytes to validate.
+  *bytes_to_write = bytes_available_to_write;
+  size_t remaining_bytes_to_validate =
+      source_stream->GetRemainingBytesToValidate();
+  if (remaining_bytes_to_validate > 0) {
+    *bytes_to_validate =
+        std::min(remaining_bytes_to_validate, bytes_available_to_write);
+    *bytes_to_write -= *bytes_to_validate;
+  }
+  if (source_stream->length() != DownloadSaveInfo::kLengthFullContent &&
+      source_stream->bytes_read() +
+              static_cast<int64_t>(bytes_available_to_write) >
+          source_stream->length()) {
+    // Total bytes to consume is capped by the length of the stream.
+    int64_t bytes_to_consume =
+        source_stream->length() - source_stream->bytes_read();
+    // The validation data should always be streamed.
+    DCHECK_GE(bytes_to_consume, static_cast<int64_t>(*bytes_to_validate));
+    *bytes_to_write = bytes_to_consume - *bytes_to_validate;
+    return true;
+  }
+
   // If a new slice finds that its target position has already been written,
-  // terminate the stream.
-  if (source_stream->bytes_written() == 0) {
+  // terminate the stream if there are no bytes to validate.
+  if (source_stream->bytes_written() == 0 && *bytes_to_write > 0) {
     for (const auto& received_slice : received_slices_) {
-      if (received_slice.offset <= source_stream->offset() &&
+      if (received_slice.offset <=
+              source_stream->starting_file_write_offset() &&
           received_slice.offset + received_slice.received_bytes >
-              source_stream->offset()) {
+              source_stream->starting_file_write_offset()) {
         *bytes_to_write = 0;
         return true;
       }
     }
   }
 
-  if (source_stream->length() != DownloadSaveInfo::kLengthFullContent &&
-      source_stream->bytes_written() +
-              static_cast<int64_t>(bytes_available_to_write) >
-          source_stream->length()) {
-    // Write a partial buffer as the incoming data exceeds the length limit.
-    *bytes_to_write = source_stream->length() - source_stream->bytes_written();
-    return true;
-  }
-
-  *bytes_to_write = bytes_available_to_write;
   return false;
 }
 
@@ -361,8 +416,9 @@
   DownloadInterruptReason reason = source_stream->GetCompletionStatus();
   if (source_stream->length() == DownloadSaveInfo::kLengthFullContent &&
       !received_slices_.empty() &&
-      (source_stream->offset() == received_slices_.back().offset +
-                                      received_slices_.back().received_bytes) &&
+      (source_stream->starting_file_write_offset() ==
+       received_slices_.back().offset +
+           received_slices_.back().received_bytes) &&
       reason == DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE) {
     // We are probably reaching the end of the stream, don't treat this
     // as an error.
@@ -508,6 +564,7 @@
   size_t incoming_data_size = 0;
   size_t total_incoming_data_size = 0;
   size_t num_buffers = 0;
+  size_t bytes_to_validate = 0;
   size_t bytes_to_write = 0;
   bool should_terminate = false;
   InputStream::StreamState state(InputStream::EMPTY);
@@ -524,24 +581,26 @@
         break;
       case InputStream::HAS_DATA: {
         ++num_buffers;
-        should_terminate = CalculateBytesToWrite(
-            source_stream, incoming_data_size, &bytes_to_write);
+        should_terminate =
+            CalculateBytesToWrite(source_stream, incoming_data_size,
+                                  &bytes_to_validate, &bytes_to_write);
         DCHECK_GE(incoming_data_size, bytes_to_write);
-        reason = WriteDataToFile(
-            source_stream->offset() + source_stream->bytes_written(),
-            incoming_data->data(), bytes_to_write);
+        reason = ValidateAndWriteDataToFile(
+            source_stream->offset() + source_stream->bytes_read(),
+            incoming_data->data(), bytes_to_validate, bytes_to_write);
         bytes_seen_ += bytes_to_write;
-        total_incoming_data_size += bytes_to_write;
+        total_incoming_data_size += incoming_data_size;
         if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) {
           int64_t prev_bytes_written = source_stream->bytes_written();
-          source_stream->OnWriteBytesToDisk(bytes_to_write);
+          source_stream->OnBytesConsumed(incoming_data_size, bytes_to_write);
           if (!IsSparseFile())
             break;
           // If the write operation creates a new slice, add it to the
           // |received_slices_| and update all the entries in
           // |source_streams_|.
           if (bytes_to_write > 0 && prev_bytes_written == 0) {
-            AddNewSlice(source_stream->offset(), bytes_to_write);
+            AddNewSlice(source_stream->starting_file_write_offset(),
+                        bytes_to_write);
           } else {
             received_slices_[source_stream->index()].received_bytes +=
                 bytes_to_write;
@@ -611,7 +670,7 @@
       }
 
       SetPotentialFileLength(source_stream->offset() +
-                             source_stream->bytes_written());
+                             source_stream->bytes_read());
     }
     num_active_streams_--;
 
@@ -698,10 +757,10 @@
   // Update the index of exising SourceStreams.
   for (auto& stream : source_streams_) {
     SourceStream* source_stream = stream.second.get();
-    if (source_stream->offset() > offset) {
+    if (source_stream->starting_file_write_offset() > offset) {
       if (slice_added && source_stream->bytes_written() > 0)
         source_stream->set_index(source_stream->index() + 1);
-    } else if (source_stream->offset() == offset) {
+    } else if (source_stream->starting_file_write_offset() == offset) {
       source_stream->set_index(index);
     } else {
       source_stream->TruncateLengthWithWrittenDataBlock(offset, length);
@@ -736,26 +795,29 @@
   source_stream->set_finished(true);
   num_active_streams_--;
 
-  // If previous stream has already written data at the starting offset of
-  // the error stream. The download can complete.
-  bool can_recover_from_error = (source_stream->length() == kNoBytesToWrite);
+  bool can_recover_from_error = false;
+  if (reason != DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH) {
+    // If previous stream has already written data at the starting offset of
+    // the error stream. The download can complete.
+    can_recover_from_error = (source_stream->length() == kNoBytesToWrite);
 
-  // See if the previous stream can download the full content.
-  // If the current stream has written some data, length of all preceding
-  // streams will be truncated.
-  if (IsSparseFile() && !can_recover_from_error) {
-    SourceStream* preceding_neighbor = FindPrecedingNeighbor(source_stream);
-    while (preceding_neighbor) {
-      if (CanRecoverFromError(source_stream, preceding_neighbor)) {
-        can_recover_from_error = true;
-        break;
+    // See if the previous stream can download the full content.
+    // If the current stream has written some data, length of all preceding
+    // streams will be truncated.
+    if (IsSparseFile() && !can_recover_from_error) {
+      SourceStream* preceding_neighbor = FindPrecedingNeighbor(source_stream);
+      while (preceding_neighbor) {
+        if (CanRecoverFromError(source_stream, preceding_neighbor)) {
+          can_recover_from_error = true;
+          break;
+        }
+
+        // If the neighbor cannot recover the error and it has already created
+        // a slice, just interrupt the download.
+        if (preceding_neighbor->bytes_written() > 0)
+          break;
+        preceding_neighbor = FindPrecedingNeighbor(preceding_neighbor);
       }
-
-      // If the neighbor cannot recover the error and it has already created
-      // a slice, just interrupt the download.
-      if (preceding_neighbor->bytes_written() > 0)
-        break;
-      preceding_neighbor = FindPrecedingNeighbor(preceding_neighbor);
     }
   }
 
@@ -784,8 +846,9 @@
   int64_t max_preceding_offset = 0;
   SourceStream* ret = nullptr;
   for (auto& stream : source_streams_) {
-    int64_t offset = stream.second->offset();
-    if (offset < source_stream->offset() && offset >= max_preceding_offset) {
+    int64_t offset = stream.second->starting_file_write_offset();
+    if (offset < source_stream->starting_file_write_offset() &&
+        offset >= max_preceding_offset) {
       ret = stream.second.get();
       max_preceding_offset = offset;
     }
@@ -805,6 +868,9 @@
   DVLOG(1) << "Total source stream count = " << source_streams_.size();
   for (const auto& stream : source_streams_) {
     DVLOG(1) << "Source stream, offset = " << stream.second->offset()
+             << " , bytes_read = " << stream.second->bytes_read()
+             << " , starting_file_write_offset = "
+             << stream.second->starting_file_write_offset()
              << " , bytes_written = " << stream.second->bytes_written()
              << " , is_finished = " << stream.second->is_finished()
              << " , length = " << stream.second->length()
diff --git a/components/download/internal/common/download_file_unittest.cc b/components/download/internal/common/download_file_unittest.cc
index fb89b4c5..a3d72dc 100644
--- a/components/download/internal/common/download_file_unittest.cc
+++ b/components/download/internal/common/download_file_unittest.cc
@@ -192,15 +192,22 @@
     closure.Run();
   }
 
-  bool CreateDownloadFile(int offset, bool calculate_hash) {
-    return CreateDownloadFile(offset, 0, calculate_hash,
-                              DownloadItem::ReceivedSlices());
+  bool CreateDownloadFile(bool calculate_hash) {
+    return CreateDownloadFile(0, calculate_hash, DownloadItem::ReceivedSlices(),
+                              -1);
   }
 
-  bool CreateDownloadFile(int offset,
-                          int length,
+  bool CreateDownloadFile(int length,
                           bool calculate_hash,
                           const DownloadItem::ReceivedSlices& received_slices) {
+    return CreateDownloadFile(length, calculate_hash,
+                              DownloadItem::ReceivedSlices(), -1);
+  }
+
+  bool CreateDownloadFile(int length,
+                          bool calculate_hash,
+                          const DownloadItem::ReceivedSlices& received_slices,
+                          int file_offset) {
     // There can be only one.
     DCHECK(!download_file_);
 
@@ -213,8 +220,23 @@
         .RetiresOnSaturation();
 
     std::unique_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo());
-    save_info->offset = offset;
+    // Fill the file by repeatedly copying |kTestData1| if |file_offset| is
+    // positive.
+    if (file_offset > 0) {
+      base::CreateTemporaryFileInDir(download_dir_.GetPath(),
+                                     &save_info->file_path);
+      int len = file_offset;
+      int data_len = strlen(kTestData1);
+      while (len > 0) {
+        int bytes_to_write = len > data_len ? data_len : len;
+        base::AppendToFile(save_info->file_path, kTestData1, bytes_to_write);
+        len -= bytes_to_write;
+      }
+    }
+
+    save_info->offset = 0;
     save_info->length = length;
+    save_info->file_offset = file_offset;
 
     download_file_.reset(new TestDownloadFileImpl(
         std::move(save_info), download_dir_.GetPath(),
@@ -462,6 +484,9 @@
   int64_t bytes_;
   int64_t bytes_per_sec_;
 
+  // Keep track of what data should be saved to the disk file.
+  std::string expected_data_;
+
  private:
   void SetRenameResult(const base::Closure& closure,
                        DownloadInterruptReason* reason_p,
@@ -476,9 +501,6 @@
   }
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  // Keep track of what data should be saved to the disk file.
-  std::string expected_data_;
 };
 
 // DownloadFile::RenameAndAnnotate and DownloadFile::RenameAndUniquify have a
@@ -531,7 +553,7 @@
 // Rename the file before any data is downloaded, after some has, after it all
 // has, and after it's closed.
 TEST_P(DownloadFileTestWithRename, RenameFileFinal) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
   base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
@@ -601,7 +623,7 @@
 // the above test because it only applies to RenameAndAnnotate().
 // RenameAndUniquify() doesn't overwrite by design.
 TEST_F(DownloadFileTest, RenameOverwrites) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
   base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
@@ -631,7 +653,7 @@
 // DownloadFileTestWithRename test because this only applies to
 // RenameAndUniquify().
 TEST_F(DownloadFileTest, RenameUniquifies) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
   base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
@@ -654,7 +676,7 @@
 // Test that RenameAndUniquify doesn't try to uniquify in the case where the
 // target filename is the same as the current filename.
 TEST_F(DownloadFileTest, RenameRecognizesSelfConflict) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
 
@@ -671,7 +693,7 @@
 
 // Test to make sure we get the proper error on failure.
 TEST_P(DownloadFileTestWithRename, RenameError) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
 
   // Create a subdirectory.
@@ -726,7 +748,7 @@
 // base::MessageLoopCurrent::Get(). Each RunLoop processes that queue until it
 // sees a QuitClosure() targeted at itself, at which point it stops processing.
 TEST_P(DownloadFileTestWithRename, RenameWithErrorRetry) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
 
   // Create a subdirectory.
@@ -797,7 +819,7 @@
 
 // Various tests of the StreamActive method.
 TEST_F(DownloadFileTest, StreamEmptySuccess) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
 
@@ -813,7 +835,7 @@
 }
 
 TEST_F(DownloadFileTest, StreamEmptyError) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
 
@@ -842,7 +864,7 @@
 }
 
 TEST_F(DownloadFileTest, StreamNonEmptySuccess) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
 
@@ -858,7 +880,7 @@
 }
 
 TEST_F(DownloadFileTest, StreamNonEmptyError) {
-  ASSERT_TRUE(CreateDownloadFile(0, true));
+  ASSERT_TRUE(CreateDownloadFile(true));
   base::FilePath initial_path(download_file_->FullPath());
   EXPECT_TRUE(base::PathExists(initial_path));
 
@@ -888,6 +910,58 @@
   DestroyDownloadFile(0);
 }
 
+// Tests that if file content validation succeeds, all the remaining data will
+// be writing to the file.
+TEST_F(DownloadFileTest, FileContentValidationSuccess) {
+  int stream_length = strlen(kTestData1) * 2;
+
+  ASSERT_TRUE(CreateDownloadFile(
+      stream_length /* length */, true /* calculate_hash */,
+      DownloadItem::ReceivedSlices(), strlen(kTestData1) - 1));
+  base::FilePath initial_path(download_file_->FullPath());
+  EXPECT_TRUE(base::PathExists(initial_path));
+
+  const char* chunks1[] = {kTestData1, kTestData1};
+  ::testing::Sequence s1;
+  SetupDataAppend(chunks1, 2 /* num_chunks */, input_stream_, s1);
+  SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, input_stream_, s1);
+  EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _));
+  sink_callback_.Run(MOJO_RESULT_OK);
+  VerifyStreamAndSize();
+  base::RunLoop().RunUntilIdle();
+  DestroyDownloadFile(0);
+}
+
+// Tests that if file content validation fails, an error will occur and no data
+// will be written.
+TEST_F(DownloadFileTest, FileContentValidationFail) {
+  int file_length = strlen(kTestData2) - 1;
+  int stream_length = strlen(kTestData1) + strlen(kTestData2);
+
+  ASSERT_TRUE(CreateDownloadFile(stream_length /* length */,
+                                 true /* calculate_hash */,
+                                 DownloadItem::ReceivedSlices(), file_length));
+  base::FilePath initial_path(download_file_->FullPath());
+  EXPECT_TRUE(base::PathExists(initial_path));
+  std::string file_content = std::string(kTestData1, 0, file_length);
+  expected_data_ = file_content;
+  VerifyStreamAndSize();
+
+  const char* chunks1[] = {kTestData2, kTestData1};
+  ::testing::Sequence s1;
+  // Only 1 chunk will be read, and it will generate an error after
+  // failing the validation.
+  SetupDataAppend(chunks1, 1 /* num_chunks */, input_stream_, s1);
+  EXPECT_CALL(*input_stream_, ClearDataReadyCallback());
+  EXPECT_CALL(*(observer_.get()),
+              MockDestinationError(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH,
+                                   file_length, _));
+  sink_callback_.Run(MOJO_RESULT_OK);
+  base::RunLoop().RunUntilIdle();
+  expected_data_ = file_content;
+  DestroyDownloadFile(0);
+}
+
 // Tests for concurrent streams handling, used for parallel download.
 //
 // Activate both streams at the same time.
@@ -895,7 +969,7 @@
   int64_t stream_0_length = GetBuffersLength(kTestData6, 2);
   int64_t stream_1_length = GetBuffersLength(kTestData7, 2);
 
-  ASSERT_TRUE(CreateDownloadFile(0, stream_0_length, true,
+  ASSERT_TRUE(CreateDownloadFile(stream_0_length, true,
                                  DownloadItem::ReceivedSlices()));
 
   PrepareStream(&input_stream_, 0, false, true, kTestData6, 2);
@@ -934,7 +1008,7 @@
   // "Range:50-".
   int64_t stream_2_length = GetBuffersLength(kTestData6, 2);
 
-  ASSERT_TRUE(CreateDownloadFile(0, stream_0_length, true,
+  ASSERT_TRUE(CreateDownloadFile(stream_0_length, true,
                                  DownloadItem::ReceivedSlices()));
 
   PrepareStream(&input_stream_, 0, false, true, kTestData6, 2);
@@ -986,7 +1060,7 @@
 TEST_F(DownloadFileTest, MultipleStreamsFirstStreamWriteAllData) {
   int64_t stream_0_length = GetBuffersLength(kTestData8, 4);
 
-  ASSERT_TRUE(CreateDownloadFile(0, DownloadSaveInfo::kLengthFullContent, true,
+  ASSERT_TRUE(CreateDownloadFile(DownloadSaveInfo::kLengthFullContent, true,
                                  DownloadItem::ReceivedSlices()));
 
   PrepareStream(&input_stream_, 0, false, true, kTestData8, 4);
@@ -1019,7 +1093,7 @@
 TEST_F(DownloadFileTest, SecondStreamStartingOffsetAlreadyWritten) {
   int64_t stream_0_length = GetBuffersLength(kTestData6, 2);
 
-  ASSERT_TRUE(CreateDownloadFile(0, stream_0_length, true,
+  ASSERT_TRUE(CreateDownloadFile(stream_0_length, true,
                                  DownloadItem::ReceivedSlices()));
 
   Sequence seq;
diff --git a/components/download/internal/common/parallel_download_utils.cc b/components/download/internal/common/parallel_download_utils.cc
index bc7266f..5aa3ad6 100644
--- a/components/download/internal/common/parallel_download_utils.cc
+++ b/components/download/internal/common/parallel_download_utils.cc
@@ -102,7 +102,7 @@
     // the error stream.
     if (error_stream->length() > 0) {
       return error_stream->offset() + error_stream->length() <=
-             preceding_neighbor->offset() + preceding_neighbor->bytes_written();
+             preceding_neighbor->offset() + preceding_neighbor->bytes_read();
     }
 
     return false;
diff --git a/components/download/internal/common/parallel_download_utils_unittest.cc b/components/download/internal/common/parallel_download_utils_unittest.cc
index af8aab7..b0c8462 100644
--- a/components/download/internal/common/parallel_download_utils_unittest.cc
+++ b/components/download/internal/common/parallel_download_utils_unittest.cc
@@ -43,7 +43,8 @@
     EXPECT_CALL(*input_stream_, GetCompletionStatus())
         .WillRepeatedly(Return(DOWNLOAD_INTERRUPT_REASON_NONE));
     return std::make_unique<DownloadFileImpl::SourceStream>(
-        offset, length, std::unique_ptr<MockInputStream>(input_stream_));
+        offset, length, offset,
+        std::unique_ptr<MockInputStream>(input_stream_));
   }
 
  protected:
@@ -166,7 +167,7 @@
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
 
   // Even if it has written some data.
-  preceding_stream->OnWriteBytesToDisk(1000u);
+  preceding_stream->OnBytesConsumed(1000u, 1000u);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
 
   // Now capped the length of preceding stream with different values.
@@ -177,14 +178,15 @@
   preceding_stream->set_finished(false);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
   preceding_stream->set_finished(true);
-  preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset);
+  int64_t bytes_consumed = kErrorStreamOffset - preceding_offset;
+  preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
 
   // Inject an error results in failure, even if data written exceeds the first
   // byte of error stream.
   EXPECT_CALL(*input_stream_, GetCompletionStatus())
       .WillRepeatedly(Return(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE));
-  preceding_stream->OnWriteBytesToDisk(1000u);
+  preceding_stream->OnBytesConsumed(1000u, 1000u);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
 
   // Make preceding stream can reach the first byte of error stream.
@@ -194,9 +196,9 @@
   preceding_stream->set_finished(false);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
   preceding_stream->set_finished(true);
-  preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset);
+  preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
-  preceding_stream->OnWriteBytesToDisk(1);
+  preceding_stream->OnBytesConsumed(1, 1);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
 
   // Preceding stream that never download data won't recover the error stream.
@@ -229,11 +231,13 @@
   // Since the preceding stream can never reach the starting offset, for an
   // unfinished stream, we rely on length instead of bytes written.
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
-  preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset);
+  int64_t bytes_consumed = kErrorStreamOffset - preceding_offset;
+  preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
-  preceding_stream->OnWriteBytesToDisk(kErrorStreamLength - 1);
+  preceding_stream->OnBytesConsumed(kErrorStreamLength - 1,
+                                    kErrorStreamLength - 1);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
-  preceding_stream->OnWriteBytesToDisk(1);
+  preceding_stream->OnBytesConsumed(1, 1);
 
   // Create preceding stream that can reach the upper bound of error stream.
   // Since it's unfinished, it potentially can take over error stream's work
@@ -248,11 +252,12 @@
   // Finished preceding stream only checks data written.
   preceding_stream = CreateSourceStream(preceding_offset, 1);
   preceding_stream->set_finished(true);
-  preceding_stream->OnWriteBytesToDisk(kErrorStreamOffset - preceding_offset);
+  preceding_stream->OnBytesConsumed(bytes_consumed, bytes_consumed);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
-  preceding_stream->OnWriteBytesToDisk(kErrorStreamLength - 1);
+  preceding_stream->OnBytesConsumed(kErrorStreamLength - 1,
+                                    kErrorStreamLength - 1);
   EXPECT_FALSE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
-  preceding_stream->OnWriteBytesToDisk(1);
+  preceding_stream->OnBytesConsumed(1, 1);
   EXPECT_TRUE(CanRecoverFromError(error_stream.get(), preceding_stream.get()));
 
   // Even if inject an error, since data written has cover the upper bound of
diff --git a/components/download/public/common/download_file_impl.h b/components/download/public/common/download_file_impl.h
index 8c350fb4..c3dee68 100644
--- a/components/download/public/common/download_file_impl.h
+++ b/components/download/public/common/download_file_impl.h
@@ -99,17 +99,19 @@
    public:
     SourceStream(int64_t offset,
                  int64_t length,
+                 int64_t starting_file_write_offset,
                  std::unique_ptr<InputStream> stream);
     ~SourceStream();
 
     void Initialize();
 
-    // Called after successfully writing a buffer to disk.
-    void OnWriteBytesToDisk(int64_t bytes_write);
+    // Called after successfully reading and writing a buffer from stream.
+    void OnBytesConsumed(int64_t bytes_read, int64_t bytes_written);
 
     // Given a data block that is already written, truncate the length of this
-    // object to avoid overwriting that block.
-    void TruncateLengthWithWrittenDataBlock(int64_t offset,
+    // object to avoid overwriting that block. Data used for validation purpose
+    // will not be truncated.
+    void TruncateLengthWithWrittenDataBlock(int64_t received_slice_offset,
                                             int64_t bytes_written);
 
     // Registers the callback that will be called when data is ready.
@@ -136,8 +138,15 @@
     InputStream::StreamState Read(scoped_refptr<net::IOBuffer>* data,
                                   size_t* length);
 
+    // Returning the remaining bytes to validate.
+    size_t GetRemainingBytesToValidate();
+
     int64_t offset() const { return offset_; }
     int64_t length() const { return length_; }
+    int64_t starting_file_write_offset() const {
+      return starting_file_write_offset_;
+    }
+    int64_t bytes_read() const { return bytes_read_; }
     int64_t bytes_written() const { return bytes_written_; }
     bool is_finished() const { return finished_; }
     void set_finished(bool finish) { finished_ = finish; }
@@ -145,15 +154,24 @@
     void set_index(size_t index) { index_ = index; }
 
    private:
-    // Starting position for the stream to write to disk.
+    // Starting position of the stream, this is from the network response.
     int64_t offset_;
 
     // The maximum length to write to the disk. If set to 0, keep writing until
     // the stream depletes.
     int64_t length_;
 
-    // Number of bytes written to disk from the stream.
-    // Next write position is (|offset_| + |bytes_written_|).
+    // All the data received before this offset are used for validation purpose
+    // and will not be written to disk. This value should always be no less than
+    // |offset_|.
+    int64_t starting_file_write_offset_;
+
+    // Number of bytes read from the stream.
+    // Next read position is (|offset_| + |bytes_read_|).
+    int64_t bytes_read_;
+
+    // Number of bytes written to the disk. This does not include the bytes used
+    // for validation.
     int64_t bytes_written_;
 
     // If all the data read from the stream has been successfully written to
@@ -172,11 +190,13 @@
 
  protected:
   // For test class overrides.
-  // Write data from the offset to the file.
-  // On OS level, it will seek to the |offset| and write from there.
-  virtual DownloadInterruptReason WriteDataToFile(int64_t offset,
-                                                  const char* data,
-                                                  size_t data_len);
+  // Validate the first |bytes_to_validate| bytes and write the next
+  // |bytes_to_write| bytes of data from the offset to the file.
+  virtual DownloadInterruptReason ValidateAndWriteDataToFile(
+      int64_t offset,
+      const char* data,
+      size_t bytes_to_validate,
+      size_t bytes_to_write);
 
   virtual base::TimeDelta GetRetryDelayForFailedRename(int attempt_number);
 
@@ -232,13 +252,14 @@
   void WillWriteToDisk(size_t data_len);
 
   // For a given SourceStream object and the bytes available to write, determine
-  // the actual number of bytes it can write to the disk. For parallel
-  // downloading, if the first disk IO writes to a location that is already
-  // written by another stream, the current stream should stop writing. Returns
-  // true if the stream can write no more data and should be finished, returns
-  // false otherwise.
+  // the number of bytes to validate and the number of bytes it can write to the
+  // disk. For parallel downloading, if the first disk IO writes to a location
+  // that is already written by another stream, the current stream should stop
+  // writing. Returns true if the stream can write no more data and should be
+  // finished, returns false otherwise.
   bool CalculateBytesToWrite(SourceStream* source_stream,
                              size_t bytes_available_to_write,
+                             size_t* bytes_to_validate,
                              size_t* bytes_to_write);
 
   // Called when a new SourceStream object is added.
diff --git a/components/download/public/common/download_save_info.cc b/components/download/public/common/download_save_info.cc
index 2b25b62..c66a6003f 100644
--- a/components/download/public/common/download_save_info.cc
+++ b/components/download/public/common/download_save_info.cc
@@ -15,4 +15,8 @@
 
 DownloadSaveInfo::DownloadSaveInfo(DownloadSaveInfo&& that) = default;
 
+int64_t DownloadSaveInfo::GetStartingFileWriteOffset() {
+  return file_offset >= 0 ? file_offset : offset;
+}
+
 }  // namespace download
diff --git a/components/download/public/common/download_save_info.h b/components/download/public/common/download_save_info.h
index a1e00c8..9f249c7 100644
--- a/components/download/public/common/download_save_info.h
+++ b/components/download/public/common/download_save_info.h
@@ -30,6 +30,8 @@
   ~DownloadSaveInfo();
   DownloadSaveInfo(DownloadSaveInfo&& that);
 
+  int64_t GetStartingFileWriteOffset();
+
   // If non-empty, contains the full target path of the download that has been
   // determined prior to download initiation. This is considered to be a trusted
   // path.
@@ -42,9 +44,18 @@
   // If valid, contains the source data stream for the file contents.
   base::File file;
 
-  // The file offset at which to start the download.
+  // The offset sent to the server when requesting the download. During
+  // resumption, |offset| could be smaller than the downloaded content length.
+  // This is because download may request some data to validate whether the
+  // content has changed.
   int64_t offset = 0;
 
+  // The file offset to start writing to disk. If this value is negative,
+  // download stream will be writing to the disk starting at |offset|.
+  // Otherwise, this value will be used. Data received before |file_offset| are
+  // used for validation purpose.
+  int64_t file_offset = -1;
+
   // The number of the bytes to download from |offset|.
   // Ask to retrieve segment of the download file when length is greater than 0.
   // Request the rest of the file starting from |offset|, when length is
diff --git a/components/ntp_tiles/features.cc b/components/ntp_tiles/features.cc
index a5baa53..0ea51f07 100644
--- a/components/ntp_tiles/features.cc
+++ b/components/ntp_tiles/features.cc
@@ -20,4 +20,7 @@
 const base::Feature kUsePopularSitesSuggestions{
     "UsePopularSitesSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kDefaultSearchShortcut{"DefaultSearchShortcut",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace ntp_tiles
diff --git a/components/ntp_tiles/features.h b/components/ntp_tiles/features.h
index d9e819607..39dc61d 100644
--- a/components/ntp_tiles/features.h
+++ b/components/ntp_tiles/features.h
@@ -26,6 +26,9 @@
 // If this feature is enabled, we enable popular sites in the suggestions UI.
 extern const base::Feature kUsePopularSitesSuggestions;
 
+// If enabled, show a Google search shortcut on the NTP by default.
+extern const base::Feature kDefaultSearchShortcut;
+
 }  // namespace ntp_tiles
 
 #endif  // COMPONENTS_NTP_TILES_FEATURES_H_
diff --git a/components/ntp_tiles_strings.grdp b/components/ntp_tiles_strings.grdp
new file mode 100644
index 0000000..6500dc8d
--- /dev/null
+++ b/components/ntp_tiles_strings.grdp
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <if expr="not is_android">
+      <!-- The title for the Google Search shortcut. -->
+      <message name="IDS_NTP_DEFAULT_SEARCH_TITLE" translateable="false" desc="The label for the default Search shortcut on the New Tab Page.">
+        Google
+      </message>
+
+      <!-- The URL for the Google Search shortcut. -->
+      <message name="IDS_NTP_DEFAULT_SEARCH_URL" translateable="false">
+        https://www.google.com
+      </message>
+  </if>
+</grit-part>
diff --git a/components/policy/core/browser/android/policy_converter.cc b/components/policy/core/browser/android/policy_converter.cc
index 9d41ad0..63e5eb3c 100644
--- a/components/policy/core/browser/android/policy_converter.cc
+++ b/components/policy/core/browser/android/policy_converter.cc
@@ -90,13 +90,12 @@
     JNIEnv* env,
     const JavaRef<jobjectArray>& array) {
   DCHECK(!array.is_null());
-  int length = static_cast<int>(env->GetArrayLength(array.obj()));
-  DCHECK_GE(length, 0) << "Invalid array length: " << length;
+  base::android::JavaObjectArrayReader<jstring> array_reader(array);
+  DCHECK_GE(array_reader.size(), 0)
+      << "Invalid array length: " << array_reader.size();
 
   std::unique_ptr<base::ListValue> list_value(new base::ListValue());
-  for (int i = 0; i < length; ++i) {
-    base::android::ScopedJavaLocalRef<jstring> j_str(
-        env, static_cast<jstring>(env->GetObjectArrayElement(array.obj(), i)));
+  for (auto j_str : array_reader) {
     list_value->AppendString(ConvertJavaStringToUTF8(env, j_str));
   }
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index adb967a..96d7269 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -14895,7 +14895,7 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
-      'supported_on': ['chrome.*:76-'],
+      'supported_on': ['chrome.*:77-'],
       'caption': '''URL of an XML file that contains URLs that should never trigger a browser switch.''',
       'tags': [],
       'desc': '''This policy is a URL, that points to an XML file in the same format as Internet Explorer's <ph name="IEEM_SITELIST_POLICY">SiteList</ph> policy. This loads rules from an XML file, without sharing those rules with Internet Explorer.
diff --git a/components/sync/test/fake_server/android/fake_server_helper_android.cc b/components/sync/test/fake_server/android/fake_server_helper_android.cc
index b09ccd9..88625c8 100644
--- a/components/sync/test/fake_server/android/fake_server_helper_android.cc
+++ b/components/sync/test/fake_server/android/fake_server_helper_android.cc
@@ -91,9 +91,7 @@
     jlong fake_server,
     const JavaParamRef<jobjectArray>& url_array) {
   std::multiset<std::string> tab_urls;
-  for (int i = 0; i < env->GetArrayLength(url_array); i++) {
-    base::android::ScopedJavaLocalRef<jstring> j_string(
-        env, static_cast<jstring>(env->GetObjectArrayElement(url_array, i)));
+  for (auto j_string : url_array.ReadElements<jstring>()) {
     tab_urls.insert(base::android::ConvertJavaStringToUTF8(env, j_string));
   }
   fake_server::SessionsHierarchy expected_sessions;
diff --git a/components/update_client/utils.cc b/components/update_client/utils.cc
index ded9add..4952c37 100644
--- a/components/update_client/utils.cc
+++ b/components/update_client/utils.cc
@@ -52,8 +52,12 @@
 }
 
 std::string GetCrxComponentID(const CrxComponent& component) {
-  const std::string result = crx_file::id_util::GenerateIdFromHash(
-      &component.pk_hash[0], component.pk_hash.size());
+  return GetCrxIdFromPublicKeyHash(component.pk_hash);
+}
+
+std::string GetCrxIdFromPublicKeyHash(const std::vector<uint8_t>& pk_hash) {
+  const std::string result =
+      crx_file::id_util::GenerateIdFromHash(&pk_hash[0], pk_hash.size());
   DCHECK(crx_file::id_util::IdIsValid(result));
   return result;
 }
diff --git a/components/update_client/utils.h b/components/update_client/utils.h
index 50e891f..14567e6 100644
--- a/components/update_client/utils.h
+++ b/components/update_client/utils.h
@@ -46,6 +46,9 @@
 // format similar with the format of an extension id.
 std::string GetCrxComponentID(const CrxComponent& component);
 
+// Returns a CRX id from a public key hash.
+std::string GetCrxIdFromPublicKeyHash(const std::vector<uint8_t>& pk_hash);
+
 // Returns true if the actual SHA-256 hash of the |filepath| matches the
 // |expected_hash|.
 bool VerifyFileHash256(const base::FilePath& filepath,
diff --git a/components/update_client/utils_unittest.cc b/components/update_client/utils_unittest.cc
index e2670e9..71a8b886 100644
--- a/components/update_client/utils_unittest.cc
+++ b/components/update_client/utils_unittest.cc
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "components/update_client/utils.h"
+
+#include <iterator>
+
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "components/update_client/updater_state.h"
-#include "components/update_client/utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -79,6 +82,16 @@
             GetCrxComponentID(component));
 }
 
+TEST(UpdateClientUtils, GetCrxIdFromPublicKeyHash) {
+  static const uint8_t kHash[16] = {
+      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+  };
+
+  EXPECT_EQ(std::string("abcdefghijklmnopabcdefghijklmnop"),
+            GetCrxIdFromPublicKeyHash({std::cbegin(kHash), std::cend(kHash)}));
+}
+
 // Tests that the name of an InstallerAttribute matches ^[-_=a-zA-Z0-9]{1,256}$
 TEST(UpdateClientUtils, IsValidInstallerAttributeName) {
   // Test the length boundaries.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index fd4ee39..74760af8 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1346,7 +1346,8 @@
     "net/view_blob_internals_job_factory.h",
     "network_service_client.cc",
     "network_service_client.h",
-    "network_service_instance.cc",
+    "network_service_instance_impl.cc",
+    "network_service_instance_impl.h",
     "notification_service_impl.cc",
     "notification_service_impl.h",
     "notifications/blink_notification_service_impl.cc",
diff --git a/content/browser/android/app_web_message_port.cc b/content/browser/android/app_web_message_port.cc
index 3711219..a1537873 100644
--- a/content/browser/android/app_web_message_port.cc
+++ b/content/browser/android/app_web_message_port.cc
@@ -22,10 +22,7 @@
     const base::android::JavaRef<jobjectArray>& jports) {
   std::vector<blink::MessagePortChannel> channels;
   if (!jports.is_null()) {
-    jsize num_ports = env->GetArrayLength(jports.obj());
-    for (jsize i = 0; i < num_ports; ++i) {
-      base::android::ScopedJavaLocalRef<jobject> jport(
-          env, env->GetObjectArrayElement(jports.obj(), i));
+    for (auto jport : jports.ReadElements<jobject>()) {
       jint native_port = Java_AppWebMessagePort_releaseNativeHandle(env, jport);
       channels.push_back(blink::MessagePortChannel(
           mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(native_port))));
diff --git a/content/browser/android/content_feature_list.cc b/content/browser/android/content_feature_list.cc
index c182bc1..d7e21b9 100644
--- a/content/browser/android/content_feature_list.cc
+++ b/content/browser/android/content_feature_list.cc
@@ -24,7 +24,6 @@
 const base::Feature* kFeaturesExposedToJava[] = {
     &features::kBackgroundMediaRendererHasModerateBinding,
     &kEnhancedSelectionInsertionHandle,
-    &features::kServiceWorkerForegroundPriority,
     &kServiceGroupImportance,
 };
 
diff --git a/content/browser/android/java/gin_java_bound_object.cc b/content/browser/android/java/gin_java_bound_object.cc
index b762a9c..ce1cc16 100644
--- a/content/browser/android/java/gin_java_bound_object.cc
+++ b/content/browser/android/java/gin_java_bound_object.cc
@@ -11,6 +11,7 @@
 #include "jni/Object_jni.h"
 
 using base::android::AttachCurrentThread;
+using base::android::JavaObjectArrayReader;
 using base::android::ScopedJavaLocalRef;
 
 namespace content {
@@ -134,16 +135,11 @@
     return;
   }
 
-  ScopedJavaLocalRef<jobjectArray> methods(GetClassMethods(env, clazz));
-  size_t num_methods = env->GetArrayLength(methods.obj());
+  JavaObjectArrayReader<jobject> methods(GetClassMethods(env, clazz));
   // Java objects always have public methods.
-  DCHECK(num_methods);
+  DCHECK_GT(methods.size(), 0);
 
-  for (size_t i = 0; i < num_methods; ++i) {
-    ScopedJavaLocalRef<jobject> java_method(
-        env,
-        env->GetObjectArrayElement(methods.obj(), i));
-
+  for (auto java_method : methods) {
     if (!safe_annotation_clazz_.is_null()) {
       if (!IsAnnotationPresent(env, java_method, safe_annotation_clazz_))
         continue;
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index e3086c3..84da2e4 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -188,9 +188,7 @@
 
 bool ChildProcessLauncherPriority::is_background() const {
   return !visible && !has_media_stream && !boost_for_pending_views &&
-         !(has_foreground_service_worker &&
-           base::FeatureList::IsEnabled(
-               features::kServiceWorkerForegroundPriority));
+         !has_foreground_service_worker;
 }
 
 bool ChildProcessLauncherPriority::operator==(
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 1362688..6fade13d 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -233,7 +233,7 @@
     // Requested version is higher than current version - upgrade needed.
     DCHECK_GT(new_version, old_version);
 
-    if (db_->connections_.empty()) {
+    if (db_->HasNoConnections()) {
       std::vector<ScopesLockManager::ScopeLockRequest> lock_requests = {
           {kDatabaseRangeLockLevel, GetDatabaseLockRange(db_->metadata_.id),
            ScopesLockManager::LockType::kExclusive}};
@@ -251,8 +251,7 @@
     // "versionchange" event was ignored.
     DCHECK_NE(pending_->data_loss_info.status,
               blink::mojom::IDBDataLoss::Total);
-    for (const auto* connection : db_->connections_)
-      connection->callbacks()->OnVersionChange(old_version, new_version);
+    db_->SendVersionChangeToAllConnections(old_version, new_version);
 
     // When all connections have closed the upgrade can proceed.
   }
@@ -278,7 +277,7 @@
       return;
     }
 
-    if (!db_->connections_.empty())
+    if (!db_->HasNoConnections())
       return;
 
     std::vector<ScopesLockManager::ScopeLockRequest> lock_requests = {
@@ -375,7 +374,7 @@
         weak_factory_(this) {}
 
   void Perform() override {
-    if (db_->connections_.empty()) {
+    if (db_->HasNoConnections()) {
       // No connections, so delete immediately.
       DoDelete();
       return;
@@ -385,8 +384,7 @@
     // close_pending set.
     const int64_t old_version = db_->metadata_.version;
     const int64_t new_version = IndexedDBDatabaseMetadata::NO_VERSION;
-    for (const auto* connection : db_->connections_)
-      connection->callbacks()->OnVersionChange(old_version, new_version);
+    db_->SendVersionChangeToAllConnections(old_version, new_version);
   }
 
   void OnVersionChangeIgnored() const override {
@@ -394,7 +392,7 @@
   }
 
   void OnConnectionClosed(IndexedDBConnection* connection) override {
-    if (!db_->connections_.empty())
+    if (!db_->HasNoConnections())
       return;
 
     DoDelete();
@@ -599,6 +597,18 @@
     active_request_->OnVersionChangeIgnored();
 }
 
+bool IndexedDBDatabase::HasNoConnections() const {
+  return force_closing_ || connections_.empty();
+}
+
+void IndexedDBDatabase::SendVersionChangeToAllConnections(int64_t old_version,
+                                                          int64_t new_version) {
+  if (force_closing_)
+    return;
+  for (const auto* connection : connections_)
+    connection->callbacks()->OnVersionChange(old_version, new_version);
+}
+
 void IndexedDBDatabase::ConnectionClosed(IndexedDBConnection* connection) {
   if (force_closing_)
     return;
@@ -1991,7 +2001,8 @@
   // connections to close, or the actual upgrade transaction from an active
   // request. Notify the active request if it's the latter.
   if (active_request_ &&
-      mode == blink::mojom::IDBTransactionMode::VersionChange) {
+      mode == blink::mojom::IDBTransactionMode::VersionChange &&
+      !force_closing_) {
     active_request_->UpgradeTransactionFinished(committed);
   }
 }
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
index fe819f7..ced3908a3 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -366,6 +366,11 @@
   // pending connection.
   void VersionChangeIgnored();
 
+  bool HasNoConnections() const;
+
+  void SendVersionChangeToAllConnections(int64_t old_version,
+                                         int64_t new_version);
+
   // This can only be called when the given connection is closed and no longer
   // has any transaction objects.
   void ConnectionClosed(IndexedDBConnection* connection);
diff --git a/content/browser/network_service_instance.cc b/content/browser/network_service_instance_impl.cc
similarity index 89%
rename from content/browser/network_service_instance.cc
rename to content/browser/network_service_instance_impl.cc
index cfeea7a..25a7cf7 100644
--- a/content/browser/network_service_instance.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/public/browser/network_service_instance.h"
+#include "content/browser/network_service_instance_impl.h"
 
 #include <map>
 #include <memory>
@@ -17,12 +17,14 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
+#include "base/threading/sequence_local_storage_slot.h"
 #include "build/build_config.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/network_service_client.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/common/network_service_util.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
@@ -45,9 +47,16 @@
 constexpr char kKrb5ConfEnvName[] = "KRB5_CONFIG";
 #endif
 
+bool g_force_create_network_service_directly = false;
 network::mojom::NetworkServicePtr* g_network_service_ptr = nullptr;
 network::NetworkConnectionTracker* g_network_connection_tracker;
-network::NetworkService* g_network_service;
+
+std::unique_ptr<network::NetworkService>& GetLocalNetworkService() {
+  static base::NoDestructor<
+      base::SequenceLocalStorageSlot<std::unique_ptr<network::NetworkService>>>
+      service;
+  return service->Get();
+}
 
 network::mojom::NetworkServiceParamsPtr CreateNetworkServiceParams() {
   network::mojom::NetworkServiceParamsPtr network_service_params =
@@ -80,14 +89,14 @@
 }
 
 void CreateNetworkServiceOnIO(network::mojom::NetworkServiceRequest request) {
-  if (g_network_service) {
+  if (GetLocalNetworkService()) {
     // GetNetworkServiceImpl() was already called and created the object, so
     // just bind it.
-    g_network_service->Bind(std::move(request));
+    GetLocalNetworkService()->Bind(std::move(request));
     return;
   }
 
-  g_network_service = new network::NetworkService(
+  GetLocalNetworkService() = std::make_unique<network::NetworkService>(
       nullptr, std::move(request), GetContentClient()->browser()->GetNetLog());
 }
 
@@ -112,10 +121,12 @@
 }  // namespace
 
 network::mojom::NetworkService* GetNetworkService() {
-  service_manager::Connector* connector =
-      base::FeatureList::IsEnabled(network::features::kNetworkService)
-          ? ServiceManagerConnection::GetForProcess()->GetConnector()
-          : nullptr;
+  service_manager::Connector* connector = nullptr;
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
+      ServiceManagerConnection::GetForProcess() &&  // null in unit tests.
+      !g_force_create_network_service_directly) {
+    connector = ServiceManagerConnection::GetForProcess()->GetConnector();
+  }
   return GetNetworkServiceFromConnector(connector);
 }
 
@@ -145,13 +156,12 @@
       auto request = mojo::MakeRequest(g_network_service_ptr);
       auto leaked_pipe = request.PassMessagePipe().release();
     } else {
-      if (is_network_service_enabled) {
+      if (is_network_service_enabled && connector) {
         connector->BindInterface(mojom::kNetworkServiceName,
                                  g_network_service_ptr);
         g_network_service_ptr->set_connection_error_handler(
             base::BindOnce(&OnNetworkServiceCrash));
       } else {
-        DCHECK(!g_network_service_ptr->is_bound());
         base::PostTaskWithTraits(
             FROM_HERE, {BrowserThread::IO},
             base::BindOnce(CreateNetworkServiceOnIO,
@@ -237,12 +247,12 @@
 network::NetworkService* GetNetworkServiceImpl() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
-  if (!g_network_service) {
-    g_network_service = new network::NetworkService(
+  if (!GetLocalNetworkService()) {
+    GetLocalNetworkService() = std::make_unique<network::NetworkService>(
         nullptr, nullptr, GetContentClient()->browser()->GetNetLog());
   }
 
-  return g_network_service;
+  return GetLocalNetworkService().get();
 }
 
 #if defined(OS_CHROMEOS)
@@ -311,4 +321,13 @@
   return instance->get();
 }
 
+void ForceCreateNetworkServiceDirectlyForTesting() {
+  g_force_create_network_service_directly = true;
+}
+
+void ResetNetworkServiceForTesting() {
+  delete g_network_service_ptr;
+  g_network_service_ptr = nullptr;
+}
+
 }  // namespace content
diff --git a/content/browser/network_service_instance_impl.h b/content/browser/network_service_instance_impl.h
new file mode 100644
index 0000000..641d081
--- /dev/null
+++ b/content/browser/network_service_instance_impl.h
@@ -0,0 +1,31 @@
+// Copyright 2019 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_NETWORK_SERVICE_INSTANCE_IMPL_H_
+#define CONTENT_BROWSER_NETWORK_SERVICE_INSTANCE_IMPL_H_
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Creates the network::NetworkService object on the IO thread directly instead
+// of trying to go through the ServiceManager.
+CONTENT_EXPORT void ForceCreateNetworkServiceDirectlyForTesting();
+
+// Resets the interface ptr to the network service.
+CONTENT_EXPORT void ResetNetworkServiceForTesting();
+
+// Registers |handler| to run (on UI thread) after NetworkServicePtr encounters
+// an error.  Note that there are no ordering guarantees wrt error handlers for
+// other interfaces (e.g. NetworkContextPtr and/or URLLoaderFactoryPtr).
+//
+// Can only be called on the UI thread.  No-op if NetworkService is disabled.
+CONTENT_EXPORT std::unique_ptr<base::CallbackList<void()>::Subscription>
+RegisterNetworkServiceCrashHandler(base::RepeatingClosure handler);
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_NETWORK_SERVICE_INSTANCE_IMPL_H_
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index 1eb5cfc..2aca979f 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/frame_host/render_frame_message_filter.h"
+#include "content/browser/network_service_instance_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/service_worker/embedded_worker_instance.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 9f1a283..2b50b23 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -109,6 +109,7 @@
 #include "content/browser/media/midi_host.h"
 #include "content/browser/mime_registry_impl.h"
 #include "content/browser/navigation_subresource_loader_params.h"
+#include "content/browser/network_service_instance_impl.h"
 #include "content/browser/payments/payment_manager.h"
 #include "content/browser/permissions/permission_service_context.h"
 #include "content/browser/permissions/permission_service_impl.h"
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index f747a13..c0094df7 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -35,7 +35,6 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "ipc/ipc_message.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -278,8 +277,6 @@
 
 void NotifyForegroundServiceWorkerOnUIThread(bool added, int process_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(
-      base::FeatureList::IsEnabled(features::kServiceWorkerForegroundPriority));
 
   RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
   if (!rph)
@@ -1190,8 +1187,6 @@
 
 void EmbeddedWorkerInstance::NotifyForegroundServiceWorkerAdded() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(
-      base::FeatureList::IsEnabled(features::kServiceWorkerForegroundPriority));
 
   if (!process_handle_ || foreground_notified_)
     return;
@@ -1205,9 +1200,6 @@
 
 void EmbeddedWorkerInstance::NotifyForegroundServiceWorkerRemoved() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(
-      !foreground_notified_ ||
-      base::FeatureList::IsEnabled(features::kServiceWorkerForegroundPriority));
 
   if (!process_handle_ || !foreground_notified_)
     return;
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index d8d548a..89450d2 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -37,7 +37,6 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/result_codes.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
@@ -2114,8 +2113,6 @@
 bool ServiceWorkerVersion::ShouldRequireForegroundPriority(
     int worker_process_id) const {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!base::FeatureList::IsEnabled(features::kServiceWorkerForegroundPriority))
-    return false;
 
   // Currently FetchEvents are the only type of event we need to really process
   // at foreground priority.  If the service worker does not have a FetchEvent
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index af701dc..cf29f82 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -28,7 +28,6 @@
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/common/service_worker/service_worker_utils.h"
-#include "content/public/common/content_features.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_service.mojom.h"
@@ -1192,9 +1191,6 @@
 
 TEST_F(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountUpdatedByControllee) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(features::kServiceWorkerForegroundPriority);
-
   // Start the worker before we have a controllee.
   base::Optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
@@ -1230,9 +1226,6 @@
 
 TEST_F(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountNotUpdatedBySameProcessControllee) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(features::kServiceWorkerForegroundPriority);
-
   // Start the worker before we have a controllee.
   base::Optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
@@ -1258,9 +1251,6 @@
 
 TEST_F(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountUpdatedByControlleeProcessIdChange) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(features::kServiceWorkerForegroundPriority);
-
   // Start the worker before we have a controllee.
   base::Optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
@@ -1313,9 +1303,6 @@
 
 TEST_F(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountUpdatedByWorkerStatus) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(features::kServiceWorkerForegroundPriority);
-
   // Add a controllee in a different process from the service worker.
   auto remote_endpoint = ActivateWithControllee();
 
@@ -1358,9 +1345,6 @@
 
 TEST_F(ServiceWorkerVersionNoFetchHandlerTest,
        ForegroundServiceWorkerCountNotUpdated) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(features::kServiceWorkerForegroundPriority);
-
   // Start the worker before we have a controllee.
   base::Optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
diff --git a/content/browser/web_package/signed_exchange_handler_unittest.cc b/content/browser/web_package/signed_exchange_handler_unittest.cc
index b2423f8..b1bf2628c 100644
--- a/content/browser/web_package/signed_exchange_handler_unittest.cc
+++ b/content/browser/web_package/signed_exchange_handler_unittest.cc
@@ -152,8 +152,6 @@
                     base::StringPiece sct_list_from_tls_extension,
                     net::SignedCertificateTimestampAndStatusList* output_scts,
                     const net::NetLogWithSource& net_log));
-  MOCK_METHOD1(SetObserver, void(CTVerifier::Observer*));
-  MOCK_CONST_METHOD0(GetObserver, CTVerifier::Observer*());
 };
 
 class MockCTPolicyEnforcer : public net::CTPolicyEnforcer {
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index 3d30bd0..c5e7ff9 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -501,9 +501,6 @@
         boolean mediaRendererHasModerate = ContentFeatureList.isEnabled(
                 ContentFeatureList.BACKGROUND_MEDIA_RENDERER_HAS_MODERATE_BINDING);
 
-        boolean serviceWorkerForegroundPriority =
-                ContentFeatureList.isEnabled(ContentFeatureList.SERVICE_WORKER_FOREGROUND_PRIORITY);
-
         @ChildProcessImportance
         int newEffectiveImportance;
         if ((visible && frameDepth == 0) || importance == ChildProcessImportance.IMPORTANT
@@ -511,8 +508,7 @@
             newEffectiveImportance = ChildProcessImportance.IMPORTANT;
         } else if ((visible && frameDepth > 0 && intersectsViewport) || boostForPendingViews
                 || importance == ChildProcessImportance.MODERATE
-                || (hasMediaStream && mediaRendererHasModerate)
-                || (hasForegroundServiceWorker && serviceWorkerForegroundPriority)) {
+                || (hasMediaStream && mediaRendererHasModerate) || hasForegroundServiceWorker) {
             newEffectiveImportance = ChildProcessImportance.MODERATE;
         } else {
             newEffectiveImportance = ChildProcessImportance.NORMAL;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
index 1d908e2..a217470 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentFeatureList.java
@@ -38,8 +38,5 @@
 
     public static final String SERVICE_GROUP_IMPORTANCE = "ServiceGroupImportance";
 
-    public static final String SERVICE_WORKER_FOREGROUND_PRIORITY =
-            "ServiceWorkerForegroundPriority";
-
     private static native boolean nativeIsEnabled(String featureName);
 }
diff --git a/content/public/browser/network_service_instance.h b/content/public/browser/network_service_instance.h
index b79141e..905385e6 100644
--- a/content/public/browser/network_service_instance.h
+++ b/content/public/browser/network_service_instance.h
@@ -50,14 +50,6 @@
 CONTENT_EXPORT network::mojom::NetworkService* GetNetworkServiceFromConnector(
     service_manager::Connector* connector);
 
-// Registers |handler| to run (on UI thread) after NetworkServicePtr encounters
-// an error.  Note that there are no ordering guarantees wrt error handlers for
-// other interfaces (e.g. NetworkContextPtr and/or URLLoaderFactoryPtr).
-//
-// Can only be called on the UI thread.  No-op if NetworkService is disabled.
-CONTENT_EXPORT std::unique_ptr<base::CallbackList<void()>::Subscription>
-RegisterNetworkServiceCrashHandler(base::RepeatingClosure handler);
-
 // When network service is disabled, returns the in-process NetworkService
 // pointer which is used to ease transition to network service.
 // Must only be called on the IO thread.  Must not be called if the network
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 5c9ae72..6a4f0b7 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -406,11 +406,6 @@
     "SendBeaconThrowForBlobWithNonSimpleType",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Keep processes with service workers controlling clients from other
-// processes at foreground priority. (crbug.com/928904)
-const base::Feature kServiceWorkerForegroundPriority{
-    "ServiceWorkerForegroundPriority", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables long running message dispatch for service workers.
 // This is a temporary addition only to be used for the Android Messages
 // integration with ChromeOS (http://crbug.com/823256).
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 062664e1..9637f71 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -99,7 +99,6 @@
 CONTENT_EXPORT extern const base::Feature kScrollAnchorSerialization;
 CONTENT_EXPORT extern const base::Feature
     kSendBeaconThrowForBlobWithNonSimpleType;
-CONTENT_EXPORT extern const base::Feature kServiceWorkerForegroundPriority;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerLongRunningMessage;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
 CONTENT_EXPORT extern const base::Feature kSharedArrayBuffer;
diff --git a/content/public/test/test_file_error_injector.cc b/content/public/test/test_file_error_injector.cc
index d8669a2..f26dd9fa 100644
--- a/content/public/test/test_file_error_injector.cc
+++ b/content/public/test/test_file_error_injector.cc
@@ -52,9 +52,11 @@
                   bool is_parallelizable) override;
 
   // DownloadFile interface.
-  download::DownloadInterruptReason WriteDataToFile(int64_t offset,
-                                                    const char* data,
-                                                    size_t data_len) override;
+  download::DownloadInterruptReason ValidateAndWriteDataToFile(
+      int64_t offset,
+      const char* data,
+      size_t bytes_to_validate,
+      size_t bytes_to_write) override;
 
   download::DownloadInterruptReason HandleStreamCompletionStatus(
       SourceStream* source_stream) override;
@@ -174,13 +176,15 @@
                                          received_slices, is_parallelizable);
 }
 
-download::DownloadInterruptReason DownloadFileWithError::WriteDataToFile(
-    int64_t offset,
-    const char* data,
-    size_t data_len) {
+download::DownloadInterruptReason
+DownloadFileWithError::ValidateAndWriteDataToFile(int64_t offset,
+                                                  const char* data,
+                                                  size_t bytes_to_validate,
+                                                  size_t bytes_to_write) {
   return ShouldReturnError(
       TestFileErrorInjector::FILE_OPERATION_WRITE,
-      download::DownloadFileImpl::WriteDataToFile(offset, data, data_len));
+      download::DownloadFileImpl::ValidateAndWriteDataToFile(
+          offset, data, bytes_to_validate, bytes_to_write));
 }
 
 download::DownloadInterruptReason
diff --git a/content/public/test/unittest_test_suite.cc b/content/public/test/unittest_test_suite.cc
index 60a4718..c949988 100644
--- a/content/public/test/unittest_test_suite.cc
+++ b/content/public/test/unittest_test_suite.cc
@@ -11,7 +11,9 @@
 #include "base/rand_util.h"
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
+#include "content/browser/network_service_instance_impl.h"
 #include "content/test/test_blink_web_unit_test_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/web/blink.h"
 
 #if defined(USE_AURA)
@@ -28,6 +30,31 @@
 
 namespace content {
 
+namespace {
+
+// The global NetworkService object could be created in some tests due to
+// various StoragePartition calls. Since it has a mojo pipe that is bound using
+// the current thread, which goes away between tests, we need to destruct it to
+// avoid calls being dropped silently.
+class ResetNetworkServiceBetweenTests : public testing::EmptyTestEventListener {
+ public:
+  ResetNetworkServiceBetweenTests() = default;
+
+  void OnTestEnd(const testing::TestInfo& test_info) override {
+    // If the network::NetworkService object was instantiated during a unit test
+    // it will be deleted because network_service_instance.cc has it in a
+    // SequenceLocalStorageSlot. However we want to synchronously destruct the
+    // InterfacePtr pointing to it to avoid it getting the connection error
+    // later and have other tests use the InterfacePtr that is invalid.
+    ResetNetworkServiceForTesting();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ResetNetworkServiceBetweenTests);
+};
+
+}  // namespace
+
 UnitTestTestSuite::UnitTestTestSuite(base::TestSuite* test_suite)
     : test_suite_(test_suite) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
@@ -36,6 +63,12 @@
   std::string disabled =
       command_line->GetSwitchValueASCII(switches::kDisableFeatures);
 
+  ForceCreateNetworkServiceDirectlyForTesting();
+
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(new ResetNetworkServiceBetweenTests);
+
   // Unit tests don't currently work with the Network Service enabled.
   // base::TestSuite will reset the FeatureList, so modify the underlying
   // CommandLine object to disable the network service when it's parsed again.
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 3de2f96..8e55869 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -28,6 +28,7 @@
     "//content/app:*",
     "//content/public/renderer:renderer_sources",
     "//content/renderer:audio_decoder_fuzzer",
+    "//chromecast/media/audio:*",
   ]
 
   sources = [
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc
index 0065675..3e1a04a 100644
--- a/extensions/browser/extension_protocols.cc
+++ b/extensions/browser/extension_protocols.cc
@@ -521,8 +521,9 @@
 }
 
 bool IsBackgroundPageURL(const GURL& url) {
-  std::string path = url.path();
-  return path.size() > 1 && path.substr(1) == kGeneratedBackgroundPageFilename;
+  base::StringPiece path_piece = url.path_piece();
+  return path_piece.size() > 1 &&
+         path_piece.substr(1) == kGeneratedBackgroundPageFilename;
 }
 
 class ExtensionProtocolHandler
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
index 3d9b309..77aac8f 100644
--- a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
+++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
@@ -24,6 +24,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h"
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
 #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
@@ -126,6 +127,22 @@
                      false));
 }
 
+void ExtensionsGuestViewMessageFilter::ReadyToCreateMimeHandlerView(
+    int32_t render_frame_id,
+    bool success) {
+  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
+    base::PostTaskWithTraits(
+        FROM_HERE, {content::BrowserThread::UI},
+        base::BindOnce(
+            &ExtensionsGuestViewMessageFilter::ReadyToCreateMimeHandlerView,
+            this, render_frame_id, success));
+    return;
+  }
+  auto* rfh =
+      content::RenderFrameHost::FromID(render_process_id_, render_frame_id);
+  if (auto* mhve = MimeHandlerViewEmbedder::Get(rfh->GetFrameTreeNodeId()))
+    mhve->ReadyToCreateMimeHandlerView(success);
+}
 void ExtensionsGuestViewMessageFilter::CreateMimeHandlerViewGuestOnUIThread(
     int render_frame_id,
     const std::string& view_id,
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.h b/extensions/browser/guest_view/extensions_guest_view_message_filter.h
index 6eb5c30..86b62e68 100644
--- a/extensions/browser/guest_view/extensions_guest_view_message_filter.h
+++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.h
@@ -82,6 +82,8 @@
       int32_t element_instance_id,
       const gfx::Size& element_size,
       mime_handler::BeforeUnloadControlPtr before_unload_control) override;
+  void ReadyToCreateMimeHandlerView(int32_t render_frame_id,
+                                    bool success) override;
 
   void CreateMimeHandlerViewGuestOnUIThread(
       int32_t render_frame_id,
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc
index 3a33caa..22ef239c 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc
@@ -33,6 +33,17 @@
 }
 }  // namespace
 
+// static
+MimeHandlerViewEmbedder* MimeHandlerViewEmbedder::Get(
+    int32_t frame_tree_node_id) {
+  const auto& map = *GetMimeHandlerViewEmbeddersMap();
+  auto it = map.find(frame_tree_node_id);
+  if (it == map.cend())
+    return nullptr;
+  return it->second.get();
+}
+
+// static
 void MimeHandlerViewEmbedder::Create(int32_t frame_tree_node_id,
                                      const GURL& resource_url,
                                      const std::string& mime_type,
@@ -96,6 +107,13 @@
       render_frame_host_->GetLastCommittedURL() != resource_url_) {
     return;
   }
+  if (!ready_to_create_mime_handler_view_) {
+    // Renderer notifies the browser about creating MimeHandlerView right after
+    // HTMLPlugInElement::RequestObject, which is before the plugin element is
+    // navigated.
+    GetMimeHandlerViewEmbeddersMap()->erase(frame_tree_node_id_);
+    return;
+  }
   outer_contents_frame_tree_node_id_ = render_frame_host->GetFrameTreeNodeId();
   element_instance_id_ = render_frame_host->GetRoutingID();
   // This suggests that a same-origin child frame is created under the
@@ -185,4 +203,12 @@
   }
   return container_manager_.get();
 }
+
+void MimeHandlerViewEmbedder::ReadyToCreateMimeHandlerView(
+    bool ready_to_create_mime_handler_view) {
+  ready_to_create_mime_handler_view_ = ready_to_create_mime_handler_view;
+  if (!ready_to_create_mime_handler_view_)
+    GetMimeHandlerViewEmbeddersMap()->erase(frame_tree_node_id_);
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.h b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.h
index 7dd20a2..0e276e4 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.h
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.h
@@ -32,6 +32,10 @@
 //.  - the embedder or the <iframe> are removed from DOM.
 class MimeHandlerViewEmbedder : public content::WebContentsObserver {
  public:
+  // Returns the instances associated with an ongoing navigation in a frame
+  // identified by |frame_tree_node_id|.
+  static MimeHandlerViewEmbedder* Get(int32_t frame_tree_node_id);
+
   static void Create(int32_t frame_tree_node_id,
                      const GURL& resource_url,
                      const std::string& mime_type,
@@ -46,6 +50,8 @@
   void DidStartNavigation(content::NavigationHandle* handle) override;
   void ReadyToCommitNavigation(content::NavigationHandle* handle) override;
 
+  void ReadyToCreateMimeHandlerView(bool result);
+
  private:
   MimeHandlerViewEmbedder(int32_t frame_tree_node_id,
                           const GURL& resource_url,
@@ -82,6 +88,8 @@
 
   const std::string internal_id_;
 
+  bool ready_to_create_mime_handler_view_ = false;
+
   base::WeakPtrFactory<MimeHandlerViewEmbedder> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(MimeHandlerViewEmbedder);
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index feb42c8..93439e9a 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -383,6 +383,8 @@
     sources = [
       "api/declarative_net_request/test_utils.cc",
       "api/declarative_net_request/test_utils.h",
+      "file_test_util.cc",
+      "file_test_util.h",
     ]
 
     deps = [
diff --git a/extensions/common/file_test_util.cc b/extensions/common/file_test_util.cc
new file mode 100644
index 0000000..f15c5f4
--- /dev/null
+++ b/extensions/common/file_test_util.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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 "extensions/common/file_test_util.h"
+#include "base/files/file_util.h"
+
+namespace extensions {
+namespace file_test_util {
+
+bool WriteFile(const base::FilePath& path, base::StringPiece content) {
+  return base::WriteFile(path, content.data(), content.size()) ==
+         static_cast<int>(content.size());
+}
+
+}  // namespace file_test_util
+}  // namespace extensions
diff --git a/extensions/common/file_test_util.h b/extensions/common/file_test_util.h
new file mode 100644
index 0000000..ecab05df
--- /dev/null
+++ b/extensions/common/file_test_util.h
@@ -0,0 +1,23 @@
+// Copyright 2019 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 EXTENSIONS_COMMON_FILE_TEST_UTIL_H_
+#define EXTENSIONS_COMMON_FILE_TEST_UTIL_H_
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace extensions {
+namespace file_test_util {
+
+// Writes |content| to |path|. Returns true if writing was successful,
+// verifying the number of bytes written equals the size of |content|.
+bool WriteFile(const base::FilePath& path, base::StringPiece content);
+
+}  // namespace file_test_util
+}  // namespace extensions
+
+#endif  // EXTENSIONS_COMMON_FILE_TEST_UTIL_H_
diff --git a/extensions/common/mojo/guest_view.mojom b/extensions/common/mojo/guest_view.mojom
index 19eae09..d348369 100644
--- a/extensions/common/mojo/guest_view.mojom
+++ b/extensions/common/mojo/guest_view.mojom
@@ -30,6 +30,13 @@
       int32 element_instance_id,
       gfx.mojom.Size element_size,
       extensions.mime_handler.BeforeUnloadControl? before_unload_control);
+
+  // Notifies the browser whether or not now is a good time to start loading the
+  // MimeHandlerView. |routing_id| identifies the embedder frame. If |success|
+  // is false, then MimeHandlerViewEmbedder should destroy itself. When
+  // |success| is true the MimeHandlerViewEmbedder should proceed with attaching
+  // the GuestView.
+  ReadyToCreateMimeHandlerView(int32 routing_id, bool success);
 };
 
 // An interface implemented by the renderer which is used for creating a
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
index c0b7c84..4e85f0f 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
@@ -146,6 +146,11 @@
 }
 
 // static
+mojom::GuestView* MimeHandlerViewContainerBase::GuestView() {
+  return GetGuestView();
+}
+
+// static
 std::vector<MimeHandlerViewContainerBase*>
 MimeHandlerViewContainerBase::FromRenderFrame(
     content::RenderFrame* render_frame) {
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h
index 1f14dc8..e18ea6b 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h
@@ -46,6 +46,8 @@
 
   ~MimeHandlerViewContainerBase() override;
 
+  static mojom::GuestView* GuestView();
+
   // TODO(ekaramad): Remove this and make MimeHandlerViewContainerManager of
   // |render_frame| hold on to the list of MimeHandlerViewContainerBase.
   static std::vector<MimeHandlerViewContainerBase*> FromRenderFrame(
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc
index 439870d..d06093d4 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc
@@ -109,6 +109,18 @@
   return true;
 }
 
+void MimeHandlerViewContainerManager::
+    DidBlockMimeHandlerViewForDisallowedPlugin(
+        const blink::WebElement& plugin_element) {
+  if (IsManagedByContainerManager(plugin_element)) {
+    // This is the one injected by HTML string. Return true so that the
+    // HTMLPlugInElement creates a child frame to be used as the outer
+    // WebContents frame.
+    MimeHandlerViewContainerBase::GuestView()->ReadyToCreateMimeHandlerView(
+        render_frame()->GetRoutingID(), false);
+  }
+}
+
 v8::Local<v8::Object> MimeHandlerViewContainerManager::GetScriptableObject(
     const blink::WebElement& plugin_element,
     v8::Isolate* isolate) {
@@ -295,6 +307,8 @@
       base::ToUpperASCII(plugin_element.GetAttribute("internalid").Utf8()) ==
           internal_id_) {
     plugin_element_ = plugin_element;
+    MimeHandlerViewContainerBase::GuestView()->ReadyToCreateMimeHandlerView(
+        render_frame()->GetRoutingID(), true);
   }
   return plugin_element_ == plugin_element;
 }
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h
index ebfca295..12abbee 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h
@@ -66,6 +66,10 @@
                             const GURL& resource_url,
                             const std::string& mime_type,
                             const content::WebPluginInfo& plugin_info);
+  // Called to notify about a failed plugin load; this could happen if a
+  // <webview> with permissions API tries to load a plugin.
+  void DidBlockMimeHandlerViewForDisallowedPlugin(
+      const blink::WebElement& plugin_element);
   // A wrapper for custom postMessage scripts. There should already be a
   // MimeHandlerViewFrameContainer for |plugin_element|.
   v8::Local<v8::Object> GetScriptableObject(
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 59f5892..034cbfc 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -197,8 +197,7 @@
   ]
 }
 
-# Disabled on mac due to build failure (crbug.com/967098).
-if (!is_android && !is_fuchsia && !is_mac) {
+if (!is_android && !is_fuchsia) {
   proto_library("gl_lpm_fuzzer_proto") {
     sources = [
       "command_buffer/tests/lpm/gl_lpm_fuzzer.proto",
@@ -246,6 +245,10 @@
 
     defines = [ "GL_GLEXT_PROTOTYPES" ]
 
+    if (is_mac) {
+      libs = [ "IOSurface.framework" ]
+    }
+
     deps = [
       ":gl_lpm_fuzzer_proto",
       ":gl_lpm_shader_to_string",
diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json
index 0042a34..03e2515 100644
--- a/gpu/config/software_rendering_list.json
+++ b/gpu/config/software_rendering_list.json
@@ -287,7 +287,6 @@
       },
       "vendor_id": "0x10de",
       "gl_vendor": "(?i)nouveau.*",
-      "driver_vendor": "Mesa",
       "features": [
         "all"
       ]
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 4b413dd..89b8bd0 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -71,6 +71,16 @@
   return success;
 }
 
+#if defined(OS_WIN)
+OverlaySupport FlagsToOverlaySupport(UINT flags) {
+  if (flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING)
+    return OverlaySupport::kScaling;
+  if (flags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT)
+    return OverlaySupport::kDirect;
+  return OverlaySupport::kNone;
+}
+#endif  // OS_WIN
+
 void InitializePlatformOverlaySettings(GPUInfo* gpu_info) {
 #if defined(OS_WIN)
 // This has to be called after a context is created, active GPU is identified,
@@ -84,19 +94,12 @@
         gl::DirectCompositionSurfaceWin::IsDirectCompositionSupported();
     gpu_info->supports_overlays =
         gl::DirectCompositionSurfaceWin::AreOverlaysSupported();
-    bool supports_scaling = false;
-    if (gl::DirectCompositionSurfaceWin::SupportsOverlayFormat(
-            DXGI_FORMAT_YUY2, &supports_scaling)) {
-      gpu_info->yuy2_overlay_support = supports_scaling
-                                           ? gpu::OverlaySupport::kScaling
-                                           : gpu::OverlaySupport::kDirect;
-    }
-    if (gl::DirectCompositionSurfaceWin::SupportsOverlayFormat(
-            DXGI_FORMAT_NV12, &supports_scaling)) {
-      gpu_info->nv12_overlay_support = supports_scaling
-                                           ? gpu::OverlaySupport::kScaling
-                                           : gpu::OverlaySupport::kDirect;
-    }
+    gpu_info->nv12_overlay_support = FlagsToOverlaySupport(
+        gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
+            DXGI_FORMAT_NV12));
+    gpu_info->yuy2_overlay_support = FlagsToOverlaySupport(
+        gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
+            DXGI_FORMAT_YUY2));
   }
 #elif defined(OS_ANDROID)
   if (gpu_info->gpu.vendor_string == "Qualcomm")
diff --git a/ios/chrome/browser/metrics/BUILD.gn b/ios/chrome/browser/metrics/BUILD.gn
index 59965713..75424c4c1 100644
--- a/ios/chrome/browser/metrics/BUILD.gn
+++ b/ios/chrome/browser/metrics/BUILD.gn
@@ -18,6 +18,16 @@
   ]
 }
 
+source_set("features") {
+  sources = [
+    "features.cc",
+    "features.h",
+  ]
+  deps = [
+    "//base",
+  ]
+}
+
 source_set("metrics") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -47,6 +57,7 @@
     ":ukm_url_recorder",
   ]
   deps = [
+    ":features",
     "//base",
     "//components/browser_sync",
     "//components/crash/core/common",
diff --git a/ios/chrome/browser/metrics/features.cc b/ios/chrome/browser/metrics/features.cc
new file mode 100644
index 0000000..d26584cb
--- /dev/null
+++ b/ios/chrome/browser/metrics/features.cc
@@ -0,0 +1,8 @@
+// Copyright 2019 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 "ios/chrome/browser/metrics/features.h"
+
+const base::Feature kLogLoadStartedInDidStartNavigation{
+    "LogLoadStartedInDidStartNavigation", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/metrics/features.h b/ios/chrome/browser/metrics/features.h
new file mode 100644
index 0000000..e542485a
--- /dev/null
+++ b/ios/chrome/browser/metrics/features.h
@@ -0,0 +1,13 @@
+// Copyright 2019 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_METRICS_FEATURES_H_
+#define IOS_CHROME_BROWSER_METRICS_FEATURES_H_
+
+#include "base/feature_list.h"
+
+// Feature flag to move -LogLoadStarted() to WebStateDidStartNavigation().
+extern const base::Feature kLogLoadStartedInDidStartNavigation;
+
+#endif  // IOS_CHROME_BROWSER_METRICS_FEATURES_H_
diff --git a/ios/chrome/browser/metrics/ios_chrome_stability_metrics_provider.mm b/ios/chrome/browser/metrics/ios_chrome_stability_metrics_provider.mm
index 4e51527..d0852a4 100644
--- a/ios/chrome/browser/metrics/ios_chrome_stability_metrics_provider.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_stability_metrics_provider.mm
@@ -4,8 +4,10 @@
 
 #include "ios/chrome/browser/metrics/ios_chrome_stability_metrics_provider.h"
 
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/metrics/features.h"
 #import "ios/web/public/web_state/navigation_context.h"
 #import "ios/web/public/web_state/web_state.h"
 
@@ -63,7 +65,8 @@
     return;
 
   UMA_HISTOGRAM_BOOLEAN(kPageLoadCountLoadingStartedMetric, true);
-  helper_.LogLoadStarted();
+  if (!base::FeatureList::IsEnabled(kLogLoadStartedInDidStartNavigation))
+    helper_.LogLoadStarted();
 }
 
 void IOSChromeStabilityMetricsProvider::WebStateDidStartNavigation(
@@ -79,7 +82,8 @@
   } else if (navigation_context->IsSameDocument()) {
     type = PageLoadCountNavigationType::SAME_DOCUMENT_WEB_NAVIGATION;
   } else {
-    // TODO(crbug.com/786547): Move helper_.LogLoadStarted() here.
+    if (base::FeatureList::IsEnabled(kLogLoadStartedInDidStartNavigation))
+      helper_.LogLoadStarted();
   }
   UMA_HISTOGRAM_ENUMERATION(kPageLoadCountMetric, type,
                             PageLoadCountNavigationType::COUNT);
diff --git a/ios/chrome/browser/ui/overlays/BUILD.gn b/ios/chrome/browser/ui/overlays/BUILD.gn
new file mode 100644
index 0000000..61faa328
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright 2019 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.
+
+source_set("overlays") {
+  public = [
+    "overlay_container_coordinator.h",
+    "overlay_coordinator_factory.h",
+  ]
+  sources = [
+    "overlay_container_coordinator.mm",
+    "overlay_coordinator_factory.mm",
+    "overlay_presenter_ui_delegate_impl.h",
+    "overlay_presenter_ui_delegate_impl.mm",
+    "overlay_request_ui_state.h",
+    "overlay_request_ui_state.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  friend = [ ":unit_tests" ]
+
+  deps = [
+    ":container_ui",
+    ":coordinators",
+    "//base",
+    "//ios/chrome/browser/main",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/common/ui_util",
+  ]
+}
+
+source_set("container_ui") {
+  sources = [
+    "overlay_container_view_controller.h",
+    "overlay_container_view_controller.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+source_set("coordinators") {
+  sources = [
+    "overlay_request_coordinator.h",
+    "overlay_request_coordinator.mm",
+    "overlay_ui_dismissal_delegate.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//base",
+    "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "overlay_request_ui_state_unittest.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":coordinators",
+    ":overlays",
+    "//base/test:test_support",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/test",
+    "//ios/chrome/browser/ui/overlays/test",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.h b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.h
new file mode 100644
index 0000000..33664b2
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.h
@@ -0,0 +1,38 @@
+// Copyright 2019 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_UI_OVERLAYS_OVERLAY_CONTAINER_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_CONTAINER_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#include "ios/chrome/browser/overlays/public/overlay_modality.h"
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+
+// Coordinator that manages displaying of UI for OverlayRequests.  An instance
+// of this coordinator should be created for each Browser at every
+// OverlayModality.
+@interface OverlayContainerCoordinator : ChromeCoordinator
+
+// Initializer for an overlay container that presents overlay for |browser| at
+// |modality|.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                  modality:(OverlayModality)modality
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:
+                                  (ios::ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+// The view controller whose presentation context is used to present overlays
+// for the OverlayPresenter corresponding with the container's Browser and
+// OverlayModality.
+@property(nonatomic, readonly) UIViewController* viewController;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_CONTAINER_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
new file mode 100644
index 0000000..e3695f5
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_container_coordinator.mm
@@ -0,0 +1,89 @@
+// Copyright 2019 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/ui/overlays/overlay_container_coordinator.h"
+
+#include <map>
+#include <memory>
+
+#include "base/logging.h"
+#import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/ui/overlays/overlay_container_view_controller.h"
+#import "ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h"
+#import "ios/chrome/common/ui_util/constraints_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface OverlayContainerCoordinator () <
+    OverlayContainerViewControllerDelegate>
+// Whether the coordinator is started.
+@property(nonatomic, assign, getter=isStarted) BOOL started;
+// The UI delegate that is used to drive presentation for this container.
+@property(nonatomic, readonly) OverlayPresenterUIDelegateImpl* UIDelegate;
+@end
+
+@implementation OverlayContainerCoordinator
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                  modality:(OverlayModality)modality {
+  if (self = [super initWithBaseViewController:viewController
+                                       browser:browser]) {
+    OverlayPresenterUIDelegateImpl::Container::CreateForUserData(browser,
+                                                                 browser);
+    _UIDelegate =
+        OverlayPresenterUIDelegateImpl::Container::FromUserData(browser)
+            ->UIDelegateForModality(modality);
+    DCHECK(_UIDelegate);
+  }
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  if (self.started)
+    return;
+  self.started = YES;
+  // Create the container view controller and add it to the base view
+  // controller.
+  OverlayContainerViewController* viewController =
+      [[OverlayContainerViewController alloc] init];
+  viewController.definesPresentationContext = YES;
+  viewController.delegate = self;
+  _viewController = viewController;
+  UIView* containerView = _viewController.view;
+  containerView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.baseViewController addChildViewController:_viewController];
+  [self.baseViewController.view addSubview:containerView];
+  AddSameConstraints(containerView, self.baseViewController.view);
+  [_viewController didMoveToParentViewController:self.baseViewController];
+}
+
+- (void)stop {
+  if (!self.started)
+    return;
+  self.started = NO;
+  self.UIDelegate->SetCoordinator(nil);
+  // Remove the container view and reset the view controller.
+  [_viewController willMoveToParentViewController:nil];
+  [_viewController.view removeFromSuperview];
+  [_viewController removeFromParentViewController];
+  _viewController = nil;
+}
+
+#pragma mark - OverlayContainerViewControllerDelegate
+
+- (void)containerViewController:
+            (OverlayContainerViewController*)containerViewController
+                didMoveToWindow:(UIWindow*)window {
+  // UIViewController presentation no-ops when attempted on window-less parent
+  // view controllers.  Wait to set UI delegate's coordinator until the
+  // container is added to a window.
+  self.UIDelegate->SetCoordinator(window ? self : nil);
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_view_controller.h b/ios/chrome/browser/ui/overlays/overlay_container_view_controller.h
new file mode 100644
index 0000000..cf1ee12a
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_container_view_controller.h
@@ -0,0 +1,29 @@
+// Copyright 2019 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_UI_OVERLAYS_OVERLAY_CONTAINER_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_CONTAINER_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+@protocol OverlayContainerViewControllerDelegate;
+
+// View controller used to show overlay UI.
+@interface OverlayContainerViewController : UIViewController
+@property(nonatomic, weak) id<OverlayContainerViewControllerDelegate> delegate;
+@end
+
+// Delegate protocol for the container view.
+@protocol OverlayContainerViewControllerDelegate <NSObject>
+
+// Called when |containerViewController|'s view moves to a new window. Overlay
+// presentation should not be attempted until the container is added to
+// a window.
+- (void)containerViewController:
+            (OverlayContainerViewController*)containerViewController
+                didMoveToWindow:(UIWindow*)window;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_CONTAINER_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_view_controller.mm b/ios/chrome/browser/ui/overlays/overlay_container_view_controller.mm
new file mode 100644
index 0000000..75ab844
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_container_view_controller.mm
@@ -0,0 +1,41 @@
+// Copyright 2019 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/ui/overlays/overlay_container_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface OverlayContainerView : UIView
+// The owning view controller.
+@property(nonatomic, weak) OverlayContainerViewController* viewController;
+@end
+
+@implementation OverlayContainerView
+
+- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
+  // Allow touches to go to subviews, but ignore touches that are routed to the
+  // container view itself.
+  UIView* hitView = [super hitTest:point withEvent:event];
+  return hitView == self ? nil : hitView;
+}
+
+- (void)didMoveToWindow {
+  [super didMoveToWindow];
+  [self.viewController.delegate containerViewController:self.viewController
+                                        didMoveToWindow:self.window];
+}
+
+@end
+
+@implementation OverlayContainerViewController
+
+- (void)loadView {
+  OverlayContainerView* containerView = [[OverlayContainerView alloc] init];
+  containerView.viewController = self;
+  self.view = containerView;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h
new file mode 100644
index 0000000..ccc7285
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h
@@ -0,0 +1,37 @@
+// Copyright 2019 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_UI_OVERLAYS_OVERLAY_COORDINATOR_FACTORY_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_COORDINATOR_FACTORY_H_
+
+#import <UIKit/UIKit.h>
+
+#include "ios/chrome/browser/overlays/public/overlay_modality.h"
+
+class Browser;
+class OverlayUIDismissalDelegate;
+@class OverlayRequestCoordinator;
+class OverlayRequest;
+
+// Factory object provided to OverlayContainerCoordinators that supply overlay
+// coordinators for a request.
+@interface OverlayRequestCoordinatorFactory : NSObject
+
+// Returns a coordinator factory for |browser| at |modality|.
++ (instancetype)factoryForBrowser:(Browser*)browser
+                         modality:(OverlayModality)modality;
+
+// OverlayRequestCoordinatorFactory must be fetched using
+// |+factoryForBrowser:modality:|.
+- (instancetype)init NS_UNAVAILABLE;
+
+// Creates a coordinator to show |request|'s overlay UI.
+- (OverlayRequestCoordinator*)
+    newCoordinatorForRequest:(OverlayRequest*)request
+           dismissalDelegate:(OverlayUIDismissalDelegate*)dismissalDelegate
+          baseViewController:(UIViewController*)baseViewController;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_COORDINATOR_FACTORY_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
new file mode 100644
index 0000000..6014c1c
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
@@ -0,0 +1,73 @@
+// Copyright 2019 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/ui/overlays/overlay_coordinator_factory.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface OverlayRequestCoordinatorFactory ()
+// The Browser passed on initialization.
+@property(nonatomic, readonly) Browser* browser;
+// The OverlayRequestCoordinator subclasses that are supported at the modality
+// associated with this coordinator factory.
+@property(nonatomic, readonly)
+    NSArray<Class>* supportedOverlayRequestCoordinatorClasses;
+// Initializer used by |+factoryForBrowser:modality:|.
+- (instancetype)initWithBrowser:(Browser*)browser
+    supportedOverlayRequestCoordinatorClasses:
+        (NSArray<Class>*)supportedOverlayClasses NS_DESIGNATED_INITIALIZER;
+@end
+
+@implementation OverlayRequestCoordinatorFactory
+
++ (instancetype)factoryForBrowser:(Browser*)browser
+                         modality:(OverlayModality)modality {
+  DCHECK(browser);
+  NSArray<Class>* supportedCoordinatorClasses = @[];
+  switch (modality) {
+    case OverlayModality::kWebContentArea:
+      // TODO(crbug.com/941745): Reset |supportedCoordinatorClasses| to contain
+      // OverlayRequestCoordinator classes once implemented.
+      break;
+  }
+  return [[self alloc] initWithBrowser:browser
+      supportedOverlayRequestCoordinatorClasses:supportedCoordinatorClasses];
+}
+
+- (instancetype)initWithBrowser:(Browser*)browser
+    supportedOverlayRequestCoordinatorClasses:
+        (NSArray<Class>*)supportedOverlayClasses {
+  if (self = [super init]) {
+    _browser = browser;
+    DCHECK(_browser);
+    _supportedOverlayRequestCoordinatorClasses = supportedOverlayClasses;
+    DCHECK(_supportedOverlayRequestCoordinatorClasses.count);
+  }
+  return self;
+}
+
+- (OverlayRequestCoordinator*)
+    newCoordinatorForRequest:(OverlayRequest*)request
+           dismissalDelegate:(OverlayUIDismissalDelegate*)dismissalDelegate
+          baseViewController:(UIViewController*)baseViewController {
+  for (Class coordinatorClass in self
+           .supportedOverlayRequestCoordinatorClasses) {
+    if ([coordinatorClass supportsRequest:request]) {
+      return [[coordinatorClass alloc]
+          initWithBaseViewController:baseViewController
+                             browser:self.browser
+                             request:request
+                   dismissalDelegate:dismissalDelegate];
+    }
+  }
+  NOTREACHED() << "Received unsupported request type.";
+  return nil;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h b/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h
new file mode 100644
index 0000000..f73ce9b
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.h
@@ -0,0 +1,137 @@
+// Copyright 2019 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_UI_OVERLAYS_OVERLAY_PRESENTER_UI_DELEGATE_IMPL_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTER_UI_DELEGATE_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#import "ios/chrome/browser/main/browser_observer.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/overlays/public/overlay_user_data.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_ui_state.h"
+#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+
+@class OverlayRequestCoordinatorFactory;
+@class OverlayContainerCoordinator;
+
+// Implementation of OverlayPresenter::UIDelegate.  An instance of this class
+// exists for every OverlayModality for each Browser.  This delegate is scoped
+// to the Browser because it needs to store state even when a Browser's UI is
+// not on screen.  When a Browser's UI is shown, the OverlayContainerCoordinator
+// for each of its OverlayModalities will supply itself to the delegate, which
+// will then present the UI using the container coordinator's presentation
+// context.
+class OverlayPresenterUIDelegateImpl : public OverlayPresenter::UIDelegate {
+ public:
+  ~OverlayPresenterUIDelegateImpl() override;
+
+  // Container that stores the UI delegate for each modality.  Usage example:
+  //
+  // OverlayPresenterUIDelegateImpl::Container::FromUserData(browser)->
+  //     UIDelegateForModality(OverlayModality::kWebContentArea);
+  class Container : public OverlayUserData<Container> {
+   public:
+    ~Container() override;
+
+    // Returns the OverlayPresenterUIDelegateImpl for |modality|.
+    OverlayPresenterUIDelegateImpl* UIDelegateForModality(
+        OverlayModality modality);
+
+   private:
+    OVERLAY_USER_DATA_SETUP(Container);
+    explicit Container(Browser* browser);
+
+    Browser* browser_ = nullptr;
+    std::map<OverlayModality, std::unique_ptr<OverlayPresenterUIDelegateImpl>>
+        ui_delegates_;
+  };
+
+  // The OverlayContainerCoordinator is used to present the overlay UI at the
+  // correct modality in the app.  Should only be set when the coordinator's
+  // presentation context is able to present.
+  OverlayContainerCoordinator* coordinator() const { return coordinator_; }
+  void SetCoordinator(OverlayContainerCoordinator* coordinator);
+
+  // OverlayPresenter::UIDelegate:
+  void ShowOverlayUI(OverlayPresenter* presenter,
+                     OverlayRequest* request,
+                     OverlayDismissalCallback dismissal_callback) override;
+  void HideOverlayUI(OverlayPresenter* presenter,
+                     OverlayRequest* request) override;
+  void CancelOverlayUI(OverlayPresenter* presenter,
+                       OverlayRequest* request) override;
+
+ private:
+  OverlayPresenterUIDelegateImpl(Browser* browser, OverlayModality modality);
+
+  // Setter for |request_|.  Setting to a new value will attempt to
+  // present the UI for |request|.
+  void SetRequest(OverlayRequest* request);
+
+  // Returns the UI state for |request|.
+  OverlayRequestUIState* GetRequestUIState(OverlayRequest* request);
+
+  // Shows the UI for the presented request using the container coordinator.
+  void ShowUIForPresentedRequest();
+
+  // Dismisses the UI for the presented request for |reason|.
+  void DismissPresentedUI(OverlayDismissalReason reason);
+
+  // Called when the UI for |request_| has finished being dismissed.
+  void OverlayUIWasDismissed();
+
+  // Notifies the state for |request_| that its UI has finished being dismissed.
+  void NotifyStateOfDismissal();
+
+  // Helper object that detaches the UI delegate for Browser shudown.
+  class BrowserShutdownHelper : public BrowserObserver {
+   public:
+    BrowserShutdownHelper(Browser* browser, OverlayPresenter* presenter);
+    ~BrowserShutdownHelper() override;
+
+    // BrowserObserver:
+    void BrowserDestroyed(Browser* browser) override;
+
+   private:
+    // The presenter whose delegate needs to be reset.
+    OverlayPresenter* presenter_ = nullptr;
+  };
+
+  // Helper object that listens for UI dismissal events.
+  class OverlayDismissalHelper : public OverlayUIDismissalDelegate {
+   public:
+    OverlayDismissalHelper(OverlayPresenterUIDelegateImpl* ui_delegate);
+    ~OverlayDismissalHelper() override;
+
+    // OverlayUIDismissalDelegate:
+    void OverlayUIDidFinishDismissal(OverlayRequest* request) override;
+
+   private:
+    OverlayPresenterUIDelegateImpl* ui_delegate_ = nullptr;
+  };
+
+  // The presenter whose UI is being handled by this delegate.
+  OverlayPresenter* presenter_ = nullptr;
+  // The cleanup helper.
+  BrowserShutdownHelper shutdown_helper_;
+  // The UI dismissal helper.
+  OverlayDismissalHelper ui_dismissal_helper_;
+  // The coordinator factory that provides the UI for the overlays at this
+  // modality.
+  OverlayRequestCoordinatorFactory* coordinator_factory_ = nil;
+  // The coordinator responsible for presenting the UI delegate's UI.
+  OverlayContainerCoordinator* coordinator_ = nil;
+  // The request that is currently presented by |presenter_|.  The UI for this
+  // request might not yet be visible if no OverlayContainerCoordinator has been
+  // provided.  When a new request is presented, the UI state for the request
+  // will be added to |states_|.
+  OverlayRequest* request_ = nullptr;
+  // Map storing the UI state for each OverlayRequest.
+  std::map<OverlayRequest*, std::unique_ptr<OverlayRequestUIState>> states_;
+  // Weak pointer factory.
+  base::WeakPtrFactory<OverlayPresenterUIDelegateImpl> weak_factory_;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_PRESENTER_UI_DELEGATE_IMPL_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.mm b/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.mm
new file mode 100644
index 0000000..db08334
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_presenter_ui_delegate_impl.mm
@@ -0,0 +1,256 @@
+// Copyright 2019 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/ui/overlays/overlay_presenter_ui_delegate_impl.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#pragma mark - OverlayPresenterUIDelegateImpl::Container
+
+OVERLAY_USER_DATA_SETUP_IMPL(OverlayPresenterUIDelegateImpl::Container);
+
+OverlayPresenterUIDelegateImpl::Container::Container(Browser* browser)
+    : browser_(browser) {
+  DCHECK(browser_);
+}
+
+OverlayPresenterUIDelegateImpl::Container::~Container() = default;
+
+OverlayPresenterUIDelegateImpl*
+OverlayPresenterUIDelegateImpl::Container::UIDelegateForModality(
+    OverlayModality modality) {
+  auto& ui_delegate = ui_delegates_[modality];
+  if (!ui_delegate) {
+    ui_delegate = base::WrapUnique(
+        new OverlayPresenterUIDelegateImpl(browser_, modality));
+  }
+  return ui_delegate.get();
+}
+
+#pragma mark - OverlayPresenterUIDelegateImpl
+
+OverlayPresenterUIDelegateImpl::OverlayPresenterUIDelegateImpl(
+    Browser* browser,
+    OverlayModality modality)
+    : presenter_(OverlayPresenter::FromBrowser(browser, modality)),
+      shutdown_helper_(browser, presenter_),
+      ui_dismissal_helper_(this),
+      coordinator_factory_([OverlayRequestCoordinatorFactory
+          factoryForBrowser:browser
+                   modality:modality]),
+      weak_factory_(this) {
+  DCHECK(presenter_);
+  DCHECK(coordinator_factory_);
+  presenter_->SetUIDelegate(this);
+}
+
+OverlayPresenterUIDelegateImpl::~OverlayPresenterUIDelegateImpl() = default;
+
+#pragma mark Public
+
+void OverlayPresenterUIDelegateImpl::SetCoordinator(
+    OverlayContainerCoordinator* coordinator) {
+  if (coordinator_ == coordinator)
+    return;
+  if (coordinator_ && request_)
+    DismissPresentedUI(OverlayDismissalReason::kHiding);
+
+  coordinator_ = coordinator;
+
+  // The new coordinator should be started before provided to the UI delegate.
+  DCHECK(!coordinator_ || coordinator_.viewController);
+
+  ShowUIForPresentedRequest();
+}
+
+#pragma mark OverlayPresenter::UIDelegate
+
+void OverlayPresenterUIDelegateImpl::ShowOverlayUI(
+    OverlayPresenter* presenter,
+    OverlayRequest* request,
+    OverlayDismissalCallback dismissal_callback) {
+  DCHECK_EQ(presenter_, presenter);
+  // Create the UI state for |request| if necessary.
+  if (!GetRequestUIState(request))
+    states_[request] = std::make_unique<OverlayRequestUIState>(request);
+  // Present the overlay UI and update the UI state.
+  GetRequestUIState(request)->OverlayPresentionRequested(
+      std::move(dismissal_callback));
+  SetRequest(request);
+}
+
+void OverlayPresenterUIDelegateImpl::HideOverlayUI(OverlayPresenter* presenter,
+                                                   OverlayRequest* request) {
+  DCHECK_EQ(presenter_, presenter);
+  DCHECK_EQ(request_, request);
+
+  OverlayRequestUIState* state = GetRequestUIState(request_);
+  DCHECK(state->has_callback());
+
+  if (coordinator_) {
+    // If the request's UI is presented by the coordinator, dismiss it.  The
+    // presented request will be reset when the dismissal animation finishes.
+    DismissPresentedUI(OverlayDismissalReason::kHiding);
+  } else {
+    // Simulate dismissal if there is no container coordinator.
+    state->set_dismissal_reason(OverlayDismissalReason::kHiding);
+    NotifyStateOfDismissal();
+  }
+}
+
+void OverlayPresenterUIDelegateImpl::CancelOverlayUI(
+    OverlayPresenter* presenter,
+    OverlayRequest* request) {
+  DCHECK_EQ(presenter_, presenter);
+  // If the coordinator is not presenting the overlay UI for |state|, it can
+  // be deleted immediately.
+  OverlayRequestUIState* state = GetRequestUIState(request);
+  DCHECK(state);
+  if (!state->has_callback() || !coordinator_) {
+    states_.erase(request);
+    return;
+  }
+
+  DismissPresentedUI(OverlayDismissalReason::kCancellation);
+}
+
+#pragma mark Accesors
+
+void OverlayPresenterUIDelegateImpl::SetRequest(OverlayRequest* request) {
+  if (request_ == request)
+    return;
+  if (request_) {
+    OverlayRequestUIState* state = GetRequestUIState(request_);
+    // The presented request should only be reset when the previously presented
+    // request's UI has finished being dismissed.
+    DCHECK(state);
+    DCHECK(!state->has_callback());
+    DCHECK(!state->coordinator().viewController.view.superview);
+    // If the overlay was dismissed for user interaction or cancellation, then
+    // the state can be destroyed, since the UI for the previously presented
+    // request will never be shown again.
+    OverlayDismissalReason reason = state->dismissal_reason();
+    if (reason == OverlayDismissalReason::kUserInteraction ||
+        reason == OverlayDismissalReason::kCancellation) {
+      states_.erase(request_);
+    }
+  }
+
+  request_ = request;
+
+  // The UI state should be created before resetting the presented request.
+  DCHECK(!request_ || GetRequestUIState(request_));
+
+  ShowUIForPresentedRequest();
+}
+
+OverlayRequestUIState* OverlayPresenterUIDelegateImpl::GetRequestUIState(
+    OverlayRequest* request) {
+  return request ? states_[request].get() : nullptr;
+}
+
+#pragma mark Presentation and Dismissal helpers
+
+void OverlayPresenterUIDelegateImpl::ShowUIForPresentedRequest() {
+  OverlayRequestUIState* state = GetRequestUIState(request_);
+  if (!state || !coordinator_)
+    return;
+
+  // Create the coordinator if necessary.
+  UIViewController* container_view_controller = coordinator_.viewController;
+  OverlayRequestCoordinator* overlay_coordinator = state->coordinator();
+  if (!overlay_coordinator ||
+      overlay_coordinator.baseViewController != container_view_controller) {
+    overlay_coordinator = [coordinator_factory_
+        newCoordinatorForRequest:request_
+               dismissalDelegate:&ui_dismissal_helper_
+              baseViewController:container_view_controller];
+    state->OverlayUIWillBePresented(overlay_coordinator);
+  }
+
+  [overlay_coordinator startAnimated:!state->has_ui_been_presented()];
+  state->OverlayUIWasPresented();
+}
+
+void OverlayPresenterUIDelegateImpl::DismissPresentedUI(
+    OverlayDismissalReason reason) {
+  OverlayRequestUIState* state = GetRequestUIState(request_);
+  DCHECK(state);
+  DCHECK(coordinator_);
+  DCHECK(state->coordinator());
+
+  state->set_dismissal_reason(reason);
+  [state->coordinator()
+      stopAnimated:reason == OverlayDismissalReason::kUserInteraction];
+}
+
+void OverlayPresenterUIDelegateImpl::OverlayUIWasDismissed() {
+  DCHECK(request_);
+  // Overlays are dismissed without animation when the container coordinator is
+  // reset, but the state should not be notified of these dismissals since the
+  // same UI will be presented again once a new container coordinator is
+  // provided.
+  if (!coordinator_)
+    return;
+  NotifyStateOfDismissal();
+}
+
+void OverlayPresenterUIDelegateImpl::NotifyStateOfDismissal() {
+  DCHECK(request_);
+  DCHECK(GetRequestUIState(request_)->has_callback());
+  // If there is another request in the active WebState's OverlayRequestQueue,
+  // executing the state's dismissal callback will trigger the presentation of
+  // the next request.  If the presented request remains unchanged after calling
+  // the dismissal callback, reset it to nullptr since the UI is no longer
+  // presented.
+  OverlayRequest* previously_presented_request = request_;
+  GetRequestUIState(request_)->OverlayUIWasDismissed();
+  if (request_ == previously_presented_request)
+    SetRequest(nullptr);
+}
+
+#pragma mark BrowserShutdownHelper
+
+OverlayPresenterUIDelegateImpl::BrowserShutdownHelper::BrowserShutdownHelper(
+    Browser* browser,
+    OverlayPresenter* presenter)
+    : presenter_(presenter) {
+  DCHECK(presenter_);
+  browser->AddObserver(this);
+}
+
+OverlayPresenterUIDelegateImpl::BrowserShutdownHelper::
+    ~BrowserShutdownHelper() = default;
+
+void OverlayPresenterUIDelegateImpl::BrowserShutdownHelper::BrowserDestroyed(
+    Browser* browser) {
+  presenter_->SetUIDelegate(nullptr);
+  browser->RemoveObserver(this);
+}
+
+#pragma mark OverlayDismissalHelper
+
+OverlayPresenterUIDelegateImpl::OverlayDismissalHelper::OverlayDismissalHelper(
+    OverlayPresenterUIDelegateImpl* ui_delegate)
+    : ui_delegate_(ui_delegate) {
+  DCHECK(ui_delegate_);
+}
+
+OverlayPresenterUIDelegateImpl::OverlayDismissalHelper::
+    ~OverlayDismissalHelper() = default;
+
+void OverlayPresenterUIDelegateImpl::OverlayDismissalHelper::
+    OverlayUIDidFinishDismissal(OverlayRequest* request) {
+  DCHECK(request);
+  DCHECK_EQ(ui_delegate_->request_, request);
+  ui_delegate_->OverlayUIWasDismissed();
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_coordinator.h b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.h
new file mode 100644
index 0000000..02bd7f34
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.h
@@ -0,0 +1,54 @@
+// Copyright 2019 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_UI_OVERLAYS_OVERLAY_REQUEST_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_REQUEST_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+
+class OverlayUIDismissalDelegate;
+class OverlayRequest;
+
+// Coordinator superclass used to present UI for an OverlayRequest.
+@interface OverlayRequestCoordinator : ChromeCoordinator
+
+// Returns whether this overlay coordinator type supports |request|.
++ (BOOL)supportsRequest:(OverlayRequest*)request;
+
+// Initializer for a coordinator for |request|.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                   request:(OverlayRequest*)request
+                         dismissalDelegate:
+                             (OverlayUIDismissalDelegate*)dismissalDelegate
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:
+                                  (ios::ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+// The OverlayUIDismissalDelegate passed on initialization.  Used to communicate
+// when the overlay UI is finished being dismissed, which may occur after
+// |-stop| even if the overlay is stopped without animation.  This notifies
+// OverlayPresenter that the presentation context is clear to show the next
+// requested overlay.
+@property(nonatomic, readonly) OverlayUIDismissalDelegate* dismissalDelegate;
+
+// The request used to configure the overlay UI.
+@property(nonatomic, readonly) OverlayRequest* request;
+
+// The view controller that displays the UI for |request|.
+@property(nonatomic, readonly) UIViewController* viewController;
+
+// OverlayRequestCoordinator's |-start| and |-stop| need to support versions
+// both with and without animation, as hidden overlays should be shown without
+// animation for subsequent presentations.
+- (void)startAnimated:(BOOL)animated;
+- (void)stopAnimated:(BOOL)animated;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_REQUEST_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_coordinator.mm b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.mm
new file mode 100644
index 0000000..0bde829
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.mm
@@ -0,0 +1,56 @@
+// Copyright 2019 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/ui/overlays/overlay_request_coordinator.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation OverlayRequestCoordinator
+
++ (BOOL)supportsRequest:(OverlayRequest*)request {
+  return NO;
+}
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                   request:(OverlayRequest*)request
+                         dismissalDelegate:
+                             (OverlayUIDismissalDelegate*)dismissalDelegate {
+  DCHECK([[self class] supportsRequest:request]);
+  self = [super initWithBaseViewController:viewController browser:browser];
+  if (self) {
+    _request = request;
+    DCHECK(_request);
+    _dismissalDelegate = dismissalDelegate;
+    DCHECK(_dismissalDelegate);
+  }
+  return self;
+}
+
+#pragma mark - Public
+
+- (void)startAnimated:(BOOL)animated {
+  NOTREACHED() << "Subclasses must implement.";
+}
+
+- (void)stopAnimated:(BOOL)animated {
+  NOTREACHED() << "Subclasses must implement.";
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  [self startAnimated:YES];
+}
+
+- (void)stop {
+  [self stopAnimated:YES];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_ui_state.h b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.h
new file mode 100644
index 0000000..1e4a2a8
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.h
@@ -0,0 +1,56 @@
+// Copyright 2019 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_UI_OVERLAYS_OVERLAY_REQUEST_UI_STATE_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_REQUEST_UI_STATE_H_
+
+#import <Foundation/Foundation.h>
+
+#include "ios/chrome/browser/overlays/public/overlay_dismissal_callback.h"
+
+@class OverlayRequestCoordinator;
+class OverlayRequest;
+
+// Container holding information about the state of overlay UI for a given
+// request.
+class OverlayRequestUIState {
+ public:
+  explicit OverlayRequestUIState(OverlayRequest* request);
+  ~OverlayRequestUIState();
+
+  // Called when the OverlayPresenter requests the presentation of |request_|.
+  // This may or may not correspond with an OverlayUIWasPresented() if the
+  // hierarchy is not ready for presentation (i.e. overlay presentation for a
+  // Browser whose UI is not currently on-screen).
+  void OverlayPresentionRequested(OverlayDismissalCallback callback);
+
+  // Notifies the state that the UI is about to be presented using
+  // |coordinator|.
+  void OverlayUIWillBePresented(OverlayRequestCoordinator* coordinator);
+
+  // Notifies the state that the UI was presented.
+  void OverlayUIWasPresented();
+
+  // Notifies the state the the UI was dismissed.
+  void OverlayUIWasDismissed();
+
+  // Accessors.
+  OverlayRequestCoordinator* coordinator() const { return coordinator_; }
+  bool has_ui_been_presented() const { return has_ui_been_presented_; }
+  bool has_callback() const { return !dismissal_callback_.is_null(); }
+  OverlayDismissalReason dismissal_reason() const { return dismissal_reason_; }
+  void set_dismissal_reason(OverlayDismissalReason dismissal_reason) {
+    dismissal_reason_ = dismissal_reason;
+  }
+
+ private:
+  OverlayRequest* request_ = nullptr;
+  OverlayRequestCoordinator* coordinator_ = nil;
+  bool has_ui_been_presented_ = false;
+  OverlayDismissalReason dismissal_reason_ =
+      OverlayDismissalReason::kUserInteraction;
+  OverlayDismissalCallback dismissal_callback_;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_REQUEST_UI_STATE_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_ui_state.mm b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.mm
new file mode 100644
index 0000000..5073250
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.mm
@@ -0,0 +1,49 @@
+// Copyright 2019 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/ui/overlays/overlay_request_ui_state.h"
+
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+OverlayRequestUIState::OverlayRequestUIState(OverlayRequest* request)
+    : request_(request) {
+  DCHECK(request_);
+}
+
+OverlayRequestUIState::~OverlayRequestUIState() {
+  if (has_callback()) {
+    set_dismissal_reason(OverlayDismissalReason::kCancellation);
+    OverlayUIWasDismissed();
+  }
+}
+
+void OverlayRequestUIState::OverlayPresentionRequested(
+    OverlayDismissalCallback callback) {
+  DCHECK(dismissal_callback_.is_null());
+  dismissal_callback_ = std::move(callback);
+  // The default dismissal reason is kUserInteraction.  This is to avoid
+  // additional bookkeeping for overlays dismissed by user interaction.
+  // Overlays explicitly dismissed by OverlayPresenter set the reason to kHide
+  // or kCancellation before dismissal.
+  dismissal_reason_ = OverlayDismissalReason::kUserInteraction;
+}
+
+void OverlayRequestUIState::OverlayUIWillBePresented(
+    OverlayRequestCoordinator* coordinator) {
+  DCHECK(coordinator);
+  coordinator_ = coordinator;
+}
+
+void OverlayRequestUIState::OverlayUIWasPresented() {
+  has_ui_been_presented_ = true;
+}
+
+void OverlayRequestUIState::OverlayUIWasDismissed() {
+  DCHECK(has_callback());
+  std::move(dismissal_callback_).Run(dismissal_reason_);
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_ui_state_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_request_ui_state_unittest.mm
new file mode 100644
index 0000000..cbc9a1c
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_request_ui_state_unittest.mm
@@ -0,0 +1,85 @@
+// Copyright 2019 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/ui/overlays/overlay_request_ui_state.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#include "ios/chrome/browser/overlays/test/fake_overlay_user_data.h"
+#import "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for OverlayRequestUIState.
+class OverlayRequestUIStateTest : public PlatformTest {
+ public:
+  OverlayRequestUIStateTest()
+      : PlatformTest(),
+        request_(
+            OverlayRequest::CreateWithConfig<FakeOverlayUserData>(nullptr)),
+        state_(request_.get()) {}
+
+  OverlayRequest* request() { return request_.get(); }
+  OverlayRequestUIState& state() { return state_; }
+
+ private:
+  std::unique_ptr<OverlayRequest> request_;
+  OverlayRequestUIState state_;
+};
+
+// Tests that OverlayPresentionRequested() passes the callback to the state and
+// resets the default dismissal reason to kUserInteraction.
+TEST_F(OverlayRequestUIStateTest, OverlayPresentionRequested) {
+  ASSERT_FALSE(state().has_callback());
+  state().OverlayPresentionRequested(base::BindOnce(^(OverlayDismissalReason){
+  }));
+  EXPECT_TRUE(state().has_callback());
+  EXPECT_EQ(OverlayDismissalReason::kUserInteraction,
+            state().dismissal_reason());
+}
+
+// Tests that OverlayUIWillBePresented() stores the coordinator in the state.
+TEST_F(OverlayRequestUIStateTest, OverlayUIWillBePresented) {
+  FakeDismissalDelegate dismissal_delegate;
+  FakeOverlayRequestCoordinator* coordinator =
+      [[FakeOverlayRequestCoordinator alloc]
+          initWithBaseViewController:nil
+                             browser:nullptr
+                             request:request()
+                   dismissalDelegate:&dismissal_delegate];
+  state().OverlayUIWillBePresented(coordinator);
+  EXPECT_EQ(coordinator, state().coordinator());
+}
+
+// Tests that OverlayUIWasPresented() correctly updates has_ui_been_presented().
+TEST_F(OverlayRequestUIStateTest, OverlayUIWasPresented) {
+  ASSERT_FALSE(state().has_ui_been_presented());
+  state().OverlayUIWasPresented();
+  EXPECT_TRUE(state().has_ui_been_presented());
+}
+
+// Tests that OverlayUIWasDismissed() executes the dismissal callback.
+TEST_F(OverlayRequestUIStateTest, OverlayUIWasDismissed) {
+  __block bool dismissal_callback_executed = false;
+  state().OverlayPresentionRequested(base::BindOnce(^(OverlayDismissalReason) {
+    dismissal_callback_executed = true;
+  }));
+  FakeDismissalDelegate dismissal_delegate;
+  FakeOverlayRequestCoordinator* coordinator =
+      [[FakeOverlayRequestCoordinator alloc]
+          initWithBaseViewController:nil
+                             browser:nullptr
+                             request:request()
+                   dismissalDelegate:&dismissal_delegate];
+  state().OverlayUIWillBePresented(coordinator);
+  state().OverlayUIWasPresented();
+  state().OverlayUIWasDismissed();
+  EXPECT_TRUE(dismissal_callback_executed);
+}
diff --git a/ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h b/ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h
new file mode 100644
index 0000000..ad84fa3
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h
@@ -0,0 +1,21 @@
+// Copyright 2019 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_UI_OVERLAYS_OVERLAY_UI_DISMISSAL_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_UI_DISMISSAL_DELEGATE_H_
+
+class OverlayRequest;
+
+// Delegate class used to communicate dismissal events back to OverlayPresenter.
+class OverlayUIDismissalDelegate {
+ public:
+  OverlayUIDismissalDelegate() = default;
+  virtual ~OverlayUIDismissalDelegate() = default;
+
+  // Called to notify the delegate that the UI for |request|'s UI is finished
+  // being dismissed.
+  virtual void OverlayUIDidFinishDismissal(OverlayRequest* request) = 0;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_UI_DISMISSAL_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/overlays/test/BUILD.gn b/ios/chrome/browser/ui/overlays/test/BUILD.gn
new file mode 100644
index 0000000..d5c019f5
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2019 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.
+
+source_set("test") {
+  testonly = true
+  sources = [
+    "fake_overlay_request_coordinator.h",
+    "fake_overlay_request_coordinator.mm",
+    "fake_overlay_ui_dismissal_delegate.cc",
+    "fake_overlay_ui_dismissal_delegate.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//base",
+    "//ios/chrome/browser/ui/overlays:coordinators",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.h b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.h
new file mode 100644
index 0000000..4ba13b5
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.h
@@ -0,0 +1,14 @@
+// Copyright 2019 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_UI_OVERLAYS_TEST_FAKE_OVERLAY_REQUEST_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_REQUEST_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+
+// Fake version of OverlayRequestCoordinator that supports all OverlayRequests.
+@interface FakeOverlayRequestCoordinator : OverlayRequestCoordinator
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_REQUEST_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.mm b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.mm
new file mode 100644
index 0000000..9f7842d
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.mm
@@ -0,0 +1,23 @@
+// Copyright 2019 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/ui/overlays/test/fake_overlay_request_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation FakeOverlayRequestCoordinator
+
++ (BOOL)supportsRequest:(OverlayRequest*)request {
+  return YES;
+}
+
+- (void)startAnimated:(BOOL)animated {
+}
+
+- (void)stopAnimated:(BOOL)animated {
+}
+
+@end
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.cc b/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.cc
new file mode 100644
index 0000000..88ee77f
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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 "ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h"
+
+FakeDismissalDelegate::FakeDismissalDelegate() = default;
+FakeDismissalDelegate::~FakeDismissalDelegate() = default;
+
+bool FakeDismissalDelegate::HasUIBeenDismissed(OverlayRequest* request) const {
+  return dismissed_requests_.find(request) != dismissed_requests_.end();
+}
+
+void FakeDismissalDelegate::OverlayUIDidFinishDismissal(
+    OverlayRequest* request) {
+  dismissed_requests_.insert(request);
+}
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h b/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h
new file mode 100644
index 0000000..0334c4c9
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h
@@ -0,0 +1,28 @@
+// Copyright 2019 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_UI_OVERLAYS_TEST_FAKE_OVERLAY_UI_DISMISSAL_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_UI_DISMISSAL_DELEGATE_H_
+
+#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+
+#include <set>
+
+// Fake implementation of OverlayUIDismissalDelegate.
+class FakeDismissalDelegate : public OverlayUIDismissalDelegate {
+ public:
+  FakeDismissalDelegate();
+  ~FakeDismissalDelegate() override;
+
+  // Whether the overlay UI for |request| has been dismissed.
+  bool HasUIBeenDismissed(OverlayRequest* request) const;
+
+  // OverlayUIDismissalDelegate:
+  void OverlayUIDidFinishDismissal(OverlayRequest* request) override;
+
+ private:
+  std::set<OverlayRequest*> dismissed_requests_;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_UI_DISMISSAL_DELEGATE_H_
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 4d0ac98..f7d3439 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -224,6 +224,7 @@
     "//ios/chrome/browser/ui/omnibox:unit_tests",
     "//ios/chrome/browser/ui/omnibox/popup:unit_tests",
     "//ios/chrome/browser/ui/open_in:unit_tests",
+    "//ios/chrome/browser/ui/overlays:unit_tests",
     "//ios/chrome/browser/ui/payments:unit_tests",
     "//ios/chrome/browser/ui/payments/cells:unit_tests",
     "//ios/chrome/browser/ui/popup_menu:unit_tests",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index ba3d819..7e39db1 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -112,6 +112,15 @@
 
 #pragma mark - Sync Utilities
 
+// Clears fake sync server data.
+- (void)clearSyncServerData;
+
+// Starts the sync server. The server should not be running when calling this.
+- (void)startSync;
+
+// Stops the sync server. The server should be running when calling this.
+- (void)stopSync;
+
 // Clears the autofill profile for the given |GUID|.
 - (void)clearAutofillProfileWithGUID:(const std::string&)GUID;
 
@@ -284,15 +293,6 @@
 
 #pragma mark - Sync Utilities
 
-// Clears fake sync server data.
-- (void)clearSyncServerData;
-
-// Starts the sync server. The server should not be running when calling this.
-- (void)startSync;
-
-// Stops the sync server. The server should be running when calling this.
-- (void)stopSync;
-
 // Verifies that |count| entities of the given |type| and |name| exist on the
 // sync FakeServer. Folders are not included in this count. Returns nil on
 // success, or else an NSError indicating why the operation failed.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index ee6c16d..bb9fa43 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -240,6 +240,18 @@
 
 #pragma mark - Sync Utilities
 
+- (void)clearSyncServerData {
+  [ChromeEarlGreyAppInterface clearSyncServerData];
+}
+
+- (void)startSync {
+  [ChromeEarlGreyAppInterface startSync];
+}
+
+- (void)stopSync {
+  [ChromeEarlGreyAppInterface stopSync];
+}
+
 - (void)clearAutofillProfileWithGUID:(const std::string&)GUID {
   NSString* nsGUID = base::SysUTF8ToNSString(GUID);
   [ChromeEarlGreyAppInterface clearAutofillProfileWithGUID:nsGUID];
@@ -501,18 +513,6 @@
 
 #pragma mark - Sync Utilities
 
-- (void)clearSyncServerData {
-  chrome_test_util::ClearSyncServerData();
-}
-
-- (void)startSync {
-  chrome_test_util::StartSync();
-}
-
-- (void)stopSync {
-  chrome_test_util::StopSync();
-}
-
 - (NSError*)waitForSyncServerEntitiesWithType:(syncer::ModelType)type
                                          name:(const std::string&)name
                                         count:(size_t)count
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 40bb71f..2f65ed4 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -116,6 +116,15 @@
 
 #pragma mark - Sync Utilities (EG2)
 
+// Clears fake sync server data.
++ (void)clearSyncServerData;
+
+// Starts the sync server. The server should not be running when calling this.
++ (void)startSync;
+
+// Stops the sync server. The server should be running when calling this.
++ (void)stopSync;
+
 // Waits for sync to be initialized or not.
 // Returns nil on success, or else an NSError indicating why the
 // operation failed.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index fc9a1c8..e63c1fc 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -187,6 +187,18 @@
 
 #pragma mark - Sync Utilities (EG2)
 
++ (void)clearSyncServerData {
+  chrome_test_util::ClearSyncServerData();
+}
+
++ (void)startSync {
+  chrome_test_util::StartSync();
+}
+
++ (void)stopSync {
+  chrome_test_util::StopSync();
+}
+
 + (NSError*)waitForSyncInitialized:(BOOL)isInitialized
                        syncTimeout:(NSTimeInterval)timeout {
   bool success = WaitUntilConditionOrTimeout(timeout, ^{
diff --git a/ios/chrome/test/earl_grey2/smoke_egtest.mm b/ios/chrome/test/earl_grey2/smoke_egtest.mm
index 68c5799..119525f 100644
--- a/ios/chrome/test/earl_grey2/smoke_egtest.mm
+++ b/ios/chrome/test/earl_grey2/smoke_egtest.mm
@@ -164,4 +164,11 @@
       waitForSufficientlyVisibleElementWithMatcher:chrome_test_util::Omnibox()];
 }
 
+// Tests sync server converted helpers in chrome_earl_grey.h.
+- (void)testSyncServerHelpers {
+  [ChromeEarlGrey startSync];
+  [ChromeEarlGrey stopSync];
+  [ChromeEarlGrey clearSyncServerData];
+}
+
 @end
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
index 1e79b66..7f94806e 100644
--- a/ios/web/navigation/navigation_manager_impl.mm
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -240,6 +240,15 @@
 }
 
 void NavigationManagerImpl::GoToIndex(int index) {
+  // Silently return if still on a restore URL.  This state should only last a
+  // few moments, but may be triggered when a user mashes the back or forward
+  // button quickly.
+  if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    NavigationItemImpl* item = GetLastCommittedItemInCurrentOrRestoredSession();
+    if (item && wk_navigation_util::IsRestoreSessionUrl(item->GetURL())) {
+      return;
+    }
+  }
   GoToIndex(index, NavigationInitiationType::BROWSER_INITIATED,
             /*has_user_gesture=*/true);
 }
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm
index 420cb2a..884d83b 100644
--- a/ios/web/navigation/navigation_manager_impl_unittest.mm
+++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -2657,6 +2657,7 @@
 
   // Call CommitPendingItem() with a valid pending item.
   auto item = std::make_unique<web::NavigationItemImpl>();
+  item->SetURL(GURL("http://www.url.com/new"));
   item->SetNavigationInitiationType(
       web::NavigationInitiationType::BROWSER_INITIATED);
   navigation_manager()->CommitPendingItem(std::move(item));
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.mm b/ios/web/navigation/wk_based_navigation_manager_impl.mm
index 27df334..82fab52 100644
--- a/ios/web/navigation/wk_based_navigation_manager_impl.mm
+++ b/ios/web/navigation/wk_based_navigation_manager_impl.mm
@@ -298,8 +298,30 @@
     empty_window_open_item_ = std::move(item);
   } else {
     empty_window_open_item_.reset();
-    SetNavigationItemInWKItem(proxy.backForwardList.currentItem,
-                              std::move(item));
+
+    const GURL item_url(item->GetURL());
+    WKBackForwardList* back_forward_list = proxy.backForwardList;
+    if (item_url == net::GURLWithNSURL(back_forward_list.currentItem.URL)) {
+      SetNavigationItemInWKItem(back_forward_list.currentItem, std::move(item));
+    } else {
+      // Sometimes |currentItem.URL| is not updated correctly while the webView
+      // URL is correctly updated. This is a bug in WKWebView. Check to see if
+      // the next or previous item matches, and update that item instead. If
+      // nothing matches, still update the the currentItem.
+      if (item_url == net::GURLWithNSURL(back_forward_list.backItem.URL)) {
+        SetNavigationItemInWKItem(back_forward_list.backItem, std::move(item));
+      } else if (item_url ==
+                 net::GURLWithNSURL(back_forward_list.forwardItem.URL)) {
+        SetNavigationItemInWKItem(back_forward_list.forwardItem,
+                                  std::move(item));
+      } else {
+        // Otherwise default here. This can happen when restoring an NTP, since
+        // |back_forward_list.currentItem.URL| doesn't get updated when going
+        // from a file:// scheme to about:// scheme.
+        SetNavigationItemInWKItem(back_forward_list.currentItem,
+                                  std::move(item));
+      }
+    }
   }
 
   pending_item_index_ = -1;
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 125c6d6..d53a0e5 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -315,18 +315,6 @@
   }
 }
 
-fuzzer_test("media_vp8_parser_fuzzer") {
-  sources = [
-    "filters/vp8_parser_fuzzertest.cc",
-  ]
-  deps = [
-    ":test_support",
-    "//base",
-  ]
-  libfuzzer_options = [ "max_len = 400000" ]
-  dict = "test/vp8.dict"
-}
-
 fuzzer_test("media_vp9_parser_fuzzer") {
   sources = [
     "filters/vp9_parser_fuzzertest.cc",
diff --git a/media/filters/BUILD.gn b/media/filters/BUILD.gn
index eec20a7..d08e594 100644
--- a/media/filters/BUILD.gn
+++ b/media/filters/BUILD.gn
@@ -65,10 +65,6 @@
     "video_cadence_estimator.h",
     "video_renderer_algorithm.cc",
     "video_renderer_algorithm.h",
-    "vp8_bool_decoder.cc",
-    "vp8_bool_decoder.h",
-    "vp8_parser.cc",
-    "vp8_parser.h",
     "vp9_bool_decoder.cc",
     "vp9_bool_decoder.h",
     "vp9_compressed_header_parser.cc",
@@ -289,8 +285,6 @@
     "video_cadence_estimator_unittest.cc",
     "video_decoder_stream_unittest.cc",
     "video_renderer_algorithm_unittest.cc",
-    "vp8_bool_decoder_unittest.cc",
-    "vp8_parser_unittest.cc",
     "vp9_parser_unittest.cc",
     "vp9_raw_bits_reader_unittest.cc",
   ]
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 3441103..ef0101f 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -276,6 +276,7 @@
     ":buildflags",
     "//base",
     "//media",
+    "//media/parsers",
     "//ui/gfx:buffer_types",
     "//ui/gfx:memory_buffer",
     "//ui/gfx/geometry",
@@ -482,6 +483,7 @@
       "//base/test:test_support",
       "//media:test_support",
       "//media/gpu",
+      "//media/parsers",
       "//mojo/core/embedder",
       "//testing/gtest",
       "//third_party/ffmpeg",
diff --git a/media/gpu/v4l2/v4l2_vp8_accelerator.cc b/media/gpu/v4l2/v4l2_vp8_accelerator.cc
index 314bc49..90a4a38d 100644
--- a/media/gpu/v4l2/v4l2_vp8_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_vp8_accelerator.cc
@@ -11,12 +11,12 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/stl_util.h"
-#include "media/filters/vp8_parser.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_decode_surface.h"
 #include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
 #include "media/gpu/v4l2/v4l2_device.h"
 #include "media/gpu/vp8_picture.h"
+#include "media/parsers/vp8_parser.h"
 
 namespace media {
 namespace {
diff --git a/media/gpu/vaapi/vaapi_vp8_accelerator.h b/media/gpu/vaapi/vaapi_vp8_accelerator.h
index a02b3604..a3bb9f0 100644
--- a/media/gpu/vaapi/vaapi_vp8_accelerator.h
+++ b/media/gpu/vaapi/vaapi_vp8_accelerator.h
@@ -6,8 +6,8 @@
 #define MEDIA_GPU_VAAPI_VAAPI_VP8_ACCELERATOR_H_
 
 #include "base/sequence_checker.h"
-#include "media/filters/vp8_parser.h"
 #include "media/gpu/vp8_decoder.h"
+#include "media/parsers/vp8_parser.h"
 
 namespace media {
 
diff --git a/media/gpu/vaapi/vp8_encoder.h b/media/gpu/vaapi/vp8_encoder.h
index 5fb94b3f..9395461c 100644
--- a/media/gpu/vaapi/vp8_encoder.h
+++ b/media/gpu/vaapi/vp8_encoder.h
@@ -11,10 +11,10 @@
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "media/base/video_bitrate_allocation.h"
-#include "media/filters/vp8_parser.h"
 #include "media/gpu/vaapi/accelerated_video_encoder.h"
 #include "media/gpu/vp8_picture.h"
 #include "media/gpu/vp8_reference_frame_vector.h"
+#include "media/parsers/vp8_parser.h"
 
 namespace media {
 
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index 4a22f31..bc1c9c3 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -54,7 +54,6 @@
 #include "media/filters/ffmpeg_video_decoder.h"
 #include "media/filters/in_memory_url_protocol.h"
 #include "media/filters/ivf_parser.h"
-#include "media/filters/vp8_parser.h"
 #include "media/filters/vp9_parser.h"
 #include "media/filters/vpx_video_decoder.h"
 #include "media/gpu/buildflags.h"
@@ -63,6 +62,7 @@
 #include "media/gpu/h264_dpb.h"
 #include "media/gpu/test/video_accelerator_unittest_helpers.h"
 #include "media/gpu/test/video_frame_helpers.h"
+#include "media/parsers/vp8_parser.h"
 #include "media/video/fake_video_encode_accelerator.h"
 #include "media/video/h264_level_limits.h"
 #include "media/video/h264_parser.h"
diff --git a/media/gpu/vp8_decoder.h b/media/gpu/vp8_decoder.h
index 8f53609f..ecc77310 100644
--- a/media/gpu/vp8_decoder.h
+++ b/media/gpu/vp8_decoder.h
@@ -12,10 +12,10 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "media/filters/vp8_parser.h"
 #include "media/gpu/accelerated_video_decoder.h"
 #include "media/gpu/vp8_picture.h"
 #include "media/gpu/vp8_reference_frame_vector.h"
+#include "media/parsers/vp8_parser.h"
 
 namespace media {
 
diff --git a/media/gpu/vp8_picture.h b/media/gpu/vp8_picture.h
index 1e02ded..2db7510 100644
--- a/media/gpu/vp8_picture.h
+++ b/media/gpu/vp8_picture.h
@@ -6,8 +6,8 @@
 #define MEDIA_GPU_VP8_PICTURE_H_
 
 #include "base/macros.h"
-#include "media/filters/vp8_parser.h"
 #include "media/gpu/codec_picture.h"
+#include "media/parsers/vp8_parser.h"
 
 namespace media {
 
diff --git a/media/gpu/vp8_reference_frame_vector.h b/media/gpu/vp8_reference_frame_vector.h
index cdb7c564..6f0604fe 100644
--- a/media/gpu/vp8_reference_frame_vector.h
+++ b/media/gpu/vp8_reference_frame_vector.h
@@ -9,7 +9,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
-#include "media/filters/vp8_parser.h"
+#include "media/parsers/vp8_parser.h"
 
 namespace media {
 
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index 5e16596..4490f8e 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -90,12 +90,6 @@
 
 const wchar_t kMSVP9DecoderDLLName[] = L"MSVP9DEC.dll";
 
-const CLSID MEDIASUBTYPE_VP80 = {
-    0x30385056,
-    0x0000,
-    0x0010,
-    {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
-
 const CLSID MEDIASUBTYPE_VP90 = {
     0x30395056,
     0x0000,
@@ -347,8 +341,8 @@
 namespace media {
 
 static const VideoCodecProfile kSupportedProfiles[] = {
-    H264PROFILE_BASELINE, H264PROFILE_MAIN,    H264PROFILE_HIGH,
-    VP8PROFILE_ANY,       VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE2};
+    H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH,
+    VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE2};
 
 CreateDXGIDeviceManager
     DXVAVideoDecodeAccelerator::create_dxgi_device_manager_ = NULL;
@@ -773,7 +767,7 @@
       break;
     }
   }
-  RETURN_ON_FAILURE(profile_supported, "Unsupported h.264, vp8, or vp9 profile",
+  RETURN_ON_FAILURE(profile_supported, "Unsupported h.264 or vp9 profile",
                     false);
 
   if (config.profile == VP9PROFILE_PROFILE2 ||
@@ -1443,8 +1437,9 @@
   // 1920 x 1088. We use 1088 to account for 16x16 macroblocks.
   ResolutionPair max_h264_resolutions(gfx::Size(1920, 1088), gfx::Size());
 
-  // VPX has no default resolutions since it may not even be supported.
-  ResolutionPair max_vpx_resolutions;
+  // VP9 has no default resolutions since it may not even be supported.
+  ResolutionPair max_vp9_profile0_resolutions;
+  ResolutionPair max_vp9_profile2_resolutions;
 
   if (base::win::GetVersion() > base::win::Version::WIN7) {
     // To detect if a driver supports the desired resolutions, we try and create
@@ -1464,31 +1459,44 @@
             {gfx::Size(2560, 1440), gfx::Size(3840, 2160),
              gfx::Size(4096, 2160), gfx::Size(4096, 2304)});
 
-        // Despite the name this is the GUID for VP8/VP9.
         if (preferences.enable_accelerated_vpx_decode &&
             !workarounds.disable_accelerated_vpx_decode) {
-          max_vpx_resolutions = GetMaxResolutionsForGUIDs(
-              max_vpx_resolutions.first, video_device.Get(),
+          max_vp9_profile0_resolutions = GetMaxResolutionsForGUIDs(
+              max_vp9_profile0_resolutions.first, video_device.Get(),
               {D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0},
               {gfx::Size(4096, 2160), gfx::Size(4096, 2304),
                gfx::Size(7680, 4320), gfx::Size(8192, 4320),
                gfx::Size(8192, 8192)});
+          max_vp9_profile2_resolutions = GetMaxResolutionsForGUIDs(
+              max_vp9_profile2_resolutions.first, video_device.Get(),
+              {D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2},
+              {gfx::Size(4096, 2160), gfx::Size(4096, 2304),
+               gfx::Size(7680, 4320), gfx::Size(8192, 4320),
+               gfx::Size(8192, 8192)});
         }
       }
     }
   }
 
   for (const auto& supported_profile : kSupportedProfiles) {
-    const bool kIsVPX = supported_profile >= VP8PROFILE_MIN &&
-                        supported_profile <= VP9PROFILE_MAX;
-
-    // Skip adding VPX profiles if it's not supported or disabled.
-    if (kIsVPX && max_vpx_resolutions.first.IsEmpty())
-      continue;
-
-    const bool kIsH264 = supported_profile >= H264PROFILE_MIN &&
+    const bool is_h264 = supported_profile >= H264PROFILE_MIN &&
                          supported_profile <= H264PROFILE_MAX;
-    DCHECK(kIsH264 || kIsVPX);
+    const bool is_vp9 = supported_profile >= VP9PROFILE_MIN &&
+                        supported_profile <= VP9PROFILE_MAX;
+    DCHECK(is_h264 || is_vp9);
+
+    ResolutionPair max_resolutions;
+    if (is_h264) {
+      max_resolutions = max_h264_resolutions;
+    } else if (supported_profile == VP9PROFILE_PROFILE0) {
+      max_resolutions = max_vp9_profile0_resolutions;
+    } else if (supported_profile == VP9PROFILE_PROFILE2) {
+      max_resolutions = max_vp9_profile2_resolutions;
+    }
+
+    // Skip adding VP9 profiles if it's not supported or disabled.
+    if (is_vp9 && max_resolutions.first.IsEmpty())
+      continue;
 
     // Windows Media Foundation H.264 decoding does not support decoding videos
     // with any dimension smaller than 48 pixels:
@@ -1496,25 +1504,23 @@
     //
     // TODO(dalecurtis): These values are too low. We should only be using
     // hardware decode for videos above ~360p, see http://crbug.com/684792.
-    const gfx::Size kMinResolution =
-        kIsH264 ? gfx::Size(48, 48) : gfx::Size(16, 16);
+    const gfx::Size min_resolution =
+        is_h264 ? gfx::Size(48, 48) : gfx::Size(16, 16);
 
     {
       SupportedProfile profile;
       profile.profile = supported_profile;
-      profile.min_resolution = kMinResolution;
-      profile.max_resolution =
-          kIsH264 ? max_h264_resolutions.first : max_vpx_resolutions.first;
+      profile.min_resolution = min_resolution;
+      profile.max_resolution = max_resolutions.first;
       profiles.push_back(profile);
     }
 
-    const gfx::Size kPortraitMax =
-        kIsH264 ? max_h264_resolutions.second : max_vpx_resolutions.second;
-    if (!kPortraitMax.IsEmpty()) {
+    const gfx::Size portrait_max_resolution = max_resolutions.second;
+    if (!portrait_max_resolution.IsEmpty()) {
       SupportedProfile profile;
       profile.profile = supported_profile;
-      profile.min_resolution = kMinResolution;
-      profile.max_resolution = kPortraitMax;
+      profile.min_resolution = min_resolution;
+      profile.max_resolution = portrait_max_resolution;
       profiles.push_back(profile);
     }
   }
@@ -1566,13 +1572,10 @@
     codec_ = kCodecH264;
     clsid = __uuidof(CMSH264DecoderMFT);
   } else if (enable_accelerated_vpx_decode_ &&
-             (profile == VP8PROFILE_ANY || profile == VP9PROFILE_PROFILE0 ||
-              profile == VP9PROFILE_PROFILE1 ||
-              profile == VP9PROFILE_PROFILE2 ||
-              profile == VP9PROFILE_PROFILE3)) {
-    if (profile != VP8PROFILE_ANY &&
-        (enable_accelerated_vpx_decode_ &
-         gpu::GpuPreferences::VPX_VENDOR_MICROSOFT)) {
+             (profile >= VP9PROFILE_PROFILE0 &&
+              profile <= VP9PROFILE_PROFILE3)) {
+    if (enable_accelerated_vpx_decode_ &
+        gpu::GpuPreferences::VPX_VENDOR_MICROSOFT) {
       codec_ = kCodecVP9;
       clsid = CLSID_MSVPxDecoder;
       decoder_dll = ::LoadLibrary(kMSVP9DecoderDLLName);
@@ -1580,18 +1583,18 @@
         using_ms_vp9_mft_ = true;
     }
 
-    int program_files_key = base::DIR_PROGRAM_FILES;
-    if (base::win::OSInfo::GetInstance()->wow64_status() ==
-        base::win::OSInfo::WOW64_ENABLED) {
-      program_files_key = base::DIR_PROGRAM_FILES6432;
-    }
-
 // Avoid loading AMD VP9 decoder on Windows ARM64.
 #if defined(ARCH_CPU_X86_FAMILY)
     // AMD
     if (!decoder_dll &&
         enable_accelerated_vpx_decode_ & gpu::GpuPreferences::VPX_VENDOR_AMD &&
         profile == VP9PROFILE_PROFILE0) {
+      int program_files_key = base::DIR_PROGRAM_FILES;
+      if (base::win::OSInfo::GetInstance()->wow64_status() ==
+          base::win::OSInfo::WOW64_ENABLED) {
+        program_files_key = base::DIR_PROGRAM_FILES6432;
+      }
+
       base::FilePath dll_path;
       if (base::PathService::Get(program_files_key, &dll_path)) {
         codec_ = media::kCodecVP9;
@@ -1784,8 +1787,6 @@
 
   if (codec_ == kCodecH264) {
     hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
-  } else if (codec_ == kCodecVP8) {
-    hr = media_type->SetGUID(MF_MT_SUBTYPE, MEDIASUBTYPE_VP80);
   } else if (codec_ == kCodecVP9) {
     hr = media_type->SetGUID(MF_MT_SUBTYPE, MEDIASUBTYPE_VP90);
   } else {
diff --git a/media/parsers/BUILD.gn b/media/parsers/BUILD.gn
index 4b2fab19..f4999d65 100644
--- a/media/parsers/BUILD.gn
+++ b/media/parsers/BUILD.gn
@@ -9,6 +9,12 @@
     "jpeg_parser.cc",
     "jpeg_parser.h",
     "media_parsers_export.h",
+    "vp8_bool_decoder.cc",
+    "vp8_bool_decoder.h",
+    "vp8_parser.cc",
+    "vp8_parser.h",
+    "webp_parser.cc",
+    "webp_parser.h",
   ]
   defines = [ "IS_MEDIA_PARSER_IMPL" ]
   deps = [
@@ -26,6 +32,9 @@
   testonly = true
   sources = [
     "jpeg_parser_unittest.cc",
+    "vp8_bool_decoder_unittest.cc",
+    "vp8_parser_unittest.cc",
+    "webp_parser_unittest.cc",
   ]
   deps = [
     ":parsers",
@@ -46,3 +55,16 @@
   seed_corpus = "//media/test/data"
   dict = "//media/test/jpeg.dict"
 }
+
+fuzzer_test("media_vp8_parser_fuzzer") {
+  sources = [
+    "vp8_parser_fuzzertest.cc",
+  ]
+  deps = [
+    ":parsers",
+    "//base",
+    "//media:test_support",
+  ]
+  libfuzzer_options = [ "max_len = 400000" ]
+  dict = "//media/test/vp8.dict"
+}
diff --git a/media/parsers/jpeg_parser.h b/media/parsers/jpeg_parser.h
index faab7e92..bd054f5 100644
--- a/media/parsers/jpeg_parser.h
+++ b/media/parsers/jpeg_parser.h
@@ -1,7 +1,7 @@
 // Copyright 2015 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 MEDIA_PARSERS_JPEG_PARSER_H_
 #define MEDIA_PARSERS_JPEG_PARSER_H_
 
diff --git a/media/filters/vp8_bool_decoder.cc b/media/parsers/vp8_bool_decoder.cc
similarity index 99%
rename from media/filters/vp8_bool_decoder.cc
rename to media/parsers/vp8_bool_decoder.cc
index 7ec4859..4f156ad 100644
--- a/media/filters/vp8_bool_decoder.cc
+++ b/media/parsers/vp8_bool_decoder.cc
@@ -40,12 +40,13 @@
 // project. (http://www.webmproject.org/code)
 // It is used to decode bits from a vp8 stream.
 
+#include "media/parsers/vp8_bool_decoder.h"
+
 #include <limits.h>
 
 #include <algorithm>
 
 #include "base/numerics/safe_conversions.h"
-#include "media/filters/vp8_bool_decoder.h"
 
 namespace media {
 
diff --git a/media/filters/vp8_bool_decoder.h b/media/parsers/vp8_bool_decoder.h
similarity index 95%
rename from media/filters/vp8_bool_decoder.h
rename to media/parsers/vp8_bool_decoder.h
index 16a98bd..0f407cfb 100644
--- a/media/filters/vp8_bool_decoder.h
+++ b/media/parsers/vp8_bool_decoder.h
@@ -40,8 +40,8 @@
 // project. (http://www.webmproject.org/code)
 // It is used to decode bits from a vp8 stream.
 
-#ifndef MEDIA_FILTERS_VP8_BOOL_DECODER_H_
-#define MEDIA_FILTERS_VP8_BOOL_DECODER_H_
+#ifndef MEDIA_PARSERS_VP8_BOOL_DECODER_H_
+#define MEDIA_PARSERS_VP8_BOOL_DECODER_H_
 
 #include <stddef.h>
 #include <stdint.h>
@@ -49,13 +49,13 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "media/base/media_export.h"
+#include "media/parsers/media_parsers_export.h"
 
 namespace media {
 
 // A class to decode the VP8's boolean entropy coded stream. It's a variant of
 // arithmetic coding. See RFC 6386 - Chapter 7. Boolean Entropy Decoder.
-class MEDIA_EXPORT Vp8BoolDecoder {
+class MEDIA_PARSERS_EXPORT Vp8BoolDecoder {
  public:
   Vp8BoolDecoder();
 
@@ -132,4 +132,4 @@
 
 }  // namespace media
 
-#endif  // MEDIA_FILTERS_VP8_BOOL_DECODER_H_
+#endif  // MEDIA_PARSERS_VP8_BOOL_DECODER_H_
diff --git a/media/filters/vp8_bool_decoder_unittest.cc b/media/parsers/vp8_bool_decoder_unittest.cc
similarity index 98%
rename from media/filters/vp8_bool_decoder_unittest.cc
rename to media/parsers/vp8_bool_decoder_unittest.cc
index f1f020b0..87968f4f 100644
--- a/media/filters/vp8_bool_decoder_unittest.cc
+++ b/media/parsers/vp8_bool_decoder_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/filters/vp8_bool_decoder.h"
+#include "media/parsers/vp8_bool_decoder.h"
 
 #include <stddef.h>
 #include <stdint.h>
diff --git a/media/filters/vp8_parser.cc b/media/parsers/vp8_parser.cc
similarity index 99%
rename from media/filters/vp8_parser.cc
rename to media/parsers/vp8_parser.cc
index cfdcce2..04345b5 100644
--- a/media/filters/vp8_parser.cc
+++ b/media/parsers/vp8_parser.cc
@@ -5,8 +5,9 @@
 // This file contains an implementation of a VP8 raw stream parser,
 // as defined in RFC 6386.
 
+#include "media/parsers/vp8_parser.h"
+
 #include "base/logging.h"
-#include "media/filters/vp8_parser.h"
 
 namespace media {
 
diff --git a/media/filters/vp8_parser.h b/media/parsers/vp8_parser.h
similarity index 92%
rename from media/filters/vp8_parser.h
rename to media/parsers/vp8_parser.h
index c1a95cc..11585bc 100644
--- a/media/filters/vp8_parser.h
+++ b/media/parsers/vp8_parser.h
@@ -5,15 +5,15 @@
 // This file contains an implementation of a VP8 raw stream parser,
 // as defined in RFC 6386.
 
-#ifndef MEDIA_FILTERS_VP8_PARSER_H_
-#define MEDIA_FILTERS_VP8_PARSER_H_
+#ifndef MEDIA_PARSERS_VP8_PARSER_H_
+#define MEDIA_PARSERS_VP8_PARSER_H_
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include "base/macros.h"
-#include "media/base/media_export.h"
-#include "media/filters/vp8_bool_decoder.h"
+#include "media/parsers/media_parsers_export.h"
+#include "media/parsers/vp8_bool_decoder.h"
 
 namespace media {
 
@@ -23,7 +23,7 @@
 
 // Member of Vp8FrameHeader and will be 0-initialized
 // in Vp8FrameHeader's constructor.
-struct MEDIA_EXPORT Vp8SegmentationHeader {
+struct Vp8SegmentationHeader {
   enum SegmentFeatureMode { FEATURE_MODE_DELTA = 0, FEATURE_MODE_ABSOLUTE = 1 };
 
   bool segmentation_enabled;
@@ -41,7 +41,7 @@
 
 // Member of Vp8FrameHeader and will be 0-initialized
 // in Vp8FrameHeader's constructor.
-struct MEDIA_EXPORT Vp8LoopFilterHeader {
+struct Vp8LoopFilterHeader {
   enum Type { LOOP_FILTER_TYPE_NORMAL = 0, LOOP_FILTER_TYPE_SIMPLE = 1 };
   Type type;
   uint8_t level;
@@ -55,7 +55,7 @@
 
 // Member of Vp8FrameHeader and will be 0-initialized
 // in Vp8FrameHeader's constructor.
-struct MEDIA_EXPORT Vp8QuantizationHeader {
+struct Vp8QuantizationHeader {
   uint8_t y_ac_qi;
   int8_t y_dc_delta;
   int8_t y2_dc_delta;
@@ -96,7 +96,7 @@
   VP8_FRAME_ALTREF = 2,
 };
 
-struct MEDIA_EXPORT Vp8FrameHeader {
+struct MEDIA_PARSERS_EXPORT Vp8FrameHeader {
   Vp8FrameHeader();
 
   enum FrameType { KEYFRAME = 0, INTERFRAME = 1 };
@@ -164,7 +164,7 @@
 };
 
 // A parser for raw VP8 streams as specified in RFC 6386.
-class MEDIA_EXPORT Vp8Parser {
+class MEDIA_PARSERS_EXPORT Vp8Parser {
  public:
   Vp8Parser();
   ~Vp8Parser();
@@ -205,4 +205,4 @@
 
 }  // namespace media
 
-#endif  // MEDIA_FILTERS_VP8_PARSER_H_
+#endif  // MEDIA_PARSERS_VP8_PARSER_H_
diff --git a/media/filters/vp8_parser_fuzzertest.cc b/media/parsers/vp8_parser_fuzzertest.cc
similarity index 95%
rename from media/filters/vp8_parser_fuzzertest.cc
rename to media/parsers/vp8_parser_fuzzertest.cc
index 1d3f972..75cc6bb 100644
--- a/media/filters/vp8_parser_fuzzertest.cc
+++ b/media/parsers/vp8_parser_fuzzertest.cc
@@ -7,7 +7,7 @@
 
 #include "base/numerics/safe_conversions.h"
 #include "media/filters/ivf_parser.h"
-#include "media/filters/vp8_parser.h"
+#include "media/parsers/vp8_parser.h"
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
diff --git a/media/filters/vp8_parser_unittest.cc b/media/parsers/vp8_parser_unittest.cc
similarity index 97%
rename from media/filters/vp8_parser_unittest.cc
rename to media/parsers/vp8_parser_unittest.cc
index 966a0b8..cbe4ff0 100644
--- a/media/filters/vp8_parser_unittest.cc
+++ b/media/parsers/vp8_parser_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/logging.h"
 #include "media/base/test_data_util.h"
 #include "media/filters/ivf_parser.h"
-#include "media/filters/vp8_parser.h"
+#include "media/parsers/vp8_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
diff --git a/media/parsers/webp_parser.cc b/media/parsers/webp_parser.cc
new file mode 100644
index 0000000..b0348037
--- /dev/null
+++ b/media/parsers/webp_parser.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 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 "media/parsers/webp_parser.h"
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "base/bits.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "build/build_config.h"
+#include "media/parsers/vp8_parser.h"
+
+#if !defined(ARCH_CPU_LITTLE_ENDIAN)
+#error Big-Endian architecture not supported.
+#endif
+
+namespace media {
+
+namespace {
+
+// The byte position storing the size of the file.
+constexpr size_t kFileSizeBytePosition = 4u;
+
+// The byte position in which the WebP image data begins.
+constexpr size_t kWebPFileBeginBytePosition = 8u;
+
+// The byte position storing the size of the VP8 frame.
+constexpr size_t kVp8FrameSizePosition = 16u;
+
+// The 12 bytes that include the FourCC "WEBPVP8 " plus the VP8 chunk size info.
+constexpr size_t kWebPFileHeaderByteSize = 12u;
+
+// A valid WebP image header and VP8 chunk header require 20 bytes.
+// The VP8 Key Frame's payload also begins at byte 20.
+constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
+
+// The max WebP file size is (2^32 - 10) per the WebP spec:
+// https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
+constexpr uint32_t kMaxWebPFileSize = (1ull << 32) - 10u;
+
+constexpr size_t kSizeOfUint32t = sizeof(uint32_t);
+
+}  // namespace
+
+bool IsLossyWebPImage(base::span<const uint8_t> encoded_data) {
+  if (encoded_data.size() < kWebPFileAndVp8ChunkHeaderSizeInBytes)
+    return false;
+
+  DCHECK(encoded_data.data());
+
+  return !memcmp(encoded_data.data(), "RIFF", 4) &&
+         !memcmp(encoded_data.data() + kWebPFileBeginBytePosition, "WEBPVP8 ",
+                 8);
+}
+
+std::unique_ptr<Vp8FrameHeader> ParseWebPImage(
+    base::span<const uint8_t> encoded_data) {
+  if (!IsLossyWebPImage(encoded_data))
+    return nullptr;
+
+  static_assert(CHAR_BIT == 8, "Size of a char is not 8 bits.");
+  static_assert(kSizeOfUint32t == 4u, "Size of uint32_t is not 4 bytes.");
+
+  // Try to acquire the WebP file size. IsLossyWebPImage() has ensured
+  // that we have enough data to read the file size.
+  DCHECK_GE(encoded_data.size(), kFileSizeBytePosition + kSizeOfUint32t);
+
+  // No need to worry about endianness because we assert little-endianness.
+  const uint32_t file_size = *reinterpret_cast<const uint32_t*>(
+      encoded_data.data() + kFileSizeBytePosition);
+
+  // Check that |file_size| is even, per the WebP spec:
+  // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
+  if (file_size % 2 != 0)
+    return nullptr;
+
+  // Check that |file_size| <= 2^32 - 10, per the WebP spec:
+  // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
+  if (file_size > kMaxWebPFileSize)
+    return nullptr;
+
+  // Check that the file size in the header matches the encoded data's size.
+  if (base::strict_cast<size_t>(file_size) !=
+      encoded_data.size() - kWebPFileBeginBytePosition) {
+    return nullptr;
+  }
+
+  // Try to acquire the VP8 key frame size and validate that it fits within the
+  // encoded data's size.
+  DCHECK_GE(encoded_data.size(), kVp8FrameSizePosition + kSizeOfUint32t);
+
+  const uint32_t vp8_frame_size = *reinterpret_cast<const uint32_t*>(
+      encoded_data.data() + kVp8FrameSizePosition);
+
+  // Check that the VP8 frame size is bounded by the WebP size.
+  if (base::strict_cast<size_t>(file_size) - kWebPFileHeaderByteSize <
+      base::strict_cast<size_t>(vp8_frame_size)) {
+    return nullptr;
+  }
+
+  // Check that the size of the encoded data is consistent.
+  const size_t vp8_padded_frame_size =
+      base::bits::Align(base::strict_cast<size_t>(vp8_frame_size), 2u);
+  if (encoded_data.size() - kWebPFileAndVp8ChunkHeaderSizeInBytes !=
+      vp8_padded_frame_size) {
+    return nullptr;
+  }
+
+  // Check that the last byte is 0 if |vp8_frame_size| is odd per WebP specs:
+  // https://developers.google.com/speed/webp/docs/riff_container#riff_file_format
+  if (vp8_frame_size % 2 &&
+      encoded_data.data()[encoded_data.size() - 1] != 0u) {
+    return nullptr;
+  }
+
+  // Attempt to parse the VP8 frame.
+  Vp8Parser vp8_parser;
+  auto result = std::make_unique<Vp8FrameHeader>();
+  if (vp8_parser.ParseFrame(
+          encoded_data.data() + kWebPFileAndVp8ChunkHeaderSizeInBytes,
+          base::strict_cast<size_t>(vp8_frame_size), result.get())) {
+    return result;
+  }
+  return nullptr;
+}
+
+}  // namespace media
diff --git a/media/parsers/webp_parser.h b/media/parsers/webp_parser.h
new file mode 100644
index 0000000..436d6c7
--- /dev/null
+++ b/media/parsers/webp_parser.h
@@ -0,0 +1,38 @@
+// Copyright 2019 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 MEDIA_PARSERS_WEBP_PARSER_H_
+#define MEDIA_PARSERS_WEBP_PARSER_H_
+
+#include <stdint.h>
+#include <memory>
+
+#include "base/containers/span.h"
+#include "media/parsers/media_parsers_export.h"
+
+namespace media {
+
+struct Vp8FrameHeader;
+
+// A lightweight WebP file header parser to extract feature and size
+// information. It validates that a given data stream encodes a simple lossy
+// WebP image and populates a Vp8FrameHeader upon successful parsing.
+// For more information, see the WebP Container Specification:
+// https://developers.google.com/speed/webp/docs/riff_container
+
+// Returns true if |encoded_data| claims to encode a simple (non-extended) lossy
+// WebP image. Returns false otherwise.
+MEDIA_PARSERS_EXPORT
+bool IsLossyWebPImage(base::span<const uint8_t> encoded_data);
+
+// Parses a simple (non-extended) lossy WebP image and returns a Vp8FrameHeader
+// containing the parsed VP8 frame contained by the image. Returns nullptr on
+// failure.
+MEDIA_PARSERS_EXPORT
+std::unique_ptr<Vp8FrameHeader> ParseWebPImage(
+    base::span<const uint8_t> encoded_data);
+
+}  // namespace media
+
+#endif  // MEDIA_PARSERS_WEBP_PARSER_H_
diff --git a/media/parsers/webp_parser_unittest.cc b/media/parsers/webp_parser_unittest.cc
new file mode 100644
index 0000000..4c9d997
--- /dev/null
+++ b/media/parsers/webp_parser_unittest.cc
@@ -0,0 +1,327 @@
+// Copyright 2019 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/base_paths.h"
+#include "base/containers/span.h"
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/path_service.h"
+#include "media/parsers/vp8_parser.h"
+#include "media/parsers/webp_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
+// clang-format off
+constexpr uint8_t kLossyWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0x0c, 0x00, 0x00, 0x00,  // == 12 (little endian)
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x00, 0x00, 0x00, 0x00  // == 0
+};
+constexpr base::span<const uint8_t> kLossyWebPEncodedData(
+    kLossyWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+constexpr base::span<const uint8_t> kInvalidWebPEncodedDataSize(
+    kLossyWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes - 5u);
+
+constexpr uint8_t kLosslessWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0x0c, 0x00, 0x00, 0x00,
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', 'L',
+    0x00, 0x00, 0x00, 0x00
+};
+constexpr base::span<const uint8_t> kLosslessWebPEncodedData(
+    kLosslessWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kExtendedWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0x0c, 0x00, 0x00, 0x00,
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', 'X',
+    0x00, 0x00, 0x00, 0x00
+};
+constexpr base::span<const uint8_t> kExtendedWebPEncodedData(
+    kExtendedWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kUnknownWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0x0c, 0x00, 0x00, 0x00,
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', '~',
+    0x00, 0x00, 0x00, 0x00
+};
+constexpr base::span<const uint8_t> kUnknownWebPEncodedData(
+    kUnknownWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kInvalidRiffWebPFileHeader[] = {
+    'X', 'I', 'F', 'F',
+    0x0c, 0x00, 0x00, 0x00,
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x00, 0x00, 0x00, 0x00
+};
+constexpr base::span<const uint8_t> kInvalidRiffWebPEncodedData(
+    kInvalidRiffWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kInvalidOddFileSizeInWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0x0d, 0x00, 0x00, 0x00,  // == 13 (Invalid: should be even)
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x00, 0x00, 0x00, 0x00,
+    0x00
+};
+constexpr base::span<const uint8_t> kInvalidOddFileSizeInHeaderWebPEncodedData(
+    kInvalidOddFileSizeInWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes + 1u);  // Match the reported size
+
+constexpr uint8_t kInvalidLargerThanLimitFileSizeInWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0xfe, 0xff, 0xff, 0xff,  // == 2^32 - 2 (Invalid: should be <= 2^32 - 10)
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x00, 0x00, 0x00, 0x00
+};
+constexpr base::span<const uint8_t>
+kInvalidLargerThanLimitFileSizeInHeaderWebPEncodedData(
+    kInvalidLargerThanLimitFileSizeInWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kInvalidLargerFileSizeInWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0x10, 0x00, 0x00, 0x00,  // == 16 (Invalid: should be 12)
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x00, 0x00, 0x00, 0x00
+};
+constexpr base::span<const uint8_t>
+kInvalidLargerFileSizeInHeaderWebPEncodedData(
+    kInvalidLargerFileSizeInWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kInvalidKeyFrameSizeInWebPFileHeader[] = {
+    'R', 'I', 'F', 'F',
+    0x0c, 0x00, 0x00, 0x00,  // == 12
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0xc8, 0x00, 0x00, 0x00  // == 200 (Invalid: should be 0)
+};
+constexpr base::span<const uint8_t> kInvalidKeyFrameSizeInWebPEncodedData(
+    kInvalidKeyFrameSizeInWebPFileHeader,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kMismatchingOddVp8FrameSizeAndDataSize[] = {
+    'R', 'I', 'F', 'F',
+    0x12, 0x00, 0x00, 0x00,  // == 18
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x03, 0x00, 0x00, 0x00,  // == 3
+    0x11, 0xa0, 0x23, 0x00,  // Valid padding byte
+    0xfa,  0xcc  // Should not exist.
+};
+constexpr base::span<const uint8_t>
+kMismatchingOddVp8FrameSizeAndDataSizeEncodedData(
+    kMismatchingOddVp8FrameSizeAndDataSize,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
+
+constexpr uint8_t kMismatchingEvenVp8FrameSizeAndDataSize[] = {
+    'R', 'I', 'F', 'F',
+    0x12, 0x00, 0x00, 0x00,  // == 18
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x04, 0x00, 0x00, 0x00,  // == 4
+    0x11, 0xa0, 0x23, 0x12,
+    0xfc, 0xcd  // Should not exist.
+};
+constexpr base::span<const uint8_t>
+kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData(
+    kMismatchingEvenVp8FrameSizeAndDataSize,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
+
+constexpr uint8_t kInvalidPaddingByteInVp8DataChunk[] = {
+    'R', 'I', 'F', 'F',
+    0x10, 0x00, 0x00, 0x00,  // == 16
+    'W', 'E', 'B', 'P',
+    'V', 'P', '8', ' ',
+    0x03, 0x00, 0x00, 0x00,  // == 3
+    0x11, 0xa0, 0x23, 0xff   // Invalid: last byte should be 0
+};
+constexpr base::span<const uint8_t>
+kInvalidPaddingByteInVp8DataChunkEncodedData(
+    kInvalidPaddingByteInVp8DataChunk,
+    kWebPFileAndVp8ChunkHeaderSizeInBytes + 4u);
+// clang-format on
+
+}  // namespace
+
+TEST(WebPParserTest, WebPImageFileValidator) {
+  // Verify that only lossy WebP formats pass.
+  ASSERT_TRUE(IsLossyWebPImage(kLossyWebPEncodedData));
+
+  // Verify that lossless, extended, and unknown WebP formats fail.
+  ASSERT_FALSE(IsLossyWebPImage(kLosslessWebPEncodedData));
+  ASSERT_FALSE(IsLossyWebPImage(kExtendedWebPEncodedData));
+  ASSERT_FALSE(IsLossyWebPImage(kUnknownWebPEncodedData));
+
+  // Verify that invalid WebP file headers and sizes fail.
+  ASSERT_FALSE(IsLossyWebPImage(kInvalidRiffWebPEncodedData));
+  ASSERT_FALSE(IsLossyWebPImage(kInvalidWebPEncodedDataSize));
+}
+
+TEST(WebPParserTest, ParseLossyWebP) {
+  base::FilePath data_dir;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+
+  base::FilePath file_path = data_dir.AppendASCII("media")
+                                 .AppendASCII("test")
+                                 .AppendASCII("data")
+                                 .AppendASCII("red_green_gradient_lossy.webp");
+
+  base::MemoryMappedFile stream;
+  ASSERT_TRUE(stream.Initialize(file_path))
+      << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(base::span<const uint8_t>(stream.data(), stream.length()));
+  ASSERT_TRUE(result);
+
+  ASSERT_TRUE(result->IsKeyframe());
+  ASSERT_TRUE(result->data);
+
+  // Original image is 3000x3000.
+  ASSERT_EQ(3000u, result->width);
+  ASSERT_EQ(3000u, result->height);
+}
+
+TEST(WebPParserTest, ParseLosslessWebP) {
+  base::FilePath data_dir;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+
+  base::FilePath file_path =
+      data_dir.AppendASCII("media")
+          .AppendASCII("test")
+          .AppendASCII("data")
+          .AppendASCII("yellow_pink_gradient_lossless.webp");
+
+  base::MemoryMappedFile stream;
+  ASSERT_TRUE(stream.Initialize(file_path))
+      << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+
+  // Should fail because WebP parser does not parse lossless webp images.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(base::span<const uint8_t>(stream.data(), stream.length()));
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseExtendedWebP) {
+  base::FilePath data_dir;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+
+  base::FilePath file_path = data_dir.AppendASCII("media")
+                                 .AppendASCII("test")
+                                 .AppendASCII("data")
+                                 .AppendASCII("bouncy_ball.webp");
+
+  base::MemoryMappedFile stream;
+  ASSERT_TRUE(stream.Initialize(file_path))
+      << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+
+  // Should fail because WebP parser does not parse extended webp images.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(base::span<const uint8_t>(stream.data(), stream.length()));
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithUnknownFormat) {
+  // Should fail when the specifier byte at position 16 holds anything but ' '.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kUnknownWebPEncodedData);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidHeaders) {
+  // Should fail because the header is an invalid WebP container.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kInvalidRiffWebPEncodedData);
+  ASSERT_FALSE(result);
+
+  // Should fail because the header has an invalid size.
+  result = ParseWebPImage(kInvalidWebPEncodedDataSize);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidOddSizeInHeader) {
+  // Should fail because the size reported in the header is odd.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kInvalidOddFileSizeInHeaderWebPEncodedData);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidLargerThanLimitSizeInHeader) {
+  // Should fail because the size reported in the header is larger than
+  // 2^32 - 10 per the WebP spec.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kInvalidLargerThanLimitFileSizeInHeaderWebPEncodedData);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidFileSizeInHeader) {
+  // Should fail because the size reported in the header does not match the
+  // actual data size.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kInvalidLargerFileSizeInHeaderWebPEncodedData);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrameAndIncorrectKeyFrameSize) {
+  // Should fail because the reported VP8 key frame size is larger than the
+  // the existing data.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kInvalidKeyFrameSizeInWebPEncodedData);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithMismatchingVp8FrameAndDataSize) {
+  // Should fail because the reported VP8 key frame size (even or odd) does not
+  // match the encoded data's size.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kMismatchingOddVp8FrameSizeAndDataSizeEncodedData);
+  ASSERT_FALSE(result);
+
+  result = ParseWebPImage(kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidPaddingByteInVp8DataChunk) {
+  // Should fail because the reported VP8 key frame size is odd and the added
+  // padding byte is not 0.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kInvalidPaddingByteInVp8DataChunkEncodedData);
+  ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrame) {
+  // Should fail because the VP8 parser is passed a data chunk of size 0.
+  std::unique_ptr<Vp8FrameHeader> result =
+      ParseWebPImage(kLossyWebPEncodedData);
+  ASSERT_FALSE(result);
+}
+
+}  // namespace media
diff --git a/media/test/data/README.md b/media/test/data/README.md
index 7e6305a5..daac16e 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -817,6 +817,17 @@
 ffmpeg -i red.webm -i a500hz.webm -map 0 -map 1 red-a500hz.webm
 ```
 
+### WebP Test Files
+
+#### bouncy_ball.webp
+An animated (extended) WebP encoded image of 450x450. Created by gildekel@ using Gimp.
+
+#### red_green_gradient_lossy.webp
+A lossy WebP encoded image of 3000x3000. Created by gildekel@ using Gimp.
+
+#### yellow_pink_gradient_lossless.webp
+A lossless WebP encoded image of 3000x3000. Created by gildekel@ using Gimp.
+
 ### JPEG Test Files
 
 #### pixel-1280x720.jpg
diff --git a/media/test/data/bouncy_ball.webp b/media/test/data/bouncy_ball.webp
new file mode 100644
index 0000000..dfa9399b
--- /dev/null
+++ b/media/test/data/bouncy_ball.webp
Binary files differ
diff --git a/media/test/data/red_green_gradient_lossy.webp b/media/test/data/red_green_gradient_lossy.webp
new file mode 100644
index 0000000..45417528
--- /dev/null
+++ b/media/test/data/red_green_gradient_lossy.webp
Binary files differ
diff --git a/media/test/data/yellow_pink_gradient_lossless.webp b/media/test/data/yellow_pink_gradient_lossless.webp
new file mode 100644
index 0000000..4b6b98c
--- /dev/null
+++ b/media/test/data/yellow_pink_gradient_lossless.webp
Binary files differ
diff --git a/net/cert/ct_log_verifier.cc b/net/cert/ct_log_verifier.cc
index f82649a..e255cae 100644
--- a/net/cert/ct_log_verifier.cc
+++ b/net/cert/ct_log_verifier.cc
@@ -59,23 +59,19 @@
 // static
 scoped_refptr<const CTLogVerifier> CTLogVerifier::Create(
     const base::StringPiece& public_key,
-    std::string description,
-    std::string dns_domain) {
+    std::string description) {
   scoped_refptr<CTLogVerifier> result(
-      new CTLogVerifier(std::move(description), std::move(dns_domain)));
+      new CTLogVerifier(std::move(description)));
   if (!result->Init(public_key))
     return nullptr;
   return result;
 }
 
-CTLogVerifier::CTLogVerifier(std::string description, std::string dns_domain)
+CTLogVerifier::CTLogVerifier(std::string description)
     : description_(std::move(description)),
-      dns_domain_(std::move(dns_domain)),
       hash_algorithm_(ct::DigitallySigned::HASH_ALGO_NONE),
       signature_algorithm_(ct::DigitallySigned::SIG_ALGO_ANONYMOUS),
-      public_key_(nullptr) {
-  DCHECK(!dns_domain_.empty());
-}
+      public_key_(nullptr) {}
 
 bool CTLogVerifier::Verify(const ct::SignedEntryData& entry,
                            const ct::SignedCertificateTimestamp& sct) const {
diff --git a/net/cert/ct_log_verifier.h b/net/cert/ct_log_verifier.h
index 395c381..fcc51189 100644
--- a/net/cert/ct_log_verifier.h
+++ b/net/cert/ct_log_verifier.h
@@ -35,21 +35,15 @@
   // using |public_key|, which is a DER-encoded SubjectPublicKeyInfo.
   // If |public_key| refers to an unsupported public key, returns NULL.
   // |description| is a textual description of the log.
-  // |dns_domain| is the DNS name of the log's DNS API endpoint, if one exists.
   static scoped_refptr<const CTLogVerifier> Create(
       const base::StringPiece& public_key,
-      std::string description,
-      std::string dns_domain);
+      std::string description);
 
   // Returns the log's key ID (RFC6962, Section 3.2)
   const std::string& key_id() const { return key_id_; }
   // Returns the log's human-readable description.
   const std::string& description() const { return description_; }
 
-  // Returns the log's DNS domain for CT over DNS queries, as described in
-  // https://github.com/google/certificate-transparency-rfcs/blob/master/dns/draft-ct-over-dns.md.
-  const std::string& dns_domain() const { return dns_domain_; }
-
   // Verifies that |sct| is valid for |entry| and was signed by this log.
   bool Verify(const ct::SignedEntryData& entry,
               const ct::SignedCertificateTimestamp& sct) const;
@@ -78,7 +72,7 @@
   FRIEND_TEST_ALL_PREFIXES(CTLogVerifierTest, VerifySignature);
   friend class base::RefCountedThreadSafe<CTLogVerifier>;
 
-  CTLogVerifier(std::string description, std::string dns_domain);
+  explicit CTLogVerifier(std::string description);
   ~CTLogVerifier();
 
   // Performs crypto-library specific initialization.
@@ -96,7 +90,6 @@
 
   std::string key_id_;
   std::string description_;
-  std::string dns_domain_;
   ct::DigitallySigned::HashAlgorithm hash_algorithm_;
   ct::DigitallySigned::SignatureAlgorithm signature_algorithm_;
 
diff --git a/net/cert/ct_log_verifier_unittest.cc b/net/cert/ct_log_verifier_unittest.cc
index fb95e22c..dcdc6ced 100644
--- a/net/cert/ct_log_verifier_unittest.cc
+++ b/net/cert/ct_log_verifier_unittest.cc
@@ -204,12 +204,10 @@
 class CTLogVerifierTest : public ::testing::Test {
  public:
   void SetUp() override {
-    log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog",
-                                 "ct.example.com");
+    log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog");
 
     ASSERT_TRUE(log_);
     EXPECT_EQ(ct::GetTestPublicKeyId(), log_->key_id());
-    EXPECT_EQ("ct.example.com", log_->dns_domain());
   }
 
  protected:
@@ -467,7 +465,7 @@
   key += "extra";
 
   scoped_refptr<const CTLogVerifier> log =
-      CTLogVerifier::Create(key, "testlog", "ct.example.com");
+      CTLogVerifier::Create(key, "testlog");
   EXPECT_FALSE(log);
 }
 
diff --git a/net/cert/ct_objects_extractor_unittest.cc b/net/cert/ct_objects_extractor_unittest.cc
index ce1a7284..57028a3 100644
--- a/net/cert/ct_objects_extractor_unittest.cc
+++ b/net/cert/ct_objects_extractor_unittest.cc
@@ -32,8 +32,7 @@
                                                   der_test_cert.length());
     ASSERT_TRUE(test_cert_);
 
-    log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog",
-                                 "dns.example.com");
+    log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog");
     ASSERT_TRUE(log_);
   }
 
diff --git a/net/cert/ct_verifier.h b/net/cert/ct_verifier.h
index 0368044..63c0923b 100644
--- a/net/cert/ct_verifier.h
+++ b/net/cert/ct_verifier.h
@@ -17,24 +17,6 @@
 // Interface for verifying Signed Certificate Timestamps over a certificate.
 class NET_EXPORT CTVerifier {
  public:
-  class NET_EXPORT Observer {
-   public:
-    // Called for each Signed Certificate Timestamp from a known log that vas
-    // verified successfully (i.e. the signature verifies). |sct| is the
-    // Signed Certificate Timestamp, |cert| is the certificate it applies to and
-    // |hostname| is the server that presented the certificate (DNS name or IP
-    // address literal). The certificate is needed to calculate the hash of the
-    // log entry, necessary for checking inclusion in the log.
-    // Note: The observer (whose implementation is expected to exist outside
-    // net/) may store the observed |cert| and |sct|.
-    virtual void OnSCTVerified(base::StringPiece hostname,
-                               X509Certificate* cert,
-                               const ct::SignedCertificateTimestamp* sct) = 0;
-
-   protected:
-    virtual ~Observer() {}
-  };
-
   virtual ~CTVerifier() {}
 
   // Verifies SCTs embedded in the certificate itself, SCTs embedded in a
@@ -56,16 +38,6 @@
                       base::StringPiece sct_list_from_tls_extension,
                       SignedCertificateTimestampAndStatusList* output_scts,
                       const NetLogWithSource& net_log) = 0;
-
-  // Registers |observer| to receive notifications of validated SCTs. Does not
-  // take ownership of the observer as the observer may be performing
-  // URLRequests which have to be cancelled before this object is destroyed.
-  // Setting |observer| to nullptr has the effect of stopping all notifications.
-  virtual void SetObserver(Observer* observer) = 0;
-
-  // Gets the Observer, if any, that is currently receiving notifications of
-  // validated SCTs.
-  virtual Observer* GetObserver() const = 0;
 };
 
 }  // namespace net
diff --git a/net/cert/do_nothing_ct_verifier.cc b/net/cert/do_nothing_ct_verifier.cc
index 63b9c0a..596c8e7 100644
--- a/net/cert/do_nothing_ct_verifier.cc
+++ b/net/cert/do_nothing_ct_verifier.cc
@@ -21,10 +21,4 @@
   output_scts->clear();
 }
 
-void DoNothingCTVerifier::SetObserver(Observer* observer) {}
-
-CTVerifier::Observer* DoNothingCTVerifier::GetObserver() const {
-  return nullptr;
-}
-
 }  // namespace net
diff --git a/net/cert/do_nothing_ct_verifier.h b/net/cert/do_nothing_ct_verifier.h
index 1aad260..64eacf32 100644
--- a/net/cert/do_nothing_ct_verifier.h
+++ b/net/cert/do_nothing_ct_verifier.h
@@ -57,9 +57,6 @@
               SignedCertificateTimestampAndStatusList* output_scts,
               const NetLogWithSource& net_log) override;
 
-  void SetObserver(Observer* observer) override;
-  Observer* GetObserver() const override;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(DoNothingCTVerifier);
 };
diff --git a/net/cert/multi_log_ct_verifier.cc b/net/cert/multi_log_ct_verifier.cc
index eb9b1483..cd473fb 100644
--- a/net/cert/multi_log_ct_verifier.cc
+++ b/net/cert/multi_log_ct_verifier.cc
@@ -54,8 +54,7 @@
 
 }  // namespace
 
-MultiLogCTVerifier::MultiLogCTVerifier() : observer_(nullptr) {
-}
+MultiLogCTVerifier::MultiLogCTVerifier() {}
 
 MultiLogCTVerifier::~MultiLogCTVerifier() = default;
 
@@ -67,14 +66,6 @@
   }
 }
 
-void MultiLogCTVerifier::SetObserver(Observer* observer) {
-  observer_ = observer;
-}
-
-CTVerifier::Observer* MultiLogCTVerifier::GetObserver() const {
-  return observer_;
-}
-
 void MultiLogCTVerifier::Verify(
     base::StringPiece hostname,
     X509Certificate* cert,
@@ -213,8 +204,6 @@
   }
 
   AddSCTAndLogStatus(sct, ct::SCT_STATUS_OK, output_scts);
-  if (observer_)
-    observer_->OnSCTVerified(hostname, cert, sct.get());
   return true;
 }
 
diff --git a/net/cert/multi_log_ct_verifier.h b/net/cert/multi_log_ct_verifier.h
index af389d6..0d603eb 100644
--- a/net/cert/multi_log_ct_verifier.h
+++ b/net/cert/multi_log_ct_verifier.h
@@ -42,9 +42,6 @@
               SignedCertificateTimestampAndStatusList* output_scts,
               const NetLogWithSource& net_log) override;
 
-  void SetObserver(Observer* observer) override;
-  Observer* GetObserver() const override;
-
  private:
   // Verify a list of SCTs from |encoded_sct_list| over |expected_entry|,
   // placing the verification results in |output_scts|. The SCTs in the list
@@ -68,8 +65,6 @@
   // of RFC6962.
   std::map<std::string, scoped_refptr<const CTLogVerifier>> logs_;
 
-  Observer* observer_;
-
   DISALLOW_COPY_AND_ASSIGN(MultiLogCTVerifier);
 };
 
diff --git a/net/cert/multi_log_ct_verifier_unittest.cc b/net/cert/multi_log_ct_verifier_unittest.cc
index e3e43f1..d935767 100644
--- a/net/cert/multi_log_ct_verifier_unittest.cc
+++ b/net/cert/multi_log_ct_verifier_unittest.cc
@@ -41,19 +41,11 @@
 const char kHostname[] = "example.com";
 const char kLogDescription[] = "somelog";
 
-class MockSCTObserver : public CTVerifier::Observer {
- public:
-  MOCK_METHOD3(OnSCTVerified,
-               void(base::StringPiece hostname,
-                    X509Certificate* cert,
-                    const ct::SignedCertificateTimestamp* sct));
-};
-
 class MultiLogCTVerifierTest : public ::testing::Test {
  public:
   void SetUp() override {
-    scoped_refptr<const CTLogVerifier> log(CTLogVerifier::Create(
-        ct::GetTestPublicKey(), kLogDescription, "dns.example.com"));
+    scoped_refptr<const CTLogVerifier> log(
+        CTLogVerifier::Create(ct::GetTestPublicKey(), kLogDescription));
     ASSERT_TRUE(log);
     log_verifiers_.push_back(log);
 
@@ -251,32 +243,6 @@
   EXPECT_EQ(old_embedded_count + 1, NumEmbeddedSCTsInHistogram());
 }
 
-TEST_F(MultiLogCTVerifierTest, NotifiesOfValidSCT) {
-  MockSCTObserver observer;
-  verifier_->SetObserver(&observer);
-
-  EXPECT_CALL(observer, OnSCTVerified(base::StringPiece(kHostname),
-                                      embedded_sct_chain_.get(), _));
-  ASSERT_TRUE(VerifySinglePrecertificateChain(embedded_sct_chain_));
-}
-
-TEST_F(MultiLogCTVerifierTest, StopsNotifyingCorrectly) {
-  MockSCTObserver observer;
-  verifier_->SetObserver(&observer);
-
-  EXPECT_CALL(observer, OnSCTVerified(base::StringPiece(kHostname),
-                                      embedded_sct_chain_.get(), _))
-      .Times(1);
-  ASSERT_TRUE(VerifySinglePrecertificateChain(embedded_sct_chain_));
-  Mock::VerifyAndClearExpectations(&observer);
-
-  EXPECT_CALL(observer, OnSCTVerified(base::StringPiece(kHostname),
-                                      embedded_sct_chain_.get(), _))
-      .Times(0);
-  verifier_->SetObserver(nullptr);
-  ASSERT_TRUE(VerifySinglePrecertificateChain(embedded_sct_chain_));
-}
-
 }  // namespace
 
 }  // namespace net
diff --git a/net/dns/context_host_resolver.cc b/net/dns/context_host_resolver.cc
index 9423f1d..34ce64e 100644
--- a/net/dns/context_host_resolver.cc
+++ b/net/dns/context_host_resolver.cc
@@ -135,20 +135,6 @@
   return host_cache_.get();
 }
 
-bool ContextHostResolver::HasCached(base::StringPiece hostname,
-                                    HostCache::Entry::Source* source_out,
-                                    HostCache::EntryStaleness* stale_out,
-                                    bool* secure_out) const {
-  if (!host_cache_)
-    return false;
-
-  const HostCache::Key* key =
-      host_cache_->GetMatchingKey(hostname, source_out, stale_out);
-  if (key && secure_out != nullptr)
-    *secure_out = key->secure;
-  return !!key;
-}
-
 std::unique_ptr<base::Value> ContextHostResolver::GetDnsConfigAsValue() const {
   return manager_->GetDnsConfigAsValue();
 }
diff --git a/net/dns/context_host_resolver.h b/net/dns/context_host_resolver.h
index e6b5cf5..722bdcb7 100644
--- a/net/dns/context_host_resolver.h
+++ b/net/dns/context_host_resolver.h
@@ -52,10 +52,6 @@
       const HostPortPair& host,
       DnsQueryType query_type) override;
   HostCache* GetHostCache() override;
-  bool HasCached(base::StringPiece hostname,
-                 HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out,
-                 bool* secure_out) const override;
   std::unique_ptr<base::Value> GetDnsConfigAsValue() const override;
   void SetRequestContext(URLRequestContext* request_context) override;
   HostResolverManager* GetManagerForTesting() override;
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
index 69a5d4f8..418a0e95 100644
--- a/net/dns/host_resolver.h
+++ b/net/dns/host_resolver.h
@@ -277,18 +277,6 @@
   // Used primarily to clear the cache and for getting debug information.
   virtual HostCache* GetHostCache();
 
-  // Checks whether this HostResolver has cached a resolution for the given
-  // hostname (or IP address literal). If so, returns true and writes the source
-  // of the resolution (e.g. DNS, HOSTS file, etc.) to |source_out|, the
-  // staleness of the resolution to |stale_out|, and whether the result was
-  // retrieved securely or not to |secure_out| (if they are not null). It tries
-  // using two common address_family and host_resolver_flag combinations when
-  // checking the cache; this means false negatives are possible, but unlikely.
-  virtual bool HasCached(base::StringPiece hostname,
-                         HostCache::Entry::Source* source_out,
-                         HostCache::EntryStaleness* stale_out,
-                         bool* secure_out) const = 0;
-
   // Returns the current DNS configuration |this| is using, as a Value, or
   // nullptr if it's configured to always use the system host resolver.
   virtual std::unique_ptr<base::Value> GetDnsConfigAsValue() const;
diff --git a/net/dns/mapped_host_resolver.cc b/net/dns/mapped_host_resolver.cc
index 182958a..af5525ab 100644
--- a/net/dns/mapped_host_resolver.cc
+++ b/net/dns/mapped_host_resolver.cc
@@ -75,13 +75,6 @@
   return impl_->GetHostCache();
 }
 
-bool MappedHostResolver::HasCached(base::StringPiece hostname,
-                                   HostCache::Entry::Source* source_out,
-                                   HostCache::EntryStaleness* stale_out,
-                                   bool* secure_out) const {
-  return impl_->HasCached(hostname, source_out, stale_out, secure_out);
-}
-
 std::unique_ptr<base::Value> MappedHostResolver::GetDnsConfigAsValue() const {
   return impl_->GetDnsConfigAsValue();
 }
diff --git a/net/dns/mapped_host_resolver.h b/net/dns/mapped_host_resolver.h
index ca8c9f0..ced94f2 100644
--- a/net/dns/mapped_host_resolver.h
+++ b/net/dns/mapped_host_resolver.h
@@ -54,10 +54,6 @@
       const base::Optional<ResolveHostParameters>& optional_parameters)
       override;
   HostCache* GetHostCache() override;
-  bool HasCached(base::StringPiece hostname,
-                 HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out,
-                 bool* secure_out) const override;
   std::unique_ptr<base::Value> GetDnsConfigAsValue() const override;
   void SetRequestContext(URLRequestContext* request_context) override;
   HostResolverManager* GetManagerForTesting() override;
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index b019d3f..f91f86a 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -290,20 +290,6 @@
   return cache_.get();
 }
 
-bool MockHostResolverBase::HasCached(base::StringPiece hostname,
-                                     HostCache::Entry::Source* source_out,
-                                     HostCache::EntryStaleness* stale_out,
-                                     bool* secure_out) const {
-  if (!cache_)
-    return false;
-
-  const HostCache::Key* key =
-      cache_->GetMatchingKey(hostname, source_out, stale_out);
-  if (key && secure_out != nullptr)
-    *secure_out = key->secure;
-  return !!key;
-}
-
 int MockHostResolverBase::LoadIntoCache(
     const HostPortPair& host,
     const base::Optional<ResolveHostParameters>& optional_parameters) {
@@ -956,13 +942,6 @@
                                        is_local_only);
 }
 
-bool HangingHostResolver::HasCached(base::StringPiece hostname,
-                                    HostCache::Entry::Source* source_out,
-                                    HostCache::EntryStaleness* stale_out,
-                                    bool* secure_out) const {
-  return false;
-}
-
 //-----------------------------------------------------------------------------
 
 ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc() = default;
diff --git a/net/dns/mock_host_resolver.h b/net/dns/mock_host_resolver.h
index c653bcf89..8215f76 100644
--- a/net/dns/mock_host_resolver.h
+++ b/net/dns/mock_host_resolver.h
@@ -121,10 +121,6 @@
       const HostPortPair& host,
       DnsQueryType query_type) override;
   HostCache* GetHostCache() override;
-  bool HasCached(base::StringPiece hostname,
-                 HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out,
-                 bool* secure_out) const override;
   void SetRequestContext(URLRequestContext* request_context) override {}
 
   // Preloads the cache with what would currently be the result of a request
@@ -447,10 +443,6 @@
       const NetLogWithSource& net_log,
       const base::Optional<ResolveHostParameters>& optional_parameters)
       override;
-  bool HasCached(base::StringPiece hostname,
-                 HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out,
-                 bool* secure_out) const override;
 
   // Use to detect cancellations since there's otherwise no externally-visible
   // differentiation between a cancelled and a hung task.
diff --git a/net/http/http_auth_cache.cc b/net/http/http_auth_cache.cc
index 8c399be6..6a36b9fb 100644
--- a/net/http/http_auth_cache.cc
+++ b/net/http/http_auth_cache.cc
@@ -45,9 +45,7 @@
 // Debug helper to check that |origin| arguments are properly formed.
 void CheckOriginIsValid(const GURL& origin) {
   DCHECK(origin.is_valid());
-  // Note that the scheme may be FTP when we're using a HTTP proxy.
-  DCHECK(origin.SchemeIsHTTPOrHTTPS() || origin.SchemeIs("ftp") ||
-         origin.SchemeIsWSOrWSS());
+  DCHECK(origin.SchemeIsHTTPOrHTTPS() || origin.SchemeIsWSOrWSS());
   DCHECK(origin.GetOrigin() == origin);
 }
 
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index ababbde0..e176dfd 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -1084,11 +1084,9 @@
 
   if (!using_spdy_) {
     DCHECK(!expect_spdy_);
-    // We may get ftp scheme when fetching ftp resources through proxy.
     bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https() ||
                         proxy_info_.is_quic()) &&
-                       (request_info_.url.SchemeIs(url::kHttpScheme) ||
-                        request_info_.url.SchemeIs(url::kFtpScheme));
+                       request_info_.url.SchemeIs(url::kHttpScheme);
     if (is_websocket_) {
       DCHECK_NE(job_type_, PRECONNECT);
       DCHECK(delegate_->websocket_handshake_stream_create_helper());
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 3c315672..e6331a7 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -83,9 +83,8 @@
 
 // static
 std::string HttpUtil::SpecForRequest(const GURL& url) {
-  // We may get ftp scheme when fetching ftp resources through proxy.
-  DCHECK(url.is_valid() && (url.SchemeIsHTTPOrHTTPS() || url.SchemeIs("ftp") ||
-                            url.SchemeIsWSOrWSS()));
+  DCHECK(url.is_valid() &&
+         (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS()));
   return SimplifyUrlForRequest(url).spec();
 }
 
diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc
index 74674f81..d8d316ca 100644
--- a/net/http/http_util_unittest.cc
+++ b/net/http/http_util_unittest.cc
@@ -734,13 +734,6 @@
   }
 }
 
-// Test SpecForRequest() for "ftp" scheme.
-TEST(HttpUtilTest, SpecForRequestForUrlWithFtpScheme) {
-  GURL ftp_url("ftp://user:pass@google.com/pub/chromium/");
-  EXPECT_EQ("ftp://google.com/pub/chromium/",
-            HttpUtil::SpecForRequest(ftp_url));
-}
-
 TEST(HttpUtilTest, GenerateAcceptLanguageHeader) {
   std::string header = HttpUtil::GenerateAcceptLanguageHeader("");
   EXPECT_TRUE(header.empty());
diff --git a/net/quic/crypto/proof_verifier_chromium_test.cc b/net/quic/crypto/proof_verifier_chromium_test.cc
index f1c498f..591f37eb 100644
--- a/net/quic/crypto/proof_verifier_chromium_test.cc
+++ b/net/quic/crypto/proof_verifier_chromium_test.cc
@@ -123,8 +123,8 @@
         .WillRepeatedly(
             Return(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS));
 
-    scoped_refptr<const CTLogVerifier> log(CTLogVerifier::Create(
-        ct::GetTestPublicKey(), kLogDescription, "dns.example.com"));
+    scoped_refptr<const CTLogVerifier> log(
+        CTLogVerifier::Create(ct::GetTestPublicKey(), kLogDescription));
     ASSERT_TRUE(log);
     log_verifiers_.push_back(log);
 
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index bbc13ab8..f07bdcd 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -766,8 +766,6 @@
                     base::StringPiece,
                     SignedCertificateTimestampAndStatusList*,
                     const NetLogWithSource&));
-  MOCK_METHOD1(SetObserver, void(CTVerifier::Observer*));
-  MOCK_CONST_METHOD0(GetObserver, CTVerifier::Observer*());
 };
 
 // A mock CTPolicyEnforcer that returns a custom verification result.
diff --git a/services/device/usb/usb_configuration_android.cc b/services/device/usb/usb_configuration_android.cc
index 2b99d72..9274d9c2 100644
--- a/services/device/usb/usb_configuration_android.cc
+++ b/services/device/usb/usb_configuration_android.cc
@@ -24,13 +24,10 @@
       Java_ChromeUsbConfiguration_isRemoteWakeup(env, wrapper),
       Java_ChromeUsbConfiguration_getMaxPower(env, wrapper));
 
-  ScopedJavaLocalRef<jobjectArray> interfaces =
-      Java_ChromeUsbConfiguration_getInterfaces(env, wrapper);
-  jsize count = env->GetArrayLength(interfaces.obj());
-  config.interfaces.reserve(count);
-  for (jsize i = 0; i < count; ++i) {
-    ScopedJavaLocalRef<jobject> interface(
-        env, env->GetObjectArrayElement(interfaces.obj(), i));
+  base::android::JavaObjectArrayReader<jobject> interfaces(
+      Java_ChromeUsbConfiguration_getInterfaces(env, wrapper));
+  config.interfaces.reserve(interfaces.size());
+  for (auto interface : interfaces) {
     config.interfaces.push_back(UsbInterfaceAndroid::Convert(env, interface));
   }
 
diff --git a/services/device/usb/usb_device_android.cc b/services/device/usb/usb_device_android.cc
index 25fe940..de98b43fc 100644
--- a/services/device/usb/usb_device_android.cc
+++ b/services/device/usb/usb_device_android.cc
@@ -18,6 +18,7 @@
 #include "services/device/usb/webusb_descriptors.h"
 
 using base::android::ConvertJavaStringToUTF16;
+using base::android::JavaObjectArrayReader;
 using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
 
@@ -131,13 +132,10 @@
       j_object_(wrapper) {
   if (base::android::BuildInfo::GetInstance()->sdk_int() >=
       base::android::SDK_VERSION_LOLLIPOP) {
-    ScopedJavaLocalRef<jobjectArray> configurations =
-        Java_ChromeUsbDevice_getConfigurations(env, j_object_);
-    jsize count = env->GetArrayLength(configurations.obj());
-    descriptor_.configurations.reserve(count);
-    for (jsize i = 0; i < count; ++i) {
-      ScopedJavaLocalRef<jobject> config(
-          env, env->GetObjectArrayElement(configurations.obj(), i));
+    JavaObjectArrayReader<jobject> configurations(
+        Java_ChromeUsbDevice_getConfigurations(env, j_object_));
+    descriptor_.configurations.reserve(configurations.size());
+    for (auto config : configurations) {
       descriptor_.configurations.push_back(
           UsbConfigurationAndroid::Convert(env, config));
     }
@@ -149,13 +147,10 @@
                                false,  // Remote wakeup, rbitrary default.
                                0);     // Maximum power, aitrary default.
 
-    ScopedJavaLocalRef<jobjectArray> interfaces =
-        Java_ChromeUsbDevice_getInterfaces(env, wrapper);
-    jsize count = env->GetArrayLength(interfaces.obj());
-    config.interfaces.reserve(count);
-    for (jsize i = 0; i < count; ++i) {
-      ScopedJavaLocalRef<jobject> interface(
-          env, env->GetObjectArrayElement(interfaces.obj(), i));
+    JavaObjectArrayReader<jobject> interfaces(
+        Java_ChromeUsbDevice_getInterfaces(env, wrapper));
+    config.interfaces.reserve(interfaces.size());
+    for (auto interface : interfaces) {
       config.interfaces.push_back(UsbInterfaceAndroid::Convert(env, interface));
     }
     descriptor_.configurations.push_back(config);
diff --git a/services/device/usb/usb_interface_android.cc b/services/device/usb/usb_interface_android.cc
index 6e2f347..7cd8ab00 100644
--- a/services/device/usb/usb_interface_android.cc
+++ b/services/device/usb/usb_interface_android.cc
@@ -33,13 +33,10 @@
       Java_ChromeUsbInterface_getInterfaceSubclass(env, wrapper),
       Java_ChromeUsbInterface_getInterfaceProtocol(env, wrapper));
 
-  ScopedJavaLocalRef<jobjectArray> endpoints =
-      Java_ChromeUsbInterface_getEndpoints(env, wrapper);
-  jsize count = env->GetArrayLength(endpoints.obj());
-  interface.endpoints.reserve(count);
-  for (jsize i = 0; i < count; ++i) {
-    ScopedJavaLocalRef<jobject> endpoint(
-        env, env->GetObjectArrayElement(endpoints.obj(), i));
+  base::android::JavaObjectArrayReader<jobject> endpoints(
+      Java_ChromeUsbInterface_getEndpoints(env, wrapper));
+  interface.endpoints.reserve(endpoints.size());
+  for (auto endpoint : endpoints) {
     interface.endpoints.push_back(UsbEndpointAndroid::Convert(env, endpoint));
   }
 
diff --git a/services/device/usb/usb_service_android.cc b/services/device/usb/usb_service_android.cc
index 7a57ca8d..8881282 100644
--- a/services/device/usb/usb_service_android.cc
+++ b/services/device/usb/usb_service_android.cc
@@ -26,10 +26,7 @@
       Java_ChromeUsbService_create(env, reinterpret_cast<jlong>(this)));
   ScopedJavaLocalRef<jobjectArray> devices =
       Java_ChromeUsbService_getDevices(env, j_object_);
-  jsize length = env->GetArrayLength(devices.obj());
-  for (jsize i = 0; i < length; ++i) {
-    ScopedJavaLocalRef<jobject> usb_device(
-        env, env->GetObjectArrayElement(devices.obj(), i));
+  for (auto usb_device : devices.ReadElements<jobject>()) {
     scoped_refptr<UsbDeviceAndroid> device =
         UsbDeviceAndroid::Create(env, weak_factory_.GetWeakPtr(), usb_device);
     AddDevice(device);
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index e895fb2..1157afba 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -392,10 +392,7 @@
   ]
 
   if (is_ct_supported) {
-    sources += [
-      "expect_ct_reporter_unittest.cc",
-      "network_context_cert_transparency_unittest.cc",
-    ]
+    sources += [ "expect_ct_reporter_unittest.cc" ]
     deps += [ "//components/certificate_transparency" ]
   }
 }
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 85c9073..6082e82 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -93,10 +93,6 @@
 #include "components/certificate_transparency/chrome_ct_policy_enforcer.h"
 #include "components/certificate_transparency/chrome_require_ct_delegate.h"
 #include "components/certificate_transparency/ct_known_logs.h"
-#include "components/certificate_transparency/features.h"
-#include "components/certificate_transparency/sth_distributor.h"
-#include "components/certificate_transparency/sth_reporter.h"
-#include "components/certificate_transparency/tree_state_tracker.h"
 #include "net/cert/ct_log_verifier.h"
 #include "net/cert/multi_log_ct_verifier.h"
 #include "services/network/expect_ct_reporter.h"
@@ -648,19 +644,6 @@
     }
 #endif  // BUILDFLAG(IS_CT_SUPPORTED)
   }
-
-#if BUILDFLAG(IS_CT_SUPPORTED)
-  if (url_request_context_ &&
-      url_request_context_->cert_transparency_verifier()) {
-    url_request_context_->cert_transparency_verifier()->SetObserver(nullptr);
-  }
-
-  if (network_service_ && network_service_->sth_reporter() &&
-      ct_tree_tracker_) {
-    network_service_->sth_reporter()->UnregisterObserver(
-        ct_tree_tracker_.get());
-  }
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
 }
 
 void NetworkContext::SetCertVerifierForTesting(
@@ -1930,8 +1913,7 @@
   if (!params_->ct_logs.empty()) {
     for (const auto& log : params_->ct_logs) {
       scoped_refptr<const net::CTLogVerifier> log_verifier =
-          net::CTLogVerifier::Create(log->public_key, log->name,
-                                     log->dns_api_endpoint);
+          net::CTLogVerifier::Create(log->public_key, log->name);
       if (!log_verifier) {
         // TODO: Signal bad configuration (such as bad key).
         continue;
@@ -2014,16 +1996,6 @@
         expect_ct_reporter_.get());
   }
 
-  if (base::FeatureList::IsEnabled(certificate_transparency::kCTLogAuditing) &&
-      network_service_ && !ct_logs.empty()) {
-    net::URLRequestContext* context = result.url_request_context.get();
-    ct_tree_tracker_ =
-        std::make_unique<certificate_transparency::TreeStateTracker>(
-            ct_logs, context->host_resolver(), context, net_log);
-    context->cert_transparency_verifier()->SetObserver(ct_tree_tracker_.get());
-    network_service_->sth_reporter()->RegisterObserver(ct_tree_tracker_.get());
-  }
-
   if (params_->enforce_chrome_ct_policy) {
     require_ct_delegate_ =
         std::make_unique<certificate_transparency::ChromeRequireCTDelegate>();
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 46183aee..05309f98 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -66,7 +66,6 @@
 
 namespace certificate_transparency {
 class ChromeRequireCTDelegate;
-class TreeStateTracker;
 }  // namespace certificate_transparency
 
 namespace domain_reliability {
@@ -550,7 +549,6 @@
 
   std::queue<SetExpectCTTestReportCallback>
       outstanding_set_expect_ct_callbacks_;
-  std::unique_ptr<certificate_transparency::TreeStateTracker> ct_tree_tracker_;
 #endif  // BUILDFLAG(IS_CT_SUPPORTED)
 
 #if defined(OS_CHROMEOS)
diff --git a/services/network/network_context_cert_transparency_unittest.cc b/services/network/network_context_cert_transparency_unittest.cc
deleted file mode 100644
index 868b466c..0000000
--- a/services/network/network_context_cert_transparency_unittest.cc
+++ /dev/null
@@ -1,439 +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 <memory>
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/memory/ref_counted.h"
-#include "base/stl_util.h"
-#include "base/strings/string_piece.h"
-#include "base/test/gtest_util.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/certificate_transparency/features.h"
-#include "components/certificate_transparency/single_tree_tracker.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "net/base/address_list.h"
-#include "net/base/ip_address.h"
-#include "net/base/ip_endpoint.h"
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/ct_policy_status.h"
-#include "net/cert/ct_serialization.h"
-#include "net/cert/ct_verifier.h"
-#include "net/cert/mock_cert_verifier.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/cert/x509_certificate.h"
-#include "net/dns/host_cache.h"
-#include "net/dns/host_resolver.h"
-#include "net/proxy_resolution/proxy_config_with_annotation.h"
-#include "net/test/cert_test_util.h"
-#include "net/test/ct_test_util.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/test_data_directory.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request_context.h"
-#include "services/network/network_context.h"
-#include "services/network/network_service.h"
-#include "services/network/public/cpp/features.h"
-#include "services/network/public/mojom/ct_log_info.mojom.h"
-#include "services/network/public/mojom/network_service.mojom.h"
-#include "services/network/public/mojom/proxy_config.mojom.h"
-#include "services/network/test/test_url_loader_client.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace network {
-
-namespace {
-
-mojom::NetworkContextParamsPtr CreateContextParams() {
-  mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New();
-
-  // Use a fixed proxy config, to avoid dependencies on local network
-  // configuration.
-  params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect();
-
-  // Configure Certificate Transparency for the context.
-  // TODO(robpercival): https://crbug.com/839612 - Use test logs for
-  // integration tests rather than production logs.
-  const char kPilotKey[] =
-      "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86"
-      "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7d\xa8\x4b\x12\x29\x80\xa3"
-      "\x3d\xad\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18"
-      "\x0c\xe8\x41\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a"
-      "\x08\x18\xba\xed\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd"
-      "\x1f\x04\x10\x41\x4c\xa0";
-  const char kAviatorKey[] =
-      "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86"
-      "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xf4\xcc\x69\xb2\xe4\x0e"
-      "\x90\xa3\x8a\xea\x5a\x70\x09\x4f\xef\x13\x62\xd0\x8d\x49\x60\xff\x1b"
-      "\x40\x50\x07\x0c\x6d\x71\x86\xda\x25\x49\x8d\x65\xe1\x08\x0d\x47\x34"
-      "\x6b\xbd\x27\xbc\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85"
-      "\x3b\x0d\xf7\x1f\x3f\xe9";
-  const char kDigiCertKey[] =
-      "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86"
-      "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\x46\xc5\xbe\x1b\xbb\x82"
-      "\x40\x16\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9"
-      "\x38\xb0\x23\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf"
-      "\x19\xc4\x00\xf6\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91"
-      "\xeb\x38\x24\x40\xac\xe8";
-  params->ct_logs.push_back(network::mojom::CTLogInfo::New(
-      std::string(kPilotKey, base::size(kPilotKey) - 1), "Google 'Pilot' Log",
-      "pilot.ct.invalid"));
-  params->ct_logs.push_back(network::mojom::CTLogInfo::New(
-      std::string(kAviatorKey, base::size(kAviatorKey) - 1),
-      "Google 'Aviator' Log", "aviator.ct.invalid"));
-  params->ct_logs.push_back(network::mojom::CTLogInfo::New(
-      std::string(kDigiCertKey, base::size(kDigiCertKey) - 1),
-      "DigiCert Log Server", "digicert.ct.invalid"));
-
-  return params;
-}
-
-// TODO(robpercival): https://crbug.com/839612 - Make it easier to use a test
-// cert that is not so tightly-coupled to production logs and STHs.
-scoped_refptr<net::X509Certificate> GetCTCertForTesting() {
-  base::ScopedAllowBlockingForTesting allow_blocking_for_loading_cert;
-  return net::CreateCertificateChainFromFile(
-      net::GetTestCertsDirectory(), "comodo-chain.pem",
-      net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
-}
-
-// The number of valid SCTs in |GetCTCertForTesting| from logs configured in
-// |CreateContextParams()|.
-const size_t kNumSCTs = 3;
-
-// Decodes a base64-encoded "DigitallySigned" TLS struct into |*sig_out|.
-// See https://tools.ietf.org/html/rfc5246#section-4.7.
-// |sig_out| must not be null.
-bool DecodeDigitallySigned(base::StringPiece base64_data,
-                           net::ct::DigitallySigned* sig_out) {
-  std::string data;
-  if (!base::Base64Decode(base64_data, &data))
-    return false;
-
-  base::StringPiece data_ptr = data;
-  if (!net::ct::DecodeDigitallySigned(&data_ptr, sig_out))
-    return false;
-
-  return true;
-}
-
-// Populates |*sth_out| with the given information.
-// |sth_out| must not be null.
-bool BuildSignedTreeHead(base::Time timestamp,
-                         uint64_t tree_size,
-                         base::StringPiece root_hash_base64,
-                         base::StringPiece signature_base64,
-                         base::StringPiece log_id_base64,
-                         net::ct::SignedTreeHead* sth_out) {
-  sth_out->version = net::ct::SignedTreeHead::V1;
-  sth_out->timestamp = timestamp;
-  sth_out->tree_size = tree_size;
-
-  std::string root_hash;
-  if (!base::Base64Decode(root_hash_base64, &root_hash)) {
-    return false;
-  }
-  root_hash.copy(sth_out->sha256_root_hash, net::ct::kSthRootHashLength);
-
-  return DecodeDigitallySigned(signature_base64, &sth_out->signature) &&
-         base::Base64Decode(log_id_base64, &sth_out->log_id);
-}
-
-TEST(NetworkContextCertTransparencyAuditingDisabledTest,
-     SCTsAreNotCheckedForInclusion) {
-  base::test::ScopedTaskEnvironment scoped_task_environment(
-      base::test::ScopedTaskEnvironment::MainThreadType::IO);
-
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      certificate_transparency::kCTLogAuditing);
-
-  std::unique_ptr<NetworkService> network_service =
-      NetworkService::CreateForTesting();
-
-  // Override the CertVerifier, so that a 'real' cert can be simulated being
-  // returned by the net::TestServer. This should be done before creating the
-  // context.
-  net::MockCertVerifier mock_cert_verifier;
-  NetworkContext::SetCertVerifierForTesting(&mock_cert_verifier);
-  base::ScopedClosureRunner cleanup(base::BindOnce(
-      [] { NetworkContext::SetCertVerifierForTesting(nullptr); }));
-
-  mojom::NetworkContextParamsPtr context_params = CreateContextParams();
-  mojom::NetworkContextPtr network_context_ptr;
-  std::unique_ptr<NetworkContext> network_context =
-      std::make_unique<NetworkContext>(network_service.get(),
-                                       mojo::MakeRequest(&network_context_ptr),
-                                       std::move(context_params));
-
-  // Certificate Transparency should be configured, but there should be
-  // nothing listening for SCTs (such as the NetworkContext's ct_tree_tracker_).
-  ASSERT_TRUE(
-      network_context->url_request_context()->cert_transparency_verifier());
-  EXPECT_FALSE(network_context->url_request_context()
-                   ->cert_transparency_verifier()
-                   ->GetObserver());
-
-  // Provide an STH from Google's Pilot log that can be used to prove
-  // inclusion for an SCT later in the test.
-  net::ct::SignedTreeHead pilot_sth;
-  ASSERT_TRUE(BuildSignedTreeHead(
-      base::Time::FromJsTime(1512419914170), 181871752,
-      "bvgljSy3Yg32Y6J8qL5WmUA3jn2WnOrEFDqxD0AxUvs=",
-      "BAMARjBEAiAwEXve2RBk3XkUR+6nACSETTgzKFaEeginxuj5U9BI/"
-      "wIgBPuQS5ACxsro6TtpY4bQyE6WlMdcSMiMd/SSGraOBOg=",
-      "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=", &pilot_sth));
-  network_service->UpdateSignedTreeHead(pilot_sth);
-
-  // Provide an STH from Google's Aviator log that is not recent enough to
-  // prove inclusion for an SCT later in the test.
-  net::ct::SignedTreeHead aviator_sth;
-  ASSERT_TRUE(BuildSignedTreeHead(
-      base::Time::FromJsTime(1442652106945), 8502329,
-      "bfG+gWZcHl9fqtNo0Z/uggs8E5YqGOtJQ0Z5zVZDRxI=",
-      "BAMARjBEAiA6elcNQoShmKLHj/"
-      "IA649UIbaQtWJEpj0Eot0q7G6fEgIgYChb7U6Reuvt0nO5PionH+3UciOxKV3Cy8/"
-      "eq59lSYY=",
-      "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=", &aviator_sth));
-  network_service->UpdateSignedTreeHead(aviator_sth);
-
-  // Start a test server on "localhost" and configure connections to it to
-  // simulate using a real certificate.
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  ASSERT_TRUE(https_server.Start());
-
-  // Configure "localhost" to be treated as if it went through DNS. This
-  // modifies the HostCache directly to simulate it being cached, rather than
-  // indirecting through a scoped HostResolverProc, as queries that use
-  // HostResolverProcs are treated as SOURCE_UNKNOWN, rather than SOURCE_DNS.
-  net::AddressList address_list;
-  ASSERT_TRUE(https_server.GetAddressList(&address_list));
-
-  net::HostCache* host_cache =
-      network_context->url_request_context()->host_resolver()->GetHostCache();
-  ASSERT_TRUE(host_cache);
-  host_cache->Set(
-      net::HostCache::Key("localhost", net::ADDRESS_FAMILY_UNSPECIFIED, 0),
-      net::HostCache::Entry(net::OK, address_list,
-                            net::HostCache::Entry::SOURCE_DNS),
-      base::TimeTicks::Now(), base::TimeDelta());
-
-  // This certificate contains 3 SCTs and fulfills the Chrome CT policy.
-  // Simulate it being trusted by a known root, as otherwise CT is skipped for
-  // private roots.
-  net::CertVerifyResult verify_result;
-  verify_result.is_issued_by_known_root = true;
-  verify_result.cert_status = 0;
-  verify_result.verified_cert = GetCTCertForTesting();
-  ASSERT_TRUE(verify_result.verified_cert);
-  mock_cert_verifier.AddResultForCert(https_server.GetCertificate(),
-                                      verify_result, net::OK);
-
-  ResourceRequest request;
-  request.url = https_server.GetURL("localhost", "/");
-  request.method = "GET";
-  request.request_initiator = url::Origin();
-
-  mojom::URLLoaderFactoryPtr loader_factory;
-  auto url_loader_factory_params =
-      network::mojom::URLLoaderFactoryParams::New();
-  url_loader_factory_params->process_id = network::mojom::kBrowserProcessId;
-  url_loader_factory_params->is_corb_enabled = false;
-  network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
-                                          std::move(url_loader_factory_params));
-
-  base::HistogramTester histograms;
-  mojom::URLLoaderPtr loader;
-  TestURLLoaderClient client;
-  int options = mojom::kURLLoadOptionSendSSLInfoWithResponse |
-                mojom::kURLLoadOptionSendSSLInfoForCertificateError;
-  loader_factory->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
-      options, request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-
-  client.RunUntilComplete();
-  EXPECT_TRUE(client.has_received_response());
-  EXPECT_TRUE(client.has_received_completion());
-
-  // Expect a successful connection.
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
-
-  // Expect 3 SCTs in this connection.
-  ASSERT_TRUE(client.ssl_info().has_value());
-  ASSERT_EQ(kNumSCTs, client.ssl_info()->signed_certificate_timestamps.size());
-
-  // Expect that all SCTs were embedded in the certificate.
-  size_t embedded_scts = 0;
-  for (const auto& sct : client.ssl_info()->signed_certificate_timestamps) {
-    if (sct.sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
-      ++embedded_scts;
-  }
-  ASSERT_EQ(kNumSCTs, embedded_scts);
-
-  // No SCTs should be eligible for inclusion checking, as inclusion checking
-  // is disabled.
-  histograms.ExpectTotalCount(
-      "Net.CertificateTransparency.CanInclusionCheckSCT", 0);
-}
-
-TEST(NetworkContextCertTransparencyAuditingEnabledTest,
-     SCTsAreCheckedForInclusion) {
-  base::test::ScopedTaskEnvironment scoped_task_environment(
-      base::test::ScopedTaskEnvironment::MainThreadType::IO);
-
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      certificate_transparency::kCTLogAuditing);
-
-  std::unique_ptr<NetworkService> network_service =
-      NetworkService::CreateForTesting();
-
-  // Override the CertVerifier, so that a 'real' cert can be simulated being
-  // returned by the net::TestServer. This should be done before creating the
-  // context.
-  net::MockCertVerifier mock_cert_verifier;
-  NetworkContext::SetCertVerifierForTesting(&mock_cert_verifier);
-  base::ScopedClosureRunner cleanup(base::BindOnce(
-      [] { NetworkContext::SetCertVerifierForTesting(nullptr); }));
-
-  mojom::NetworkContextParamsPtr context_params = CreateContextParams();
-  mojom::NetworkContextPtr network_context_ptr;
-  std::unique_ptr<NetworkContext> network_context =
-      std::make_unique<NetworkContext>(network_service.get(),
-                                       mojo::MakeRequest(&network_context_ptr),
-                                       std::move(context_params));
-
-  // Certificate Transparency should be configured, but there should be
-  // nothing listening for SCTs (such as the NetworkContext's ct_tree_tracker_).
-  ASSERT_TRUE(
-      network_context->url_request_context()->cert_transparency_verifier());
-  EXPECT_TRUE(network_context->url_request_context()
-                  ->cert_transparency_verifier()
-                  ->GetObserver());
-
-  // Provide an STH from Google's Pilot log that can be used to prove
-  // inclusion for an SCT later in the test.
-  net::ct::SignedTreeHead pilot_sth;
-  ASSERT_TRUE(BuildSignedTreeHead(
-      base::Time::FromJsTime(1512419914170), 181871752,
-      "bvgljSy3Yg32Y6J8qL5WmUA3jn2WnOrEFDqxD0AxUvs=",
-      "BAMARjBEAiAwEXve2RBk3XkUR+6nACSETTgzKFaEeginxuj5U9BI/"
-      "wIgBPuQS5ACxsro6TtpY4bQyE6WlMdcSMiMd/SSGraOBOg=",
-      "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=", &pilot_sth));
-  network_service->UpdateSignedTreeHead(pilot_sth);
-
-  // Provide an STH from Google's Aviator log that is not recent enough to
-  // prove inclusion for an SCT later in the test.
-  net::ct::SignedTreeHead aviator_sth;
-  ASSERT_TRUE(BuildSignedTreeHead(
-      base::Time::FromJsTime(1442652106945), 8502329,
-      "bfG+gWZcHl9fqtNo0Z/uggs8E5YqGOtJQ0Z5zVZDRxI=",
-      "BAMARjBEAiA6elcNQoShmKLHj/"
-      "IA649UIbaQtWJEpj0Eot0q7G6fEgIgYChb7U6Reuvt0nO5PionH+3UciOxKV3Cy8/"
-      "eq59lSYY=",
-      "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=", &aviator_sth));
-  network_service->UpdateSignedTreeHead(aviator_sth);
-
-  // Start a test server on "localhost" and configure connections to it to
-  // simulate using a real certificate.
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  ASSERT_TRUE(https_server.Start());
-
-  // Configure "localhost" to be treated as if it went through DNS. This
-  // modifies the HostCache directly to simulate it being cached, rather than
-  // indirecting through a scoped HostResolverProc, as queries that use
-  // HostResolverProcs are treated as SOURCE_UNKNOWN, rather than SOURCE_DNS.
-  net::AddressList address_list;
-  ASSERT_TRUE(https_server.GetAddressList(&address_list));
-
-  net::HostCache* host_cache =
-      network_context->url_request_context()->host_resolver()->GetHostCache();
-  ASSERT_TRUE(host_cache);
-  host_cache->Set(
-      net::HostCache::Key("localhost", net::ADDRESS_FAMILY_UNSPECIFIED, 0),
-      net::HostCache::Entry(net::OK, address_list,
-                            net::HostCache::Entry::SOURCE_DNS),
-      base::TimeTicks::Now(), base::TimeDelta());
-
-  // This certificate contains 3 SCTs and fulfills the Chrome CT policy.
-  // Simulate it being trusted by a known root, as otherwise CT is skipped for
-  // private roots.
-  net::CertVerifyResult verify_result;
-  verify_result.is_issued_by_known_root = true;
-  verify_result.cert_status = 0;
-  verify_result.verified_cert = GetCTCertForTesting();
-  ASSERT_TRUE(verify_result.verified_cert);
-  mock_cert_verifier.AddResultForCert(https_server.GetCertificate(),
-                                      verify_result, net::OK);
-
-  base::HistogramTester histograms;
-
-  ResourceRequest request;
-  request.url = https_server.GetURL("localhost", "/");
-  request.method = "GET";
-  request.request_initiator = url::Origin();
-
-  mojom::URLLoaderFactoryPtr loader_factory;
-  auto url_loader_factory_params =
-      network::mojom::URLLoaderFactoryParams::New();
-  url_loader_factory_params->process_id = network::mojom::kBrowserProcessId;
-  url_loader_factory_params->is_corb_enabled = false;
-  network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
-                                          std::move(url_loader_factory_params));
-
-  mojom::URLLoaderPtr loader;
-  TestURLLoaderClient client;
-  int options = mojom::kURLLoadOptionSendSSLInfoWithResponse |
-                mojom::kURLLoadOptionSendSSLInfoForCertificateError;
-  loader_factory->CreateLoaderAndStart(
-      mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
-      options, request, client.CreateInterfacePtr(),
-      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
-
-  client.RunUntilComplete();
-  EXPECT_TRUE(client.has_received_response());
-  EXPECT_TRUE(client.has_received_completion());
-
-  // Expect a successful connection.
-  EXPECT_EQ(net::OK, client.completion_status().error_code);
-
-  // Expect 3 SCTs in this connection.
-  ASSERT_TRUE(client.ssl_info().has_value());
-  ASSERT_EQ(kNumSCTs, client.ssl_info()->signed_certificate_timestamps.size());
-
-  // Expect that all SCTs were embedded in the certificate.
-  size_t embedded_scts = 0;
-  for (const auto& sct : client.ssl_info()->signed_certificate_timestamps) {
-    if (sct.sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
-      ++embedded_scts;
-  }
-  ASSERT_EQ(kNumSCTs, embedded_scts);
-
-  // The Pilot SCT should be eligible for inclusion checking, because a recent
-  // enough Pilot STH is available.
-  histograms.ExpectBucketCount(
-      "Net.CertificateTransparency.CanInclusionCheckSCT",
-      certificate_transparency::CAN_BE_CHECKED, 1);
-  // The Aviator SCT should not be eligible for inclusion checking, because
-  // there is not a recent enough Aviator STH available.
-  histograms.ExpectBucketCount(
-      "Net.CertificateTransparency.CanInclusionCheckSCT",
-      certificate_transparency::NEWER_STH_REQUIRED, 1);
-  // The DigiCert SCT should not be eligible for inclusion checking, because
-  // there is no DigiCert STH available.
-  histograms.ExpectBucketCount(
-      "Net.CertificateTransparency.CanInclusionCheckSCT",
-      certificate_transparency::VALID_STH_REQUIRED, 1);
-}
-
-}  // namespace
-
-}  // namespace network
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 2448249a..652ccfc 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -54,10 +54,6 @@
 #include "services/network/url_loader.h"
 #include "services/network/url_request_context_builder_mojo.h"
 
-#if BUILDFLAG(IS_CT_SUPPORTED)
-#include "components/certificate_transparency/sth_distributor.h"
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
-
 #if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
 #include "crypto/openssl_util.h"
 #include "third_party/boringssl/src/include/openssl/cpu.h"
@@ -326,10 +322,6 @@
 
   http_auth_cache_copier_ = std::make_unique<HttpAuthCacheCopier>();
 
-#if BUILDFLAG(IS_CT_SUPPORTED)
-  sth_distributor_ =
-      std::make_unique<certificate_transparency::STHDistributor>();
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
   crl_set_distributor_ = std::make_unique<CRLSetDistributor>();
 }
 
@@ -598,12 +590,6 @@
                      std::move(callback)));
 }
 
-#if BUILDFLAG(IS_CT_SUPPORTED)
-void NetworkService::UpdateSignedTreeHead(const net::ct::SignedTreeHead& sth) {
-  sth_distributor_->NewSTHObserved(sth);
-}
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
-
 void NetworkService::UpdateCRLSet(base::span<const uint8_t> crl_set) {
   crl_set_distributor_->OnNewCRLSet(crl_set);
 }
@@ -687,12 +673,6 @@
     MaybeStartUpdateLoadInfoTimer();
 }
 
-#if BUILDFLAG(IS_CT_SUPPORTED)
-certificate_transparency::STHReporter* NetworkService::sth_reporter() {
-  return sth_distributor_.get();
-}
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
-
 void NetworkService::OnBindInterface(
     const service_manager::BindSourceInfo& source_info,
     const std::string& interface_name,
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 233b74c5..1c61321 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -49,13 +49,6 @@
 class URLRequestContext;
 }  // namespace net
 
-#if BUILDFLAG(IS_CT_SUPPORTED)
-namespace certificate_transparency {
-class STHDistributor;
-class STHReporter;
-}  // namespace certificate_transparency
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
-
 namespace network {
 
 class CRLSetDistributor;
@@ -177,9 +170,6 @@
   void GetNetworkList(
       uint32_t policy,
       mojom::NetworkService::GetNetworkListCallback callback) override;
-#if BUILDFLAG(IS_CT_SUPPORTED)
-  void UpdateSignedTreeHead(const net::ct::SignedTreeHead& sth) override;
-#endif  // !BUILDFLAG(IS_CT_SUPPORTED)
   void UpdateCRLSet(base::span<const uint8_t> crl_set) override;
   void OnCertDBChanged() override;
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -231,10 +221,6 @@
     return http_auth_cache_copier_.get();
   }
 
-#if BUILDFLAG(IS_CT_SUPPORTED)
-  certificate_transparency::STHReporter* sth_reporter();
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
-
   CRLSetDistributor* crl_set_distributor() {
     return crl_set_distributor_.get();
   }
@@ -346,9 +332,6 @@
 
   bool os_crypt_config_set_ = false;
 
-#if BUILDFLAG(IS_CT_SUPPORTED)
-  std::unique_ptr<certificate_transparency::STHDistributor> sth_distributor_;
-#endif  // BUILDFLAG(IS_CT_SUPPORTED)
   std::unique_ptr<CRLSetDistributor> crl_set_distributor_;
 
   // A timer that periodically calls UpdateLoadInfo while there are pending
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index a618fea..d27d3c4 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -201,10 +201,6 @@
     "url_request_mojom_traits_unittest.cc",
   ]
 
-  if (is_ct_supported) {
-    sources += [ "signed_tree_head_mojom_traits_unittest.cc" ]
-  }
-
   if (!is_ios) {
     sources += [ "server/http_server_unittest.cc" ]
   }
diff --git a/services/network/public/cpp/signed_tree_head.typemap b/services/network/public/cpp/signed_tree_head.typemap
deleted file mode 100644
index 92dbaee..0000000
--- a/services/network/public/cpp/signed_tree_head.typemap
+++ /dev/null
@@ -1,18 +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.
-
-mojom = "//services/network/public/mojom/signed_tree_head.mojom"
-public_headers = [ "//net/cert/signed_tree_head.h" ]
-traits_headers =
-    [ "//services/network/public/cpp/signed_tree_head_mojom_traits.h" ]
-sources = [
-  "//services/network/public/cpp/signed_tree_head_mojom_traits.cc",
-]
-type_mappings = [
-  "network.mojom.SignedTreeHead=net::ct::SignedTreeHead",
-  "network.mojom.SignedTreeHeadVersion=net::ct::SignedTreeHead::Version",
-]
-public_deps = [
-  "//net",
-]
diff --git a/services/network/public/cpp/signed_tree_head_mojom_traits.cc b/services/network/public/cpp/signed_tree_head_mojom_traits.cc
deleted file mode 100644
index 9195950..0000000
--- a/services/network/public/cpp/signed_tree_head_mojom_traits.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 "services/network/public/cpp/signed_tree_head_mojom_traits.h"
-
-#include <memory>
-#include <vector>
-
-#include "mojo/public/cpp/base/time_mojom_traits.h"
-#include "services/network/public/cpp/digitally_signed_mojom_traits.h"
-
-namespace mojo {
-
-// static
-bool StructTraits<
-    network::mojom::SignedTreeHeadDataView,
-    net::ct::SignedTreeHead>::Read(network::mojom::SignedTreeHeadDataView data,
-                                   net::ct::SignedTreeHead* out) {
-  std::vector<uint8_t> sha256_root_hash;
-  if (!data.ReadVersion(&out->version) ||
-      !data.ReadTimestamp(&out->timestamp) ||
-      !data.ReadSignature(&out->signature) || !data.ReadLogId(&out->log_id) ||
-      !data.ReadSha256RootHash(&sha256_root_hash)) {
-    return false;
-  }
-  if (out->log_id.empty()) {
-    return false;
-  }
-
-  out->tree_size = data.tree_size();
-
-  // The Mojo bindings should have validated the size constraint as part of
-  // ReadSha256RootHash().
-  DCHECK_EQ(sha256_root_hash.size(), sizeof(out->sha256_root_hash));
-  memcpy(out->sha256_root_hash, sha256_root_hash.data(),
-         sizeof(out->sha256_root_hash));
-
-  return true;
-}
-
-}  // namespace mojo
diff --git a/services/network/public/cpp/signed_tree_head_mojom_traits.h b/services/network/public/cpp/signed_tree_head_mojom_traits.h
deleted file mode 100644
index f38cf4c7..0000000
--- a/services/network/public/cpp/signed_tree_head_mojom_traits.h
+++ /dev/null
@@ -1,75 +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 SERVICES_NETWORK_PUBLIC_CPP_SIGNED_TREE_HEAD_MOJOM_TRAITS_H_
-#define SERVICES_NETWORK_PUBLIC_CPP_SIGNED_TREE_HEAD_MOJOM_TRAITS_H_
-
-#include "base/containers/span.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-#include "mojo/public/cpp/bindings/enum_traits.h"
-#include "mojo/public/cpp/bindings/struct_traits.h"
-#include "net/cert/signed_tree_head.h"
-#include "services/network/public/mojom/digitally_signed.mojom.h"
-#include "services/network/public/mojom/signed_tree_head.mojom.h"
-
-namespace mojo {
-
-template <>
-struct EnumTraits<network::mojom::SignedTreeHeadVersion,
-                  net::ct::SignedTreeHead::Version> {
-  static network::mojom::SignedTreeHeadVersion ToMojom(
-      net::ct::SignedTreeHead::Version input) {
-    switch (input) {
-      case net::ct::SignedTreeHead::V1:
-        return network::mojom::SignedTreeHeadVersion::V1;
-    }
-    NOTREACHED();
-    return network::mojom::SignedTreeHeadVersion::kMaxValue;
-  }
-
-  static bool FromMojom(network::mojom::SignedTreeHeadVersion input,
-                        net::ct::SignedTreeHead::Version* output) {
-    switch (input) {
-      case network::mojom::SignedTreeHeadVersion::V1:
-        *output = net::ct::SignedTreeHead::V1;
-        return true;
-    }
-    NOTREACHED();
-    return false;
-  }
-};
-
-template <>
-struct StructTraits<network::mojom::SignedTreeHeadDataView,
-                    net::ct::SignedTreeHead> {
-  static net::ct::SignedTreeHead::Version version(
-      const net::ct::SignedTreeHead& sth) {
-    return sth.version;
-  }
-  static base::Time timestamp(const net::ct::SignedTreeHead& sth) {
-    return sth.timestamp;
-  }
-  static uint64_t tree_size(const net::ct::SignedTreeHead& sth) {
-    return sth.tree_size;
-  }
-  static base::span<const uint8_t> sha256_root_hash(
-      const net::ct::SignedTreeHead& sth) {
-    return base::as_bytes(base::make_span(sth.sha256_root_hash));
-  }
-  static const net::ct::DigitallySigned& signature(
-      const net::ct::SignedTreeHead& sth) {
-    return sth.signature;
-  }
-  static base::StringPiece log_id(const net::ct::SignedTreeHead& sth) {
-    return sth.log_id;
-  }
-
-  static bool Read(network::mojom::SignedTreeHeadDataView obj,
-                   net::ct::SignedTreeHead* out);
-};
-
-}  // namespace mojo
-
-#endif  // SERVICES_NETWORK_PUBLIC_CPP_SIGNED_TREE_HEAD_MOJOM_TRAITS_H_
diff --git a/services/network/public/cpp/signed_tree_head_mojom_traits_unittest.cc b/services/network/public/cpp/signed_tree_head_mojom_traits_unittest.cc
deleted file mode 100644
index 3742b832..0000000
--- a/services/network/public/cpp/signed_tree_head_mojom_traits_unittest.cc
+++ /dev/null
@@ -1,64 +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 "services/network/public/cpp/signed_tree_head_mojom_traits.h"
-
-#include "base/time/time.h"
-#include "mojo/public/cpp/base/time_mojom_traits.h"
-#include "mojo/public/cpp/test_support/test_utils.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/test/ct_test_util.h"
-#include "services/network/public/cpp/digitally_signed_mojom_traits.h"
-#include "services/network/public/mojom/signed_tree_head.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace network {
-namespace {
-
-TEST(SignedTreeHeadTraitsTest, Roundtrips) {
-  net::ct::SignedTreeHead original;
-  net::ct::SignedTreeHead copied;
-
-  // First try with a populated STH.
-  ASSERT_TRUE(net::ct::GetSampleSignedTreeHead(&original));
-  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
-      &original, &copied));
-  EXPECT_EQ(original, copied);
-
-  // Then try an STH for an empty tree.
-  ASSERT_TRUE(net::ct::GetSampleEmptySignedTreeHead(&original));
-  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
-      &original, &copied));
-  EXPECT_EQ(original, copied);
-
-  // Then try a syntactically-valid but semantically-bad STH.
-  ASSERT_TRUE(net::ct::GetBadEmptySignedTreeHead(&original));
-  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
-      &original, &copied));
-  EXPECT_EQ(original, copied);
-}
-
-TEST(SignedTreeHeadTraitsTest, RequiresLogId) {
-  net::ct::SignedTreeHead original;
-  ASSERT_TRUE(net::ct::GetSampleSignedTreeHead(&original));
-  original.log_id.clear();
-
-  net::ct::SignedTreeHead copied;
-  EXPECT_FALSE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
-      &original, &copied));
-}
-
-TEST(SignedTreeHeadTraitsTest, RequiresValidDigitallySigned) {
-  net::ct::SignedTreeHead original;
-  ASSERT_TRUE(net::ct::GetSampleSignedTreeHead(&original));
-  original.signature.signature_data.clear();
-
-  net::ct::SignedTreeHead copied;
-  EXPECT_FALSE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
-      &original, &copied));
-}
-
-}  // namespace
-}  // namespace network
diff --git a/services/network/public/cpp/typemaps.gni b/services/network/public/cpp/typemaps.gni
index 06a2827..b0677ef 100644
--- a/services/network/public/cpp/typemaps.gni
+++ b/services/network/public/cpp/typemaps.gni
@@ -28,7 +28,3 @@
   "//services/network/public/cpp/url_request_redirect_info.typemap",
   "//services/network/public/cpp/url_response_head.typemap",
 ]
-
-if (is_ct_supported) {
-  typemaps += [ "//services/network/public/cpp/signed_tree_head.typemap" ]
-}
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index d07b01290..036bcc4 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -126,10 +126,7 @@
 
   if (is_ct_supported) {
     enabled_features += [ "is_ct_supported" ]
-    sources += [
-      "ct_log_info.mojom",
-      "signed_tree_head.mojom",
-    ]
+    sources += [ "ct_log_info.mojom" ]
   }
 
   if (!is_ios) {
diff --git a/services/network/public/mojom/ct_log_info.mojom b/services/network/public/mojom/ct_log_info.mojom
index f4c26f7..294892a 100644
--- a/services/network/public/mojom/ct_log_info.mojom
+++ b/services/network/public/mojom/ct_log_info.mojom
@@ -14,9 +14,4 @@
   // The human-readable, log-supplied log name. Note that this will not be
   // translated.
   string name;
-
-  // The DNS API endpoint for the log.
-  // This will be used as the parent domain for all queries about the log.
-  // See https://github.com/google/certificate-transparency-rfcs/blob/master/dns/draft-ct-over-dns.md
-  string dns_api_endpoint;
 };
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index 60c8ab74..53be8c9 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -24,9 +24,6 @@
 import "url/mojom/origin.mojom";
 import "url/mojom/url.mojom";
 
-[EnableIf=is_ct_supported]
-import "services/network/public/mojom/signed_tree_head.mojom";
-
 [EnableIf=is_android]
 import "mojo/public/mojom/base/application_state.mojom";
 
@@ -338,12 +335,6 @@
   // network interfaces. Corresponds to enum net::HostAddressSelectionPolicy.
   GetNetworkList(uint32 policy) => (array<NetworkInterface>? networks);
 
-  // Updates Signed Tree Heads (STH) used in the handling of Certificate
-  // Transparency. Broadcast to each NetworkContext using the NetworkService.
-  // NetworkContextes ignore STHs from unrecognized logs.
-  [EnableIf=is_ct_supported]
-  UpdateSignedTreeHead(SignedTreeHead signed_tree_head);
-
   // Updates the CRLSet used in the verification of certificates. CRLSets that
   // cannot be parsed using net::CRLSet::Parse will be ignored, as will older
   // CRLSets (where older is determined by the sequence number). All Network
diff --git a/services/network/public/mojom/signed_tree_head.mojom b/services/network/public/mojom/signed_tree_head.mojom
deleted file mode 100644
index 6296dc6..0000000
--- a/services/network/public/mojom/signed_tree_head.mojom
+++ /dev/null
@@ -1,27 +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.
-module network.mojom;
-
-import "mojo/public/mojom/base/time.mojom";
-import "services/network/public/mojom/digitally_signed.mojom";
-
-// Mirror of net::ct::SignedTreeHead::Version
-enum SignedTreeHeadVersion {
-  // The Version enum in RFC 6962, Section 3.2.
-  V1 = 0,
-};
-
-// Mirror of net::ct::SignedTreeHead.
-// These fields are defined in Section 3.5 and Section 4.3 of RFC 6962.
-struct SignedTreeHead {
-  SignedTreeHeadVersion version;
-  mojo_base.mojom.Time timestamp;
-  uint64 tree_size;
-  array<uint8, 32> sha256_root_hash;
-  DigitallySigned signature;
-
-  // Added in RFC6962-bis, Appendix A, but useful with RFC 6962 to track
-  // which log a given STH is associated with.
-  string log_id;
-};
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index b51fc85..fc1a665a 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -272,8 +272,7 @@
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -296,8 +295,7 @@
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -322,8 +320,7 @@
               "os": "Ubuntu-14.04",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -346,8 +343,7 @@
               "os": "Ubuntu-14.04",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -626,8 +622,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -651,8 +646,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -677,8 +671,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -701,8 +694,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -979,8 +971,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -1003,8 +994,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -1029,8 +1019,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -1053,8 +1042,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -1327,8 +1315,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -1351,8 +1338,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -1377,8 +1363,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -1401,8 +1386,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index ec19ee0..b8ec7942 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -7136,8 +7136,7 @@
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -7160,8 +7159,7 @@
               "os": "Ubuntu",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -7186,8 +7184,7 @@
               "os": "Ubuntu-14.04",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -7210,8 +7207,7 @@
               "os": "Ubuntu-14.04",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -7235,8 +7231,7 @@
               "gpu": "8086:0a2e",
               "os": "Mac-10.12.6"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -7258,8 +7253,7 @@
               "gpu": "8086:0a2e",
               "os": "Mac-10.12.6"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -7285,8 +7279,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -7310,8 +7303,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -7337,8 +7329,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -7362,8 +7353,7 @@
               "os": "Mac-10.13.6",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -7388,8 +7378,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -7412,8 +7401,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -7438,8 +7426,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       },
@@ -7462,8 +7449,7 @@
               "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
-          ],
-          "shards": 4
+          ]
         },
         "test": "dawn_end2end_tests"
       }
@@ -26254,8 +26240,7 @@
               "pool": "Chrome-GPU"
             }
           ],
-          "expiration": 21600,
-          "shards": 4
+          "expiration": 21600
         },
         "test": "dawn_end2end_tests"
       },
@@ -26279,8 +26264,7 @@
               "pool": "Chrome-GPU"
             }
           ],
-          "expiration": 21600,
-          "shards": 4
+          "expiration": 21600
         },
         "test": "dawn_end2end_tests"
       },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 409464e..2ed7c72 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3154,9 +3154,6 @@
           # Dawn test retries deliberately disabled to prevent flakiness.
           '--test-launcher-retry-limit=0'
         ],
-        'swarming': {
-          'shards': 4,
-        },
       },
       'dawn_end2end_wire_tests': {
         'desktop_args': [
@@ -3165,9 +3162,6 @@
           '--test-launcher-retry-limit=0',
           '--use-wire',
         ],
-        'swarming': {
-          'shards': 4,
-        },
         'test': 'dawn_end2end_tests',
       },
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index be3bd6f..8f8c400 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -493,6 +493,21 @@
             ]
         }
     ],
+    "AppListLaunchRecorder": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "AppListLaunchRecorder",
+                    "enable_features": [
+                        "EnableAppListLaunchRecording"
+                    ]
+                }
+            ]
+        }
+    ],
     "AppleScriptExecuteJavaScript": [
         {
             "platforms": [
@@ -1418,24 +1433,6 @@
             ]
         }
     ],
-    "CertificateTransparencyLogAuditing": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CertificateTransparencyLogAuditing"
-                    ]
-                }
-            ]
-        }
-    ],
     "ChromeCleanupDistribution": [
         {
             "platforms": [
@@ -4045,6 +4042,21 @@
             ]
         }
     ],
+    "PrefetchSRPAndroid": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_threshold_1_holdback_20190521",
+                    "enable_features": [
+                        "GWSPrefetchHoldback"
+                    ]
+                }
+            ]
+        }
+    ],
     "PreloadMetadataLazyLoad": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 726df7a..56b20ab6 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -402,14 +402,18 @@
   UpdateContainerPolicy();
 
   KURL url_to_request = url.IsNull() ? BlankURL() : url;
+  ResourceRequest request(url_to_request);
+  request.SetReferrerPolicy(ReferrerPolicyAttribute());
+
   if (ContentFrame()) {
     // TODO(sclittle): Support lazily loading frame navigations.
-    FrameLoadRequest request(&GetDocument(), ResourceRequest(url_to_request));
-    request.SetClientRedirectReason(ClientNavigationReason::kFrameNavigation);
+    FrameLoadRequest frame_load_request(&GetDocument(), request);
+    frame_load_request.SetClientRedirectReason(
+        ClientNavigationReason::kFrameNavigation);
     WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
     if (replace_current_item)
       frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
-    ContentFrame()->Navigate(request, frame_load_type);
+    ContentFrame()->Navigate(frame_load_request, frame_load_type);
     return true;
   }
 
@@ -426,10 +430,6 @@
   if (!child_frame)
     return false;
 
-  ResourceRequest request(url_to_request);
-  network::mojom::ReferrerPolicy policy = ReferrerPolicyAttribute();
-  request.SetReferrerPolicy(policy);
-
   WebFrameLoadType child_load_type = WebFrameLoadType::kReplaceCurrentItem;
   if (!GetDocument().LoadEventFinished() &&
       GetDocument().Loader()->LoadType() ==
diff --git a/third_party/blink/renderer/core/html/html_rt_element.h b/third_party/blink/renderer/core/html/html_rt_element.h
index 3b2fe46..04a6891 100644
--- a/third_party/blink/renderer/core/html/html_rt_element.h
+++ b/third_party/blink/renderer/core/html/html_rt_element.h
@@ -17,6 +17,7 @@
 
  private:
   LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
+  bool TypeShouldForceLegacyLayout() const final { return true; }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/background_html_parser.cc b/third_party/blink/renderer/core/html/parser/background_html_parser.cc
index f9e285f..1eaf3d4 100644
--- a/third_party/blink/renderer/core/html/parser/background_html_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/background_html_parser.cc
@@ -273,7 +273,7 @@
                          chunk.get(), TRACE_EVENT_FLAG_FLOW_OUT);
 
   chunk->preloads.swap(pending_preloads_);
-  if (viewport_description_.set)
+  if (viewport_description_.has_value())
     chunk->viewport = viewport_description_;
   chunk->xss_infos.swap(pending_xss_infos_);
   chunk->tokenizer_state = tokenizer_->GetState();
diff --git a/third_party/blink/renderer/core/html/parser/background_html_parser.h b/third_party/blink/renderer/core/html/parser/background_html_parser.h
index 474f5717..76b01e4 100644
--- a/third_party/blink/renderer/core/html/parser/background_html_parser.h
+++ b/third_party/blink/renderer/core/html/parser/background_html_parser.h
@@ -30,6 +30,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/renderer/core/dom/document_encoding_data.h"
 #include "third_party/blink/renderer/core/html/parser/background_html_input_stream.h"
@@ -40,6 +41,7 @@
 #include "third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.h"
 #include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
 #include "third_party/blink/renderer/core/html/parser/xss_auditor_delegate.h"
+#include "third_party/blink/renderer/core/page/viewport_description.h"
 
 namespace blink {
 
@@ -116,7 +118,7 @@
 
   CompactHTMLTokenStream pending_tokens_;
   PreloadRequestStream pending_preloads_;
-  ViewportDescriptionWrapper viewport_description_;
+  base::Optional<ViewportDescription> viewport_description_;
   XSSInfoStream pending_xss_infos_;
 
   std::unique_ptr<XSSAuditor> xss_auditor_;
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index c8ab1dd..5cce22f 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -326,7 +326,7 @@
     // Note that on commit, the loader dispatched preloads for all the non-media
     // links.
     GetDocument()->Loader()->DispatchLinkHeaderPreloads(
-        &chunk->viewport, PreloadHelper::kOnlyLoadMedia);
+        chunk->viewport, PreloadHelper::kOnlyLoadMedia);
     tried_loading_link_headers_ = true;
     if (GetDocument()->Loader()->GetPrefetchedSignedExchangeManager()) {
       // Link header preloads for prefetched signed exchanges won't be started
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index c935616..df11f78d 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
 #include "third_party/blink/renderer/core/html/parser/xss_auditor.h"
 #include "third_party/blink/renderer/core/html/parser/xss_auditor_delegate.h"
+#include "third_party/blink/renderer/core/page/viewport_description.h"
 #include "third_party/blink/renderer/core/script/html_parser_script_runner_host.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_position.h"
@@ -112,7 +113,7 @@
    public:
     CompactHTMLTokenStream tokens;
     PreloadRequestStream preloads;
-    ViewportDescriptionWrapper viewport;
+    base::Optional<ViewportDescription> viewport;
     XSSInfoStream xss_infos;
     HTMLTokenizer::State tokenizer_state;
     HTMLTreeBuilderSimulator::State tree_builder_state;
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index d23aebb..bcfa14c7 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -758,7 +758,7 @@
 void TokenPreloadScanner::Scan(const HTMLToken& token,
                                const SegmentedString& source,
                                PreloadRequestStream& requests,
-                               ViewportDescriptionWrapper* viewport,
+                               base::Optional<ViewportDescription>* viewport,
                                bool* is_csp_meta_tag) {
   ScanCommon(token, source, requests, viewport, is_csp_meta_tag);
 }
@@ -766,7 +766,7 @@
 void TokenPreloadScanner::Scan(const CompactHTMLToken& token,
                                const SegmentedString& source,
                                PreloadRequestStream& requests,
-                               ViewportDescriptionWrapper* viewport,
+                               base::Optional<ViewportDescription>* viewport,
                                bool* is_csp_meta_tag) {
   ScanCommon(token, source, requests, viewport, is_csp_meta_tag);
 }
@@ -775,17 +775,15 @@
     const String& attribute_value,
     const CachedDocumentParameters* document_parameters,
     MediaValuesCached* media_values,
-    ViewportDescriptionWrapper* viewport) {
+    base::Optional<ViewportDescription>* viewport) {
   if (!document_parameters->viewport_meta_enabled)
     return;
   ViewportDescription description(ViewportDescription::kViewportMeta);
   HTMLMetaElement::GetViewportDescriptionFromContentAttribute(
       attribute_value, description, nullptr,
       document_parameters->viewport_meta_zero_values_quirk);
-  if (viewport) {
-    viewport->description = description;
-    viewport->set = true;
-  }
+  if (viewport)
+    *viewport = description;
   FloatSize initial_viewport(media_values->DeviceWidth(),
                              media_values->DeviceHeight());
   PageScaleConstraints constraints = description.Resolve(
@@ -814,7 +812,7 @@
     CachedDocumentParameters* document_parameters,
     MediaValuesCached* media_values,
     CSSPreloadScanner* css_scanner,
-    ViewportDescriptionWrapper* viewport) {
+    base::Optional<ViewportDescription>* viewport) {
   const typename Token::Attribute* name_attribute =
       token.GetAttributeItem(kNameAttr);
   if (!name_attribute)
@@ -840,11 +838,12 @@
 }
 
 template <typename Token>
-void TokenPreloadScanner::ScanCommon(const Token& token,
-                                     const SegmentedString& source,
-                                     PreloadRequestStream& requests,
-                                     ViewportDescriptionWrapper* viewport,
-                                     bool* is_csp_meta_tag) {
+void TokenPreloadScanner::ScanCommon(
+    const Token& token,
+    const SegmentedString& source,
+    PreloadRequestStream& requests,
+    base::Optional<ViewportDescription>* viewport,
+    bool* is_csp_meta_tag) {
   if (!document_parameters_->do_html_preload_scanning)
     return;
 
@@ -992,7 +991,7 @@
 
 PreloadRequestStream HTMLPreloadScanner::Scan(
     const KURL& starting_base_element_url,
-    ViewportDescriptionWrapper* viewport,
+    base::Optional<ViewportDescription>* viewport,
     bool& has_csp_meta_tag) {
   // HTMLTokenizer::updateStateFor only works on the main thread.
   DCHECK(IsMainThread());
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
index e480e1d..45f6493 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
@@ -32,6 +32,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/media_values_cached.h"
 #include "third_party/blink/renderer/core/html/parser/compact_html_token.h"
@@ -50,12 +51,6 @@
 class HTMLTokenizer;
 class SegmentedString;
 
-struct ViewportDescriptionWrapper {
-  ViewportDescription description;
-  bool set;
-  ViewportDescriptionWrapper() : set(false) {}
-};
-
 struct CORE_EXPORT CachedDocumentParameters {
   USING_FAST_MALLOC(CachedDocumentParameters);
 
@@ -88,12 +83,12 @@
   void Scan(const HTMLToken&,
             const SegmentedString&,
             PreloadRequestStream& requests,
-            ViewportDescriptionWrapper*,
+            base::Optional<ViewportDescription>*,
             bool* is_csp_meta_tag);
   void Scan(const CompactHTMLToken&,
             const SegmentedString&,
             PreloadRequestStream& requests,
-            ViewportDescriptionWrapper*,
+            base::Optional<ViewportDescription>*,
             bool* is_csp_meta_tag);
 
   void SetPredictedBaseElementURL(const KURL& url) {
@@ -112,7 +107,7 @@
   inline void ScanCommon(const Token&,
                          const SegmentedString&,
                          PreloadRequestStream& requests,
-                         ViewportDescriptionWrapper*,
+                         base::Optional<ViewportDescription>*,
                          bool* is_csp_meta_tag);
 
   template <typename Token>
@@ -181,7 +176,7 @@
 
   void AppendToEnd(const SegmentedString&);
   PreloadRequestStream Scan(const KURL& document_base_element_url,
-                            ViewportDescriptionWrapper*,
+                            base::Optional<ViewportDescription>*,
                             bool& has_csp_meta_tag);
 
  private:
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 082edae..dc240d0 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -322,8 +322,7 @@
   LayoutObject* layout_object = text_node->GetLayoutObject();
   if (!layout_object || !layout_object->IsText())
     return text_info;
-  PhysicalRect bounding_box =
-      ToLayoutText(layout_object)->PhysicalVisualOverflowRect();
+  LayoutRect bounding_box = ToLayoutText(layout_object)->VisualOverflowRect();
   text_info->setString("nodeWidth", bounding_box.Width().ToString());
   text_info->setString("nodeHeight", bounding_box.Height().ToString());
   text_info->setString("tagName", "#text");
@@ -846,7 +845,8 @@
 
   if (layout_object->IsText()) {
     LayoutText* layout_text = ToLayoutText(layout_object);
-    PhysicalRect text_rect = layout_text->PhysicalVisualOverflowRect();
+    PhysicalRect text_rect =
+        layout_text->FlipForWritingMode(layout_text->VisualOverflowRect());
     content_box = text_rect;
     padding_box = text_rect;
     border_box = text_rect;
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index e72f5b6..193728f4 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -506,8 +506,8 @@
     return LayoutSize(LayoutOverflowRect().MaxX(), LayoutOverflowRect().MaxY());
   }
 
-  LayoutRect VisualOverflowRect() const;
-  PhysicalRect PhysicalVisualOverflowRect() const final {
+  LayoutRect VisualOverflowRect() const override;
+  PhysicalRect PhysicalVisualOverflowRect() const {
     return FlipForWritingMode(VisualOverflowRect());
   }
   LayoutUnit LogicalLeftVisualOverflow() const {
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.h b/third_party/blink/renderer/core/layout/layout_box_model_object.h
index 23e65eb6..6aaea9d 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.h
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.h
@@ -177,7 +177,7 @@
   // border boxes.
   virtual IntRect BorderBoundingBox() const = 0;
 
-  virtual PhysicalRect PhysicalVisualOverflowRect() const = 0;
+  virtual LayoutRect VisualOverflowRect() const = 0;
 
   bool UsesCompositedScrolling() const;
 
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index 17b2b0e..94e5ede 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -1242,16 +1242,11 @@
   return nullptr;
 }
 
-PhysicalRect LayoutInline::CulledInlineVisualOverflowBoundingBox() const {
-  PhysicalRect result;
-  CollectCulledLineBoxRects(
-      [&result](const PhysicalRect& r) { result.UniteIfNonZero(r); });
-  if (!FirstChild())
-    return result;
-
+LayoutRect LayoutInline::CulledInlineVisualOverflowBoundingBox() const {
+  LayoutRect result;
+  CollectCulledLineBoxRectsInFlippedBlocksDirection(
+      [&result](const LayoutRect& r) { result.UniteIfNonZero(r); }, this);
   bool is_horizontal = StyleRef().IsHorizontalWritingMode();
-  const LayoutBlock* block_for_flipping =
-      UNLIKELY(HasFlippedBlocksWritingMode()) ? ContainingBlock() : nullptr;
   for (LayoutObject* curr = FirstChild(); curr; curr = curr->NextSibling()) {
     if (curr->IsFloatingOrOutOfFlowPositioned())
       continue;
@@ -1264,31 +1259,29 @@
             curr_box->LogicalVisualOverflowRectForPropagation();
         if (is_horizontal) {
           logical_rect.MoveBy(curr_box->Location());
-          result.UniteIfNonZero(PhysicalRect(logical_rect));
+          result.UniteIfNonZero(logical_rect);
         } else {
           logical_rect.MoveBy(curr_box->Location());
-          result.UniteIfNonZero(FlipForWritingMode(
-              logical_rect.TransposedRect(), block_for_flipping));
+          result.UniteIfNonZero(logical_rect.TransposedRect());
         }
       }
     } else if (curr->IsLayoutInline()) {
       // If the child doesn't need line boxes either, then we can recur.
       LayoutInline* curr_inline = ToLayoutInline(curr);
-      if (!curr_inline->AlwaysCreateLineBoxes()) {
+      if (!curr_inline->AlwaysCreateLineBoxes())
         result.UniteIfNonZero(
             curr_inline->CulledInlineVisualOverflowBoundingBox());
-      } else if (!curr_inline->HasSelfPaintingLayer()) {
-        result.UniteIfNonZero(curr_inline->PhysicalVisualOverflowRect());
-      }
+      else if (!curr_inline->HasSelfPaintingLayer())
+        result.UniteIfNonZero(curr_inline->VisualOverflowRect());
     } else if (curr->IsText()) {
       LayoutText* curr_text = ToLayoutText(curr);
-      result.UniteIfNonZero(curr_text->PhysicalVisualOverflowRect());
+      result.UniteIfNonZero(curr_text->VisualOverflowRect());
     }
   }
   return result;
 }
 
-PhysicalRect LayoutInline::LinesVisualOverflowBoundingBox() const {
+LayoutRect LayoutInline::LinesVisualOverflowBoundingBox() const {
   if (IsInLayoutNGInlineFormattingContext()) {
     PhysicalRect result;
     NGPaintFragment::InlineFragmentsIncludingCulledFor(
@@ -1300,14 +1293,14 @@
           result->Unite(child_rect);
         },
         &result);
-    return result;
+    return FlipForWritingMode(result);
   }
 
   if (!AlwaysCreateLineBoxes())
     return CulledInlineVisualOverflowBoundingBox();
 
   if (!FirstLineBox() || !LastLineBox())
-    return PhysicalRect();
+    return LayoutRect();
 
   // Return the width of the minimal left side and the maximal right side.
   LayoutUnit logical_left_side = LayoutUnit::Max();
@@ -1333,13 +1326,13 @@
                   logical_height);
   if (!StyleRef().IsHorizontalWritingMode())
     rect = rect.TransposedRect();
-  return FlipForWritingMode(rect);
+  return rect;
 }
 
 PhysicalRect LayoutInline::VisualRectInDocument(VisualRectFlags flags) const {
   PhysicalRect rect;
   if (!Continuation()) {
-    rect = PhysicalVisualOverflowRect();
+    rect = FlipForWritingMode(VisualOverflowRect());
   } else {
     // Should also cover continuations.
     rect = UnionRect(OutlineRects(PhysicalOffset(),
@@ -1361,11 +1354,11 @@
 
   // VisualOverflowRect() is in "physical coordinates with flipped blocks
   // direction", while all "VisualRect"s are in pure physical coordinates.
-  return PhysicalVisualOverflowRect();
+  return FlipForWritingMode(VisualOverflowRect());
 }
 
-PhysicalRect LayoutInline::PhysicalVisualOverflowRect() const {
-  PhysicalRect overflow_rect = LinesVisualOverflowBoundingBox();
+LayoutRect LayoutInline::VisualOverflowRect() const {
+  LayoutRect overflow_rect = LinesVisualOverflowBoundingBox();
   LayoutUnit outline_outset(StyleRef().OutlineOutsetExtent());
   if (outline_outset) {
     Vector<PhysicalRect> rects;
@@ -1385,7 +1378,7 @@
                       OutlineRectsShouldIncludeBlockVisualOverflow());
     }
     if (!rects.IsEmpty()) {
-      PhysicalRect outline_rect = UnionRectEvenIfEmpty(rects);
+      LayoutRect outline_rect = FlipForWritingMode(UnionRectEvenIfEmpty(rects));
       outline_rect.Inflate(outline_outset);
       overflow_rect.Unite(outline_rect);
     }
diff --git a/third_party/blink/renderer/core/layout/layout_inline.h b/third_party/blink/renderer/core/layout/layout_inline.h
index bae52a7..5d1c691 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.h
+++ b/third_party/blink/renderer/core/layout/layout_inline.h
@@ -147,7 +147,7 @@
   FloatRect LocalBoundingBoxRectForAccessibility() const final;
 
   PhysicalRect PhysicalLinesBoundingBox() const;
-  PhysicalRect PhysicalVisualOverflowRect() const final;
+  LayoutRect VisualOverflowRect() const final;
   PhysicalRect ReferenceBoxForClipPath() const;
 
   InlineFlowBox* CreateAndAppendInlineFlowBox();
@@ -287,13 +287,13 @@
   bool ComputeInitialShouldCreateBoxFragment() const;
   bool ComputeInitialShouldCreateBoxFragment(const ComputedStyle& style) const;
 
-  PhysicalRect CulledInlineVisualOverflowBoundingBox() const;
+  LayoutRect CulledInlineVisualOverflowBoundingBox() const;
   InlineBox* CulledInlineFirstLineBox() const;
   InlineBox* CulledInlineLastLineBox() const;
 
-  // For PhysicalVisualOverflowRect() only, to get bounding box of visual
-  // overflow of line boxes.
-  PhysicalRect LinesVisualOverflowBoundingBox() const;
+  // For visualOverflowRect() only, to get bounding box of visual overflow of
+  // line boxes.
+  LayoutRect LinesVisualOverflowBoundingBox() const;
 
   // PhysicalRectCollector should be like a function:
   // void (const PhysicalRect&).
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index 00166c6..1356d385 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -1971,13 +1971,13 @@
   return result;
 }
 
-PhysicalRect LayoutText::PhysicalVisualOverflowRect() const {
-  if (base::Optional<PhysicalRect> rect =
+LayoutRect LayoutText::VisualOverflowRect() const {
+  if (base::Optional<PhysicalRect> physical_rect =
           NGPaintFragment::LocalVisualRectFor(*this))
-    return *rect;
+    return FlipForWritingMode(*physical_rect);
 
   if (!FirstTextBox())
-    return PhysicalRect();
+    return LayoutRect();
 
   // Return the width of the minimal left side and the maximal right side.
   LayoutUnit logical_left_side = LayoutUnit::Max();
@@ -2017,11 +2017,16 @@
                   logical_height);
   if (!StyleRef().IsHorizontalWritingMode())
     rect = rect.TransposedRect();
-  return FlipForWritingMode(rect);
+  return rect;
 }
 
 PhysicalRect LayoutText::LocalVisualRectIgnoringVisibility() const {
-  return UnionRect(PhysicalVisualOverflowRect(), LocalSelectionVisualRect());
+  PhysicalRect rect;
+  if (const auto& r = NGPaintFragment::LocalVisualRectFor(*this))
+    rect = *r;
+  else
+    rect = FlipForWritingMode(VisualOverflowRect());
+  return UnionRect(rect, LocalSelectionVisualRect());
 }
 
 PhysicalRect LayoutText::LocalSelectionVisualRect() const {
diff --git a/third_party/blink/renderer/core/layout/layout_text.h b/third_party/blink/renderer/core/layout/layout_text.h
index 94dc1ea..7d2ce62 100644
--- a/third_party/blink/renderer/core/layout/layout_text.h
+++ b/third_party/blink/renderer/core/layout/layout_text.h
@@ -184,7 +184,7 @@
 
   // Returns the bounding box of visual overflow rects of all line boxes,
   // in containing block's physical coordinates with flipped blocks direction.
-  PhysicalRect PhysicalVisualOverflowRect() const;
+  LayoutRect VisualOverflowRect() const;
 
   PhysicalOffset FirstLineBoxTopLeft() const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc
index 6ef6cde..1e7fb8e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc
@@ -422,7 +422,7 @@
   const auto node_and_offset = ToNodeOffsetPair(position);
   const Node& node = node_and_offset.first;
   const unsigned offset = node_and_offset.second;
-  while (unit != units_.end() && unit->GetOwner() == node) {
+  while (unit != units_.end() && unit->AssociatedNode() == node) {
     if (unit->DOMEnd() > offset &&
         unit->GetType() != NGOffsetMappingUnitType::kCollapsed) {
       const unsigned result = std::max(offset, unit->DOMStart());
@@ -443,7 +443,7 @@
   const auto node_and_offset = ToNodeOffsetPair(position);
   const Node& node = node_and_offset.first;
   const unsigned offset = node_and_offset.second;
-  while (unit->GetOwner() == node) {
+  while (unit->AssociatedNode() == node) {
     if (unit->DOMStart() < offset &&
         unit->GetType() != NGOffsetMappingUnitType::kCollapsed) {
       const unsigned result = std::min(offset, unit->DOMEnd());
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
index 4313efe..78b74c7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
@@ -1268,6 +1268,41 @@
   TEST_RANGE(mapping.GetRanges(), text, 0u, 1u);
 }
 
+// https://crbug.com/967106
+TEST_F(NGOffsetMappingTest, StartOfNextNonCollapsedContentWithPseudo) {
+  // The white spaces are necessary for bug repro. Do not remove them.
+  SetupHtml("t", R"HTML(
+    <style>span#quote::before { content: '"'}</style>
+    <div id=t>
+      <span>foo </span>
+      <span id=quote>bar</span>
+    </div>)HTML");
+
+  const Element* quote = GetElementById("quote");
+  const Node* text = quote->previousSibling();
+  const Position position = Position::FirstPositionInNode(*text);
+
+  EXPECT_EQ(Position(),
+            GetOffsetMapping().StartOfNextNonCollapsedContent(position));
+}
+
+// https://crbug.com/967106
+TEST_F(NGOffsetMappingTest, EndOfLastNonCollapsedContentWithPseudo) {
+  // The white spaces are necessary for bug repro. Do not remove them.
+  SetupHtml("t", R"HTML(
+    <style>span#quote::after { content: '" '}</style>
+    <div id=t>
+      <span id=quote>foo</span>
+      <span>bar</span>
+    </div>)HTML");
+
+  const Element* quote = GetElementById("quote");
+  const Node* text = quote->nextSibling();
+  const Position position = Position::LastPositionInNode(*text);
+
+  EXPECT_EQ(Position(),
+            GetOffsetMapping().EndOfLastNonCollapsedContent(position));
+}
 // Test |GetOffsetMapping| which is available both for LayoutNG and for legacy.
 class NGOffsetMappingGetterTest : public RenderingTest,
                                   public testing::WithParamInterface<bool>,
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index e02e350..61c0739 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -307,7 +307,7 @@
 }
 
 void DocumentLoader::DispatchLinkHeaderPreloads(
-    ViewportDescriptionWrapper* viewport,
+    const base::Optional<ViewportDescription>& viewport,
     PreloadHelper::MediaPreloadPolicy media_policy) {
   DCHECK_GE(state_, kCommitted);
   PreloadHelper::LoadLinksFromHeader(
@@ -1202,7 +1202,7 @@
       response_.HttpHeaderField(http_names::kLink),
       response_.CurrentRequestUrl(), *GetFrame(), nullptr,
       PreloadHelper::kDoNotLoadResources, PreloadHelper::kLoadAll,
-      nullptr /* viewport_description_wrapper */,
+      base::nullopt /* viewport_description */,
       nullptr /* alternate_resource_info */);
   if (!frame_->IsMainFrame() && response_.HasMajorCertificateErrors()) {
     MixedContentChecker::HandleCertificateError(
@@ -1405,7 +1405,8 @@
 
   // Links with media values need more information (like viewport information).
   // This happens after the first chunk is parsed in HTMLDocumentParser.
-  DispatchLinkHeaderPreloads(nullptr, PreloadHelper::kOnlyLoadNonMedia);
+  DispatchLinkHeaderPreloads(base::nullopt /* viewport */,
+                             PreloadHelper::kOnlyLoadNonMedia);
 
   frame_->GetPage()->DidCommitLoad(frame_);
   GetUseCounter().DidCommitLoad(frame_);
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index bf6c92292..efaa869 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -32,6 +32,7 @@
 
 #include <memory>
 #include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
 #include "base/unguessable_token.h"
 #include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-shared.h"
 #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h"
@@ -81,7 +82,6 @@
 class SerializedScriptValue;
 class SubresourceFilter;
 class WebServiceWorkerNetworkProvider;
-struct ViewportDescriptionWrapper;
 
 namespace mojom {
 enum class CommitResult : int32_t;
@@ -217,7 +217,7 @@
 
   bool WasBlockedAfterCSP() { return was_blocked_after_csp_; }
 
-  void DispatchLinkHeaderPreloads(ViewportDescriptionWrapper*,
+  void DispatchLinkHeaderPreloads(const base::Optional<ViewportDescription>&,
                                   PreloadHelper::MediaPreloadPolicy);
 
   void SetServiceWorkerNetworkProvider(
diff --git a/third_party/blink/renderer/core/loader/link_loader.cc b/third_party/blink/renderer/core/loader/link_loader.cc
index 3969926..7132c27f 100644
--- a/third_party/blink/renderer/core/loader/link_loader.cc
+++ b/third_party/blink/renderer/core/loader/link_loader.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/loader/private/prerender_handle.h"
 #include "third_party/blink/renderer/core/loader/resource/css_style_sheet_resource.h"
 #include "third_party/blink/renderer/core/loader/subresource_integrity_helper.h"
+#include "third_party/blink/renderer/core/page/viewport_description.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
@@ -185,7 +186,7 @@
 
   Resource* resource = PreloadHelper::PreloadIfNeeded(
       params, document, NullURL(), PreloadHelper::kLinkCalledFromMarkup,
-      nullptr,
+      base::nullopt /* viewport_description */,
       client_->IsLinkCreatedByParser() ? kParserInserted : kNotParserInserted);
   if (!resource) {
     resource = PreloadHelper::PrefetchIfNeeded(params, document);
@@ -193,7 +194,8 @@
   if (resource)
     finish_observer_ = MakeGarbageCollected<FinishObserver>(this, resource);
 
-  PreloadHelper::ModulePreloadIfNeeded(params, document, nullptr, this);
+  PreloadHelper::ModulePreloadIfNeeded(
+      params, document, base::nullopt /* viewport_description */, this);
 
   if (const unsigned prerender_rel_types =
           PrerenderRelTypesFromRelAttribute(params.rel, document)) {
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index 9e4bfe4..33f5b7a 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -83,11 +83,12 @@
   return false;
 }
 
-MediaValues* CreateMediaValues(Document& document,
-                               ViewportDescription* viewport_description) {
+MediaValues* CreateMediaValues(
+    Document& document,
+    const base::Optional<ViewportDescription>& viewport_description) {
   MediaValues* media_values =
       MediaValues::CreateDynamicIfFrameExists(document.GetFrame());
-  if (viewport_description) {
+  if (viewport_description.has_value()) {
     FloatSize initial_viewport(media_values->DeviceWidth(),
                                media_values->DeviceHeight());
     PageScaleConstraints constraints = viewport_description->Resolve(
@@ -209,7 +210,7 @@
     Document& document,
     const KURL& base_url,
     LinkCaller caller,
-    ViewportDescription* viewport_description,
+    const base::Optional<ViewportDescription>& viewport_description,
     ParserDisposition parser_disposition) {
   if (!document.Loader() || !params.rel.IsLinkPreload())
     return nullptr;
@@ -303,7 +304,7 @@
 void PreloadHelper::ModulePreloadIfNeeded(
     const LinkLoadParameters& params,
     Document& document,
-    ViewportDescription* viewport_description,
+    const base::Optional<ViewportDescription>& viewport_description,
     SingleModuleClient* client) {
   if (!document.Loader() || !params.rel.IsModulePreload())
     return;
@@ -466,7 +467,7 @@
     Document* document,
     CanLoadResources can_load_resources,
     MediaPreloadPolicy media_policy,
-    ViewportDescriptionWrapper* viewport_description_wrapper,
+    const base::Optional<ViewportDescription>& viewport_description,
     std::unique_ptr<AlternateSignedExchangeResourceInfo>
         alternate_resource_info) {
   if (header_value.IsEmpty())
@@ -518,11 +519,6 @@
     }
     if (can_load_resources != kDoNotLoadResources) {
       DCHECK(document);
-      ViewportDescription* viewport_description =
-          (viewport_description_wrapper && viewport_description_wrapper->set)
-              ? &(viewport_description_wrapper->description)
-              : nullptr;
-
       PreloadIfNeeded(params, *document, base_url, kLinkCalledFromHeader,
                       viewport_description, kNotParserInserted);
       PrefetchIfNeeded(params, *document);
diff --git a/third_party/blink/renderer/core/loader/preload_helper.h b/third_party/blink/renderer/core/loader/preload_helper.h
index 7ef6395..b045e8e3 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.h
+++ b/third_party/blink/renderer/core/loader/preload_helper.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PRELOAD_HELPER_H_
 
 #include "base/optional.h"
+#include "third_party/blink/renderer/core/page/viewport_description.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
 
 namespace blink {
@@ -15,8 +16,6 @@
 class LocalFrame;
 class SingleModuleClient;
 struct LinkLoadParameters;
-struct ViewportDescription;
-struct ViewportDescriptionWrapper;
 
 // PreloadHelper is a helper class for preload, module preload, prefetch,
 // DNS prefetch, and preconnect triggered by <link> elements and "Link" HTTP
@@ -42,7 +41,7 @@
       Document*,  // can be nullptr
       CanLoadResources,
       MediaPreloadPolicy,
-      ViewportDescriptionWrapper*,
+      const base::Optional<ViewportDescription>&,
       std::unique_ptr<AlternateSignedExchangeResourceInfo>);
   static Resource* StartPreload(ResourceType,
                                 FetchParameters&,
@@ -67,11 +66,11 @@
                                    Document&,
                                    const KURL& base_url,
                                    LinkCaller,
-                                   ViewportDescription*,
+                                   const base::Optional<ViewportDescription>&,
                                    ParserDisposition);
   static void ModulePreloadIfNeeded(const LinkLoadParameters&,
                                     Document&,
-                                    ViewportDescription*,
+                                    const base::Optional<ViewportDescription>&,
                                     SingleModuleClient*);
 
   static base::Optional<ResourceType> GetResourceTypeFromAsAttribute(
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
index c08d392e..593ef5b6 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
@@ -135,7 +135,7 @@
       response.HttpHeaderField(http_names::kLink), response.CurrentRequestUrl(),
       frame, &frame_or_imported_document_->GetDocument(),
       resource_loading_policy, PreloadHelper::kLoadAll,
-      nullptr /* viewport_description_wrapper */,
+      base::nullopt /* viewport_description */,
       std::move(alternate_resource_info));
 
   if (response.HasMajorCertificateErrors()) {
diff --git a/third_party/blink/renderer/core/paint/filter_effect_builder.cc b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
index 63acec2..67b5222 100644
--- a/third_party/blink/renderer/core/paint/filter_effect_builder.cc
+++ b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
@@ -121,14 +121,17 @@
 
 }  // namespace
 
-FilterEffectBuilder::FilterEffectBuilder(const FloatRect& reference_box,
-                                         float zoom,
-                                         const PaintFlags* fill_flags,
-                                         const PaintFlags* stroke_flags)
+FilterEffectBuilder::FilterEffectBuilder(
+    const FloatRect& reference_box,
+    float zoom,
+    const PaintFlags* fill_flags,
+    const PaintFlags* stroke_flags,
+    SkBlurImageFilter::TileMode blur_tile_mode)
     : reference_box_(reference_box),
       zoom_(zoom),
       fill_flags_(fill_flags),
-      stroke_flags_(stroke_flags) {}
+      stroke_flags_(stroke_flags),
+      blur_tile_mode_(blur_tile_mode) {}
 
 FilterEffect* FilterEffectBuilder::BuildFilterEffect(
     const FilterOperations& operations,
@@ -370,7 +373,7 @@
       case FilterOperation::BLUR: {
         float pixel_radius =
             To<BlurFilterOperation>(*op).StdDeviation().GetFloatValue();
-        filters.AppendBlurFilter(pixel_radius);
+        filters.AppendBlurFilter(pixel_radius, blur_tile_mode_);
         break;
       }
       case FilterOperation::DROP_SHADOW: {
diff --git a/third_party/blink/renderer/core/paint/filter_effect_builder.h b/third_party/blink/renderer/core/paint/filter_effect_builder.h
index cd7716c..b23df97d 100644
--- a/third_party/blink/renderer/core/paint/filter_effect_builder.h
+++ b/third_party/blink/renderer/core/paint/filter_effect_builder.h
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/skia/include/effects/SkBlurImageFilter.h"
 
 namespace blink {
 
@@ -50,7 +51,9 @@
   FilterEffectBuilder(const FloatRect& reference_box,
                       float zoom,
                       const PaintFlags* fill_flags = nullptr,
-                      const PaintFlags* stroke_flags = nullptr);
+                      const PaintFlags* stroke_flags = nullptr,
+                      SkBlurImageFilter::TileMode blur_tile_mode =
+                          SkBlurImageFilter::kClampToBlack_TileMode);
 
   Filter* BuildReferenceFilter(SVGFilterElement&,
                                FilterEffect* previous_effect,
@@ -69,6 +72,7 @@
   float zoom_;
   const PaintFlags* fill_flags_;
   const PaintFlags* stroke_flags_;
+  const SkBlurImageFilter::TileMode blur_tile_mode_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 8e2333e2..ede359b 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -712,9 +712,9 @@
     needs_descendant_dependent_flags_update_ = false;
 
     if (IsSelfPaintingLayer() && needs_visual_overflow_recalc_) {
-      auto old_visual_rect = GetLayoutObject().PhysicalVisualOverflowRect();
+      LayoutRect old_visual_rect = GetLayoutObject().VisualOverflowRect();
       GetLayoutObject().RecalcVisualOverflow();
-      if (old_visual_rect != GetLayoutObject().PhysicalVisualOverflowRect()) {
+      if (old_visual_rect != GetLayoutObject().VisualOverflowRect()) {
         SetNeedsCompositingInputsUpdateInternal();
         MarkAncestorChainForFlagsUpdate(DoesNotNeedDescendantDependentUpdate);
       }
@@ -2566,11 +2566,19 @@
 }
 
 PhysicalRect PaintLayer::LocalBoundingBox() const {
-  PhysicalRect rect = GetLayoutObject().PhysicalVisualOverflowRect();
+  PhysicalRect rect;
+  if (GetLayoutObject().IsBox()) {
+    rect = ToLayoutBox(GetLayoutObject()).PhysicalVisualOverflowRect();
+  } else {
+    LayoutRect layout_rect = GetLayoutObject().VisualOverflowRect();
+    rect = GetLayoutObject().FlipForWritingMode(layout_rect);
+  }
+
   if (GetLayoutObject().IsEffectiveRootScroller() || IsRootLayer()) {
     rect.Unite(
         PhysicalRect(rect.offset, GetLayoutObject().View()->ViewRect().size));
   }
+
   return rect;
 }
 
@@ -3310,7 +3318,10 @@
   }
   float zoom = style.EffectiveZoom();
   FloatRect reference_box = BackdropFilterReferenceBox();
-  return_value = FilterEffectBuilder(reference_box, zoom)
+  // Use kClamp tile mode to avoid pixel moving filters bringing in black
+  // transparent pixels from the viewport edge.
+  return_value = FilterEffectBuilder(reference_box, zoom, nullptr, nullptr,
+                                     SkBlurImageFilter::kClamp_TileMode)
                      .BuildFilterOperations(style.BackdropFilter());
   DCHECK(!return_value.IsEmpty());
   return return_value;
diff --git a/third_party/blink/renderer/devtools/PRESUBMIT.py b/third_party/blink/renderer/devtools/PRESUBMIT.py
index 68de82a5..f19b2666 100644
--- a/third_party/blink/renderer/devtools/PRESUBMIT.py
+++ b/third_party/blink/renderer/devtools/PRESUBMIT.py
@@ -94,16 +94,33 @@
     ]
 
 
-def _CheckDevtoolsLocalization(input_api, output_api):  # pylint: disable=invalid-name
+def _CheckDevtoolsWithNodeScript(input_api, output_api, script_path, script_arguments=None):  # pylint: disable=invalid-name
     affected_front_end_files = _getAffectedFrontEndFiles(input_api)
     if len(affected_front_end_files) == 0:
         return []
     else:
-        affected_front_end_files = [
-            input_api.os_path.join(input_api.PresubmitLocalPath(), file_path) for file_path in affected_front_end_files
-        ]
-        script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), "scripts", "check_localizability.js")
-        return _checkWithNodeScript(input_api, output_api, script_path, affected_front_end_files)
+        if script_arguments is None:
+            script_arguments = []
+        return _checkWithNodeScript(input_api, output_api, script_path, script_arguments)
+
+
+def _CheckDevtoolsLocalizableResources(input_api, output_api):  # pylint: disable=invalid-name
+    affected_front_end_files = _getAffectedFrontEndFiles(input_api)
+    if len(affected_front_end_files) == 0:
+        return []
+    script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), "scripts", "check_localizable_resources.js")
+    args = ['--autofix']
+    return _CheckDevtoolsWithNodeScript(input_api, output_api, script_path, args)
+
+
+def _CheckDevtoolsLocalization(input_api, output_api):  # pylint: disable=invalid-name
+    affected_front_end_files = [
+        input_api.os_path.join(input_api.PresubmitLocalPath(), file_path) for file_path in _getAffectedFrontEndFiles(input_api)
+    ]
+    if len(affected_front_end_files) == 0:
+        return []
+    script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), "scripts", "check_localizability.js")
+    return _checkWithNodeScript(input_api, output_api, script_path, affected_front_end_files)
 
 
 def _CheckDevtoolsStyle(input_api, output_api):
@@ -123,9 +140,9 @@
 
 def _CompileDevtoolsFrontend(input_api, output_api):
     compile_path = input_api.os_path.join(input_api.PresubmitLocalPath(), "scripts", "compile_frontend.py")
-    out, _ = input_api.subprocess.Popen(
-        [input_api.python_executable, compile_path], stdout=input_api.subprocess.PIPE,
-        stderr=input_api.subprocess.STDOUT).communicate()
+    out, _ = input_api.subprocess.Popen([input_api.python_executable, compile_path],
+                                        stdout=input_api.subprocess.PIPE,
+                                        stderr=input_api.subprocess.STDOUT).communicate()
     if "ERROR" in out or "WARNING" in out:
         return [output_api.PresubmitError(out)]
     if "NOTE" in out:
@@ -202,6 +219,7 @@
     results = []
     results.extend(_CheckBuildGN(input_api, output_api))
     results.extend(_CheckFormat(input_api, output_api))
+    results.extend(_CheckDevtoolsLocalizableResources(input_api, output_api))
     results.extend(_CheckDevtoolsLocalization(input_api, output_api))
     results.extend(_CheckDevtoolsStyle(input_api, output_api))
     results.extend(_CompileDevtoolsFrontend(input_api, output_api))
@@ -237,7 +255,7 @@
     return [input_api.os_path.relpath(file_name, devtools_root) for file_name in affected_js_files]
 
 
-def _checkWithNodeScript(input_api, output_api, script_path, files=None):  # pylint: disable=invalid-name
+def _checkWithNodeScript(input_api, output_api, script_path, script_arguments=None):  # pylint: disable=invalid-name
     original_sys_path = sys.path
     try:
         sys.path = sys.path + [input_api.os_path.join(input_api.PresubmitLocalPath(), "scripts")]
@@ -247,11 +265,11 @@
 
     node_path = local_node.node_path()
 
-    if files is None:
-        files = []
+    if script_arguments is None:
+        script_arguments = []
 
     process = input_api.subprocess.Popen(
-        [node_path, script_path] + files, stdout=input_api.subprocess.PIPE, stderr=input_api.subprocess.STDOUT)
+        [node_path, script_path] + script_arguments, stdout=input_api.subprocess.PIPE, stderr=input_api.subprocess.STDOUT)
     out, _ = process.communicate()
 
     if process.returncode != 0:
diff --git a/third_party/blink/renderer/devtools/front_end/accessibility/accessibility_strings.grdp b/third_party/blink/renderer/devtools/front_end/accessibility/accessibility_strings.grdp
new file mode 100644
index 0000000..4023c8c
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/accessibility/accessibility_strings.grdp
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_185551542d4a950d6ed4a90e0875dfde" desc="">
+    Computed Properties
+  </message>
+  <message name="IDS_DEVTOOLS_191abc41f8f74fb8c9671ca8f21b7433" desc="">
+    Element not interesting for accessibility.
+  </message>
+  <message name="IDS_DEVTOOLS_1c2be4cda7b85620e6dca5fb505feedc" desc="">
+    Element has empty alt text.
+  </message>
+  <message name="IDS_DEVTOOLS_3b1afa99cd297c96939424e1438e12f2" desc="">
+    Element inherits presentational role from 
+  </message>
+  <message name="IDS_DEVTOOLS_4ada42850cc2d4e41f3da5254ec2feee" desc="">
+    Element is inert.
+  </message>
+  <message name="IDS_DEVTOOLS_52886045a6932b0bbb56620fe5c584bf" desc="">
+    Ancestor&apos;s children are all presentational: 
+  </message>
+  <message name="IDS_DEVTOOLS_5a51e4de1a235f3f67e816cb561d3d5a" desc="">
+    Element is <ph name="ARIAHIDDENSPAN">$1s</ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_5cd2b4477fa05212ce32e073702b6938" desc="">
+    No ARIA attributes
+  </message>
+  <message name="IDS_DEVTOOLS_5d910721a6256ce42c8c6308dc60ff40" desc="">
+    Static text node is used as name for 
+  </message>
+  <message name="IDS_DEVTOOLS_5f05a00053834655de812d3447545a17" desc="">
+    No node with this ID.
+  </message>
+  <message name="IDS_DEVTOOLS_7d615068dd4fb142c11a29553b9b83bf" desc="">
+    Element is presentational.
+  </message>
+  <message name="IDS_DEVTOOLS_80c9f4e5b244cf617075b8ce23a3ed27" desc="">
+    ARIA Attributes
+  </message>
+  <message name="IDS_DEVTOOLS_939aaa05e0943b39eab3a34fd609384d" desc="">
+    Scroll into view
+  </message>
+  <message name="IDS_DEVTOOLS_970e14ef4e4d726d36b6ed0ea92bef83" desc="">
+    Accessibility node not exposed
+  </message>
+  <message name="IDS_DEVTOOLS_98b6f72f9b4c1883deddc61bc62d71eb" desc="">
+    Element is not visible.
+  </message>
+  <message name="IDS_DEVTOOLS_a4853b77c462337ed9fecc32e4d44d7b" desc="">
+    Accessibility Tree
+  </message>
+  <message name="IDS_DEVTOOLS_a858dfbc32e2169e89668adf8e997f6f" desc="">
+    No accessibility node
+  </message>
+  <message name="IDS_DEVTOOLS_aff2b966e54c2212e86343d2bb8d3f88" desc="">
+    Part of label element: 
+  </message>
+  <message name="IDS_DEVTOOLS_b05cf8cea038a89494e9e232e65af3f3" desc="">
+    Element is not rendered.
+  </message>
+  <message name="IDS_DEVTOOLS_cd9da90ce06215f24f648786501e4073" desc="">
+    Element is hidden by active modal dialog: 
+  </message>
+  <message name="IDS_DEVTOOLS_cfa5234b2737df4bc3dc737484605c39" desc="">
+    <ph name="ARIAHIDDENSPAN">$1s</ph> is <ph name="TRUESPAN">$2s</ph> on ancestor: 
+  </message>
+  <message name="IDS_DEVTOOLS_d7dff47e75bfd5f107407eded09ee6f0" desc="">
+    Element has <ph name="ROLEPRESENTATIONSPAN">$1s</ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_d96143ba1b15645919cea00ec9d1be62" desc="">
+    Ignored
+  </message>
+  <message name="IDS_DEVTOOLS_dd3195ced9c1810388e28ca2b4eb921f" desc="">
+    Not specified
+  </message>
+  <message name="IDS_DEVTOOLS_e0dcc9aef6db5e61e4102efdf5d54378" desc="">
+    Invalid source.
+  </message>
+  <message name="IDS_DEVTOOLS_e0e4fc6213e8b3593495a7260c3a4c2e" desc="">
+    Accessibility
+  </message>
+  <message name="IDS_DEVTOOLS_e71c63b8e5a312662941ee8606e8c43a" desc="">
+    Label for 
+  </message>
+  <message name="IDS_DEVTOOLS_e7be8f80bd8db0b8ca6bb2dee175b623" desc="">
+    Element is in an inert subtree from 
+  </message>
+  <message name="IDS_DEVTOOLS_fc6dc044799975f19c864e9b31e3bb76" desc="">
+    No text content.
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/animation/animation_strings.grdp b/third_party/blink/renderer/devtools/front_end/animation/animation_strings.grdp
new file mode 100644
index 0000000..656e1fb
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/animation/animation_strings.grdp
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_043d2f0a7edca94cae1cfbf3f90b3b68" desc="">
+    Animations
+  </message>
+  <message name="IDS_DEVTOOLS_105b296a83f9c105355403f3332af50f" desc="">
+    Pause
+  </message>
+  <message name="IDS_DEVTOOLS_31fb6ba50d7a98f1650f282ecc45af77" desc="">
+    Set speed to <ph name="BUTTON_TEXTCONTENT">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_4008ed4e30be53588254064e9bca502e" desc="">
+    Pause timeline
+  </message>
+  <message name="IDS_DEVTOOLS_5fe41afb268c87f4f56027023d3e6406" desc="">
+    <ph name="PLAYBACKRATE______">$1s</ph>%
+  </message>
+  <message name="IDS_DEVTOOLS_81aa2078d1eceede831b2976dbf32e62" desc="">
+    Clear all
+  </message>
+  <message name="IDS_DEVTOOLS_96952fa958a61d47ed4dee3f2c3dee64" desc="">
+    Pause all
+  </message>
+  <message name="IDS_DEVTOOLS_96b5ffa6e4c4b57e832f8e26229ed786" desc="">
+    Play timeline
+  </message>
+  <message name="IDS_DEVTOOLS_9ea575b416c0fb4d80772ab08ce039f8" desc="">
+    Select an effect above to inspect and modify.
+  </message>
+  <message name="IDS_DEVTOOLS_ba5ef3fb1e406e7707dc5f449adaa176" desc="">
+    Listening for animations...
+  </message>
+  <message name="IDS_DEVTOOLS_bdf578b505f481a1dbb4555c16dd9a51" desc="">
+    Replay timeline
+  </message>
+  <message name="IDS_DEVTOOLS_bffa3e523792264bd31830c746b6d388" desc="">
+    Resume all
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/audits2_strings.grdp b/third_party/blink/renderer/devtools/front_end/audits2/audits2_strings.grdp
new file mode 100644
index 0000000..8202dda
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/audits2/audits2_strings.grdp
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_026d213afe4c17e37ac8306b77e9f870" desc="">
+    Auditing your web page
+  </message>
+  <message name="IDS_DEVTOOLS_0503ffa75a95f35877fe7420d061b6a9" desc="">
+    Progressive Web App
+  </message>
+  <message name="IDS_DEVTOOLS_070716fc9cd3859f51f3860a349bad2f" desc="">
+    Simulated Slow 4G, 4x CPU Slowdown
+  </message>
+  <message name="IDS_DEVTOOLS_0f39dfdd740bf99080ca83e56e2cfb4c" desc="">
+    💡 <ph name="THIS__FASTFACTSQUEUED_FASTFACTINDEX_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_1421c82f3616812789ac346b1638e39d" desc="">
+    Run audits
+  </message>
+  <message name="IDS_DEVTOOLS_1583d36995134f9120fac115c08e0174" desc="">
+    Loading…
+  </message>
+  <message name="IDS_DEVTOOLS_15a895a381c23eff5397d8b1f318a726" desc="">
+    No network or CPU throttling used. (Useful when not evaluating performance)
+  </message>
+  <message name="IDS_DEVTOOLS_161bf73e31de6b8e3d856815473cbe1c" desc="">
+    View Trace
+  </message>
+  <message name="IDS_DEVTOOLS_17de62900ceac9101c8814b4c4874328" desc="">
+    Apply mobile emulation during auditing
+  </message>
+  <message name="IDS_DEVTOOLS_193c4edb3ab576449959f6710e017a49" desc="">
+    Typical DevTools throttling, with actual traffic shaping and CPU slowdown applied
+  </message>
+  <message name="IDS_DEVTOOLS_1c481aa99d081c32182011a758f73d33" desc="">
+    <ph name="MATCH_MESSAGE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2310408a63388fe57e3a4177168a8798" desc="">
+    Desktop
+  </message>
+  <message name="IDS_DEVTOOLS_2a96c07a6c11ca7add2b1ffde86efe25" desc="">
+    Can only audit HTTP/HTTPS pages and Chrome extensions. Navigate to a different page to start an audit.
+  </message>
+  <message name="IDS_DEVTOOLS_39680b3025b1f8d881f9d983623bf071" desc="">
+    Lighthouse is loading your page
+  </message>
+  <message name="IDS_DEVTOOLS_3b7e043f46813f1d8ffbd462eb77fb6c" desc="">
+    How long does this app take to show content and become usable
+  </message>
+  <message name="IDS_DEVTOOLS_3e3f8a3abd60f2389b744ca8ed0aa4d9" desc="">
+    No throttling
+  </message>
+  <message name="IDS_DEVTOOLS_3ee3aab0791156ff1d5e0481ed4589b2" desc="">
+    Applied Slow 4G, 4x CPU Slowdown
+  </message>
+  <message name="IDS_DEVTOOLS_41fdb410354e76ef7674f98e1cc84c8f" desc="">
+    Throttling
+  </message>
+  <message name="IDS_DEVTOOLS_48d24d11c7c71282366fe2007d4cee3b" desc="">
+    Throttling is simulated, resulting in faster audit runs with similar measurement accuracy
+  </message>
+  <message name="IDS_DEVTOOLS_4ab5093468559edd5940ac59f1c6ac02" desc="">
+    Try to navigate to the URL in a fresh Chrome profile without any other tabs or extensions open and try again.
+  </message>
+  <message name="IDS_DEVTOOLS_55f562701dc2cc775f9adc0478644b2a" desc="">
+    Perform an audit…
+  </message>
+  <message name="IDS_DEVTOOLS_56395991012586c2067aa7bcb5905b50" desc="">
+    Cancelling
+  </message>
+  <message name="IDS_DEVTOOLS_64a64ca70e7a909a5a96c07082070bfc" desc="">
+    Ah, sorry! We ran into an error.
+  </message>
+  <message name="IDS_DEVTOOLS_64d266bad80369458003a71a9e04dd50" desc="">
+    Cancelling…
+  </message>
+  <message name="IDS_DEVTOOLS_6556b7f666de143f5a9826e048d5dcd8" desc="">
+    Is this page usable by people with disabilities or impairments
+  </message>
+  <message name="IDS_DEVTOOLS_6565973a762b83f476e9a76a0015b539" desc="">
+    At least one category must be selected.
+  </message>
+  <message name="IDS_DEVTOOLS_671b3beee69d9180412202b6528ec8f7" desc="">
+    Download report
+  </message>
+  <message name="IDS_DEVTOOLS_67aa731132e0e7bbabb1b3d3c363e7d6" desc="">
+    Multiple tabs are being controlled by the same service worker. Close your other tabs on the same origin to audit this page.
+  </message>
+  <message name="IDS_DEVTOOLS_72c4a2e545d220fd8c9ead876a59a89d" desc="">
+    Is this page optimized for search engine results ranking
+  </message>
+  <message name="IDS_DEVTOOLS_7bd1e4f7363173e2c8329551ca4d38cc" desc="">
+    (new audit)
+  </message>
+  <message name="IDS_DEVTOOLS_87d17f4624a514e81dc7c8e016a7405c" desc="">
+    Mobile
+  </message>
+  <message name="IDS_DEVTOOLS_88450f419690173d62fb0a30fb29a951" desc="">
+    Auditing <ph name="PAGEHOST">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_8eb7115e5b4ba55f56931ec24c789f9e" desc="">
+    Lighthouse is warming up…
+  </message>
+  <message name="IDS_DEVTOOLS_8fe967af0fb77d0261a6ead91bcd607f" desc="">
+    Reset storage (localStorage, IndexedDB, etc) before auditing. (Good for performance &amp; PWA testing)
+  </message>
+  <message name="IDS_DEVTOOLS_9446a98ad14416153cc4d45ab8b531bf" desc="">
+    Performance
+  </message>
+  <message name="IDS_DEVTOOLS_b06c94565d768dd75535120f2f8482a7" desc="">
+    Audits
+  </message>
+  <message name="IDS_DEVTOOLS_b1b01acb63f79615b05f6d816705d7d3" desc="">
+    Does this page meet the standard of a Progressive Web App
+  </message>
+  <message name="IDS_DEVTOOLS_bc28aa52ba5b534ad9d198157c375d67" desc="">
+    Clear storage
+  </message>
+  <message name="IDS_DEVTOOLS_bcf2457dd80557dd7ebdc1504b6149e6" desc="">
+    Does this page follow best practices for modern web development
+  </message>
+  <message name="IDS_DEVTOOLS_c2ffe02d76c4f089648f1647b43e4ee5" desc="">
+    Best practices
+  </message>
+  <message name="IDS_DEVTOOLS_c3669fa42d4becdd2831b9685cf607bb" desc="">
+    Identify and fix common problems that affect your site&apos;s performance,
+                accessibility, and user experience.
+  </message>
+  <message name="IDS_DEVTOOLS_c91c7b93c28cd18741b71f727ee81ee3" desc="">
+    Reports
+  </message>
+  <message name="IDS_DEVTOOLS_d59048f21fd887ad520398ce677be586" desc="">
+    Learn more
+  </message>
+  <message name="IDS_DEVTOOLS_d88946b678e4c2f251d4e292e8142291" desc="">
+    SEO
+  </message>
+  <message name="IDS_DEVTOOLS_e0ac20adce6ffee48c7151b070aa5737" desc="">
+    Device
+  </message>
+  <message name="IDS_DEVTOOLS_e499f5109f685235e6280179fe22beec" desc="">
+    Drop audit file here
+  </message>
+  <message name="IDS_DEVTOOLS_ea4788705e6873b424c65e91c2846b19" desc="">
+    Cancel
+  </message>
+  <message name="IDS_DEVTOOLS_ea60ba1496b05c6a5816fb75d0665c8d" desc="">
+    If this issue is reproducible, please report it at the Lighthouse GitHub repo.
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/bindings/bindings_strings.grdp b/third_party/blink/renderer/devtools/front_end/bindings/bindings_strings.grdp
new file mode 100644
index 0000000..dd879ed0
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/bindings/bindings_strings.grdp
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_412dac8227bbaf752fecaad8826f20e6" desc="">
+    LiveEdit failed: <ph name="ERROR">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_d3ff22990e37ccce8a501ffba38626b7" desc="">
+    LiveEdit compile failed: <ph name="EXCEPTIONDETAILS_TEXT">$1s</ph>
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/browser_debugger/browser_debugger_strings.grdp b/third_party/blink/renderer/devtools/front_end/browser_debugger/browser_debugger_strings.grdp
new file mode 100644
index 0000000..1d7528f48
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/browser_debugger/browser_debugger_strings.grdp
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_0ca08f8f4a8a19f7f1ae87c119494327" desc="">
+    Overrides
+  </message>
+  <message name="IDS_DEVTOOLS_1106d01848580ffc52dd36e2d60316d9" desc="">
+    XHR/fetch Breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_193cfc9be3b995831c6af2fea6650e60" desc="">
+    Page
+  </message>
+  <message name="IDS_DEVTOOLS_2daf59f0ff992924e42c14c1e674bd6a" desc="">
+    Attribute modified
+  </message>
+  <message name="IDS_DEVTOOLS_30d67fd6c32c9a9ff5ecb4472b44ed54" desc="">
+    Break when URL contains:
+  </message>
+  <message name="IDS_DEVTOOLS_340641a167ad6da1bcb4016357f4695d" desc="">
+    Content scripts
+  </message>
+  <message name="IDS_DEVTOOLS_39c145d69ad05e44e74017346c116251" desc="">
+    Remove all DOM breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_47bda3033cb8fe0e76c45c293db011b5" desc="">
+    No breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_59eaf6955f44a94237b6d26911c1d983" desc="">
+    Break on
+  </message>
+  <message name="IDS_DEVTOOLS_626585724f35f9d9fce0bd36525cb7de" desc="">
+    DOM Breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_63a6a88c066880c5ac42394a22803ca6" desc="">
+    Refresh
+  </message>
+  <message name="IDS_DEVTOOLS_66b74432bdc2797086f419010cc5ff86" desc="">
+    DOM Breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_6ba3a9ac3e0d57683c56299484b7ee3b" desc="">
+    Remove all breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_b4e79b551a1ceae24b7de243ab1fd27c" desc="">
+    Add breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_b839f802a330e4d4145cb182e6767f45" desc="">
+    Any XHR or fetch
+  </message>
+  <message name="IDS_DEVTOOLS_c22470110316d6334bed53ae61a08f59" desc="">
+    URL contains &quot;<ph name="URL">$1s</ph>&quot;
+  </message>
+  <message name="IDS_DEVTOOLS_c4bd9c57c6b315e3f7374cf77143ca58" desc="">
+    Subtree modified
+  </message>
+  <message name="IDS_DEVTOOLS_d5bfaaca8e28f8b9b9211038f86a4494" desc="">
+    Remove breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_e30c4292775f68b2bc9eb3957a69f899" desc="">
+    Global Listeners
+  </message>
+  <message name="IDS_DEVTOOLS_ebdb33cde9015fa4b294220ee89cff00" desc="">
+    Event Listener Breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_f80938ab8ce27f3427af09c97a718fff" desc="">
+    Node removed
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/browser_sdk/browser_sdk_strings.grdp b/third_party/blink/renderer/devtools/front_end/browser_sdk/browser_sdk_strings.grdp
new file mode 100644
index 0000000..27e6fc6
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/browser_sdk/browser_sdk_strings.grdp
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_07c7413a65777a3d70e32ea32dafa509" desc="">
+    Do not preserve log on page reload / navigation
+  </message>
+  <message name="IDS_DEVTOOLS_6aed54027cf3a260de904a43cf439cc9" desc="">
+    Preserve log on page reload / navigation
+  </message>
+  <message name="IDS_DEVTOOLS_edd24cce7afedea5a1b2f90675880687" desc="">
+    Preserve log
+  </message>
+  <message name="IDS_DEVTOOLS_eec89088ee408b80387155272b113256" desc="">
+    Network
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/changes/changes_strings.grdp b/third_party/blink/renderer/devtools/front_end/changes/changes_strings.grdp
new file mode 100644
index 0000000..2da2df97
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/changes/changes_strings.grdp
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_025502db49a6cf61a8245998129bd973" desc="">
+    <ph name="DELETIONS">$1s</ph> deletions (-)
+  </message>
+  <message name="IDS_DEVTOOLS_5c2892cb638ec999fe22612587cd4a00" desc="">
+    No changes
+  </message>
+  <message name="IDS_DEVTOOLS_70c85e43e99bc0cfcbb656123306eb19" desc="">
+    <ph name="INSERTIONS">$1s</ph> insertion (+),
+  </message>
+  <message name="IDS_DEVTOOLS_af2c230293677261fe33ba6d3fbf02e3" desc="">
+    <ph name="INSERTIONS">$1s</ph> insertions (+),
+  </message>
+  <message name="IDS_DEVTOOLS_c112bb3542e98308d12d5ecb10a67abc" desc="">
+    Changes
+  </message>
+  <message name="IDS_DEVTOOLS_c73e4e53c15d9971d9293fcff6c0d8c0" desc="">
+    ( … Skipping <ph name="LINES_LENGTH___PADDINGLINES____">$1d</ph> matching lines … )
+  </message>
+  <message name="IDS_DEVTOOLS_ca012662d53a8bde28b4d82722aaff7e" desc="">
+    Changes drawer
+  </message>
+  <message name="IDS_DEVTOOLS_df5f7764a0991b372d30c55eb57f6a55" desc="">
+    Revert all changes
+  </message>
+  <message name="IDS_DEVTOOLS_e0e5ea9203300d2eb8ced39dd88c214e" desc="">
+    <ph name="DELETIONS">$1s</ph> deletion (-)
+  </message>
+  <message name="IDS_DEVTOOLS_f550ec70278cc72604795d91ff8dcd30" desc="">
+    Binary data
+  </message>
+  <message name="IDS_DEVTOOLS_fa6711f918fe2018131a4ad0380b9e56" desc="">
+    <ph name="THIS_UISOURCECODE_DISPLAYNAME__">$1s</ph> (from source map)
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/color_picker/color_picker_strings.grdp b/third_party/blink/renderer/devtools/front_end/color_picker/color_picker_strings.grdp
new file mode 100644
index 0000000..2ddfef3
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/color_picker/color_picker_strings.grdp
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_146ffe2fd9fa5bec3b63b52543793ec7" desc="">
+    Show more
+  </message>
+  <message name="IDS_DEVTOOLS_158eabad9d23e8a99bbcfd6f411c575f" desc="">
+    Remove color
+  </message>
+  <message name="IDS_DEVTOOLS_1690e1c6a6b4c615cc36ab66570996b4" desc="">
+    Toggle color picker
+  </message>
+  <message name="IDS_DEVTOOLS_19dcdfb6d075db15609e75214f67189f" desc="">
+    Remove all to the right
+  </message>
+  <message name="IDS_DEVTOOLS_2e8882b1e1deaa1bb50cbdfe188755f6" desc="">
+    Copy color to clipboard
+  </message>
+  <message name="IDS_DEVTOOLS_38d7dc102a918c3b0031c9fde807308a" desc="">
+    Pick background color
+  </message>
+  <message name="IDS_DEVTOOLS_39186fc9ea0f3ccf697b31a288e0723f" desc="">
+    Toggle background color picker
+  </message>
+  <message name="IDS_DEVTOOLS_3b98e2dffc6cb06a89dcb0d5c60a0206" desc="">
+    AA
+  </message>
+  <message name="IDS_DEVTOOLS_74421a69b1bc40c166be4de5caf0b036" desc="">
+    Contrast ratio
+  </message>
+  <message name="IDS_DEVTOOLS_b201b2e7e7a20df48b625f20c2f0933e" desc="">
+    Color Palettes
+  </message>
+  <message name="IDS_DEVTOOLS_b74447172f9edfe492a21feee374c1fd" desc="">
+    Clear palette
+  </message>
+  <message name="IDS_DEVTOOLS_bbe0cb0a04956e97e8fc70f519d10e0d" desc="">
+    <ph name="PALETTE_COLORS_I_">$1s</ph>. Long-click to show alternate shades.
+  </message>
+  <message name="IDS_DEVTOOLS_c74ea6dbff701bfa23819583c52ebd97" desc="">
+    Show less
+  </message>
+  <message name="IDS_DEVTOOLS_ce82e07fa1f0521f5cee30e368fe2e73" desc="">
+    : <ph name="AA_TOFIXED___">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_d1e2107b34fa404fabd54bcce4fd858f" desc="">
+    Add to palette
+  </message>
+  <message name="IDS_DEVTOOLS_e1faffb3e614e6c2fba74296962386b7" desc="">
+    AAA
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/components/components_strings.grdp b/third_party/blink/renderer/devtools/front_end/components/components_strings.grdp
new file mode 100644
index 0000000..0ad7f6d2
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/components/components_strings.grdp
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_013cf261f9ccf5d4f027bc001d0d1d0c" desc="">
+    (unknown)
+  </message>
+  <message name="IDS_DEVTOOLS_0b484cfcf50c8fabff48c0aad426f544" desc="">
+    Dock to left
+  </message>
+  <message name="IDS_DEVTOOLS_131c97f8ba5bc0277386d660a18f13b6" desc="">
+    Reveal in <ph name="DESTINATION">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_393c3f5ea8ad35c02691d507bdbb31b0" desc="">
+    Reveal
+  </message>
+  <message name="IDS_DEVTOOLS_7f52f5f786eb8d9bf9e56ce89fb3db3a" desc="">
+    Show 1 more frame
+  </message>
+  <message name="IDS_DEVTOOLS_93898e12951c906e5286ccb36d980ce5" desc="">
+    Blackbox script
+  </message>
+  <message name="IDS_DEVTOOLS_9df22f196a33acd0b372fe502de51211" desc="">
+    auto
+  </message>
+  <message name="IDS_DEVTOOLS_a498f0751d1221223efb0defc71cc804" desc="">
+    <ph name="RENDEREDWIDTH">$1s</ph> × <ph name="RENDEREDHEIGHT">$2s</ph> pixels
+  </message>
+  <message name="IDS_DEVTOOLS_b836d99a391a4d2baf6624f8f4acbfc0" desc="">
+     (intrinsic: <ph name="INTRINSICWIDTH">$1s</ph> × <ph name="INTRINSICHEIGHT">$2s</ph> pixels)
+  </message>
+  <message name="IDS_DEVTOOLS_b9dee6bade160c89fb7f0e539d453513" desc="">
+    Dock to bottom
+  </message>
+  <message name="IDS_DEVTOOLS_c3fe109bf99ade2d76a55e5737015e3e" desc="">
+    Stop blackboxing
+  </message>
+  <message name="IDS_DEVTOOLS_c850ae12703fa2b00eeaf8445ecf00ef" desc="">
+    Show <ph name="TOTALHIDDENCALLFRAMESCOUNT">$1s</ph> more frames
+  </message>
+  <message name="IDS_DEVTOOLS_d3d2e617335f08df83599665eef8a418" desc="">
+    Close
+  </message>
+  <message name="IDS_DEVTOOLS_e48081c5f0698efee665fbf8e9d123f9" desc="">
+    Open using <ph name="TITLE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_e6cd0f8a4fe4fb9e08505b784e48caa2" desc="">
+    Link handling:
+  </message>
+  <message name="IDS_DEVTOOLS_f53944c3a55bdb5ad65c6226e358a626" desc="">
+    Undock into separate window
+  </message>
+  <message name="IDS_DEVTOOLS_fe7fb037b290768d6a6be30b237e183d" desc="">
+    Dock to right
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
new file mode 100644
index 0000000..7d58b2c
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_02969932cf572213e2401893f4182af2" desc="">
+    Remove all expressions
+  </message>
+  <message name="IDS_DEVTOOLS_03342349032c8ef8f5898c49a088de55" desc="">
+    Accept suggestion
+  </message>
+  <message name="IDS_DEVTOOLS_047e62ee63b0c14a249a79bc6be0a493" desc="">
+    Do not group similar messages in console
+  </message>
+  <message name="IDS_DEVTOOLS_04c247c2ba261c7511e4f479728bd7ea" desc="">
+    Do not treat evaluation as user activation
+  </message>
+  <message name="IDS_DEVTOOLS_0511551e6cf7d61acd9b62f9304efa93" desc="">
+    Do not clear log on page reload / navigation
+  </message>
+  <message name="IDS_DEVTOOLS_07213a0161f52846ab198be103b5ab43" desc="">
+    errors
+  </message>
+  <message name="IDS_DEVTOOLS_0a2f561bf52ee6d8cd2dda123c8f74fe" desc="">
+    Default levels
+  </message>
+  <message name="IDS_DEVTOOLS_0b03bf8af8380330766ad55ff2004a95" desc="">
+    Selected context only
+  </message>
+  <message name="IDS_DEVTOOLS_0bafe921e13474bf5fc3f1ad62496905" desc="">
+    [Intervention] <ph name="MESSAGETEXT">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_0bbce02e304562c295a1d57d66c296d3" desc="">
+    &lt;URL&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_0eaadb4fcb48a0a0ed7bc9868be9fbaa" desc="">
+    Warning
+  </message>
+  <message name="IDS_DEVTOOLS_0fa06588bd5c83bd6f3b1734eefc6b16" desc="">
+    Hide messages from <ph name="NEW_COMMON_PARSEDURL_CONSOLEMESSAGE_URL__DISPLAYNAME">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_142329f312b9f9a97f9726342813dec2" desc="">
+    Next/previous line
+  </message>
+  <message name="IDS_DEVTOOLS_1a37d25ba6689d174f51ba8e2425fde6" desc="">
+    &lt;other&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_1ebc7540a5327b857144f04cf1435b4e" desc="">
+    Replay XHR
+  </message>
+  <message name="IDS_DEVTOOLS_26649c8f3cadc9c0170f2443e6fc0252" desc="">
+    &lt;attribute&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_278cf66841e1d342f3115006e82c5dd9" desc="">
+    Repeat <ph name="THIS__REPEATCOUNT">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2a5a5a712c2bfb6c506cb669b8d81709" desc="">
+    Clear console history
+  </message>
+  <message name="IDS_DEVTOOLS_2b76cfdf5b790de0a27ba7fc58a919c8" desc="">
+    Warning <ph name="ACCESSIBLENAME">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2b918231caba2d08f2223246ac06222c" desc="">
+    Copy visible styled selection
+  </message>
+  <message name="IDS_DEVTOOLS_2c7aea4237e25b4f8ee3b0bf77d6fed0" desc="">
+    verbose
+  </message>
+  <message name="IDS_DEVTOOLS_2d82fe5a069854a35204b4e64e8e08ae" desc="">
+    Console settings
+  </message>
+  <message name="IDS_DEVTOOLS_2f20693f226f0545a8b68ef4f59fb02e" desc="">
+    [Violation] <ph name="MESSAGETEXT">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_375855385115b292f160b5131495003d" desc="">
+    console.clear() was prevented due to &apos;Preserve log&apos;
+  </message>
+  <message name="IDS_DEVTOOLS_383b1deb90603a79d86f3ae82e55a9e2" desc="">
+    <ph name="THIS__HIDDENBYFILTERCOUNT">$1s</ph> hidden
+  </message>
+  <message name="IDS_DEVTOOLS_3879149292f9af4469cec013785d6dfd" desc="">
+    warnings
+  </message>
+  <message name="IDS_DEVTOOLS_388024c56c38c3d1c635b09a2b28b8ac" desc="">
+    Value below was evaluated just now.
+  </message>
+  <message name="IDS_DEVTOOLS_3cec12c2368b11d9585823ac9d631edb" desc="">
+    Hide all
+  </message>
+  <message name="IDS_DEVTOOLS_4059b0251f66a18cb56f544728796875" desc="">
+    Info
+  </message>
+  <message name="IDS_DEVTOOLS_405b66a12f196edc715fe9f3f2c84b04" desc="">
+    Hide timestamps
+  </message>
+  <message name="IDS_DEVTOOLS_411f3a865d83fba7f4772925666cfa7c" desc="">
+    Hide network messages
+  </message>
+  <message name="IDS_DEVTOOLS_468312e6ff2ebf1b104e5d7f489de74d" desc="">
+    Eagerly evaluate text in the prompt
+  </message>
+  <message name="IDS_DEVTOOLS_4c8b530161336f95d0c2f13337556954" desc="">
+    Show network messages
+  </message>
+  <message name="IDS_DEVTOOLS_4cad9e20fde3f8991c5dd1d6a0fe13e7" desc="">
+    Remove expression
+  </message>
+  <message name="IDS_DEVTOOLS_4d1b0b75a9737b7dcdc3a15a84fe856b" desc="">
+    Searching…
+  </message>
+  <message name="IDS_DEVTOOLS_4f9afe303d0e8c68e3518756e1734924" desc="">
+    Clear console prompt
+  </message>
+  <message name="IDS_DEVTOOLS_53f0fca28a013c116a1df533d9bdf764" desc="">
+    [Deprecation] <ph name="MESSAGETEXT">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_5ef0c737746fae2ca90e66c39333f8f6" desc="">
+    Errors
+  </message>
+  <message name="IDS_DEVTOOLS_5f8442a46861496e9d2a3d11be13692b" desc="">
+    Writing file…
+  </message>
+  <message name="IDS_DEVTOOLS_6047a6c9fb775557639afb3fbc90b4e7" desc="">
+    Log levels
+  </message>
+  <message name="IDS_DEVTOOLS_63e4e92bb7d207ca577b11c07f827279" desc="">
+    Extension
+  </message>
+  <message name="IDS_DEVTOOLS_650140bd73628d283d870d4648ae3324" desc="">
+    Find string in logs
+  </message>
+  <message name="IDS_DEVTOOLS_689202409e48743b914713f96d93947c" desc="">
+    Value
+  </message>
+  <message name="IDS_DEVTOOLS_6cdd8769d46bb96331ac4a96d3ea84b8" desc="">
+    Clear all messages with <ph name="UI_SHORTCUTREGISTRY_SHORTCUTTITLEFORACTION__CONSOLE_CLEAR__">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_6eed4ce2d5859b11dc44a5e3bd91af20" desc="">
+    Eager evaluation
+  </message>
+  <message name="IDS_DEVTOOLS_78e731027d8fd50ed642340b7c9a63b3" desc="">
+    message
+  </message>
+  <message name="IDS_DEVTOOLS_7a1920d61156abc05a60135aefe8bc67" desc="">
+    Default
+  </message>
+  <message name="IDS_DEVTOOLS_7b83d3f08fa392b79e3f553b585971cd" desc="">
+    warning
+  </message>
+  <message name="IDS_DEVTOOLS_7be1a30a7758269755609f1f7434d828" desc="">
+    Group similar messages in console
+  </message>
+  <message name="IDS_DEVTOOLS_84d0c431d1472a0609d69b39dcb3a287" desc="">
+    (index)
+  </message>
+  <message name="IDS_DEVTOOLS_902b0d55fddef6f8d651fe1035b7d4bd" desc="">
+    Error
+  </message>
+  <message name="IDS_DEVTOOLS_93977f1310f482395375a9950b512462" desc="">
+    Eagerly evaluate console prompt text
+  </message>
+  <message name="IDS_DEVTOOLS_95c74dafb449d894014c2eb1d80ded01" desc="">
+    Edit expression
+  </message>
+  <message name="IDS_DEVTOOLS_988aa266ba49ce9da4c7e10f4b477ec4" desc="">
+    Next/previous command
+  </message>
+  <message name="IDS_DEVTOOLS_9aae9fe27dbf2db0ad90762b5b59d3a7" desc="">
+    Clear console
+  </message>
+  <message name="IDS_DEVTOOLS_9d8c209e49e328528c39c273798e98c9" desc="">
+    Save as...
+  </message>
+  <message name="IDS_DEVTOOLS_a092483ed730ed040e5df5776dca49e5" desc="">
+    Log XMLHttpRequests
+  </message>
+  <message name="IDS_DEVTOOLS_a1948ddb50d0bbd31e36f1fbc45479a0" desc="">
+    Autocomplete from history
+  </message>
+  <message name="IDS_DEVTOOLS_a1a6657be79cc0fc1e9b23b9e108f043" desc="">
+    Expression
+  </message>
+  <message name="IDS_DEVTOOLS_a3eb3c95c4cb8e06fd3682c1f0d70bc0" desc="">
+    Only show messages from the current context (top, iframe, worker, extension)
+  </message>
+  <message name="IDS_DEVTOOLS_abba8787b900565790eae8ceceed3c2b" desc="">
+    Do not autocomplete from history
+  </message>
+  <message name="IDS_DEVTOOLS_ac17bdcb3f6c8d86ccb64aff1ab8db7c" desc="">
+    Console was cleared
+  </message>
+  <message name="IDS_DEVTOOLS_ac2671ba859591dd83845c680aaa144e" desc="">
+    user messages
+  </message>
+  <message name="IDS_DEVTOOLS_ace3e0307b2ce23a81b18747fc5f555f" desc="">
+    Function was resolved from bound function.
+  </message>
+  <message name="IDS_DEVTOOLS_b184e7a44bed11a41d9c104529010e23" desc="">
+    <ph name="THIS__LEVELLABELS_NAME_">$1s</ph> only
+  </message>
+  <message name="IDS_DEVTOOLS_b2f8489bbd55a4e9b9edbbbfe49edf70" desc="">
+    not available
+  </message>
+  <message name="IDS_DEVTOOLS_b5735f8c6deff6306a2216612ca80f0e" desc="">
+    JavaScript contexts
+  </message>
+  <message name="IDS_DEVTOOLS_bafd7322c6e97d25b6299b5d6fe8920b" desc="">
+    No
+  </message>
+  <message name="IDS_DEVTOOLS_bb63b17b21e67aa2cc30cf01bae36695" desc="">
+    Execute command
+  </message>
+  <message name="IDS_DEVTOOLS_bccaa4aa80831b76c11240a16447975f" desc="">
+    Console
+  </message>
+  <message name="IDS_DEVTOOLS_c0274fa278f2e0dfef862234e0eb9b8b" desc="">
+    &lt;exception&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_c9818b2c0890a205d294a2b31354f8b4" desc="">
+    All levels
+  </message>
+  <message name="IDS_DEVTOOLS_ca3c4b269c23985d3c1204c203ad84d8" desc="">
+    Group similar
+  </message>
+  <message name="IDS_DEVTOOLS_ca6ceb8e597abb1298b84b1aaadc229b" desc="">
+    Show timestamps
+  </message>
+  <message name="IDS_DEVTOOLS_caf037034c3205725511c1216f772402" desc="">
+    &lt;some&gt; event
+  </message>
+  <message name="IDS_DEVTOOLS_caf9b6b99962bf5c2264824231d7a40c" desc="">
+    info
+  </message>
+  <message name="IDS_DEVTOOLS_cb5e100e5a9a3e7f6d1fd97512215282" desc="">
+    error
+  </message>
+  <message name="IDS_DEVTOOLS_cf3eed6aa30c47cec1321a835dc7b1ac" desc="">
+    Treat evaluation as user activation
+  </message>
+  <message name="IDS_DEVTOOLS_d0990066fbdfd3753fb45300d57e4364" desc="">
+    Hide network
+  </message>
+  <message name="IDS_DEVTOOLS_d35866a5d1161b48670e28e8c8550e89" desc="">
+    Assertion failed: 
+  </message>
+  <message name="IDS_DEVTOOLS_d3f526e5326c06d47ab12240d604f87a" desc="">
+    took &lt;N&gt;ms
+  </message>
+  <message name="IDS_DEVTOOLS_d4a9fa383ab700c5bdd6f31cf7df0faf" desc="">
+    Verbose
+  </message>
+  <message name="IDS_DEVTOOLS_d61de8e8b9dfdf0e7a52bf2e4fd23e72" desc="">
+     M&lt;XX&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_d7746aa6b562c73487a4015eef3244e9" desc="">
+    IFrame
+  </message>
+  <message name="IDS_DEVTOOLS_d7778d0c64b6ba21494c97f77a66885a" desc="">
+    Filter
+  </message>
+  <message name="IDS_DEVTOOLS_d779282283c011149d163edbcd5e5f11" desc="">
+    Evaluate, allowing side effects
+  </message>
+  <message name="IDS_DEVTOOLS_de6fc8cb2d1b20158aeda5cd1cb2e03c" desc="">
+    Show messages from all contexts
+  </message>
+  <message name="IDS_DEVTOOLS_de70938849b75d3db63bba421c93e018" desc="">
+    messages
+  </message>
+  <message name="IDS_DEVTOOLS_dfcc689f6e70e39a408b78912822cec9" desc="">
+    Error <ph name="ACCESSIBLENAME">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_e178ba306750ffbfdba284f60a3743b0" desc="">
+    Do not eagerly evaluate console prompt text
+  </message>
+  <message name="IDS_DEVTOOLS_e17fdb615452f440c793b5ddad90dd5e" desc="">
+    Warnings
+  </message>
+  <message name="IDS_DEVTOOLS_e1dd1b8e32626f508bd3a5612a60ab97" desc="">
+    Create live expression
+  </message>
+  <message name="IDS_DEVTOOLS_eacaf2bd1c1414e5086cf04573e154ef" desc="">
+    e.g. /event\d/ -cdn url:a.com
+  </message>
+  <message name="IDS_DEVTOOLS_ebfab4df1bb91688c62d4523eba870a5" desc="">
+    Evaluate triggers user activation
+  </message>
+  <message name="IDS_DEVTOOLS_f8ec55cf268ca6106fb23a98523b7549" desc="">
+    user message
+  </message>
+  <message name="IDS_DEVTOOLS_fb554075057f4d116097df434bcef393" desc="">
+    This value will not be collected until console is cleared.
+  </message>
+  <message name="IDS_DEVTOOLS_fbb5dc83cad4daf746720c90bfb8d306" desc="">
+    Custom levels
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/console_counters/console_counters_strings.grdp b/third_party/blink/renderer/devtools/front_end/console_counters/console_counters_strings.grdp
new file mode 100644
index 0000000..d2ead3e
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/console_counters/console_counters_strings.grdp
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_5415b9709f6fda34d7148558bbf4e36b" desc="">
+    <ph name="WARNINGS">$1s</ph> warning
+  </message>
+  <message name="IDS_DEVTOOLS_8e8fda2f7538782b27f82f8ec3c160eb" desc="">
+    <ph name="WARNINGS">$1s</ph> warnings
+  </message>
+  <message name="IDS_DEVTOOLS_9a075279073f00bab27c1c7612bb9fc0" desc="">
+    <ph name="ERRORS">$1s</ph> errors
+  </message>
+  <message name="IDS_DEVTOOLS_a1c00c8d8c543411f73883be7ecfb01b" desc="">
+    <ph name="ERRORS">$1s</ph> error
+  </message>
+  <message name="IDS_DEVTOOLS_dac90a385f9ee1c4d4cac02579d68185" desc="">
+    <ph name="VIOLATIONS">$1s</ph> violation
+  </message>
+  <message name="IDS_DEVTOOLS_de292077f92bbd36f5b8bd7131f2ef5a" desc="">
+    <ph name="VIOLATIONS">$1s</ph> violations
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/cookie_table/cookie_table_strings.grdp b/third_party/blink/renderer/devtools/front_end/cookie_table/cookie_table_strings.grdp
new file mode 100644
index 0000000..3c816c9
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/cookie_table/cookie_table_strings.grdp
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_2e7501c512976a9d9a9dca45bce9f128" desc="">
+    Expires / Max-Age
+  </message>
+  <message name="IDS_DEVTOOLS_382b0f5185773fa0f67a8ed8056c7759" desc="">
+    N/A
+  </message>
+  <message name="IDS_DEVTOOLS_49ee3087348e8d44e1feda1917443987" desc="">
+    Name
+  </message>
+  <message name="IDS_DEVTOOLS_6c8f0cef6081f0f97ec0f01a5a3ce2a7" desc="">
+    HttpOnly
+  </message>
+  <message name="IDS_DEVTOOLS_6f6cb72d544962fa333e2e34ce64f719" desc="">
+    Size
+  </message>
+  <message name="IDS_DEVTOOLS_71c7ae294b7abd866b3fb295b3b9e4a4" desc="">
+    Session
+  </message>
+  <message name="IDS_DEVTOOLS_7a2ccf251ecb20b2b84ce0e3c3f72a29" desc="">
+    Secure
+  </message>
+  <message name="IDS_DEVTOOLS_9f8e4d5e9b6d7a439dc6b5ebc5eedca4" desc="">
+    SameSite
+  </message>
+  <message name="IDS_DEVTOOLS_ac70412e939d72a9234cdebb1af5867b" desc="">
+    Path
+  </message>
+  <message name="IDS_DEVTOOLS_eae639a70006feff484a39363c977e24" desc="">
+    Domain
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/coverage/coverage_strings.grdp b/third_party/blink/renderer/devtools/front_end/coverage/coverage_strings.grdp
new file mode 100644
index 0000000..42fd14a8
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/coverage/coverage_strings.grdp
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_0975cd80b6efbfa27546aa1567bc8143" desc="">
+    Instrument coverage
+  </message>
+  <message name="IDS_DEVTOOLS_140f5ed61f8c7390c3e38d7f6af6bd4b" desc="">
+    Click the record button <ph name="RECORDBUTTON">$1s</ph> to start capturing coverage.
+Click the reload button <ph name="RELOADBUTTON">$2s</ph> to reload and start capturing coverage.
+  </message>
+  <message name="IDS_DEVTOOLS_1a49594b5f00e4f511ae8935c027cd8e" desc="">
+    Unused Bytes
+  </message>
+  <message name="IDS_DEVTOOLS_2adbfb69a37aa4a29ca846736d2111db" desc="">
+    JS (coarse)
+  </message>
+  <message name="IDS_DEVTOOLS_2c56c360580420d293172f42d85dfbed" desc="">
+    CSS
+  </message>
+  <message name="IDS_DEVTOOLS_5bc06f5800d415cc95e1349edbaca425" desc="">
+    JS
+  </message>
+  <message name="IDS_DEVTOOLS_6525b37c568c526bde7c02fac8195c73" desc="">
+    <ph name="UNUSEDSIZE___THIS__COVERAGEINFO_SIZE________">$1.1f</ph> %%
+  </message>
+  <message name="IDS_DEVTOOLS_73af525212a812236f1a3618e9cfa717" desc="">
+    Export...
+  </message>
+  <message name="IDS_DEVTOOLS_8324d82d6d4baaa0405621a1a15f365a" desc="">
+    Stop instrumenting coverage and show results
+  </message>
+  <message name="IDS_DEVTOOLS_832e506a71f4a57ed15ecbcd771f9247" desc="">
+    Total Bytes
+  </message>
+  <message name="IDS_DEVTOOLS_8e8c9f0a8c3469d9900728e8b1d451cb" desc="">
+    URL filter
+  </message>
+  <message name="IDS_DEVTOOLS_95dd3a82779cda9791819969c51c97d3" desc="">
+    Click the record button <ph name="RECORDBUTTON">$1s</ph> to start capturing coverage.
+  </message>
+  <message name="IDS_DEVTOOLS_9841bdc50c4226cb6ec5db76494249e6" desc="">
+    Coverage
+  </message>
+  <message name="IDS_DEVTOOLS_a1fa27779242b4902f7ae3bdd5c6d508" desc="">
+    Type
+  </message>
+  <message name="IDS_DEVTOOLS_bca27ccb808f436cd1ce828dd47604b7" desc="">
+    Start instrumenting coverage and reload page
+  </message>
+  <message name="IDS_DEVTOOLS_c4ba5b981ea7d2c17178a6c879cc03bd" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_UNUSED_">$1s</ph> of <ph name="NUMBER_BYTESTOSTRING_TOTAL_">$2s</ph> bytes are not used. (<ph name="PERCENTUNUSED">$3d</ph>%%)
+  </message>
+  <message name="IDS_DEVTOOLS_d95b97f3bac1a32fc1f27686f74b97de" desc="">
+    JS coverage is function-level only. Reload the page for block-level coverage.
+  </message>
+  <message name="IDS_DEVTOOLS_d98bd5257b9f29c266777264411735a3" desc="">
+    Include extension content scripts
+  </message>
+  <message name="IDS_DEVTOOLS_e6b391a8d2c4d45902a23a8b6585703d" desc="">
+    URL
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/data_grid/data_grid_strings.grdp b/third_party/blink/renderer/devtools/front_end/data_grid/data_grid_strings.grdp
new file mode 100644
index 0000000..0cd51ef
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/data_grid/data_grid_strings.grdp
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_28de94c6a82d4419a4014addb8aba93c" desc="">
+    Show <ph name="THIS__CHUNKSIZE">$1d</ph> after
+  </message>
+  <message name="IDS_DEVTOOLS_b33d161950658a73abe8675d311345d2" desc="">
+    Edit &quot;<ph name="THIS__COLUMNS_COLUMNID__TITLE">$1s</ph>&quot;
+  </message>
+  <message name="IDS_DEVTOOLS_d73d1ee203c4075522513de6fe5557c8" desc="">
+    Show <ph name="THIS__CHUNKSIZE">$1d</ph> before
+  </message>
+  <message name="IDS_DEVTOOLS_e199c6bb662785b037c5f330f58878ce" desc="">
+    Show all <ph name="TOTALSIZE">$1d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_ef61fb324d729c341ea8ab9901e23566" desc="">
+    Add new
+  </message>
+  <message name="IDS_DEVTOOLS_f2a6c498fb90ee345d997f888fce3b18" desc="">
+    Delete
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/devices/devices_strings.grdp b/third_party/blink/renderer/devtools/front_end/devices/devices_strings.grdp
new file mode 100644
index 0000000..07a3116
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/devices/devices_strings.grdp
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_00fcbe531dd989e566cab0a9e250f14c" desc="">
+    Port Forwarding:
+  </message>
+  <message name="IDS_DEVTOOLS_03fc5c0bd59679c442b86074c7a7cc3a" desc="">
+    Port forwarding
+  </message>
+  <message name="IDS_DEVTOOLS_042909a8044ec8e1a97069bfdde2fe58" desc="">
+    Connected: <ph name="CONNECTED_JOIN______">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_049c45b298c6bc7540d84ab593bedaa6" desc="">
+    Transient: <ph name="TRANSIENT_JOIN______">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_0dec6438ad2d934e57d33026770654d1" desc="">
+    remote debugging documentation
+  </message>
+  <message name="IDS_DEVTOOLS_10e10a814f3c1f9a5e847376c975ea3c" desc="">
+    Error: <ph name="ERROR_JOIN______">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2157ba7738ed20565fd089f2a6287cdd" desc="">
+    View less tabs…
+  </message>
+  <message name="IDS_DEVTOOLS_258771f02f7548d3282560829e7c9b69" desc="">
+    Define the listening port on your device that maps to a port accessible from your development machine. 
+  </message>
+  <message name="IDS_DEVTOOLS_2ec0d16e4ca169baedb9b2d50ec5c6d7" desc="">
+    Connected
+  </message>
+  <message name="IDS_DEVTOOLS_34fdf6d76ae3464206146053c8aedcd7" desc="">
+    <ph name="THIS__DEVICES_LENGTH">$1d</ph> devices detected.
+  </message>
+  <message name="IDS_DEVTOOLS_35a9dbe6b748771b9ea4d5962d37ff6f" desc="">
+    Inspect
+  </message>
+  <message name="IDS_DEVTOOLS_405590b3c5865df635f2373ee4cb799c" desc="">
+    Remote devices
+  </message>
+  <message name="IDS_DEVTOOLS_4d1c8263ba1036754f8db14a98f9f006" desc="">
+    Reload
+  </message>
+  <message name="IDS_DEVTOOLS_67afa587522e088dedac9f252362d16a" desc="">
+    Discover USB devices
+  </message>
+  <message name="IDS_DEVTOOLS_6ab4529805e55f79b589f0676435dc4e" desc="">
+    Pending Authorization
+  </message>
+  <message name="IDS_DEVTOOLS_85bd3d2945bf008087e0a3a7d714ae80" desc="">
+    localhost:
+  </message>
+  <message name="IDS_DEVTOOLS_88183b946cc5f0e8c96b2e66e1c74a7e" desc="">
+    Unknown
+  </message>
+  <message name="IDS_DEVTOOLS_8c7074069d61ba9d2f76b078fb1dd154" desc="">
+    Need help? Read Chrome 
+  </message>
+  <message name="IDS_DEVTOOLS_926f97ab5b90b053d6a3a31082316f15" desc="">
+    No browsers detected.
+  </message>
+  <message name="IDS_DEVTOOLS_9b5e7fc14b157afb268b71b01d397eef" desc="">
+    Add rule
+  </message>
+  <message name="IDS_DEVTOOLS_9ba01fbecae523e4160afa26f75f1757" desc="">
+    View more tabs…
+  </message>
+  <message name="IDS_DEVTOOLS_a105a2ea6a8f53afe9640d8684ffe5d5" desc="">
+     Read 
+  </message>
+  <message name="IDS_DEVTOOLS_a2040c5569ac3718b10af77aaa3fbb03" desc="">
+    Pending authentication: please accept debugging session on the device.
+  </message>
+  <message name="IDS_DEVTOOLS_bcca96534b404f08faf65d76be6aa782" desc="">
+    1 device detected.
+  </message>
+  <message name="IDS_DEVTOOLS_c03ca67dda321195d74c951097f240c6" desc="">
+    Devices
+  </message>
+  <message name="IDS_DEVTOOLS_c3bf447eabe632720a3aa1a7ce401274" desc="">
+    Open
+  </message>
+  <message name="IDS_DEVTOOLS_c9d2bd107ad7a2c20f80efcaaf365d23" desc="">
+     for more information.
+  </message>
+  <message name="IDS_DEVTOOLS_cc63a5ac37dd9d1c607a59aad3292392" desc="">
+    No rules
+  </message>
+  <message name="IDS_DEVTOOLS_ceb747e16b7e536d03047492e4491314" desc="">
+    remote debugging documentation.
+  </message>
+  <message name="IDS_DEVTOOLS_d38dcac8af912356e255cbace8984e9a" desc="">
+    New tab:
+  </message>
+  <message name="IDS_DEVTOOLS_dabcc308341c912a6b620e1c980e16d6" desc="">
+    No devices detected.
+  </message>
+  <message name="IDS_DEVTOOLS_e1f70f4a4265cb51c1b219aa60eaa441" desc="">
+    Enter URL
+  </message>
+  <message name="IDS_DEVTOOLS_e24ee2487879116dcab772c0ac4fe341" desc="">
+    Focus
+  </message>
+  <message name="IDS_DEVTOOLS_f114563447e4786042d7e7ce05f1157c" desc="">
+    Device port (3333)
+  </message>
+  <message name="IDS_DEVTOOLS_f4f70727dc34561dfde1a3c529b6205c" desc="">
+    Settings
+  </message>
+  <message name="IDS_DEVTOOLS_ff89b835f4fc414c9c8f4f96695c2ea8" desc="">
+    Local address (dev.example.corp:3333)
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
new file mode 100644
index 0000000..bf0f5fb2
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_068bc495b79facfaa7918369fa9909eb" desc="">
+    Cut element
+  </message>
+  <message name="IDS_DEVTOOLS_07f01edb2a05909a9c0d04031d7c43e1" desc="">
+    Filter Styles
+  </message>
+  <message name="IDS_DEVTOOLS_081c9f670acb11b080077b3fb6127b4c" desc="">
+    Add box-shadow
+  </message>
+  <message name="IDS_DEVTOOLS_084cd932d6dd317197a20945c76603e2" desc="">
+    Inherited from<ph name="___">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_0b17e22ff13be64e3216014d9c2a5354" desc="">
+    Enable DOM word wrap
+  </message>
+  <message name="IDS_DEVTOOLS_10d084d908583b210d2b1c470855a1d4" desc="">
+    Hide HTML comments
+  </message>
+  <message name="IDS_DEVTOOLS_14ec3eb094717596986aeef905ecb2d2" desc="">
+    New Style Rule
+  </message>
+  <message name="IDS_DEVTOOLS_1bc005d87dc22486bbb3c662b6fe46ce" desc="">
+    Pseudo ::<ph name="PSEUDOTYPE">$1s</ph> element
+  </message>
+  <message name="IDS_DEVTOOLS_1cc5c88b14a818f9f8d053f3c9f9249e" desc="">
+    Word wrap
+  </message>
+  <message name="IDS_DEVTOOLS_20bfc7bb53e1d2639556bce9852c32d8" desc="">
+    Resolve event listeners bound with framework
+  </message>
+  <message name="IDS_DEVTOOLS_21c12abe8367dcbdf5d3fee56a4ae9d8" desc="">
+    Show detailed inspect tooltip
+  </message>
+  <message name="IDS_DEVTOOLS_22a11a2e07e4412afd106977262d7617" desc="">
+    Copy selector
+  </message>
+  <message name="IDS_DEVTOOLS_254dd634c2a4b01fad95fe963b193ba8" desc="">
+    Page DOM
+  </message>
+  <message name="IDS_DEVTOOLS_28c0a3050f784583d52339ddb0bc1274" desc="">
+    Paste element
+  </message>
+  <message name="IDS_DEVTOOLS_2becb310c9830fe3fb19d18eb1808712" desc="">
+    (<ph name="USAGE">$1d</ph> glyph)
+  </message>
+  <message name="IDS_DEVTOOLS_2c0a7043a9e736eaf14b6614fff102c0" desc="">
+    Computed
+  </message>
+  <message name="IDS_DEVTOOLS_2ce5967e3862c73a31059c4aa5847464" desc="">
+    Open shadow editor.
+  </message>
+  <message name="IDS_DEVTOOLS_2ddaa8e6378b906b42429dc4003edbd3" desc="">
+    Reveal in Elements panel
+  </message>
+  <message name="IDS_DEVTOOLS_2de42fbb00305f8282310f1b3e10ce9a" desc="">
+    border
+  </message>
+  <message name="IDS_DEVTOOLS_2dff19d6ed78c6ca35cb3beab7b5bf55" desc="">
+    Rendered Fonts
+  </message>
+  <message name="IDS_DEVTOOLS_2e6591ba49ec23bf6725b74589b66ba3" desc="">
+    styles sidebar
+  </message>
+  <message name="IDS_DEVTOOLS_2f76fab71405b40cf521f64eab0d350f" desc="">
+    Delete element
+  </message>
+  <message name="IDS_DEVTOOLS_3427db2c4cb9d8d61c1dd32d218d5699" desc="">
+    Use $0 in the console to refer to this element.
+  </message>
+  <message name="IDS_DEVTOOLS_365fee797b55ffeac84d2ba82507b721" desc="">
+    user agent stylesheet
+  </message>
+  <message name="IDS_DEVTOOLS_3afbd9828e011526955ca93b48b57524" desc="">
+    Screenshot
+  </message>
+  <message name="IDS_DEVTOOLS_3bb3e8c8a24891ba0f7608bcc96f8b0a" desc="">
+    Frame
+  </message>
+  <message name="IDS_DEVTOOLS_3e255d6c811a40b9ad197fedeadc342c" desc="">
+    Local file
+  </message>
+  <message name="IDS_DEVTOOLS_3e9bec182c7495247f5f93d3881d630b" desc="">
+    :hov
+  </message>
+  <message name="IDS_DEVTOOLS_3f8f88e3686b2345cecc9530e19e6172" desc="">
+    Store as global variable
+  </message>
+  <message name="IDS_DEVTOOLS_417117e823d14c9b7c6533e4b04600a6" desc="">
+    (text)
+  </message>
+  <message name="IDS_DEVTOOLS_44beac36afb00f088edb0aeab28a300d" desc="">
+    Edit text
+  </message>
+  <message name="IDS_DEVTOOLS_45a921ffccbc28c2ff80adada350224f" desc="">
+    &lt;node&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_47210336f82b1488b69932b14038bdff" desc="">
+    Increment/decrement with mousewheel or up/down keys. <ph name="CMDORCTRL">$1s</ph>: ±100, Shift: ±10, Alt: ±0.1
+  </message>
+  <message name="IDS_DEVTOOLS_4757fe07fd492a8be0ea6a760d683d6e" desc="">
+    position
+  </message>
+  <message name="IDS_DEVTOOLS_4c1c7d945fcd04d68bd3b3f1d99a55a1" desc="">
+    Styles
+  </message>
+  <message name="IDS_DEVTOOLS_4ca94080ccbd5784f5fe296bf1ae3e12" desc="">
+    Event Listeners
+  </message>
+  <message name="IDS_DEVTOOLS_4cf8e5f6ffbb182b5d480cbabae6a028" desc="">
+    Edit attribute
+  </message>
+  <message name="IDS_DEVTOOLS_4d9468d79ef34f71ba78a28688fa5d31" desc="">
+    Ancestors
+  </message>
+  <message name="IDS_DEVTOOLS_5134111907df828fc575c69e22480c1e" desc="">
+    Hide element
+  </message>
+  <message name="IDS_DEVTOOLS_51f30f4743bd8f0e7141781bd2d93abb" desc="">
+    Set color format as authored
+  </message>
+  <message name="IDS_DEVTOOLS_55e1bbb4e00add64284ed28f15d40b25" desc="">
+    Color format:
+  </message>
+  <message name="IDS_DEVTOOLS_581b87189d0bd86aa707e42553032a3e" desc="">
+    Children:
+  </message>
+  <message name="IDS_DEVTOOLS_5ad5ecf0bc5b6e08d886efd6f6e5649b" desc="">
+    Show HTML comments
+  </message>
+  <message name="IDS_DEVTOOLS_5adda1916cde33ce6e49c22ba58f03f7" desc="">
+    injected stylesheet
+  </message>
+  <message name="IDS_DEVTOOLS_5c6236c705b0086fbbdb2df3133f8a06" desc="">
+    Reveal DOM node on hover
+  </message>
+  <message name="IDS_DEVTOOLS_5de39fa1bce4bbf9232597a2120bc4d4" desc="">
+    Open cubic bezier editor.
+  </message>
+  <message name="IDS_DEVTOOLS_5eeb03a9c080c299d4804ac765c818c2" desc="">
+    No matching selector or style
+  </message>
+  <message name="IDS_DEVTOOLS_5fb63579fc981698f97d55bfecb213ea" desc="">
+    Copy
+  </message>
+  <message name="IDS_DEVTOOLS_6053f828a109e7d9da3690a7416e4eec" desc="">
+    Copy XPath
+  </message>
+  <message name="IDS_DEVTOOLS_605e072850e19521394f8bad7082acf4" desc="">
+    Force element state
+  </message>
+  <message name="IDS_DEVTOOLS_63041e09c8660107e507c9c27c08199c" desc="">
+    Expand recursively
+  </message>
+  <message name="IDS_DEVTOOLS_686676f33cf19ca00788564f43b992cb" desc="">
+    Filter Computed Styles
+  </message>
+  <message name="IDS_DEVTOOLS_68a483c15a1730a88f2a57b1b1b8b69a" desc="">
+    Event listeners category
+  </message>
+  <message name="IDS_DEVTOOLS_69f83bbab45ca27c374e76d304ab76be" desc="">
+    Find by string, selector, or XPath
+  </message>
+  <message name="IDS_DEVTOOLS_6ac7142d0aa79d17f03677fac1249c98" desc="">
+    Force state
+  </message>
+  <message name="IDS_DEVTOOLS_6ae2f9f803d108269a2cdb714b9851a1" desc="">
+    <ph name="NODE_NODENAMEINCORRECTCASE__">$1s</ph>[Attributes Style]
+  </message>
+  <message name="IDS_DEVTOOLS_6f3cab519fe7e8f3b7834cf882f9dfe6" desc="">
+    Element Classes
+  </message>
+  <message name="IDS_DEVTOOLS_78e0b69d17e0287aaae4f79171e5b059" desc="">
+    Disable DOM word wrap
+  </message>
+  <message name="IDS_DEVTOOLS_795630f9fe735d5c002f28f851d78fac" desc="">
+    Framework listeners
+  </message>
+  <message name="IDS_DEVTOOLS_7c90d60be6418bb4a858b3733b2560a8" desc="">
+    Unknown property name
+  </message>
+  <message name="IDS_DEVTOOLS_8551fe4a09c280c278283c10a5a6aa4e" desc="">
+    Increment/decrement with mousewheel or up/down keys. <ph name="CMDORCTRL">$1s</ph>: R ±1, Shift: G ±1, Alt: B ±1
+  </message>
+  <message name="IDS_DEVTOOLS_88556207e275bc7d802b4706268d61ae" desc="">
+    Copy JS path
+  </message>
+  <message name="IDS_DEVTOOLS_88a306e559954dc8c8ae9eb55d62297f" desc="">
+    Add attribute
+  </message>
+  <message name="IDS_DEVTOOLS_8bd236209fc42bdeffdecd1cc68d7b86" desc="">
+    Open color picker. <ph name="SHIFTCLICKMESSAGE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_8d41e41bf0856d309b43d776c24a0316" desc="">
+    Add new class
+  </message>
+  <message name="IDS_DEVTOOLS_8f17243e20db8af877d205e488b1f521" desc="">
+    Element state: <ph name="______NODE_DOMMODEL___CSSMODEL___PSEUDOSTATE_NODE__JOIN_______">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_91d17eff6a1529e8a4f67a5aa4cabec7" desc="">
+    No matching property
+  </message>
+  <message name="IDS_DEVTOOLS_956853d02d2c6947b6c15515e669dfad" desc="">
+    Set color format to RGB
+  </message>
+  <message name="IDS_DEVTOOLS_9835cebf3cf1d09803b6e2c02bc71401" desc="">
+    HSL: hsl(300, 80%, 90%)
+  </message>
+  <message name="IDS_DEVTOOLS_9a0364b9e99bb480dd25e1f0284c8555" desc="">
+    content
+  </message>
+  <message name="IDS_DEVTOOLS_9fc2d28c05ed9eb1d75ba4465abf15a9" desc="">
+    Properties
+  </message>
+  <message name="IDS_DEVTOOLS_a05fb8660778069f6d4a5c5b40a6dbc9" desc="">
+    Show user agent shadow DOM
+  </message>
+  <message name="IDS_DEVTOOLS_a2f8f2d457d122dd0d135781187aef31" desc="">
+    Invalid property value
+  </message>
+  <message name="IDS_DEVTOOLS_a78163b8c3f5e5a69fddd2a70c7f7309" desc="">
+    Style Attribute
+  </message>
+  <message name="IDS_DEVTOOLS_a98895bde5922dd43c82ad7050abc05a" desc="">
+    Show listeners on the ancestors
+  </message>
+  <message name="IDS_DEVTOOLS_aa56a2e65d8106aef3c61e4f6bf94fdb" desc="">
+    Elements
+  </message>
+  <message name="IDS_DEVTOOLS_abdf882c25e08d9ba219fe33f17591fe" desc="">
+    reveal
+  </message>
+  <message name="IDS_DEVTOOLS_acc24772ac31677d076f17d9002b57cd" desc="">
+    Collapse children
+  </message>
+  <message name="IDS_DEVTOOLS_ad4ac01e4a5688063dace5ba5d0f04de" desc="">
+    Insert Style Rule Below
+  </message>
+  <message name="IDS_DEVTOOLS_b0351810a5721f657b088608865849dd" desc="">
+    via inspector
+  </message>
+  <message name="IDS_DEVTOOLS_b1c94ca2fbc3e78fc30069c8d0f01680" desc="">
+    All
+  </message>
+  <message name="IDS_DEVTOOLS_b70cdb878a204fecf91c7dd1af312421" desc="">
+    padding
+  </message>
+  <message name="IDS_DEVTOOLS_b9740e9a829cf440e2e863124d53eba3" desc="">
+    Shift + Click to change color format.
+  </message>
+  <message name="IDS_DEVTOOLS_bd0ca6be53b0f3d2886fd53fcb52574e" desc="">
+    Blocking
+  </message>
+  <message name="IDS_DEVTOOLS_be9548cd999232697505d4ca2eed2a23" desc="">
+    Elements panel
+  </message>
+  <message name="IDS_DEVTOOLS_bf22eb56a74b42a33fecf10c831e9509" desc="">
+    Copy outerHTML
+  </message>
+  <message name="IDS_DEVTOOLS_c3c20058cbdd08782b780612204daafd" desc="">
+    Select an element in the page to inspect it
+  </message>
+  <message name="IDS_DEVTOOLS_c63621d0ad5163e8c643bb65c65b5aee" desc="">
+    Set color format to HEX
+  </message>
+  <message name="IDS_DEVTOOLS_c64955a6a59816d4e1206d600020a9fe" desc="">
+    Copy element
+  </message>
+  <message name="IDS_DEVTOOLS_c736f6b512801b5fe4670e5968ee7be7" desc="">
+    Toggle Element State
+  </message>
+  <message name="IDS_DEVTOOLS_c8466ef18413aebb4d6ebfce4e1f739d" desc="">
+    RGB: rgb(128, 255, 255)
+  </message>
+  <message name="IDS_DEVTOOLS_c8997c00026b49aa07e0a8c11511e390" desc="">
+    Add text-shadow
+  </message>
+  <message name="IDS_DEVTOOLS_c8f439c7ee24c34bc0b2e39a976509ad" desc="">
+    (<ph name="USAGE">$1d</ph> glyphs)
+  </message>
+  <message name="IDS_DEVTOOLS_cbb022167129a9f635cb4a73b5c72caa" desc="">
+    &lt;value is too large to edit&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_d0ad356f675744ef8f0311e83400b6ba" desc="">
+    Network resource
+  </message>
+  <message name="IDS_DEVTOOLS_d42f4851e770aa0f758b01388874f67b" desc="">
+    margin
+  </message>
+  <message name="IDS_DEVTOOLS_d838bef326860912aa08d977cf3a5f8b" desc="">
+    HEX: #dac0de
+  </message>
+  <message name="IDS_DEVTOOLS_d981fcf0c74837202331700aca88c801" desc="">
+    As authored
+  </message>
+  <message name="IDS_DEVTOOLS_eb92025cb8c66f1850c13a9b602a1856" desc="">
+    Show all
+  </message>
+  <message name="IDS_DEVTOOLS_eeb9a8e813f34f5f036332efc0257246" desc="">
+    Capture area screenshot
+  </message>
+  <message name="IDS_DEVTOOLS_f2a70e3a6d14a56de7ebbb5b748ed127" desc="">
+    Add color
+  </message>
+  <message name="IDS_DEVTOOLS_f3d48b9ad0a801282e3adc8b452d5fc0" desc="">
+    Edit as HTML
+  </message>
+  <message name="IDS_DEVTOOLS_f750c8807bc5f4a7bd259788114a4ebd" desc="">
+    Add background-color
+  </message>
+  <message name="IDS_DEVTOOLS_f80bc338b6146b566004a046f8137c85" desc="">
+    Passive
+  </message>
+  <message name="IDS_DEVTOOLS_f8d0f1896a9e4ad16d43caa28534df32" desc="">
+    Show All Properties (<ph name="PROPERTIES_LENGTH___COUNT">$1s</ph> more)
+  </message>
+  <message name="IDS_DEVTOOLS_fa480abba67730dd6bd15aa40308d78a" desc="">
+    Set color format to HSL
+  </message>
+  <message name="IDS_DEVTOOLS_fcc980247db918e1b666b78ec7093d8c" desc="">
+    Element is hidden
+  </message>
+  <message name="IDS_DEVTOOLS_fd9bcae718daadf263a2e73c7745f0a7" desc="">
+    Show All Nodes (<ph name="VISIBLECHILDREN_LENGTH___EXPANDEDCHILDCOUNT">$1d</ph> More)
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/emulated_devices/emulated_devices_strings.grdp b/third_party/blink/renderer/devtools/front_end/emulated_devices/emulated_devices_strings.grdp
new file mode 100644
index 0000000..ab04673
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/emulated_devices/emulated_devices_strings.grdp
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_012528cb9fb45d278755f9fec090ed11" desc="">
+    Laptop with MDPI screen
+  </message>
+  <message name="IDS_DEVTOOLS_079a3d134d2387adc52b2b3895f85e55" desc="">
+    iPhone X
+  </message>
+  <message name="IDS_DEVTOOLS_0d1160fe394d87ddbc2ca8e7cc25379d" desc="">
+    Laptop with touch
+  </message>
+  <message name="IDS_DEVTOOLS_0e5aeadd53726e96d7d9faa671b423ae" desc="">
+    Kindle Fire HDX
+  </message>
+  <message name="IDS_DEVTOOLS_1b9018182a49e16ba85bb095f224867c" desc="">
+    iPad
+  </message>
+  <message name="IDS_DEVTOOLS_2235bd527cc1684230b1db146c570a9d" desc="">
+    Pixel 2
+  </message>
+  <message name="IDS_DEVTOOLS_2c17ed2506add59c15ea763801cfafb1" desc="">
+    Nexus 6
+  </message>
+  <message name="IDS_DEVTOOLS_2d1fbe61c136940101281ec1df805c01" desc="">
+    Nexus 5
+  </message>
+  <message name="IDS_DEVTOOLS_2d9a25096b47a022c95787ab90308d6b" desc="">
+    LG Optimus L70
+  </message>
+  <message name="IDS_DEVTOOLS_4a321c67ad4bac0f3386f5e257b1fba3" desc="">
+    Laptop with HiDPI screen
+  </message>
+  <message name="IDS_DEVTOOLS_6247fa438881677e5f910b89db1974a7" desc="">
+    Nokia N9
+  </message>
+  <message name="IDS_DEVTOOLS_659810deafc0279869114daf8446ef4a" desc="">
+    Nexus 10
+  </message>
+  <message name="IDS_DEVTOOLS_6d9131a4a569108e7d19038b27111c86" desc="">
+    Nexus 5X
+  </message>
+  <message name="IDS_DEVTOOLS_71e3580e3c3a03f17798ebccaad9e6cb" desc="">
+    Nexus 7
+  </message>
+  <message name="IDS_DEVTOOLS_71f859515e31ad9bb417977247315f0a" desc="">
+    iPad Pro
+  </message>
+  <message name="IDS_DEVTOOLS_76c4064f51952f873baa0f9a201b49a3" desc="">
+    Nokia Lumia 520
+  </message>
+  <message name="IDS_DEVTOOLS_8be08fbcf1f787159e451a97a7c1cc90" desc="">
+    iPhone 6/7/8 Plus
+  </message>
+  <message name="IDS_DEVTOOLS_8d0c6a99bcfbb75724c123476d683d3f" desc="">
+    iPhone 4
+  </message>
+  <message name="IDS_DEVTOOLS_99d1a0f994cdce14eea8b6ae823d5a76" desc="">
+    Galaxy S III
+  </message>
+  <message name="IDS_DEVTOOLS_a75624548994321c1cc7173b9933438b" desc="">
+    Microsoft Lumia 550
+  </message>
+  <message name="IDS_DEVTOOLS_aa5fa1bdadb078da054c34752031a5e9" desc="">
+    iPhone 6/7/8
+  </message>
+  <message name="IDS_DEVTOOLS_b9e5554cdd9d9f2d3df1f30914b617c2" desc="">
+    Galaxy Note 3
+  </message>
+  <message name="IDS_DEVTOOLS_be6a9d69eff86af39e5a73345c585da6" desc="">
+    iPad Mini
+  </message>
+  <message name="IDS_DEVTOOLS_c99b8431589c0273278e87ebf44a7fd6" desc="">
+    iPhone 5/SE
+  </message>
+  <message name="IDS_DEVTOOLS_cc810fb8b8bc7c7d6a8327f0e7b2aead" desc="">
+    Nexus 6P
+  </message>
+  <message name="IDS_DEVTOOLS_d4f2f5453f28c5fd35c68f52e4da07cd" desc="">
+    BlackBerry Z30
+  </message>
+  <message name="IDS_DEVTOOLS_d6162eb3dd4dc5a9bc67222e24f19b25" desc="">
+    Nexus 4
+  </message>
+  <message name="IDS_DEVTOOLS_d8860bf736e83cfb5c13ee82a8f61a43" desc="">
+    Microsoft Lumia 950
+  </message>
+  <message name="IDS_DEVTOOLS_dfd6e576e036db4f857c8c03c977d975" desc="">
+    Galaxy Note II
+  </message>
+  <message name="IDS_DEVTOOLS_e0261d2f1730ebff89bbde9a4bc74ac5" desc="">
+    JioPhone 2
+  </message>
+  <message name="IDS_DEVTOOLS_e770076f957c87605ff906d0ed2c4b52" desc="">
+    Blackberry PlayBook
+  </message>
+  <message name="IDS_DEVTOOLS_eb850446088fbc75e974788cc2b39caa" desc="">
+    Pixel 2 XL
+  </message>
+  <message name="IDS_DEVTOOLS_f8896f769d62b6102e48039154c4ca5e" desc="">
+    Galaxy S5
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/emulation/emulation_strings.grdp b/third_party/blink/renderer/devtools/front_end/emulation/emulation_strings.grdp
new file mode 100644
index 0000000..129c27d
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/emulation/emulation_strings.grdp
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_09109d98eee81aa68f1196a7a0753240" desc="">
+    DPR: <ph name="DEVICESCALE">$1.1f</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_0c78cd3aa4c94a64675bc6aa77802843" desc="">
+    Device-based
+  </message>
+  <message name="IDS_DEVTOOLS_0eb264d46c88af61e587c18f60662df0" desc="">
+    Device pixel ratio
+  </message>
+  <message name="IDS_DEVTOOLS_0edd9b471d32d5e3bbec7ca8df113568" desc="">
+    Toggle device toolbar
+  </message>
+  <message name="IDS_DEVTOOLS_138d1a442ecb2e2fd3b083f1842658d0" desc="">
+    Custom orientation...
+  </message>
+  <message name="IDS_DEVTOOLS_146bdebb324a64d327b1dde22a07d0bd" desc="">
+    Laptop
+  </message>
+  <message name="IDS_DEVTOOLS_19ada73af65f7c221bd28e78b2eda5cd" desc="">
+    Edit…
+  </message>
+  <message name="IDS_DEVTOOLS_1b962c3d36319947efad5a45c1ee62b0" desc="">
+    Desktop (touch)
+  </message>
+  <message name="IDS_DEVTOOLS_1c7444be9626d149ab598fb79b639f96" desc="">
+    Portrait
+  </message>
+  <message name="IDS_DEVTOOLS_21e2bb42873eddcb9b476b8eafbe0c18" desc="">
+    Reset to defaults
+  </message>
+  <message name="IDS_DEVTOOLS_223bf25f178896660d7d73af6e0a540d" desc="">
+    Laptop L
+  </message>
+  <message name="IDS_DEVTOOLS_2496af30b64c3a4ff21e8505ea439a73" desc="">
+    50%
+  </message>
+  <message name="IDS_DEVTOOLS_29260f495ba8adcc62fd1307c758ff4e" desc="">
+    Show rulers
+  </message>
+  <message name="IDS_DEVTOOLS_29296fcb28eb4edf5d0049b6cac7bbd1" desc="">
+    <ph name="THIS__MODEL_SCALE________">$1.0f</ph>%%
+  </message>
+  <message name="IDS_DEVTOOLS_29eaeee66d8210c1261d748071fb7b0a" desc="">
+    Landscape right
+  </message>
+  <message name="IDS_DEVTOOLS_2a04aad9d1e40781d70537def184749d" desc="">
+    α (alpha)
+  </message>
+  <message name="IDS_DEVTOOLS_2d7084def320939c86d40f1208717083" desc="">
+    Location unavailable
+  </message>
+  <message name="IDS_DEVTOOLS_30bd7ce7de206924302499f197c7a966" desc="">
+    100%
+  </message>
+  <message name="IDS_DEVTOOLS_323d4eb70b252acb4a04eaf9e0882597" desc="">
+    Geolocation
+  </message>
+  <message name="IDS_DEVTOOLS_32954654ac8fe66a1d09be19001de2d4" desc="">
+    Width
+  </message>
+  <message name="IDS_DEVTOOLS_34e34c43ec6b943c10a3cc1a1a16fb11" desc="">
+    Manage
+  </message>
+  <message name="IDS_DEVTOOLS_36384a9ea3ec791e6bd4ab6b36f2ff2a" desc="">
+    Tablet
+  </message>
+  <message name="IDS_DEVTOOLS_3836c33415a6d95d4a63388a6bcadf3b" desc="">
+    Device type
+  </message>
+  <message name="IDS_DEVTOOLS_38795baa79c9e2422a84156b83d0b73a" desc="">
+    Mobile (no touch)
+  </message>
+  <message name="IDS_DEVTOOLS_39335086fbaba7128eff0ef52d396131" desc="">
+    Show media queries
+  </message>
+  <message name="IDS_DEVTOOLS_396f64970a094d8b66d9863bd073eb72" desc="">
+    Force enabled
+  </message>
+  <message name="IDS_DEVTOOLS_3b0eb7469ba9c95f3a05c4cef1f6aac4" desc="">
+    Latitude
+  </message>
+  <message name="IDS_DEVTOOLS_414b730ab2cf9123d9230740864ffeec" desc="">
+    Close DevTools
+  </message>
+  <message name="IDS_DEVTOOLS_4252b72e6ebcd4d4b4c2e46a786f03d2" desc="">
+    Zoom
+  </message>
+  <message name="IDS_DEVTOOLS_45f80006d304f294d6c1de50c244856e" desc="">
+    Add location...
+  </message>
+  <message name="IDS_DEVTOOLS_4dab36ac83853282fc0d7bae20c19e90" desc="">
+    More options
+  </message>
+  <message name="IDS_DEVTOOLS_50510c6a5a0e14ddd3a68f6dd1cf2f79" desc="">
+    Device name
+  </message>
+  <message name="IDS_DEVTOOLS_526d688f37a86d3c3f27d0c5016eb71d" desc="">
+    Reset
+  </message>
+  <message name="IDS_DEVTOOLS_58cc7602635a11277aa7531e14b32c97" desc="">
+    Portrait upside down
+  </message>
+  <message name="IDS_DEVTOOLS_59c06928c1dcdb1a687bf21d73e53a09" desc="">
+    Show device frame
+  </message>
+  <message name="IDS_DEVTOOLS_614103b76fd0d9de068d69034fb6f987" desc="">
+    (<ph name="THIS__MODEL_DEVICE___TITLE">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_6149711fb8edc5e2adeea7aa63ee11ff" desc="">
+    150%
+  </message>
+  <message name="IDS_DEVTOOLS_62759e0d418d7391bf2d54e70cbcc05d" desc="">
+    Remove device type
+  </message>
+  <message name="IDS_DEVTOOLS_633c01074b6726183e20f42bb3ee01c0" desc="">
+    4K
+  </message>
+  <message name="IDS_DEVTOOLS_649676015173b6195516eed77e5aee60" desc="">
+    125%
+  </message>
+  <message name="IDS_DEVTOOLS_6adf97f83acf6453d4a6a4b1070f3754" desc="">
+    None
+  </message>
+  <message name="IDS_DEVTOOLS_7087f70b643f941c968dc3b327cbe111" desc="">
+    Adjust with mousewheel or up/down keys. <ph name="CMDORCTRL">$1s</ph>: ±10, Shift: ±1, Alt: ±0.01
+  </message>
+  <message name="IDS_DEVTOOLS_75a37bfcbfe2678114513aef903c4ba5" desc="">
+    Default: <ph name="DEFAULTVALUE">$1.1f</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_7ad87024f63b3bb0292c21d76f2c7438" desc="">
+    Add device pixel ratio
+  </message>
+  <message name="IDS_DEVTOOLS_7addb86955014a622eaefae5134fcb7a" desc="">
+    Remove device pixel ratio
+  </message>
+  <message name="IDS_DEVTOOLS_7f163d0494446906a484df67e90c1d6b" desc="">
+    Hide device frame
+  </message>
+  <message name="IDS_DEVTOOLS_815dff01257e5ef182b25d4c1ef0a7a0" desc="">
+    Landscape
+  </message>
+  <message name="IDS_DEVTOOLS_81c3744a7fe979d029d1e7b84afd613f" desc="">
+    *Requires reload
+  </message>
+  <message name="IDS_DEVTOOLS_82a9b801e28c4dec617b1cabc329f5bd" desc="">
+    Capture screenshot
+  </message>
+  <message name="IDS_DEVTOOLS_8394f0347c184cf156ac5924dccb773b" desc="">
+    Long
+  </message>
+  <message name="IDS_DEVTOOLS_8d2de5368588552fbae54044ac5c7b3d" desc="">
+    Rotate
+  </message>
+  <message name="IDS_DEVTOOLS_8d7b5211545faceef57a041ee56a2f5e" desc="">
+    Capture full size screenshot
+  </message>
+  <message name="IDS_DEVTOOLS_901bc0a4714ff81c24943a50e620de71" desc="">
+    User agent string
+  </message>
+  <message name="IDS_DEVTOOLS_95129d2a7de16087427f1632dc691287" desc="">
+    Hide media queries
+  </message>
+  <message name="IDS_DEVTOOLS_9b4dabc50f0b8ccba1c8981831abdad8" desc="">
+    Longitude
+  </message>
+  <message name="IDS_DEVTOOLS_9fa08fa6b5a65fd3a2e14858a2559027" desc="">
+    β (beta)
+  </message>
+  <message name="IDS_DEVTOOLS_9fd04ab471b39141991551708305ceb5" desc="">
+    Add custom device...
+  </message>
+  <message name="IDS_DEVTOOLS_a043e816a2c42b9290b672b1aa22cb73" desc="">
+    Mobile M
+  </message>
+  <message name="IDS_DEVTOOLS_a07f7588275e6b286450c54c3fb5bc92" desc="">
+    No override
+  </message>
+  <message name="IDS_DEVTOOLS_a68cfcae283e3b2a60c99386c809fd6e" desc="">
+    Shift+drag horizontally to rotate around the y-axis
+  </message>
+  <message name="IDS_DEVTOOLS_a88789aba0125292a7a2b8774a979b37" desc="">
+    Geolocations
+  </message>
+  <message name="IDS_DEVTOOLS_abbd64f40c34c537d3a571af068fce29" desc="">
+    Orientation
+  </message>
+  <message name="IDS_DEVTOOLS_af29aa178ae43211319e28f627e96590" desc="">
+    Sensors
+  </message>
+  <message name="IDS_DEVTOOLS_b0ac9a67aaac87b4b70f5e1b7292b2cc" desc="">
+    Fit to window (<ph name="THIS__MODEL_FITSCALE________">$1.0f</ph>%%)
+  </message>
+  <message name="IDS_DEVTOOLS_b1596787a9a32a2626743258abf34e80" desc="">
+    Landscape left
+  </message>
+  <message name="IDS_DEVTOOLS_b2b22e112d16f958f75ea888f9ce8eeb" desc="">
+    Display down
+  </message>
+  <message name="IDS_DEVTOOLS_b2b2df378a7c7ade199bbaf944cb3f35" desc="">
+    Responsive
+  </message>
+  <message name="IDS_DEVTOOLS_b435e227d5dd201e1768b2bcb2e0aa81" desc="">
+    height
+  </message>
+  <message name="IDS_DEVTOOLS_b85e1916c38f0c0c2d0f7c8d210bcf9e" desc="">
+    Height (leave empty for full)
+  </message>
+  <message name="IDS_DEVTOOLS_b8d750b6ef9a441434c83df4334c4fa0" desc="">
+    Display up
+  </message>
+  <message name="IDS_DEVTOOLS_b90203c84f857a0403751bf77f100400" desc="">
+    Reveal in source code
+  </message>
+  <message name="IDS_DEVTOOLS_bb3680a6300bf660b59ba8783b18689b" desc="">
+    Hide rulers
+  </message>
+  <message name="IDS_DEVTOOLS_c107e6e26d3b1b5eadbf9e842f6c0e05" desc="">
+    Add device type
+  </message>
+  <message name="IDS_DEVTOOLS_c23fc6f13afcbcd7dee750fe4f074421" desc="">
+    Lat
+  </message>
+  <message name="IDS_DEVTOOLS_c3abc91ecb930fd1d8a6b1e738eb36be" desc="">
+    Custom Geolocations
+  </message>
+  <message name="IDS_DEVTOOLS_c3c3720080d9497b9df37fcd78994144" desc="">
+    Location name
+  </message>
+  <message name="IDS_DEVTOOLS_c4ca4238a0b923820dcc509a6f75849b" desc="">
+    1
+  </message>
+  <message name="IDS_DEVTOOLS_c81e728d9d4c2f636f067f89cc14862c" desc="">
+    2
+  </message>
+  <message name="IDS_DEVTOOLS_cb211766ff41a03bb9f324be3db9dae0" desc="">
+    Mobile L
+  </message>
+  <message name="IDS_DEVTOOLS_d15305d7a4e34e02489c74a5ef542f36" desc="">
+    Off
+  </message>
+  <message name="IDS_DEVTOOLS_da31f3ff326e70f6b08748520c553920" desc="">
+    γ (gamma)
+  </message>
+  <message name="IDS_DEVTOOLS_decc67818f6be186e03a3c27c2eedc9e" desc="">
+    Double-click for full height
+  </message>
+  <message name="IDS_DEVTOOLS_e67750c8b85dbe1b938397e55488e2d8" desc="">
+    Other…
+  </message>
+  <message name="IDS_DEVTOOLS_e85c8bc51cfa9b2d3713091fdc87d551" desc="">
+    Auto-adjust zoom
+  </message>
+  <message name="IDS_DEVTOOLS_eccbc87e4b5ce2fe28308fd9f2a7baf3" desc="">
+    3
+  </message>
+  <message name="IDS_DEVTOOLS_ee1cab1975e28575b1631ee8fc4ea749" desc="">
+    Capture node screenshot
+  </message>
+  <message name="IDS_DEVTOOLS_ee4cf994f6defec5951824b44f8047b1" desc="">
+    Screen options
+  </message>
+  <message name="IDS_DEVTOOLS_f0f31c9700c6b10d8a20dc487b2ae6a8" desc="">
+    Touch
+  </message>
+  <message name="IDS_DEVTOOLS_f2a72a44f7b7d30829c0405ed2385345" desc="">
+    Mobile S
+  </message>
+  <message name="IDS_DEVTOOLS_f819de81247be4a5428dc0e169de28b8" desc="">
+    75%
+  </message>
+  <message name="IDS_DEVTOOLS_fd0a6990abd15cd06c80309237b01262" desc="">
+    Emulated Devices
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/event_listeners/event_listeners_strings.grdp b/third_party/blink/renderer/devtools/front_end/event_listeners/event_listeners_strings.grdp
new file mode 100644
index 0000000..5e2d7f6
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/event_listeners/event_listeners_strings.grdp
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_0b9c1def3b0d84fe6234388ff8dfa8b7" desc="">
+    Toggle whether event listener is passive or blocking
+  </message>
+  <message name="IDS_DEVTOOLS_1063e38cb53d94d386f21227fcd84717" desc="">
+    Remove
+  </message>
+  <message name="IDS_DEVTOOLS_7667495b756ef15210cd60285d8de58a" desc="">
+    No event listeners
+  </message>
+  <message name="IDS_DEVTOOLS_8d036bf35b7274602d923724afeb7878" desc="">
+    Toggle Passive
+  </message>
+  <message name="IDS_DEVTOOLS_ba54da0de85f689f31c5e48b3e4c0de3" desc="">
+    Delete event listener
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/heap_snapshot_worker_strings.grdp b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/heap_snapshot_worker_strings.grdp
new file mode 100644
index 0000000..32fd74a
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/heap_snapshot_worker/heap_snapshot_worker_strings.grdp
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_239026e523e6f9780426e03637c5b4a4" desc="">
+    Loading strings…
+  </message>
+  <message name="IDS_DEVTOOLS_58e8a5ca400f834526b09d680ddbc5d5" desc="">
+    Loading samples…
+  </message>
+  <message name="IDS_DEVTOOLS_6a542856aa30e3e814eaed146e67b2fd" desc="">
+    Loading nodes… <ph name="PH1">$1d</ph>%%
+  </message>
+  <message name="IDS_DEVTOOLS_7da2f27d62adf93b01c2c041f37667b9" desc="">
+    Processing snapshot…
+  </message>
+  <message name="IDS_DEVTOOLS_84c0ec1ee2bc769daf265e63bd793201" desc="">
+    Loading snapshot info…
+  </message>
+  <message name="IDS_DEVTOOLS_9bdae72f224411b7f1027a2c5c7c519b" desc="">
+    Loading locations…
+  </message>
+  <message name="IDS_DEVTOOLS_b0da59cea23d810234b75c2357748ad3" desc="">
+    Loading edges… <ph name="PH1">$1d</ph>%%
+  </message>
+  <message name="IDS_DEVTOOLS_d67d63eca2747a30e1ffaeb04389c0bb" desc="">
+    Loading allocation traces… <ph name="PH1">$1d</ph>%%
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/help/help_strings.grdp b/third_party/blink/renderer/devtools/front_end/help/help_strings.grdp
new file mode 100644
index 0000000..8c9f0a79
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/help/help_strings.grdp
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_6a26f548831e6a8c26bfbbd9f6ec61e0" desc="">
+    Help
+  </message>
+  <message name="IDS_DEVTOOLS_7d0ee6fed10d3d4e5c9ee496729ab519" desc="">
+    Release notes
+  </message>
+  <message name="IDS_DEVTOOLS_8aac48e1ab540a0467ac1c4b884f68c1" desc="">
+    Do not show What&apos;s New after each update
+  </message>
+  <message name="IDS_DEVTOOLS_8caba5463b13f01c4168decae23f9aff" desc="">
+    Show What&apos;s New after each update
+  </message>
+  <message name="IDS_DEVTOOLS_91770f038cd944a1d3b9b347edeb2b10" desc="">
+    What&apos;s New
+  </message>
+  <message name="IDS_DEVTOOLS_a1c58e94227389415de133efdf78ea6e" desc="">
+    Appearance
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/host/host_strings.grdp b/third_party/blink/renderer/devtools/front_end/host/host_strings.grdp
new file mode 100644
index 0000000..1f48de4
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/host/host_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_92bb7e733a12d24684262f046afcc2fd" desc="">
+    DevTools - <ph name="URL_REPLACE___HTTPS____________">$1s</ph>
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/inline_editor/inline_editor_strings.grdp b/third_party/blink/renderer/devtools/front_end/inline_editor/inline_editor_strings.grdp
new file mode 100644
index 0000000..ed320a5b
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/inline_editor/inline_editor_strings.grdp
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_180bc3b1c5619aaef40e803f49921012" desc="">
+    Spread
+  </message>
+  <message name="IDS_DEVTOOLS_2134e3ae0c489ce87a007a082d6a9a29" desc="">
+    X offset
+  </message>
+  <message name="IDS_DEVTOOLS_575ed851c8964c290e73055ffdc085a6" desc="">
+    Y offset
+  </message>
+  <message name="IDS_DEVTOOLS_5e9fb94d2ec46e6f829196c229e2b194" desc="">
+    Outset
+  </message>
+  <message name="IDS_DEVTOOLS_8b52de510bc5497ac43abfe566be48ab" desc="">
+    Blur
+  </message>
+  <message name="IDS_DEVTOOLS_b56d315e49178ba86341ff3b4e337c6b" desc="">
+    Inset
+  </message>
+  <message name="IDS_DEVTOOLS_d65b5983f317c42c6f6dff5678e628ca" desc="">
+    Shift-click to change color format
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/inspector_main/inspector_main_strings.grdp b/third_party/blink/renderer/devtools/front_end/inspector_main/inspector_main_strings.grdp
new file mode 100644
index 0000000..a50e0df
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/inspector_main/inspector_main_strings.grdp
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_099969ccad146161b304e766f9a1fe56" desc="">
+    Show ads on this site, if allowed
+  </message>
+  <message name="IDS_DEVTOOLS_0b2cccf3868759cb77614faf9afd0342" desc="">
+    JavaScript is disabled
+  </message>
+  <message name="IDS_DEVTOOLS_0be2a0127487580b30b7d44eb6dd57ae" desc="">
+    Paint flashing
+  </message>
+  <message name="IDS_DEVTOOLS_0ef16a6645018dec4609e392c7e9bda1" desc="">
+    Do not auto-open DevTools for popups
+  </message>
+  <message name="IDS_DEVTOOLS_115c68d941dcba0167cee3483c1bbbb2" desc="">
+    Shows layer borders (orange/olive) and tiles (cyan)
+  </message>
+  <message name="IDS_DEVTOOLS_1808fb32cf4e8a03daa326b48d4246eb" desc="">
+    Disable paused state overlay
+  </message>
+  <message name="IDS_DEVTOOLS_2b1a578586beb46008e1542e9283ad99" desc="">
+    Scrolling performance issues
+  </message>
+  <message name="IDS_DEVTOOLS_417316246f12b8235535182cc9eebe45" desc="">
+    Forces media type for testing print and screen styles
+  </message>
+  <message name="IDS_DEVTOOLS_441e4ae4f15d6bdee7939aa9bfd61010" desc="">
+    Plots frames per second, frame rate distribution, and GPU memory
+  </message>
+  <message name="IDS_DEVTOOLS_482a4dca0bbd8fcdda5acc6f95f3c279" desc="">
+    DevTools
+  </message>
+  <message name="IDS_DEVTOOLS_53f2f05226edcd77bd4351cb27d07ba8" desc="">
+    Highlights frames (red) detected to be ads.
+  </message>
+  <message name="IDS_DEVTOOLS_6eac03f446434baa29ece59d88410812" desc="">
+    FPS meter
+  </message>
+  <message name="IDS_DEVTOOLS_6f762270888a17bde49797355db7a410" desc="">
+    Shows borders around hit-test regions
+  </message>
+  <message name="IDS_DEVTOOLS_7962fd6013e985379fd3a4964d769703" desc="">
+    Hit-test borders
+  </message>
+  <message name="IDS_DEVTOOLS_7e9495c56e55fa2c5236512bf80e5d2b" desc="">
+    Don&apos;t show Chrome Data Saver warning
+  </message>
+  <message name="IDS_DEVTOOLS_8339f28e0c74e2c821b05332280c754b" desc="">
+    Force ad blocking on this site
+  </message>
+  <message name="IDS_DEVTOOLS_846495f9ceed11accf8879f555936a7d" desc="">
+    Navigation
+  </message>
+  <message name="IDS_DEVTOOLS_8634af2a16e41305fc8dca2d67360810" desc="">
+    Open dedicated DevTools for Node.js
+  </message>
+  <message name="IDS_DEVTOOLS_869a8b1ed99306604574dca474a13994" desc="">
+    Emulate CSS media
+  </message>
+  <message name="IDS_DEVTOOLS_86f849e1a655c2df19f28cb3dfe07bc9" desc="">
+    Block ads on this site
+  </message>
+  <message name="IDS_DEVTOOLS_886f598a8a9e6a4bfe0c09fcf7779611" desc="">
+    Emulate a focused page
+  </message>
+  <message name="IDS_DEVTOOLS_9182eda0635ccdc88276ba9e013c5c1b" desc="">
+    Do not emulate a focused page
+  </message>
+  <message name="IDS_DEVTOOLS_960568e27eb66ea554c3be4cfbefe3d0" desc="">
+    Layer borders
+  </message>
+  <message name="IDS_DEVTOOLS_a02c83a7dbd96295beaefb72c2bee2de" desc="">
+    Main
+  </message>
+  <message name="IDS_DEVTOOLS_a15932c4dfbbcbc1ac6f5c27c6fa530f" desc="">
+    Highlights elements (teal) that can slow down scrolling, including touch &amp; wheel event handlers and other main-thread scrolling situations.
+  </message>
+  <message name="IDS_DEVTOOLS_a6e8f9aed2ac6481dc25a18a33342d03" desc="">
+    Rendering
+  </message>
+  <message name="IDS_DEVTOOLS_cb835af5f855f79e8611dd3f8fec6aac" desc="">
+    Reload page
+  </message>
+  <message name="IDS_DEVTOOLS_d543dbe64db0c952d6e13c9519218b3e" desc="">
+    Hard reload page
+  </message>
+  <message name="IDS_DEVTOOLS_ef95393ad48336d7c3543625354a3d56" desc="">
+    Highlight ad frames
+  </message>
+  <message name="IDS_DEVTOOLS_f23c9ba06e7123f0b4c906de90fbcc9f" desc="">
+    Auto-open DevTools for popups
+  </message>
+  <message name="IDS_DEVTOOLS_f6b139a6b392ab11c9c5c8210c525688" desc="">
+    Highlights areas of the page (green) that need to be repainted
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/js_profiler/js_profiler_strings.grdp b/third_party/blink/renderer/devtools/front_end/js_profiler/js_profiler_strings.grdp
new file mode 100644
index 0000000..cbe64e0
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/js_profiler/js_profiler_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_65b4c7424dd695c30efa73da8396c90c" desc="">
+    Profiler
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd b/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
new file mode 100644
index 0000000..132a5c4
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- DevTools-specific strings. -->
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false" source_lang_id="en">
+  <outputs>
+    <output filename="devtools_ui_strings.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="devtools_ui_strings_en-US.pak" type="data_package" lang="en-US" />
+  </outputs>
+  <translations>
+  </translations>
+  <release seq="1" allow_pseudo="false">
+    <messages fallback_to_english="true">
+      <part file="../accessibility/accessibility_strings.grdp" />
+      <part file="../animation/animation_strings.grdp" />
+      <part file="../audits2/audits2_strings.grdp" />
+      <part file="../bindings/bindings_strings.grdp" />
+      <part file="../browser_debugger/browser_debugger_strings.grdp" />
+      <part file="../browser_sdk/browser_sdk_strings.grdp" />
+      <part file="../changes/changes_strings.grdp" />
+      <part file="../color_picker/color_picker_strings.grdp" />
+      <part file="../components/components_strings.grdp" />
+      <part file="../console/console_strings.grdp" />
+      <part file="../console_counters/console_counters_strings.grdp" />
+      <part file="../cookie_table/cookie_table_strings.grdp" />
+      <part file="../coverage/coverage_strings.grdp" />
+      <part file="../data_grid/data_grid_strings.grdp" />
+      <part file="../devices/devices_strings.grdp" />
+      <part file="../elements/elements_strings.grdp" />
+      <part file="../emulated_devices/emulated_devices_strings.grdp" />
+      <part file="../emulation/emulation_strings.grdp" />
+      <part file="../event_listeners/event_listeners_strings.grdp" />
+      <part file="../heap_snapshot_worker/heap_snapshot_worker_strings.grdp" />
+      <part file="../help/help_strings.grdp" />
+      <part file="../host/host_strings.grdp" />
+      <part file="../inline_editor/inline_editor_strings.grdp" />
+      <part file="../inspector_main/inspector_main_strings.grdp" />
+      <part file="../js_profiler/js_profiler_strings.grdp" />
+      <part file="../layer_viewer/layer_viewer_strings.grdp" />
+      <part file="../layers/layers_strings.grdp" />
+      <part file="../main/main_strings.grdp" />
+      <part file="../mobile_throttling/mobile_throttling_strings.grdp" />
+      <part file="../network/network_strings.grdp" />
+      <part file="../node_debugger/node_debugger_strings.grdp" />
+      <part file="../node_main/node_main_strings.grdp" />
+      <part file="../object_ui/object_ui_strings.grdp" />
+      <part file="../perf_ui/perf_ui_strings.grdp" />
+      <part file="../performance_monitor/performance_monitor_strings.grdp" />
+      <part file="../persistence/persistence_strings.grdp" />
+      <part file="../product_registry/product_registry_strings.grdp" />
+      <part file="../profiler/profiler_strings.grdp" />
+      <part file="../protocol_monitor/protocol_monitor_strings.grdp" />
+      <part file="../quick_open/quick_open_strings.grdp" />
+      <part file="../resources/resources_strings.grdp" />
+      <part file="../screencast/screencast_strings.grdp" />
+      <part file="../sdk/sdk_strings.grdp" />
+      <part file="../search/search_strings.grdp" />
+      <part file="../security/security_strings.grdp" />
+      <part file="../settings/settings_strings.grdp" />
+      <part file="../snippets/snippets_strings.grdp" />
+      <part file="../source_frame/source_frame_strings.grdp" />
+      <part file="../sources/sources_strings.grdp" />
+      <part file="../terminal/terminal_strings.grdp" />
+      <part file="../text_editor/text_editor_strings.grdp" />
+      <part file="../timeline/timeline_strings.grdp" />
+      <part file="../timeline_model/timeline_model_strings.grdp" />
+      <part file="../ui/ui_strings.grdp" />
+      <part file="../web_audio/web_audio_strings.grdp" />
+      <part file="../workspace/workspace_strings.grdp" />
+    </messages>
+  </release>
+</grit>
diff --git a/third_party/blink/renderer/devtools/front_end/layer_viewer/layer_viewer_strings.grdp b/third_party/blink/renderer/devtools/front_end/layer_viewer/layer_viewer_strings.grdp
new file mode 100644
index 0000000..8fc580f
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/layer_viewer/layer_viewer_strings.grdp
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_05245b74ad79bb210a68107f7de0c27f" desc="">
+    Layer for scrolling container.
+  </message>
+  <message name="IDS_DEVTOOLS_08415c56f6c6c2efb0218629836e31bc" desc="">
+    Composition due to association with an element with a &quot;backface-visibility: hidden&quot; style.
+  </message>
+  <message name="IDS_DEVTOOLS_0aa3ae694efd06789ae1867c80d30222" desc="">
+    Composition due to association with an element with a &quot;position: fixed&quot; or &quot;position: sticky&quot; style.
+  </message>
+  <message name="IDS_DEVTOOLS_0b4eb322e51b61340668247151340346" desc="">
+    Rotate mode (V)
+  </message>
+  <message name="IDS_DEVTOOLS_0dd70cbbb618cf0ea39d7e01238ca1b9" desc="">
+    Composition due to association with an animated element.
+  </message>
+  <message name="IDS_DEVTOOLS_135d78fcc62ff8df8028c33c768900c5" desc="">
+    Layer for background.
+  </message>
+  <message name="IDS_DEVTOOLS_219e2fb2d37a581ab2bae57397b601f1" desc="">
+    &lt;unnamed&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_233612553595af00e5d227e136252422" desc="">
+    Composition due to association with an element with composited descendants.
+  </message>
+  <message name="IDS_DEVTOOLS_25d066be1268d5c07cae5cc26e06ad2b" desc="">
+    Slow scroll regions
+  </message>
+  <message name="IDS_DEVTOOLS_28b5b4891eb05cc50f537526b24038a9" desc="">
+    Composition due to association with a masked element and composited descendants.
+  </message>
+  <message name="IDS_DEVTOOLS_2a2924d1d77c00d27ce2509c34861804" desc="">
+    Layer for clip.
+  </message>
+  <message name="IDS_DEVTOOLS_2ae207ddfe5cd256a768769d93a879e5" desc="">
+    Paint Profiler
+  </message>
+  <message name="IDS_DEVTOOLS_2ca2308d45c04266f0fb51786e407287" desc="">
+    Composition due to association with an element with CSS filters applied and composited descendants.
+  </message>
+  <message name="IDS_DEVTOOLS_3228417bf9ed949e7447e7c8dcb17090" desc="">
+    Memory estimate
+  </message>
+  <message name="IDS_DEVTOOLS_37de7295335e95cb7caf78303a1edad3" desc="">
+    Composition due to association with an element with a CSS 3D transform.
+  </message>
+  <message name="IDS_DEVTOOLS_398b889c147f3b2cb3c674cfb07315b5" desc="">
+    Composition due to association with an element with opacity applied and composited descendants.
+  </message>
+  <message name="IDS_DEVTOOLS_3f03ed88ee58335aa3df453443b9c8bc" desc="">
+    Profiling…
+  </message>
+  <message name="IDS_DEVTOOLS_441f5e043a240bac8dca50f90f7be3de" desc="">
+    repaints on scroll
+  </message>
+  <message name="IDS_DEVTOOLS_45567a52ec61e3e2b5469b397db3fc2a" desc="">
+    <ph name="LAYERVIEWER_LAYERDETAILSVIEW__SLOWSCROLLRECTNAMES_GET_SCROLLRECT_TYPE_">$1s</ph> <ph name="SCROLLRECT_RECT_X">$2d</ph> × <ph name="SCROLLRECT_RECT_Y">$3d</ph> (at <ph name="SCROLLRECT_RECT_WIDTH">$4d</ph>, <ph name="SCROLLRECT_RECT_HEIGHT">$5d</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_459be6cdd6650871d6b740d1ce6bc7a3" desc="">
+    mousewheel event listener
+  </message>
+  <message name="IDS_DEVTOOLS_4881cc76c5e57422d2841026b9b93692" desc="">
+    Wheel event handler
+  </message>
+  <message name="IDS_DEVTOOLS_49ceda18652e222780b8a35f506b4678" desc="">
+    Pan mode (X)
+  </message>
+  <message name="IDS_DEVTOOLS_4a1a29f7d8a02e0294dc9134e97e9134" desc="">
+    Sticky position constraint
+  </message>
+  <message name="IDS_DEVTOOLS_4ee55709b19d806bb2f83a264a8a0f75" desc="">
+    Sticky Box <ph name="STICKYBOXRECT_WIDTH">$1d</ph> × <ph name="STICKYBOXRECT_HEIGHT">$2d</ph> (at <ph name="STICKYBOXRECT_X">$3d</ph>, <ph name="STICKYBOXRECT_Y">$4d</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_5337a3314b26561ccf364e1777455933" desc="">
+    Composition due to association with a &lt;video&gt; element.
+  </message>
+  <message name="IDS_DEVTOOLS_5639e2813d9549835010567a48349127" desc="">
+     (<ph name="THIS__LAYER_WIDTH__">$1d</ph> × <ph name="THIS__LAYER_HEIGHT__">$2d</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_602f4c0713480f019b24f09218dcc7e0" desc="">
+    Reset View
+  </message>
+  <message name="IDS_DEVTOOLS_697e0fb75c6cf44734697517d44b5922" desc="">
+    Composition due to association with an element clipping compositing descendants.
+  </message>
+  <message name="IDS_DEVTOOLS_6fac3a4381f0fb07bbf498a08d301d40" desc="">
+    Repaints on scroll
+  </message>
+  <message name="IDS_DEVTOOLS_7069651490219723e31a486d60d96fc8" desc="">
+    Nearest Layer Shifting Sticky Box
+  </message>
+  <message name="IDS_DEVTOOLS_70baf8087d35f6bf5bde8b7c0645a1e8" desc="">
+    <ph name="TITLE">$1s</ph>: <ph name="NAME">$2s</ph> (<ph name="LAYER_ID__">$3s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_71084df87a4cc161f7542a0f5295a950" desc="">
+    Select a layer to see its details
+  </message>
+  <message name="IDS_DEVTOOLS_73c57dbc070bac535d75b4abe9f614a3" desc="">
+    Composition due to association with an element overlapping other composited elements.
+  </message>
+  <message name="IDS_DEVTOOLS_74248c725e00bf9fe04df4e35b249a19" desc="">
+    Misc
+  </message>
+  <message name="IDS_DEVTOOLS_74ae7b35b54cd38a932ccc4f1d1c3775" desc="">
+    Touch event handler
+  </message>
+  <message name="IDS_DEVTOOLS_791faaf55ae39199cc0b4edde837f07a" desc="">
+    Layer for video overlay.
+  </message>
+  <message name="IDS_DEVTOOLS_7eb830853b9ed22a567e4d7582eafe7c" desc="">
+    Composition due to association with an element with CSS filters applied.
+  </message>
+  <message name="IDS_DEVTOOLS_82505980aa6b0cabf4cb8013a8a43b62" desc="">
+    Non fast scrollable
+  </message>
+  <message name="IDS_DEVTOOLS_82d7a4981a35bb7f6dfc8a5485a61bc6" desc="">
+    <ph name="LAYER_WIDTH__">$1d</ph> × <ph name="LAYER_HEIGHT__">$2d</ph> (at <ph name="LAYER_OFFSETX__">$3d</ph>,<ph name="LAYER_OFFSETY__">$4d</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_86ee74baff479d85d18f2cda9f8a9518" desc="">
+    Bitmap
+  </message>
+  <message name="IDS_DEVTOOLS_8810c4e0c498b81a6e4adaeabcb9f624" desc="">
+    Composition due to association with an element with a &quot;overflow-scrolling: touch&quot; style.
+  </message>
+  <message name="IDS_DEVTOOLS_894982f4a3bc0e770cece61dd12dde55" desc="">
+    Composition due to association with an &lt;iframe&gt; element.
+  </message>
+  <message name="IDS_DEVTOOLS_932a3ef55ce8133be7b33ded2992fd97" desc="">
+    Compositing Reasons
+  </message>
+  <message name="IDS_DEVTOOLS_93c3341232864688bcea4d53dff1bf10" desc="">
+    Composition due to association with an element with perspective applied.
+  </message>
+  <message name="IDS_DEVTOOLS_991bd14abd9e6004de4e7b9bc0769d9d" desc="">
+    Layer for foreground.
+  </message>
+  <message name="IDS_DEVTOOLS_9b8379825de60ccb900acf83934e1409" desc="">
+    Show internal layers
+  </message>
+  <message name="IDS_DEVTOOLS_9dffbf69ffba8bc38bc4e01abf4b1675" desc="">
+    Text
+  </message>
+  <message name="IDS_DEVTOOLS_a673813c8d6c5f511cc4d80830fc4503" desc="">
+    Can&apos;t display layers,
+  </message>
+  <message name="IDS_DEVTOOLS_aefd09cd861bbb01d381cfd74856e877" desc="">
+    Composition due to the element being a &lt;canvas&gt; element.
+  </message>
+  <message name="IDS_DEVTOOLS_b336b2cbaa7391e6e0dfef888d35a837" desc="">
+    touch event listener
+  </message>
+  <message name="IDS_DEVTOOLS_b4cc0ce8b989d360cf2bef966626e683" desc="">
+    Layer information is not yet available.
+  </message>
+  <message name="IDS_DEVTOOLS_b520b88ac5b8fbc5138849baddef2040" desc="">
+    Paint count
+  </message>
+  <message name="IDS_DEVTOOLS_b7fec868f7541e449cbb32b506aa3668" desc="">
+    Check <ph name="UI_XLINK_CREATE__ABOUT_GPU__">$1s</ph> for possible reasons.
+  </message>
+  <message name="IDS_DEVTOOLS_c0f4be326648f3fee18ffd278b614126" desc="">
+    Profiling Results
+  </message>
+  <message name="IDS_DEVTOOLS_c634209b1884c963528d21a21cb64ac8" desc="">
+    Nearest Layer Shifting Containing Block
+  </message>
+  <message name="IDS_DEVTOOLS_cd5be434fea99c341680590738e3b6d5" desc="">
+    WebGL support is disabled in your browser.
+  </message>
+  <message name="IDS_DEVTOOLS_ce1efcf7f040106cc8cb2f24c9efee35" desc="">
+    Show Paint Profiler
+  </message>
+  <message name="IDS_DEVTOOLS_cf4db365a841e7208aad0d474cbfa8e3" desc="">
+    Composition due to association with an element with descendants that have a negative z-index.
+  </message>
+  <message name="IDS_DEVTOOLS_d446f65fe70f9a640908576792497e25" desc="">
+    Composition due to association with an element with a reflection and composited descendants.
+  </message>
+  <message name="IDS_DEVTOOLS_dd4d335a4deb975546acc9fb5aa3ffce" desc="">
+    Composition due to association with an element that may overlap other composited elements.
+  </message>
+  <message name="IDS_DEVTOOLS_dff689f21faf977ee865e312c011c84a" desc="">
+    Root layer.
+  </message>
+  <message name="IDS_DEVTOOLS_e397c87a940fc2c10225a407c7f124a7" desc="">
+    Composition due to association with a plugin.
+  </message>
+  <message name="IDS_DEVTOOLS_e763043c4af617c6f33da361d990a77b" desc="">
+    Reset transform (0)
+  </message>
+  <message name="IDS_DEVTOOLS_e7ee6a7ae0e6f824f5609c7bdac34375" desc="">
+    Shapes
+  </message>
+  <message name="IDS_DEVTOOLS_ed3a481889f16ef0aab2349f5d8b5f12" desc="">
+    Containing Block <ph name="CONTAININGBLOCKRECT_WIDTH">$1d</ph> × <ph name="CONTAININGBLOCKRECT_HEIGHT">$2d</ph> (at <ph name="CONTAININGBLOCKRECT_X">$3d</ph>, <ph name="CONTAININGBLOCKRECT_Y">$4d</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_f1eb415fdb8a15856c29cc2b679e416b" desc="">
+    Composition due to association with an element with a &quot;transform-style: preserve-3d&quot; style.
+  </message>
+  <message name="IDS_DEVTOOLS_f2a4f55a424726ae504f2e694e3dc2a2" desc="">
+    Composition due to association with an element with CSS blending applied and composited descendants.
+  </message>
+  <message name="IDS_DEVTOOLS_fa055d01c0a331d642c2f55a39d4e1bb" desc="">
+    Composition due to association with an element that has blend mode other than &quot;normal&quot;.
+  </message>
+  <message name="IDS_DEVTOOLS_fb8ed4e0cd9d6ac3630df425c80d9036" desc="">
+    Layer for mask.
+  </message>
+  <message name="IDS_DEVTOOLS_fd63c694ddc8073cf10bb8025aa9aaaf" desc="">
+    Layer for scrollbar.
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/layers/layers_strings.grdp b/third_party/blink/renderer/devtools/front_end/layers/layers_strings.grdp
new file mode 100644
index 0000000..ce01cd6
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/layers/layers_strings.grdp
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_3ec365dd533ddb7ef3d1c111186ce872" desc="">
+    Details
+  </message>
+  <message name="IDS_DEVTOOLS_87bfda183c4f851a101e97bbb1bbace7" desc="">
+    Layers
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/main/main_strings.grdp b/third_party/blink/renderer/devtools/front_end/main/main_strings.grdp
new file mode 100644
index 0000000..5f0c5c16
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/main/main_strings.grdp
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_00463255013f500126fb5cc8c824e384" desc="">
+    Panel layout:
+  </message>
+  <message name="IDS_DEVTOOLS_08e71f00acd63d0a546ba42f9e780ddc" desc="">
+    Customize and control DevTools
+  </message>
+  <message name="IDS_DEVTOOLS_0a0afb342478f201b92cbc89d19bf14d" desc="">
+    Switch to dark theme
+  </message>
+  <message name="IDS_DEVTOOLS_13348442cc6a27032d2b4aa28b75a5d3" desc="">
+    Search
+  </message>
+  <message name="IDS_DEVTOOLS_15fa1acc2a998159c4ed2a950c5c231d" desc="">
+    Toggle drawer
+  </message>
+  <message name="IDS_DEVTOOLS_1b1ac1c7a54457ce73ddd25a5b51c47d" desc="">
+    Drawer sidebar
+  </message>
+  <message name="IDS_DEVTOOLS_1cf59b16fd253d6ff4bbee7c237cea0d" desc="">
+    Show console
+  </message>
+  <message name="IDS_DEVTOOLS_2ab5656e27a0ed871c8de44368f807c6" desc="">
+    Styles Pane
+  </message>
+  <message name="IDS_DEVTOOLS_2ad9d63b69c4a10a5cc9cad923133bc4" desc="">
+    Bottom
+  </message>
+  <message name="IDS_DEVTOOLS_36634aa462472ceb907f3aa4401d3a61" desc="">
+    Undocked
+  </message>
+  <message name="IDS_DEVTOOLS_3b40834e60f26865c1b9582f1745476e" desc="">
+    Use horizontal panel layout
+  </message>
+  <message name="IDS_DEVTOOLS_3b7e62b6a175b75f4ecf012d17ba7fef" desc="">
+    Focus debuggee
+  </message>
+  <message name="IDS_DEVTOOLS_4505cad087312551a6fbbe6ebe163e0f" desc="">
+    horizontal
+  </message>
+  <message name="IDS_DEVTOOLS_4f1133a5722c0189f59e0490b2ae1c31" desc="">
+    Go to source
+  </message>
+  <message name="IDS_DEVTOOLS_581138047ce1459b079a8c4abba128a1" desc="">
+    Go to the panel to the left/right
+  </message>
+  <message name="IDS_DEVTOOLS_586740a88e6502850bc64f9f2d04d380" desc="">
+    Placement of DevTools relative to the page. (<ph name="TOGGLEDOCKSIDESHORCUTS____NAME">$1s</ph> to restore last position)
+  </message>
+  <message name="IDS_DEVTOOLS_5bf4a92da989a6943e3fed84f18c3f0f" desc="">
+    Search across all sources
+  </message>
+  <message name="IDS_DEVTOOLS_5e2f5f3c24ae8c6ab3eca618826b0e23" desc="">
+    Extensions
+  </message>
+  <message name="IDS_DEVTOOLS_62fcd77216b456123c822a40ded03347" desc="">
+    Select node to inspect
+  </message>
+  <message name="IDS_DEVTOOLS_71f0df44b05415f2d20b59bb6188a118" desc="">
+    Toggle dock side
+  </message>
+  <message name="IDS_DEVTOOLS_75def4784fa71c4ccde7762316ed0e3e" desc="">
+    Find next/previous
+  </message>
+  <message name="IDS_DEVTOOLS_787ae37366b7a8c53f381abfd315c647" desc="">
+    Elements Panel
+  </message>
+  <message name="IDS_DEVTOOLS_8645bf45e76c3f958ae99c114c419743" desc="">
+    Hide console drawer
+  </message>
+  <message name="IDS_DEVTOOLS_92b09c7c48c520c3c55e497875da437c" desc="">
+    Right
+  </message>
+  <message name="IDS_DEVTOOLS_93445501d5239cedddc0bd6a0a453a95" desc="">
+    Dock side
+  </message>
+  <message name="IDS_DEVTOOLS_9914a0ce04a7b7b6a8e39bec55064b82" desc="">
+    Light
+  </message>
+  <message name="IDS_DEVTOOLS_9b1ed2cbe56f7a315f0a365d2b97cb59" desc="">
+    Drawer
+  </message>
+  <message name="IDS_DEVTOOLS_a18366b217ebf811ad1886e4f4f865b2" desc="">
+    Dark
+  </message>
+  <message name="IDS_DEVTOOLS_a9e4402481bd9b8e36752bf731f67eb6" desc="">
+    Theme:
+  </message>
+  <message name="IDS_DEVTOOLS_b0659ae6872366589300ed6c2033ba26" desc="">
+    Toggle device mode
+  </message>
+  <message name="IDS_DEVTOOLS_b3bf148a3dc8b3367191419e52380982" desc="">
+    Use vertical panel layout
+  </message>
+  <message name="IDS_DEVTOOLS_b8da6df14bf06283cbf588df6998722e" desc="">
+    Panel
+  </message>
+  <message name="IDS_DEVTOOLS_b94d8a074eddd267702810179875737f" desc="">
+    Debugger
+  </message>
+  <message name="IDS_DEVTOOLS_b9cd538a753713464d2ea17d7e975041" desc="">
+    More tools
+  </message>
+  <message name="IDS_DEVTOOLS_c0fef4f5cd015e4b1e5477f511dc3939" desc="">
+    Use automatic panel layout
+  </message>
+  <message name="IDS_DEVTOOLS_c2376cec424de4cead5272c492d3d269" desc="">
+    Restore last dock position
+  </message>
+  <message name="IDS_DEVTOOLS_d33bf99e0756e1d5008cb78902e38ca9" desc="">
+    All Panels
+  </message>
+  <message name="IDS_DEVTOOLS_db5d5dd2ff01f52ffee4b57ca2b1fcfd" desc="">
+    Enable Ctrl + 1-9 shortcut to switch panels
+  </message>
+  <message name="IDS_DEVTOOLS_debb0ba164edd1f9254b644de3c6d716" desc="">
+    Enable ⌘ + 1-9 shortcut to switch panels
+  </message>
+  <message name="IDS_DEVTOOLS_e472458a8014f14176e3c3c3d4dd0ad9" desc="">
+    Switch to light theme
+  </message>
+  <message name="IDS_DEVTOOLS_e6dec152d6a941fccb0a5e8cc2579cc3" desc="">
+    vertical
+  </message>
+  <message name="IDS_DEVTOOLS_eafbab965ec3970b0e97705882ebf8d0" desc="">
+    Show console drawer
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/mobile_throttling/mobile_throttling_strings.grdp b/third_party/blink/renderer/devtools/front_end/mobile_throttling/mobile_throttling_strings.grdp
new file mode 100644
index 0000000..ec6a7fbe
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/mobile_throttling/mobile_throttling_strings.grdp
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_07a573e3035e9f8a7380a801df3a920f" desc="">
+    Slow 3G &amp; 6x CPU slowdown
+  </message>
+  <message name="IDS_DEVTOOLS_0936559ca32dc012bcc81444bec17ea4" desc="">
+    Force disconnected from network
+  </message>
+  <message name="IDS_DEVTOOLS_1363b7d2b4c0a3ac3a89b3ba69d59be7" desc="">
+    Network throttling is enabled
+  </message>
+  <message name="IDS_DEVTOOLS_17f66753218a6a1e2762c6a60db3d7c4" desc="">
+    Mid-tier mobile
+  </message>
+  <message name="IDS_DEVTOOLS_26ae7bdd1d6fb8c4886e6fde8d12601c" desc="">
+    Latency
+  </message>
+  <message name="IDS_DEVTOOLS_275e2c1079aeffdb5f4e22580127b95b" desc="">
+    <ph name="CONDITIONS_LATENCY">$1d</ph>ms
+  </message>
+  <message name="IDS_DEVTOOLS_3602f195c0823b75591b5546c3d5df00" desc="">
+    Requests may be rewritten by local overrides
+  </message>
+  <message name="IDS_DEVTOOLS_37234a1bcbabe4e8e7448f4bbcf5c2b4" desc="">
+    <ph name="THROUGHPUTINKBPS">$1d</ph><ph name="DELIMITER">$2s</ph>kb/s
+  </message>
+  <message name="IDS_DEVTOOLS_38c8cb828795cd6ff1cddda9b4ffcfa8" desc="">
+    <ph name="THROUGHPUTINKBPS___________">$1d</ph><ph name="DELIMITER">$2s</ph>Mb/s
+  </message>
+  <message name="IDS_DEVTOOLS_4fafba98ddd8a8663d8775a928f2450e" desc="">
+    Requests may be blocked
+  </message>
+  <message name="IDS_DEVTOOLS_56d7a17ecb7d30faf8daf8a6eb57e3bb" desc="">
+    Go offline
+  </message>
+  <message name="IDS_DEVTOOLS_6ba7dc45786c25d1e5879b2166894fc5" desc="">
+    Go online
+  </message>
+  <message name="IDS_DEVTOOLS_801ab24683a4a8c433c6eb40c48bcd9d" desc="">
+    Download
+  </message>
+  <message name="IDS_DEVTOOLS_8493534a13107469ea63be99410bebfa" desc="">
+    Enable slow 3G throttling
+  </message>
+  <message name="IDS_DEVTOOLS_8d9da4bc0e49a50e09ac9f7e56789d39" desc="">
+    Offline
+  </message>
+  <message name="IDS_DEVTOOLS_8fd93ea382b6642f13ff291b76f5bc85" desc="">
+    Profile Name
+  </message>
+  <message name="IDS_DEVTOOLS_90589c47f06eb971d548591f23c285af" desc="">
+    Custom
+  </message>
+  <message name="IDS_DEVTOOLS_91412465ea9169dfd901dd5e7c96dd99" desc="">
+    Upload
+  </message>
+  <message name="IDS_DEVTOOLS_9b6545e4cea9b4ad4979d41bb9170e2b" desc="">
+    Advanced
+  </message>
+  <message name="IDS_DEVTOOLS_a808cbcba081a943f4faedda831298b5" desc="">
+    kb/s
+  </message>
+  <message name="IDS_DEVTOOLS_b27fdc01c81857f96f3d5d37d323bcb6" desc="">
+    Enable fast 3G throttling
+  </message>
+  <message name="IDS_DEVTOOLS_b9f5c797ebbf55adccdd8539a65a0241" desc="">
+    Disabled
+  </message>
+  <message name="IDS_DEVTOOLS_c47c85f963782364d32f428fcf7631ff" desc="">
+    No internet connectivity
+  </message>
+  <message name="IDS_DEVTOOLS_c939c288fa6bfab8b8bfb9b86593be20" desc="">
+    Add custom profile...
+  </message>
+  <message name="IDS_DEVTOOLS_c96084dae377c53ea7e233fc8255e6d5" desc="">
+    Fast 3G &amp; 4x CPU slowdown
+  </message>
+  <message name="IDS_DEVTOOLS_cb67c2b6640235af7393ca5970c1e887" desc="">
+    <ph name="RATE">$1d</ph>× slowdown
+  </message>
+  <message name="IDS_DEVTOOLS_d33468989239f9d3c708d05dcd7824f3" desc="">
+    <ph name="THROUGHPUTINKBPS_______">$1.1f</ph><ph name="DELIMITER">$2s</ph>Mb/s
+  </message>
+  <message name="IDS_DEVTOOLS_d49429dd3e92758c218f94ed0219d620" desc="">
+    CPU throttling is enabled
+  </message>
+  <message name="IDS_DEVTOOLS_d57c24f3fe52d16e7169b912dd647f0d" desc="">
+    optional
+  </message>
+  <message name="IDS_DEVTOOLS_eb81441fe20963ff9e8e69c5b32fc625" desc="">
+    Presets
+  </message>
+  <message name="IDS_DEVTOOLS_ee33e909372d935d190f4fcb2a92d542" desc="">
+    ms
+  </message>
+  <message name="IDS_DEVTOOLS_f410c3f1a07f566591283f599701e4ab" desc="">
+    Add…
+  </message>
+  <message name="IDS_DEVTOOLS_fa481f0018f093f34f49bd0f555653f3" desc="">
+    Check Network and Performance panels
+  </message>
+  <message name="IDS_DEVTOOLS_fcd5c08df997867efb2d80f45c4e1a6c" desc="">
+    Low-end mobile
+  </message>
+  <message name="IDS_DEVTOOLS_ff45c788a49f2900f274da0c350dcd1d" desc="">
+    Network Throttling Profiles
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
new file mode 100644
index 0000000..175cbe94a
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
@@ -0,0 +1,768 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_0194336f90a2b1de65553e3bec01becd" desc="">
+    Copy as fetch
+  </message>
+  <message name="IDS_DEVTOOLS_01a8f976083c550f4d5ba8452ec80efb" desc="">
+    No custom headers
+  </message>
+  <message name="IDS_DEVTOOLS_02d56cf7754ce16d7ce0193fbca7c90a" desc="">
+    Caching
+  </message>
+  <message name="IDS_DEVTOOLS_040c4b52a3c06c6067fac76c4c7c3a2c" desc="">
+    Network conditions
+  </message>
+  <message name="IDS_DEVTOOLS_059f48d76d79fe0727740d75dc81fa94" desc="">
+    <ph name="SELECTEDTRANSFERSIZE">$1s</ph> B / <ph name="TRANSFERSIZE">$2s</ph> B transferred
+  </message>
+  <message name="IDS_DEVTOOLS_06337a0bc6e91b2adb10567bee4d6f14" desc="">
+    Copy all as HAR
+  </message>
+  <message name="IDS_DEVTOOLS_079accf18103a0f8b9a76ce09cb147d7" desc="">
+    Add pattern
+  </message>
+  <message name="IDS_DEVTOOLS_08af23a4affc4689447d59935ba3b3b3" desc="">
+    Hit <ph name="RELOADSHORTCUTDESCRIPTOR_NAME">$1s</ph> to reload and capture filmstrip.
+  </message>
+  <message name="IDS_DEVTOOLS_09bad44011fa6a5a64ada3424ad85f03" desc="">
+    <ph name="LOCALIZEDDESCRIPTION">$1s</ph> (Opcode <ph name="OPCODE">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_09c9ea004b0c0822b75bdec322b90ff4" desc="">
+    Waterfall
+  </message>
+  <message name="IDS_DEVTOOLS_0bcc4ec10c2e4cee4b9bbabd48501304" desc="">
+    Select message to browse its content.
+  </message>
+  <message name="IDS_DEVTOOLS_0db377921f4ce762c62526131097968f" desc="">
+    General
+  </message>
+  <message name="IDS_DEVTOOLS_0f44e9e39cbe2f22bd1bd7b6070cf13d" desc="">
+    Enable request blocking
+  </message>
+  <message name="IDS_DEVTOOLS_0f591b1d87c91e77d4fa11dc6e44288c" desc="">
+    Request and response timeline
+  </message>
+  <message name="IDS_DEVTOOLS_117068a6e55846888cbb60978fe82ca2" desc="">
+    Stalled
+  </message>
+  <message name="IDS_DEVTOOLS_11803f40c277b679b08ce0b8d38fdefa" desc="">
+    Preload
+  </message>
+  <message name="IDS_DEVTOOLS_12881f9e98bb240671bc3e1787d64a03" desc="">
+    Color code by resource type
+  </message>
+  <message name="IDS_DEVTOOLS_16f78b9465f03f7ec5a03c24918fbde3" desc="">
+    Status Code
+  </message>
+  <message name="IDS_DEVTOOLS_1854180b02f297deebed0ec452090842" desc="">
+    Served from memory cache, resource size: <ph name="RESOURCESIZE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_197d4c4eb38925e0c8ab3f2998cd893f" desc="">
+    Perform a request or hit <ph name="RELOADSHORTCUTNODE">$1s</ph> to record the reload.
+  </message>
+  <message name="IDS_DEVTOOLS_1a0534b264da645e89fcf2cc86ec4cb5" desc="">
+    (from prefetch cache)
+  </message>
+  <message name="IDS_DEVTOOLS_1a8b6125c4be73210788e4fad96bdbdb" desc="">
+    Server Push
+  </message>
+  <message name="IDS_DEVTOOLS_1b581452e4e1158b75e514c99a760431" desc="">
+    Request Payload
+  </message>
+  <message name="IDS_DEVTOOLS_1c047e32d57295a9f0319976c543162e" desc="">
+    (blocked:<ph name="REASON">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_1cb07ce276f37ba2acdfb8605aaefbb8" desc="">
+    <ph name="RESOURCESIZE">$1s</ph> B resources
+  </message>
+  <message name="IDS_DEVTOOLS_1dc4a71b18d2c3a1442267136158128e" desc="">
+    Copy all as cURL (cmd)
+  </message>
+  <message name="IDS_DEVTOOLS_1eb445fa6bd078346bf63ecac35fa77c" desc="">
+    Base64
+  </message>
+  <message name="IDS_DEVTOOLS_2537b3e6907e17001b7cdf121cd39dc0" desc="">
+    (from ServiceWorker)
+  </message>
+  <message name="IDS_DEVTOOLS_26b6b5ae2c6e2575b2a451c35e94df23" desc="">
+    Waiting (TTFB)
+  </message>
+  <message name="IDS_DEVTOOLS_296d9aa276c7b25db8cf45733d8d4042" desc="">
+    Signed HTTP exchange
+  </message>
+  <message name="IDS_DEVTOOLS_29d37bdcfeb74d25d56bcf841866ae32" desc="">
+    Served from ServiceWorker, resource size: <ph name="RESOURCESIZE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2bbc845892f400626baab5af687527c5" desc="">
+    Start Time
+  </message>
+  <message name="IDS_DEVTOOLS_2d13df6f8b5e4c5af9f87e0dc39df69d" desc="">
+    Pending
+  </message>
+  <message name="IDS_DEVTOOLS_2ef0ae0c4d76a87b28176de205d7d346" desc="">
+    Recording frames...
+  </message>
+  <message name="IDS_DEVTOOLS_2f618132e0f9cadb9eb3759327b70e74" desc="">
+    Import HAR file...
+  </message>
+  <message name="IDS_DEVTOOLS_30ad3ea5e350075c1c0a1171c5006ed7" desc="">
+    Fetching frames...
+  </message>
+  <message name="IDS_DEVTOOLS_3136ed000d43bebb4ca53a9312fbd32c" desc="">
+    Add pattern.
+  </message>
+  <message name="IDS_DEVTOOLS_31977081ea2c828cc70e6151ab5d7da8" desc="">
+    view decoded
+  </message>
+  <message name="IDS_DEVTOOLS_31fde7b05ac8952dacf4af8a704074ec" desc="">
+    Preview
+  </message>
+  <message name="IDS_DEVTOOLS_336439019ce67912717a20d54298bf24" desc="">
+    Receiving Push
+  </message>
+  <message name="IDS_DEVTOOLS_346ff32eaa3c09983fb2ec057816d352" desc="">
+    TIME
+  </message>
+  <message name="IDS_DEVTOOLS_3681a6c01fa5b440938e8112bfc13996" desc="">
+    ServiceWorker Preparation
+  </message>
+  <message name="IDS_DEVTOOLS_36d95f430159f0ac7a477e4ee6c5ad21" desc="">
+    Remove all patterns
+  </message>
+  <message name="IDS_DEVTOOLS_376815607a4b79934f79305216a8048c" desc="">
+    Record (<ph name="RECORDNODE">$1s</ph>) or reload (<ph name="RELOADSHORTCUTNODE">$2s</ph>) to display network activity.
+  </message>
+  <message name="IDS_DEVTOOLS_37bb3bd9caf5dafb7c8f9eccb3b9800e" desc="">
+    (unable to decode value)
+  </message>
+  <message name="IDS_DEVTOOLS_383fc50a38f1666adc3f06d0bdf71467" desc="">
+    Are you sure you want to clear browser cookies?
+  </message>
+  <message name="IDS_DEVTOOLS_3b5904efd4efd03d09ee9a8f964f39d6" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_RESOURCESIZE_">$1s</ph> resources
+  </message>
+  <message name="IDS_DEVTOOLS_3b83f6ae91bb21147fd97b8b8229287f" desc="">
+    Certificate URL
+  </message>
+  <message name="IDS_DEVTOOLS_3c06899dae839e1ca3051475a2b8fa05" desc="">
+    Preview not available
+  </message>
+  <message name="IDS_DEVTOOLS_3c2f5decde47940c8baf3b80dea449bd" desc="">
+    csp
+  </message>
+  <message name="IDS_DEVTOOLS_3c309d9a9f7ff31319c7ecb0a19b2b3f" desc="">
+    Select automatically
+  </message>
+  <message name="IDS_DEVTOOLS_3dab30e9b9adf193562ff9a09dcd41a5" desc="">
+    Remote Address
+  </message>
+  <message name="IDS_DEVTOOLS_3e0dc7bd31f5ad41b900fef5b7aba791" desc="">
+    Don&apos;t group network log items by frame
+  </message>
+  <message name="IDS_DEVTOOLS_41de6d6cfb8953c021bbe4ba0701c8a1" desc="">
+    Messages
+  </message>
+  <message name="IDS_DEVTOOLS_41e85145baafb89d0b709ae827218084" desc="">
+    Clear browser cache
+  </message>
+  <message name="IDS_DEVTOOLS_4202ef115ebede37eb22297113f5fb32" desc="">
+    Redirect
+  </message>
+  <message name="IDS_DEVTOOLS_42d61b587a0123390822777d84e0f6bd" desc="">
+    Response code
+  </message>
+  <message name="IDS_DEVTOOLS_44749712dbec183e983dcd78a7736c41" desc="">
+    Date
+  </message>
+  <message name="IDS_DEVTOOLS_4566ce384ed4a889beb6f6432d5906d7" desc="">
+    Network panel
+  </message>
+  <message name="IDS_DEVTOOLS_459c830504806e5747ddc40008ccbade" desc="">
+    Add custom header…
+  </message>
+  <message name="IDS_DEVTOOLS_46ff2c7bbdbb02687287512c874d391f" desc="">
+    signed-exchange
+  </message>
+  <message name="IDS_DEVTOOLS_490aa6e856ccf208a054389e47ce0d06" desc="">
+    Id
+  </message>
+  <message name="IDS_DEVTOOLS_4a5a4b959ef84dd7b7ee73e837b6a54e" desc="">
+    Reveal in Network panel
+  </message>
+  <message name="IDS_DEVTOOLS_4a7ba0d17eafde708ca8f5f34debcfef" desc="">
+    <ph name="TRANSFERSIZE">$1s</ph> B transferred
+  </message>
+  <message name="IDS_DEVTOOLS_4bd7c2ff07dcc66801a9368957d4bff8" desc="">
+    Initiator
+  </message>
+  <message name="IDS_DEVTOOLS_4c3880bb027f159e801041b1021e88e8" desc="">
+    Method
+  </message>
+  <message name="IDS_DEVTOOLS_4dea2ac78e9450e62e96c166ef9f5d4c" desc="">
+    Manage Header Columns…
+  </message>
+  <message name="IDS_DEVTOOLS_4e88c13fb97f356a81c48bf2644bef51" desc="">
+    Connection Start
+  </message>
+  <message name="IDS_DEVTOOLS_5023e5b1d1c02cf168a9a67dda6d2d87" desc="">
+    Show overview
+  </message>
+  <message name="IDS_DEVTOOLS_502996d9790340c5fd7b86a5b93b1c9f" desc="">
+    Priority
+  </message>
+  <message name="IDS_DEVTOOLS_5101cdda28ea3097ac00cdbfdcedc353" desc="">
+    Use default colors
+  </message>
+  <message name="IDS_DEVTOOLS_51360304ea03557e79bdf5ff9cd2e234" desc="">
+    Issuer
+  </message>
+  <message name="IDS_DEVTOOLS_53fa249ce3d02c8a686b657b31d02ee7" desc="">
+    Copy response
+  </message>
+  <message name="IDS_DEVTOOLS_5639d3df5aea3d9e78027b843ee3cbb2" desc="">
+    Receive
+  </message>
+  <message name="IDS_DEVTOOLS_566bbee0f961ad71b54c3c2fd36db053" desc="">
+    extension
+  </message>
+  <message name="IDS_DEVTOOLS_56ecb97c626815b7f982c04e736f624b" desc="">
+    Response headers
+  </message>
+  <message name="IDS_DEVTOOLS_57db0f3677bfe1e5af2f568c71369e9e" desc="">
+    Save all as HAR with content
+  </message>
+  <message name="IDS_DEVTOOLS_58f6ae6f525fa723c603752e682be6a7" desc="">
+    This request has no response data available.
+  </message>
+  <message name="IDS_DEVTOOLS_59535aed1e49266dfd0b93172f5a7242" desc="">
+    Group by frame
+  </message>
+  <message name="IDS_DEVTOOLS_597b56e53847cd6a4712ac183f61fa68" desc="">
+    Cookies
+  </message>
+  <message name="IDS_DEVTOOLS_5cb98890f19c13e5fce52287187d9803" desc="">
+    <ph name="SELECTEDNODENUMBER">$1s</ph> / <ph name="NODECOUNT">$2s</ph> requests
+  </message>
+  <message name="IDS_DEVTOOLS_5e2f683aacb9d5f3635b3f88583dec80" desc="">
+    Custom...
+  </message>
+  <message name="IDS_DEVTOOLS_5f0ea62e5bc9f795512ef292ff162a2a" desc="">
+    Disable cache (while DevTools is open)
+  </message>
+  <message name="IDS_DEVTOOLS_5f903b8316b9649547e1b3ceef934183" desc="">
+    Stop recording network log
+  </message>
+  <message name="IDS_DEVTOOLS_611a2b5dcde004cf68ffd56345584d40" desc="">
+    ETag
+  </message>
+  <message name="IDS_DEVTOOLS_619d32de7168c6e770464dfc43e374d1" desc="">
+    Request Headers
+  </message>
+  <message name="IDS_DEVTOOLS_6311ae17c1ee52b36e68aaf4ad066387" desc="">
+    Other
+  </message>
+  <message name="IDS_DEVTOOLS_6490fac2ddb40e1e2a7e0c43fb661c7b" desc="">
+    Failed to load response data
+  </message>
+  <message name="IDS_DEVTOOLS_649635a49f102b60bd81884f3e2fd3ca" desc="">
+    Collecting content…
+  </message>
+  <message name="IDS_DEVTOOLS_65a9d5faea2e8027d9a96544c579e312" desc="">
+    Certificate SHA256
+  </message>
+  <message name="IDS_DEVTOOLS_6638d1cbb8be347d80370bc5bb58a0d1" desc="">
+    Copy as UTF-8
+  </message>
+  <message name="IDS_DEVTOOLS_6a7c804797a8bb913c82824a6465ede1" desc="">
+    Header Name
+  </message>
+  <message name="IDS_DEVTOOLS_6a98894cd6b4628f0b5117412aab083e" desc="">
+    Cache-Control
+  </message>
+  <message name="IDS_DEVTOOLS_6c2a1bf87fb84c91fc7e1120f4e3d0c7" desc="">
+    Content Download
+  </message>
+  <message name="IDS_DEVTOOLS_6c3ae60298916e218f26a95f4ff21085" desc="">
+    Response Cookies
+  </message>
+  <message name="IDS_DEVTOOLS_6dd7482f131dc036ea08395a7f3f5e08" desc="">
+    Enter a custom user agent
+  </message>
+  <message name="IDS_DEVTOOLS_6e68a529a38966508d348e9f65d7ea31" desc="">
+    Record network log
+  </message>
+  <message name="IDS_DEVTOOLS_71fb2761cd2a446400a8069fe16cd45e" desc="">
+    Block request domain
+  </message>
+  <message name="IDS_DEVTOOLS_722e6ea747a62b7f93bb017d3dd04cbd" desc="">
+    Continuation Frame
+  </message>
+  <message name="IDS_DEVTOOLS_72d569ab3718d10a89315f80cf05cc73" desc="">
+    Group network log by frame
+  </message>
+  <message name="IDS_DEVTOOLS_73c1ba64f0484588be030de81de469ae" desc="">
+    Initial connection
+  </message>
+  <message name="IDS_DEVTOOLS_7543ba96bb16a5f17f8fb25bdd995d3f" desc="">
+    Copy as Base64
+  </message>
+  <message name="IDS_DEVTOOLS_76669aaf74dda2a59e3c363da10c3faf" desc="">
+    Reading Push
+  </message>
+  <message name="IDS_DEVTOOLS_77f02f5047451625e1c6bb24884cdfe6" desc="">
+    Copied as Hex
+  </message>
+  <message name="IDS_DEVTOOLS_79307e20670213e6f99203c2d81bbbaf" desc="">
+    (ServiceWorker)
+  </message>
+  <message name="IDS_DEVTOOLS_7954bc50e461d57fa640f77741cea491" desc="">
+    Resource Scheduling
+  </message>
+  <message name="IDS_DEVTOOLS_795f3202b17cb6bc3d4b771d8c6c9eaf" desc="">
+    other
+  </message>
+  <message name="IDS_DEVTOOLS_7b5bea45778433caf34b0ba556a7d93b" desc="">
+    (failed)
+  </message>
+  <message name="IDS_DEVTOOLS_7c49b153d4b59f8c0cf8c3e18dc80cb7" desc="">
+    origin
+  </message>
+  <message name="IDS_DEVTOOLS_7de2b6840dd6a1683f809daaf2d95fb0" desc="">
+    Hex Viewer
+  </message>
+  <message name="IDS_DEVTOOLS_7e1c4c7b01e0fd0ad1e89ca7512f353e" desc="">
+    Ping Message
+  </message>
+  <message name="IDS_DEVTOOLS_804910399e8d26511871e33416ccd127" desc="">
+    Served from disk cache, resource size: <ph name="RESOURCESIZE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_814f9c3c7e7aa04c21f2e61f5b2dcf18" desc="">
+    Copy response headers
+  </message>
+  <message name="IDS_DEVTOOLS_8166a4b3fb4979da3a24ed21ced7fdf7" desc="">
+    Learn more
+  </message>
+  <message name="IDS_DEVTOOLS_827452688eed02a12178e96f924ac529" desc="">
+    Scheme
+  </message>
+  <message name="IDS_DEVTOOLS_838c672414343cdd527a63d910fb132c" desc="">
+    Hide data URLs
+  </message>
+  <message name="IDS_DEVTOOLS_850985cd851d0fe440f03f77762e2590" desc="">
+    Content-Length
+  </message>
+  <message name="IDS_DEVTOOLS_8644fe7afb1a31ea7274f5ee90dff50b" desc="">
+    view URL encoded
+  </message>
+  <message name="IDS_DEVTOOLS_878ff0bac714397ab62012691efc2087" desc="">
+    Text Message
+  </message>
+  <message name="IDS_DEVTOOLS_881e9a305ce80960413ab7f227ef879e" desc="">
+    User agent
+  </message>
+  <message name="IDS_DEVTOOLS_883d7615c4d2de3fa1218f1298c46d0e" desc="">
+    Headers
+  </message>
+  <message name="IDS_DEVTOOLS_884680dd63eae99eab1a407133df66b7" desc="">
+    View certificate
+  </message>
+  <message name="IDS_DEVTOOLS_888a77f5ac0748b6c8001822417df8b6" desc="">
+    Protocol
+  </message>
+  <message name="IDS_DEVTOOLS_889f316cd1f0ac68f8ea3052e8568061" desc="">
+    Copy as PowerShell
+  </message>
+  <message name="IDS_DEVTOOLS_896ef2edb423627350de605744e10385" desc="">
+    Set Cookies
+  </message>
+  <message name="IDS_DEVTOOLS_8c074f405ad9737b1b59d35d6aab7cab" desc="">
+    Last-Modified
+  </message>
+  <message name="IDS_DEVTOOLS_8c09001c99ecb6fdd8d6023fcf039054" desc="">
+    Signature
+  </message>
+  <message name="IDS_DEVTOOLS_8c2297c17712046f3a7ffd65c2c5ad50" desc="">
+    Validity URL
+  </message>
+  <message name="IDS_DEVTOOLS_8e40e89c8c95a050dcadea2d7e4d40cc" desc="">
+    devtools
+  </message>
+  <message name="IDS_DEVTOOLS_8eb2a485218c2fd5b53651ec35337858" desc="">
+    Use large request rows
+  </message>
+  <message name="IDS_DEVTOOLS_8eed70a0dcf3e8f43d0548cd1e8f22ed" desc="">
+    Hides data: and blob: URLs
+  </message>
+  <message name="IDS_DEVTOOLS_8f32afe4e92699a3c6923a66815f5e16" desc="">
+    Request sent
+  </message>
+  <message name="IDS_DEVTOOLS_8f3d10eb21bd36347c258679eba9e92b" desc="">
+    Finished
+  </message>
+  <message name="IDS_DEVTOOLS_90198f1c07c67e3a44ccc6853651ca2c" desc="">
+    Response Headers
+  </message>
+  <message name="IDS_DEVTOOLS_9146d056f26c33effdc2f83dd9e79395" desc="">
+    Copy all as cURL (bash)
+  </message>
+  <message name="IDS_DEVTOOLS_91d92a76f6e3919419b019b07889c73b" desc="">
+    Clear browser cookies
+  </message>
+  <message name="IDS_DEVTOOLS_92480c24ad37b4f15c6a43fd54ec2376" desc="">
+    WebSocket messages
+  </message>
+  <message name="IDS_DEVTOOLS_9254e453e41bb4058ee1e8c1a2b81a4d" desc="">
+    Queueing
+  </message>
+  <message name="IDS_DEVTOOLS_935f218785830f964d0c537b23027045" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_SELECTEDTRANSFERSIZE_">$1s</ph> / <ph name="NUMBER_BYTESTOSTRING_TRANSFERSIZE_">$2s</ph> transferred
+  </message>
+  <message name="IDS_DEVTOOLS_948ff4d0d124f02ff1dc0a9c56ef8ade" desc="">
+    Recording network activity…
+  </message>
+  <message name="IDS_DEVTOOLS_94966d90747b97d1f0f206c98a8b1ac3" desc="">
+    Send
+  </message>
+  <message name="IDS_DEVTOOLS_96b0141273eabab320119c467cdcaf17" desc="">
+    Total
+  </message>
+  <message name="IDS_DEVTOOLS_96d960cc2b3394824ebfbf25bb960a90" desc="">
+    <ph name="LOCALIZEDDESCRIPTION">$1s</ph> (Opcode <ph name="OPCODE">$2s</ph>, mask)
+  </message>
+  <message name="IDS_DEVTOOLS_96e9bc575b5d3ed541113a249da8bd24" desc="">
+    Capture screenshots
+  </message>
+  <message name="IDS_DEVTOOLS_97b7320892b0db86ff1d8dc28ba1af95" desc="">
+    Are you sure you want to clear browser cache?
+  </message>
+  <message name="IDS_DEVTOOLS_97f0a2ba508f8299c686818366ad712a" desc="">
+    (data)
+  </message>
+  <message name="IDS_DEVTOOLS_985187565936a7c1e02c9f96852c0f9a" desc="">
+    subresource-filter
+  </message>
+  <message name="IDS_DEVTOOLS_986371172510b03c03ac7cd17c1f84d4" desc="">
+    Served from Signed HTTP Exchange, resource size: <ph name="RESOURCESIZE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_986491ef3610ed7ef82bd863e4ee00a2" desc="">
+    Request blocking
+  </message>
+  <message name="IDS_DEVTOOLS_9979c41f94d4d232c06b4c87f2d0f951" desc="">
+    Push / 
+  </message>
+  <message name="IDS_DEVTOOLS_9a41e9b9ca01e7239185b042be18f9eb" desc="">
+    Connection ID
+  </message>
+  <message name="IDS_DEVTOOLS_9a83ab0d60fed7c37d928ccb30d1b6ae" desc="">
+    Parser
+  </message>
+  <message name="IDS_DEVTOOLS_9aa1b03934893d7134a660af4204f2a9" desc="">
+    Server
+  </message>
+  <message name="IDS_DEVTOOLS_9c1ab57e621c2bb257798752dbbe6f14" desc="">
+    view source
+  </message>
+  <message name="IDS_DEVTOOLS_9cabd6c0f7878c8106104aee4a24a41e" desc="">
+    Copy as cURL (cmd)
+  </message>
+  <message name="IDS_DEVTOOLS_9e08e91089d39bc26c4c57e9780c2f60" desc="">
+    (from memory cache)
+  </message>
+  <message name="IDS_DEVTOOLS_9e65b51e82f2a9b9f72ebe3e083582bb" desc="">
+    (empty)
+  </message>
+  <message name="IDS_DEVTOOLS_9ffaf77e2c524827dff95993361e7115" desc="">
+    Raw response data
+  </message>
+  <message name="IDS_DEVTOOLS_a00705e19cc5f358d77688d197604cc3" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_TRANSFERSIZE_">$1s</ph> transferred
+  </message>
+  <message name="IDS_DEVTOOLS_a0820b213c0f695e95fc0c278a05400f" desc="">
+    <ph name="COUNT">$1d</ph> blocked
+  </message>
+  <message name="IDS_DEVTOOLS_a217246afb12245a032617715f81923b" desc="">
+    Manage Header Columns
+  </message>
+  <message name="IDS_DEVTOOLS_a227f203e4a2e520debf44ae78f16847" desc="">
+    Copied as Base64
+  </message>
+  <message name="IDS_DEVTOOLS_a249aec821f73349cc457658bd6c04f3" desc="">
+    Copy as Hex
+  </message>
+  <message name="IDS_DEVTOOLS_a299d2f506523f8104dc4202de7b0c64" desc="">
+    Export HAR...
+  </message>
+  <message name="IDS_DEVTOOLS_a5d6e0224f694699dde6d1bd34d608bd" desc="">
+    Copy as cURL
+  </message>
+  <message name="IDS_DEVTOOLS_a612782072a3b8f151c5e2120ef20efd" desc="">
+    Copy to clipboard
+  </message>
+  <message name="IDS_DEVTOOLS_a74e6894e1f36ce12a6773b9a6b9aea8" desc="">
+    <ph name="NODECOUNT">$1s</ph> requests
+  </message>
+  <message name="IDS_DEVTOOLS_a76d4ef5f3f6a672bbfab2865563e530" desc="">
+    Time
+  </message>
+  <message name="IDS_DEVTOOLS_a976624c5265b8187b19aea9df9230eb" desc="">
+    Color-code resource types
+  </message>
+  <message name="IDS_DEVTOOLS_abe6fec6834dc303e1abfb03f9874786" desc="">
+    DOMContentLoaded: <ph name="NUMBER_SECONDSTOSTRING_THIS__MAINREQUESTDOMCONTENTLOADEDTIME___BASETIME_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_ac5eb504b5a840271e066bf6fd3000b8" desc="">
+    view parsed
+  </message>
+  <message name="IDS_DEVTOOLS_ad86890fb40822a3b12627efaca4ecd7" desc="">
+    (disk cache)
+  </message>
+  <message name="IDS_DEVTOOLS_add0160d0ea7e2969930b281a6ba1456" desc="">
+    Drop HAR files here
+  </message>
+  <message name="IDS_DEVTOOLS_ae3b3df9970b49b6523e608759bc957d" desc="">
+    UTF-8
+  </message>
+  <message name="IDS_DEVTOOLS_af0a229a0c5be77de07889743db3b409" desc="">
+    Copied as UTF-8
+  </message>
+  <message name="IDS_DEVTOOLS_af6ef9fe7e48178f3765aa8e44f9ce5d" desc="">
+    Group network log items by frame
+  </message>
+  <message name="IDS_DEVTOOLS_b021df6aac4654c454f46c77646e745f" desc="">
+    Label
+  </message>
+  <message name="IDS_DEVTOOLS_b1f63146853f7f450d8622aae6a73408" desc="">
+    Copy as cURL (bash)
+  </message>
+  <message name="IDS_DEVTOOLS_b2844b8e17ecaaeae68d018fe9418af0" desc="">
+    Valid until
+  </message>
+  <message name="IDS_DEVTOOLS_b4774747599df05309e1de2e2734dd3e" desc="">
+    Request to ServiceWorker
+  </message>
+  <message name="IDS_DEVTOOLS_b6434258c743f8673997b5aeb3721c27" desc="">
+    Request Method
+  </message>
+  <message name="IDS_DEVTOOLS_b72ac10807b29c77f5b7e4b80ea40414" desc="">
+    Explanation
+  </message>
+  <message name="IDS_DEVTOOLS_b926fd82158cde57655d0cd1dd8dbc70" desc="">
+    Query String Parameters
+  </message>
+  <message name="IDS_DEVTOOLS_b9449f52ba4f16fc7dd7fb7cd0c6c5c7" desc="">
+    Network throttling
+  </message>
+  <message name="IDS_DEVTOOLS_b94a47df08b4ab492c78deb7a2940662" desc="">
+    Disable cache
+  </message>
+  <message name="IDS_DEVTOOLS_b9ad5629c19552041dadd5b4feb22772" desc="">
+    Response Time
+  </message>
+  <message name="IDS_DEVTOOLS_ba2a9c6c8c77e03f83ef8bf543612275" desc="">
+    Length
+  </message>
+  <message name="IDS_DEVTOOLS_bd474658f3a7ae560cc97b6b64264045" desc="">
+    mixed-content
+  </message>
+  <message name="IDS_DEVTOOLS_bd9176ee57c46268a853e038b133966a" desc="">
+    Keep-Alive
+  </message>
+  <message name="IDS_DEVTOOLS_bdaacef16991cfa4cf17a388579e7c06" desc="">
+    EventStream
+  </message>
+  <message name="IDS_DEVTOOLS_c02504bf83e5cc0d2f582f189a804aef" desc="">
+    Request Cookies
+  </message>
+  <message name="IDS_DEVTOOLS_c2cc7082a89c1ad6631a2f66af5f00c0" desc="">
+    Connection
+  </message>
+  <message name="IDS_DEVTOOLS_c33fa8b7d663932b656f0f7f53a74e77" desc="">
+    Requests are not blocked. 
+  </message>
+  <message name="IDS_DEVTOOLS_c3c14eb17a6cf9c6120f381790ed06eb" desc="">
+    Copy all as PowerShell
+  </message>
+  <message name="IDS_DEVTOOLS_c4e4fcc7d371549aec967e7157ba436d" desc="">
+    SignedExchange error
+  </message>
+  <message name="IDS_DEVTOOLS_c54dcc2fd5e72371ef21f1553d79e740" desc="">
+    (canceled)
+  </message>
+  <message name="IDS_DEVTOOLS_c6f969f563d21beac9731f177053484c" desc="">
+    <ph name="SELECTEDRESOURCESIZE">$1s</ph> B / <ph name="RESOURCESIZE">$2s</ph> B resources
+  </message>
+  <message name="IDS_DEVTOOLS_c7892ebbb139886662c6f2fc8c450710" desc="">
+    Subject
+  </message>
+  <message name="IDS_DEVTOOLS_c81e295bfd7fbaec6257ea14992f4643" desc="">
+    Request and response cookies
+  </message>
+  <message name="IDS_DEVTOOLS_c84718b71b8ad70dde23736e79e25e83" desc="">
+    Pong Message
+  </message>
+  <message name="IDS_DEVTOOLS_c91a577b72313356fad611c55f43c10f" desc="">
+    End Time
+  </message>
+  <message name="IDS_DEVTOOLS_cb64a2679b345ae476ea3a7fb6a70080" desc="">
+    Binary Message
+  </message>
+  <message name="IDS_DEVTOOLS_cc0af601bfd673427a8abb171f62c707" desc="">
+    content-type
+  </message>
+  <message name="IDS_DEVTOOLS_cebd9a3a94f022d3600e0f81a9aa2060" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_SELECTEDRESOURCESIZE_">$1s</ph> / <ph name="NUMBER_BYTESTOSTRING_RESOURCESIZE_">$2s</ph> resources
+  </message>
+  <message name="IDS_DEVTOOLS_cf5c8b06caaf075b4e453ce29d27760f" desc="">
+    Served from prefetch cache, resource size: <ph name="RESOURCESIZE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_cfc36842a01da69c484ffdcc6782f437" desc="">
+    Expires
+  </message>
+  <message name="IDS_DEVTOOLS_d0a9f074f48f9416d043f27687c1f030" desc="">
+    Proxy negotiation
+  </message>
+  <message name="IDS_DEVTOOLS_d0e5383d7c91948cfab6cacccec8812d" desc="">
+    (from signed-exchange)
+  </message>
+  <message name="IDS_DEVTOOLS_d3b69e993f4e9bf9c479c7e794ede387" desc="">
+    Timing
+  </message>
+  <message name="IDS_DEVTOOLS_d64ed3e9c10229648e069f56e32f4c8e" desc="">
+    Response
+  </message>
+  <message name="IDS_DEVTOOLS_d76e44f856d0114826e7436a630a3fa7" desc="">
+    (signed-exchange)
+  </message>
+  <message name="IDS_DEVTOOLS_d7e76ea0c048939444b2aa8653f048d0" desc="">
+    Record (<ph name="RECORDNODE">$1s</ph>) to display network activity.
+  </message>
+  <message name="IDS_DEVTOOLS_d82a834165b99c4ea0969316296a2bc2" desc="">
+    Vary
+  </message>
+  <message name="IDS_DEVTOOLS_db67b2de0114bd82fe1383aa067fa6b2" desc="">
+    Unblock <ph name="CROPPEDURL">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_dc30bc0c7914db5918da4263fce93ad2" desc="">
+    Clear
+  </message>
+  <message name="IDS_DEVTOOLS_dc5d60b066d9a30e68dd648dcabf8579" desc="">
+    This request has no cookies.
+  </message>
+  <message name="IDS_DEVTOOLS_dc66e209093fbef0daa224f2b6401b39" desc="">
+    Request/Response
+  </message>
+  <message name="IDS_DEVTOOLS_dd47445f60115097d07d4cf2e61d933b" desc="">
+    CAUTION: request is not finished yet!
+  </message>
+  <message name="IDS_DEVTOOLS_de4642e93235c872dcc21d7e4c926c36" desc="">
+    Copy all as fetch
+  </message>
+  <message name="IDS_DEVTOOLS_de9b14d980b77c264d42463f407180be" desc="">
+    Provisional headers are shown
+  </message>
+  <message name="IDS_DEVTOOLS_df080c10e2e9f1e98c180fe2645f6132" desc="">
+    Request URL
+  </message>
+  <message name="IDS_DEVTOOLS_e186ebc46177c666cd1d5bda52ade420" desc="">
+    Response preview
+  </message>
+  <message name="IDS_DEVTOOLS_e1905358311029126da11f4e24fca90d" desc="">
+     (<ph name="PARAMS_LENGTH">$1d</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_e44c5fc0f8fc0dc0181254373cb07c47" desc="">
+    Load: <ph name="NUMBER_SECONDSTOSTRING_THIS__MAINREQUESTLOADTIME___BASETIME_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_e49d4e7e01be604cfb0cf20a72e0853c" desc="">
+    Server Timing
+  </message>
+  <message name="IDS_DEVTOOLS_e6702855087694e24e954afab8b7e5d0" desc="">
+    Connection Close Message
+  </message>
+  <message name="IDS_DEVTOOLS_e6fb9da54f521b9c33d41121d4fcd35c" desc="">
+    (memory cache)
+  </message>
+  <message name="IDS_DEVTOOLS_e77fef0167598bb39846637ac3915f4e" desc="">
+    Clear All
+  </message>
+  <message name="IDS_DEVTOOLS_ea52c36203c5f99c3ce2442d531b1a22" desc="">
+    SSL
+  </message>
+  <message name="IDS_DEVTOOLS_eb0bd7de3ba805621bf9e03e4d16b510" desc="">
+    Queued at <ph name="CALCULATOR_FORMATVALUE_REQUEST_ISSUETIME______">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_eb0f48a107df1a0f343d4cd513b555e6" desc="">
+    Certificate
+  </message>
+  <message name="IDS_DEVTOOLS_eb902cf204f3e4dfffeb56d92a9b5c26" desc="">
+    Valid from
+  </message>
+  <message name="IDS_DEVTOOLS_ec53a8c4f07baed5d8825072c89799be" desc="">
+    Status
+  </message>
+  <message name="IDS_DEVTOOLS_ec559fc895e8cc77ef0c4d6fdff5bdcb" desc="">
+    Form Data
+  </message>
+  <message name="IDS_DEVTOOLS_ec9dca2a4b061ad202945d88657e5ddf" desc="">
+    Started at <ph name="CALCULATOR_FORMATVALUE_REQUEST_STARTTIME____">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_edfffa5a1ffba7492b25869813e7e15e" desc="">
+    Content-Encoding
+  </message>
+  <message name="IDS_DEVTOOLS_ef110dd7355d7315c73995a47fe77dfd" desc="">
+    DNS Lookup
+  </message>
+  <message name="IDS_DEVTOOLS_f1019fecd69b0e6b6bd12db7f58bbb90" desc="">
+    Copy message
+  </message>
+  <message name="IDS_DEVTOOLS_f1495279152e772a4ecf5cbf17a07c64" desc="">
+    Copy message...
+  </message>
+  <message name="IDS_DEVTOOLS_f15c1cae7882448b3fb0404682e17e61" desc="">
+    Content
+  </message>
+  <message name="IDS_DEVTOOLS_f312b3f268931a1367de22756237b197" desc="">
+    Headers and request body
+  </message>
+  <message name="IDS_DEVTOOLS_f3f97de67c80480904f958df15b8a57b" desc="">
+    Integrity
+  </message>
+  <message name="IDS_DEVTOOLS_f508b22c8fc4486b19fee39d5ded3841" desc="">
+    Network settings
+  </message>
+  <message name="IDS_DEVTOOLS_f531e0844f007af2fa80b328e7c82e72" desc="">
+    (from disk cache)
+  </message>
+  <message name="IDS_DEVTOOLS_f6068daa29dbb05a7ead1e3b5a48bbee" desc="">
+    Data
+  </message>
+  <message name="IDS_DEVTOOLS_f6c804a6be9719c98a260380833bfde7" desc="">
+    Block request URL
+  </message>
+  <message name="IDS_DEVTOOLS_f6d1d4157b75f9b68298f7df187a177c" desc="">
+    Finish: <ph name="NUMBER_SECONDSTOSTRING_MAXTIME___BASETIME_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_f9069c6541beaac49cce4b2542069f02" desc="">
+    Referrer Policy
+  </message>
+  <message name="IDS_DEVTOOLS_f907e651164789346ae0a1e257c462d8" desc="">
+    Script
+  </message>
+  <message name="IDS_DEVTOOLS_f95d3d09fc16ae9294fbef87ac8d17cb" desc="">
+    (pending)
+  </message>
+  <message name="IDS_DEVTOOLS_fae900d6e2cf6b54ffae8cad77b9e2a6" desc="">
+    (prefetch cache)
+  </message>
+  <message name="IDS_DEVTOOLS_fb0df508eafce0076286884cd92fc8fd" desc="">
+    Copy request headers
+  </message>
+  <message name="IDS_DEVTOOLS_fb6024596d77a7d0ac213f1f12fa1d8b" desc="">
+    Text pattern to block matching requests; use * for wildcard
+  </message>
+  <message name="IDS_DEVTOOLS_fd747cf7093bd6b5c8eb3b40658a3b13" desc="">
+    Total Duration
+  </message>
+  <message name="IDS_DEVTOOLS_fe1bc3eb2f3e1a9a90b5401eb6baa5b9" desc="">
+    Copy all as cURL
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/node_debugger/node_debugger_strings.grdp b/third_party/blink/renderer/devtools/front_end/node_debugger/node_debugger_strings.grdp
new file mode 100644
index 0000000..0e094599
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/node_debugger/node_debugger_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_6c3a6944a808a7c0bbb6788dbec54a9f" desc="">
+    Node
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/node_main/node_main_strings.grdp b/third_party/blink/renderer/devtools/front_end/node_main/node_main_strings.grdp
new file mode 100644
index 0000000..45a7dad
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/node_main/node_main_strings.grdp
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_28e2b83d6f10a98be7535c7b40b05c6e" desc="">
+    Add connection
+  </message>
+  <message name="IDS_DEVTOOLS_3fd49f986cbd5dd3148c27c9aaaaa700" desc="">
+    Network address (e.g. localhost:9229)
+  </message>
+  <message name="IDS_DEVTOOLS_75997049f01ae448c39b8a937c4e2035" desc="">
+    No connections specified
+  </message>
+  <message name="IDS_DEVTOOLS_81c731365b52528d7d0fdb477ebc3b1d" desc="">
+    Specify network endpoint and DevTools will connect to it automatically. 
+  </message>
+  <message name="IDS_DEVTOOLS_b1f0f05f6f7dcbf25e065bb4c31fec72" desc="">
+    Node.js: <ph name="TARGETINFO_URL">$1s</ph>
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/object_ui/object_ui_strings.grdp b/third_party/blink/renderer/devtools/front_end/object_ui/object_ui_strings.grdp
new file mode 100644
index 0000000..46d6fa38
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/object_ui/object_ui_strings.grdp
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_1381e657bdaaff914f1cf300d9ae12ba" desc="">
+    The property is computed with a getter
+  </message>
+  <message name="IDS_DEVTOOLS_1e7528fc0ca67eed52813139d5208882" desc="">
+    No property getter
+  </message>
+  <message name="IDS_DEVTOOLS_21a396ebc3d3f5a85721e8328da386dc" desc="">
+    &lt;string is too large to edit&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_2c8631472a99e236bd93244d69705c7c" desc="">
+    Copy property path
+  </message>
+  <message name="IDS_DEVTOOLS_2f68e12de25d8be39f50adf5f7714b3d" desc="">
+    Show as JavaScript object
+  </message>
+  <message name="IDS_DEVTOOLS_376f6694932cd7a556445e2058dd4255" desc="">
+    empty × <ph name="COUNT">$1d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_4ced885857185cb26f013fcaaa03a086" desc="">
+    &lt;unreadable&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_59aeb2c9970b7b25be2fab2317e31fcb" desc="">
+    keywords
+  </message>
+  <message name="IDS_DEVTOOLS_a2e4822a98337283e39f7b60acf85ec9" desc="">
+    empty
+  </message>
+  <message name="IDS_DEVTOOLS_a6565c5180039dd2ca1249796bba00a5" desc="">
+    No properties
+  </message>
+  <message name="IDS_DEVTOOLS_a73e6bf278578e09d2351ee2ec7a7908" desc="">
+    Keys
+  </message>
+  <message name="IDS_DEVTOOLS_a983530c8e824d30e214200ef6f8a2d7" desc="">
+    (...)
+  </message>
+  <message name="IDS_DEVTOOLS_ad921d60486366258809553a3db49a4a" desc="">
+    unknown
+  </message>
+  <message name="IDS_DEVTOOLS_b0d4998a26f5b5742ad38c4af8817e32" desc="">
+    Exception
+  </message>
+  <message name="IDS_DEVTOOLS_b46f6ce1cece499f73c9d5c36e4a3de4" desc="">
+    Invoke property getter
+  </message>
+  <message name="IDS_DEVTOOLS_d50bbea3c85be098dcf221e622cd7718" desc="">
+    Lexical scope variables
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/perf_ui/perf_ui_strings.grdp b/third_party/blink/renderer/devtools/front_end/perf_ui/perf_ui_strings.grdp
new file mode 100644
index 0000000..d38f94e0
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/perf_ui/perf_ui_strings.grdp
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_105078d294d30c978ca2badf7f376934" desc="">
+    Scroll
+  </message>
+  <message name="IDS_DEVTOOLS_28d0edd045e05cf5af64e35ae0c4c6ef" desc="">
+    Low
+  </message>
+  <message name="IDS_DEVTOOLS_2bd10b449de36fcff52a5d91a97a7365" desc="">
+    %.<ph name="FRACTIONDIGITS">$1s</ph>f
+  </message>
+  <message name="IDS_DEVTOOLS_3c4684d2d502d7f9d20e05451c2f581c" desc="">
+    Next frame
+  </message>
+  <message name="IDS_DEVTOOLS_3f21c340f01b96cb3325a5d38ece9da6" desc="">
+    Live memory allocation annotations
+  </message>
+  <message name="IDS_DEVTOOLS_4789f23283b3a61f858b641a1bef19a3" desc="">
+    Memory
+  </message>
+  <message name="IDS_DEVTOOLS_4a6d0345e7b6cfcf339237b9cf2d27b4" desc="">
+    Flamechart mouse wheel action:
+  </message>
+  <message name="IDS_DEVTOOLS_529a032eab49b8ddc6f49f8bcc086607" desc="">
+    Previous frame
+  </message>
+  <message name="IDS_DEVTOOLS_582996407922dab08d5cf2b3d2a7c1c9" desc="">
+    Highest
+  </message>
+  <message name="IDS_DEVTOOLS_655d20c1ca69519ca647684edbb2db35" desc="">
+    High
+  </message>
+  <message name="IDS_DEVTOOLS_77c168e60d966216cd1261c200f87e27" desc="">
+    Show live memory allocation annotations
+  </message>
+  <message name="IDS_DEVTOOLS_87f8a6ab85c9ced3702b4ea641ad4bb5" desc="">
+    Medium
+  </message>
+  <message name="IDS_DEVTOOLS_8d8fcc1abd550c5f25dbfaa57d59cb67" desc="">
+    MB
+  </message>
+  <message name="IDS_DEVTOOLS_90c96ccfe47a731972d5e64deb5fea36" desc="">
+    Hide live memory allocation annotations
+  </message>
+  <message name="IDS_DEVTOOLS_ab57fd0432e25d5b3013133a1c910d56" desc="">
+    KB
+  </message>
+  <message name="IDS_DEVTOOLS_b5b8e20937205384be7b9e0c29a28fdb" desc="">
+    Lowest
+  </message>
+  <message name="IDS_DEVTOOLS_d085f278b95f303c39ba30f1817cca95" desc="">
+    Collect garbage
+  </message>
+  <message name="IDS_DEVTOOLS_dac0f9afd816c579ba5c6d7a987c4ba6" desc="">
+    <ph name="VALUE">$1.1f</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_e2885880b7034cd40dce71aeffa3662f" desc="">
+    Doubleclick to zoom image. Click to view preceding requests.
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/performance_monitor/performance_monitor_strings.grdp b/third_party/blink/renderer/devtools/front_end/performance_monitor/performance_monitor_strings.grdp
new file mode 100644
index 0000000..33164578
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/performance_monitor/performance_monitor_strings.grdp
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_029ce83ef9e87df61fb9e580ac165b4a" desc="">
+    DOM Nodes
+  </message>
+  <message name="IDS_DEVTOOLS_23ab71fd0704d52052b95e54f8e72fb9" desc="">
+    Performance monitor
+  </message>
+  <message name="IDS_DEVTOOLS_2bd1b3d3ed5950a8e5a28340e368a4ee" desc="">
+    Document Frames
+  </message>
+  <message name="IDS_DEVTOOLS_5c1131c8862a07ce54be057f516c840e" desc="">
+    Layouts / sec
+  </message>
+  <message name="IDS_DEVTOOLS_6cce53fb6e5ccd4313ae3ec26fa42ca4" desc="">
+    JS event listeners
+  </message>
+  <message name="IDS_DEVTOOLS_81f2166e08f25234e2fbb2cd3175f83e" desc="">
+    Graphs displaying a real-time view of performance metrics
+  </message>
+  <message name="IDS_DEVTOOLS_9ff3fcf082193aaf2b6889fb4662706a" desc="">
+    JS heap size
+  </message>
+  <message name="IDS_DEVTOOLS_ac2137ec7c503f6997ddb414265791a9" desc="">
+    CPU usage
+  </message>
+  <message name="IDS_DEVTOOLS_b4cb01a2ff8576df8cbad0872cc6e4a2" desc="">
+    Style recalcs / sec
+  </message>
+  <message name="IDS_DEVTOOLS_e99180abf47a8b3a856e0bcb2656990a" desc="">
+    Paused
+  </message>
+  <message name="IDS_DEVTOOLS_f28128b38efbc6134dc40751ee21fd29" desc="">
+    Documents
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/persistence/persistence_strings.grdp b/third_party/blink/renderer/devtools/front_end/persistence/persistence_strings.grdp
new file mode 100644
index 0000000..4a042b4
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/persistence/persistence_strings.grdp
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_03250d684b667899552f89911582dbdc" desc="">
+    <ph name="ITEM">$1s</ph> (via .devtools)
+  </message>
+  <message name="IDS_DEVTOOLS_03a21915504df93cf5dba0636080f62e" desc="">
+    Workspace
+  </message>
+  <message name="IDS_DEVTOOLS_03ad5ac9c1a313e19064670f92981353" desc="">
+    Add folder…
+  </message>
+  <message name="IDS_DEVTOOLS_14f221497f99c10993cd293628adeefd" desc="">
+    Open in containing folder
+  </message>
+  <message name="IDS_DEVTOOLS_17d5f7dcd4ef6b9702a8b2ac7d237e5b" desc="">
+    Enable override network requests
+  </message>
+  <message name="IDS_DEVTOOLS_25ee7702f0b5dbe737d8f0f2ba3ce591" desc="">
+    Save for overrides
+  </message>
+  <message name="IDS_DEVTOOLS_486658cc6336ffa59ef2fdff0b977a91" desc="">
+    Excluded folders
+  </message>
+  <message name="IDS_DEVTOOLS_5f6bb21e40e670952b73ca35eafbe35a" desc="">
+    Disable override network requests
+  </message>
+  <message name="IDS_DEVTOOLS_62def4422771353f5e30763d92474b43" desc="">
+    Linked to source map: <ph name="BINDING_NETWORK_URL___TRIMMIDDLE_____">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_8d646fe956bbc874d742471885ffd77c" desc="">
+    Folder exclude pattern
+  </message>
+  <message name="IDS_DEVTOOLS_ab130072b340b847d8d45ec45d191c3b" desc="">
+    Mappings are inferred automatically.
+  </message>
+  <message name="IDS_DEVTOOLS_afb4ea732ebcf8bd51a3d99a1b8909a8" desc="">
+    Linked to <ph name="PATH">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_cf703cce77386955bfbff86f6ad9be03" desc="">
+    Unable to add filesystem: <ph name="ERRORMESSAGE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_da0b472ac86851a3c0b623260d09546c" desc="">
+    Persistence
+  </message>
+  <message name="IDS_DEVTOOLS_e13c8cb15857d020445d01a8817f7d34" desc="">
+    File system error: <ph name="ERROR_MESSAGE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_e2b63f5cab4f80fb6745f2f941105371" desc="">
+    Enable Local Overrides
+  </message>
+  <message name="IDS_DEVTOOLS_ec211f7c20af43e742bf2570c3cb84f9" desc="">
+    Add
+  </message>
+  <message name="IDS_DEVTOOLS_fddede572fa09e1b1bb28504d8c43343" desc="">
+    Folder path
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/product_registry/product_registry_strings.grdp b/third_party/blink/renderer/devtools/front_end/product_registry/product_registry_strings.grdp
new file mode 100644
index 0000000..af16ee2
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/product_registry/product_registry_strings.grdp
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_8cb0e09c4d24f1c4aef14d8e2f1592a6" desc="">
+    Show third party badges
+  </message>
+  <message name="IDS_DEVTOOLS_c9b7856510bf5901af2e388926d77b94" desc="">
+    Show third party URL badges
+  </message>
+  <message name="IDS_DEVTOOLS_e56f0c64e0eb84e8799baa5bf890acb5" desc="">
+    Do not show third party badges
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
new file mode 100644
index 0000000..b46a803
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
@@ -0,0 +1,439 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_00018e8c8fc32e1ec073c0cdbadb5575" desc="">
+    <ph name="NUMBER_WITHTHOUSANDSSEPARATOR_VALUE________">$1s</ph> KB
+  </message>
+  <message name="IDS_DEVTOOLS_007c41a9025be2c8e14b496ed3ee00f8" desc="">
+    Save…
+  </message>
+  <message name="IDS_DEVTOOLS_02184526968aa861a744df910647fcf0" desc="">
+    Freed Size
+  </message>
+  <message name="IDS_DEVTOOLS_0257542bea15eb6baa3fc0364cd3ba24" desc="">
+    <ph name="NUMBER_WITHTHOUSANDSSEPARATOR_MATH_ROUND_VALUE_________">$1s</ph> KB
+  </message>
+  <message name="IDS_DEVTOOLS_030361ea56fa177ebcbefe708a45b0f2" desc="">
+    # Deleted
+  </message>
+  <message name="IDS_DEVTOOLS_04042b5589b3d4fd4e1e7e44265ad247" desc="">
+    Total page JS heap size change trend over the last <ph name="TRENDINTERVALMINUTES">$1s</ph> minutes.
+  </message>
+  <message name="IDS_DEVTOOLS_05ce989cc1015cd221844376fc4cf206" desc="">
+    Snapshot <ph name="PROFILE_UID">$1d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_07b20ae970048fc2002d756f27acc863" desc="">
+    Renderer
+  </message>
+  <message name="IDS_DEVTOOLS_07d2bff541ce0f9ec4389e93a97997ee" desc="">
+    Objects allocated between <ph name="LIST_I______TITLE">$1s</ph> and <ph name="LIST_I__TITLE">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_0a63e0039bb8cb9846ad74ace8e1e5de" desc="">
+    Call tree
+  </message>
+  <message name="IDS_DEVTOOLS_0aa6f4210bf373c95eda00232e93cd98" desc="">
+    Distance
+  </message>
+  <message name="IDS_DEVTOOLS_11a755d598c0c417f9a36758c3da7481" desc="">
+    Stop
+  </message>
+  <message name="IDS_DEVTOOLS_13b5bfe96f3e2fe411c9f66f4a582adf" desc="">
+    in
+  </message>
+  <message name="IDS_DEVTOOLS_164c4aca31febd73a41b50c4a9c406be" desc="">
+    Snapshotting…
+  </message>
+  <message name="IDS_DEVTOOLS_17321b102f09de9aaa68f2b16a530247" desc="">
+    Total JS heap size
+  </message>
+  <message name="IDS_DEVTOOLS_17c34fb9ce9a7a20604a5f10e447edce" desc="">
+    Heap size change trend over the last <ph name="TRENDINTERVALMINUTES">$1s</ph> minutes.
+  </message>
+  <message name="IDS_DEVTOOLS_1816452291df3cd48db5667463361f25" desc="">
+    Focus selected function
+  </message>
+  <message name="IDS_DEVTOOLS_1d36783e12317ed400ebeddeb072a27b" desc="">
+    Chart
+  </message>
+  <message name="IDS_DEVTOOLS_1eaeeaeb638fdf7f6eeb047abbfd0f1a" desc="">
+    Self Time
+  </message>
+  <message name="IDS_DEVTOOLS_1f1e990a1d2ba8ab4dd873a192237c30" desc="">
+    <ph name="VALUE">$1.1f</ph> ms
+  </message>
+  <message name="IDS_DEVTOOLS_27c0ad7a8ff8f9df8e13bb2d974c95d0" desc="">
+    Allocation
+  </message>
+  <message name="IDS_DEVTOOLS_290612199861c31d1036b185b4e69b75" desc="">
+    Summary
+  </message>
+  <message name="IDS_DEVTOOLS_2d7b69664bc4226fb198124fb707e57c" desc="">
+    Snapshot <ph name="THIS_NEXTPROFILEUID__">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2db1f9be46028cfdf9b8f96a0110e637" desc="">
+    <ph name="VALUE">$1.2f</ph> %%
+  </message>
+  <message name="IDS_DEVTOOLS_2def01e5dee6051b68f5b1e5e2fc8ad4" desc="">
+    System Objects
+  </message>
+  <message name="IDS_DEVTOOLS_2f318b400ebcb22420251535452efef0" desc="">
+    Containment
+  </message>
+  <message name="IDS_DEVTOOLS_32e679bdbd5dd7a0bacf024bc30e8a75" desc="">
+    Recording…
+  </message>
+  <message name="IDS_DEVTOOLS_35066690e799cd4e59123262e41b29c5" desc="">
+    Stop CPU profiling
+  </message>
+  <message name="IDS_DEVTOOLS_36917e785bd31d786ab9dd7790a9a4c2" desc="">
+    JS Heap
+  </message>
+  <message name="IDS_DEVTOOLS_3a9619400e054970a7ab8b67c4ce5109" desc="">
+    Restore all functions
+  </message>
+  <message name="IDS_DEVTOOLS_3ae3895607d1203d8e73e18dd4c4551c" desc="">
+    Allocation profiles show sampled native memory allocations from the renderer process.
+  </message>
+  <message name="IDS_DEVTOOLS_3b6077563702806850baeddff55ee1c6" desc="">
+    Preview is not available
+  </message>
+  <message name="IDS_DEVTOOLS_3ce9ab6602763824b2f317965c6a1a20" desc="">
+    Heap size in use by live JS objects.
+  </message>
+  <message name="IDS_DEVTOOLS_3e6defd685231cdd12065add41996c89" desc="">
+    All objects
+  </message>
+  <message name="IDS_DEVTOOLS_3eea5ef5cf936633884b33c458451013" desc="">
+    Show native functions in JS Profile
+  </message>
+  <message name="IDS_DEVTOOLS_412ec4c9168f10f6e2061f65f0358079" desc="">
+    Typed Arrays
+  </message>
+  <message name="IDS_DEVTOOLS_41a813362d7dc28fd247f233f620cb7b" desc="">
+    Can&apos;t load profile while another profile is being recorded.
+  </message>
+  <message name="IDS_DEVTOOLS_42db600672cae99246e499a23827146b" desc="">
+    Heap snapshot profiles show memory distribution among your page&apos;s JavaScript objects and related DOM nodes.
+  </message>
+  <message name="IDS_DEVTOOLS_4307cf6e92b003765b9f493109f8fa51" desc="">
+    Native memory allocation sampling
+  </message>
+  <message name="IDS_DEVTOOLS_43190a68dbd847e2f98a7ddd04b2eec5" desc="">
+    Live Size
+  </message>
+  <message name="IDS_DEVTOOLS_433660c1f9743978edc4ca196b5b9f19" desc="">
+    Find by cost (&gt;50ms), name or file
+  </message>
+  <message name="IDS_DEVTOOLS_435eaeed5b69a2dfb7f343e28d109ce6" desc="">
+    Distance from window object
+  </message>
+  <message name="IDS_DEVTOOLS_4715ae2a2790290ba123375faa3a53f2" desc="">
+    High resolution CPU profiling
+  </message>
+  <message name="IDS_DEVTOOLS_497031794414a552435f90151ac3b54b" desc="">
+    Object
+  </message>
+  <message name="IDS_DEVTOOLS_4b08055cf5445d820914943222d4910c" desc="">
+    Script URL
+  </message>
+  <message name="IDS_DEVTOOLS_4ca19c06c6e03c07bced1d97ca861cf0" desc="">
+    # Delta
+  </message>
+  <message name="IDS_DEVTOOLS_4d4d76db5d9ef72b504f83d89af5b6dd" desc="">
+    Selected size: <ph name="NUMBER_BYTESTOSTRING_EVENT_DATA_SIZE_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_4d8aa0a7e336e08bad365b81a197f184" desc="">
+    NATIVE SAMPLING PROFILES
+  </message>
+  <message name="IDS_DEVTOOLS_4daace4b0bc45c13739427cfc06cf7b7" desc="">
+    Text (Top Down)
+  </message>
+  <message name="IDS_DEVTOOLS_5209fac92c957988ce1453bb139790ce" desc="">
+    Failed to read file
+  </message>
+  <message name="IDS_DEVTOOLS_54a238e21ab577ca9c3100d0ed4cd02d" desc="">
+    Retained Size
+  </message>
+  <message name="IDS_DEVTOOLS_54ae9758deaa74226306072bea256c3c" desc="">
+    Stop heap profiling
+  </message>
+  <message name="IDS_DEVTOOLS_54afafa921428eed23978f633af4ce99" desc="">
+    Allocation sampling
+  </message>
+  <message name="IDS_DEVTOOLS_57b1250c3cbec60c67810ac83afb2c64" desc="">
+    (internal array)
+  </message>
+  <message name="IDS_DEVTOOLS_5dd1d9f5baa173ae9267b3631da17a83" desc="">
+    Self size
+  </message>
+  <message name="IDS_DEVTOOLS_5e0ebd581894697a7ae357bea6b7e590" desc="">
+    Start CPU profiling
+  </message>
+  <message name="IDS_DEVTOOLS_60dcffb26f0ed1b0c73f8cea5d5d9772" desc="">
+    Native memory allocation snapshot (<ph name="PROCESSTYPE">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_61b4b76027d49142c386737a0e94cedb" desc="">
+    NATIVE SNAPSHOTS
+  </message>
+  <message name="IDS_DEVTOOLS_62b96ca53ee08544d8d96269112df323" desc="">
+    JS Arrays
+  </message>
+  <message name="IDS_DEVTOOLS_678d0a4421b60dc59999ac02837496a6" desc="">
+    Constructor
+  </message>
+  <message name="IDS_DEVTOOLS_6b59ba1fcfd868474cae169876f9cbfa" desc="">
+    Native memory snapshots show sampled native allocations in the renderer process since start up.
+              Chrome has to be started with --memlog=all flag. Check flags at chrome://flags
+  </message>
+  <message name="IDS_DEVTOOLS_6bb61b1a929f2a0186f2671cd8258bd9" desc="">
+    Not optimized: <ph name="THIS__DEOPTREASON">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_6d27e30b2f09c2a451a6d123c1a35805" desc="">
+    Run <ph name="_">$1d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_6db0d0f1832acb4a56fc82f2317e61e6" desc="">
+    Parsing…
+  </message>
+  <message name="IDS_DEVTOOLS_6e9dd9cf5419fd65f433f1e4bbc5e6ad" desc="">
+    SAMPLING PROFILES
+  </message>
+  <message name="IDS_DEVTOOLS_710686e22674bcc79ef72c2485ca96b9" desc="">
+    Alloc. Size
+  </message>
+  <message name="IDS_DEVTOOLS_7327aea04bd0403771340a3fe03aa42b" desc="">
+    Stopping…
+  </message>
+  <message name="IDS_DEVTOOLS_7381d487d18845b379422325c0a768d6" desc="">
+    Loaded
+  </message>
+  <message name="IDS_DEVTOOLS_75d436b4fd4142db941be900a86b9b3e" desc="">
+    Allocation stack
+  </message>
+  <message name="IDS_DEVTOOLS_7602c7108888133a3e00de63256ae444" desc="">
+    Shallow Size
+  </message>
+  <message name="IDS_DEVTOOLS_7806c2199c12b018327832bcb3ae20ce" desc="">
+    Detached from DOM tree
+  </message>
+  <message name="IDS_DEVTOOLS_78a1e5aee115faf6ebf8f2a38ce553d4" desc="">
+    Clear all profiles
+  </message>
+  <message name="IDS_DEVTOOLS_7ae47a5ac9257db8c7bb89c7afa4db7c" desc="">
+    Take snapshot
+  </message>
+  <message name="IDS_DEVTOOLS_7cb94d94002a34c350cad72e6649c5fe" desc="">
+    Stack was not recorded for this object because it had been allocated before this profile recording started.
+  </message>
+  <message name="IDS_DEVTOOLS_7d3d3a2b500a662396911596807715d0" desc="">
+    Tree (Top Down)
+  </message>
+  <message name="IDS_DEVTOOLS_7e2586c09a41264908762549314a94c0" desc="">
+    CPU PROFILES
+  </message>
+  <message name="IDS_DEVTOOLS_85000e6433d4ac4033e6d7f5a3466f92" desc="">
+    Reveal in Summary view
+  </message>
+  <message name="IDS_DEVTOOLS_86408593c34af77fdd90df932f8b5261" desc="">
+    Function
+  </message>
+  <message name="IDS_DEVTOOLS_86a699c935e3e7e36e334bb61a336f02" desc="">
+    Start heap profiling
+  </message>
+  <message name="IDS_DEVTOOLS_86de9c70eae18dbd7e1f99cc73f5ce43" desc="">
+    # New
+  </message>
+  <message name="IDS_DEVTOOLS_89be9433646f5939040a78971a5d103a" desc="">
+    Strings
+  </message>
+  <message name="IDS_DEVTOOLS_8b8561904173cf8ded80c66eb9628bc3" desc="">
+    ⬇<ph name="CHANGERATETEXT">$1s</ph>/s
+  </message>
+  <message name="IDS_DEVTOOLS_8df507632e20dff27e88902c825db002" desc="">
+    Heap profiler is recording
+  </message>
+  <message name="IDS_DEVTOOLS_8e77c83fffc021286d4790b1a28bd695" desc="">
+    Select profiling type
+  </message>
+  <message name="IDS_DEVTOOLS_9075bd476d18e24259dc388a4569b723" desc="">
+    Record memory allocations using sampling method.
+              This profile type has minimal performance overhead and can be used for long running operations.
+              It provides good approximation of allocations broken down by JavaScript execution stack.
+  </message>
+  <message name="IDS_DEVTOOLS_9206de0db462c24e8a56433e2f7b7c75" desc="">
+    Heap Snapshot
+  </message>
+  <message name="IDS_DEVTOOLS_959f661cf87130eea5c8e5671c013741" desc="">
+    Objects allocated before <ph name="LIST_I__TITLE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_967d1bd7c21531622e5edfa6c5bf320a" desc="">
+    Take heap snapshot
+  </message>
+  <message name="IDS_DEVTOOLS_974eabe9dfc86e52b41b310218ac7d05" desc="">
+    Total size
+  </message>
+  <message name="IDS_DEVTOOLS_9794a8cf80d4f6b67dca3c7381847486" desc="">
+    Self Size (bytes)
+  </message>
+  <message name="IDS_DEVTOOLS_9d247d9b5e90f2c983240d32113d06d2" desc="">
+    Load…
+  </message>
+  <message name="IDS_DEVTOOLS_9e854bf5f75e670228f33f9fe857793d" desc="">
+    Size of the object plus the graph it retains in bytes
+  </message>
+  <message name="IDS_DEVTOOLS_9f29da220ed82809ec5dd70af4e52904" desc="">
+    Total Time
+  </message>
+  <message name="IDS_DEVTOOLS_a6122a65eaa676f700ae68d393054a37" desc="">
+    Start
+  </message>
+  <message name="IDS_DEVTOOLS_a73538b7cd30c19b094438e16da9403d" desc="">
+    Exclude selected function
+  </message>
+  <message name="IDS_DEVTOOLS_a7bd935a88c629dc11c52e0c16c2a8a0" desc="">
+    <ph name="DISTANCE">$1d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_a8445619abd08f3ba0ebfcb31183f7f9" desc="">
+    −
+  </message>
+  <message name="IDS_DEVTOOLS_a92758ee94689bc49f2d6b891e746737" desc="">
+    HEAP SNAPSHOTS
+  </message>
+  <message name="IDS_DEVTOOLS_a97cce5b0a66cd7d7b7b7d2e06b7605c" desc="">
+    Can&apos;t load file. Supported file extensions: &apos;<ph name="ARRAY_FROM_EXTENSIONS__JOIN________">$1s</ph>&apos;.
+  </message>
+  <message name="IDS_DEVTOOLS_aab112fba39c10ed5df335ae0e47f329" desc="">
+    Not optimized
+  </message>
+  <message name="IDS_DEVTOOLS_ad49e602302b1ca117245c4f19ef4450" desc="">
+    Retainers
+  </message>
+  <message name="IDS_DEVTOOLS_ad857c29fb46afef4e17d658f34ead22" desc="">
+    An error occurred when a call to method &apos;<ph name="DATA_ERRORMETHODNAME">$1s</ph>&apos; was requested
+  </message>
+  <message name="IDS_DEVTOOLS_aebc3d61d99dd094fd29afc12dcd8e1f" desc="">
+    Allocation instrumentation on timeline
+  </message>
+  <message name="IDS_DEVTOOLS_b59674dba88cebbb4b94a32066ad3dd0" desc="">
+    Loading… <ph name="NUMBER_BYTESTOSTRING_THIS__JSONIFIEDPROFILE_LENGTH_">$1d</ph>%%
+  </message>
+  <message name="IDS_DEVTOOLS_b8e4045b98241a124561c4d2a69731b8" desc="">
+    Heavy (Bottom Up)
+  </message>
+  <message name="IDS_DEVTOOLS_bc033a16b36d2d313489933a63a938cf" desc="">
+    Live Heap Profile
+  </message>
+  <message name="IDS_DEVTOOLS_bd0a5e31e9b3c87eb2d3c19b00cedfc3" desc="">
+    Heap memory usage
+  </message>
+  <message name="IDS_DEVTOOLS_c22d30084d8ddc9a571512ddee6a83e9" desc="">
+    Total time
+  </message>
+  <message name="IDS_DEVTOOLS_c2b9b79ea4770ae01a139fe953310039" desc="">
+    Live Count
+  </message>
+  <message name="IDS_DEVTOOLS_c33e404a441c6ba9648f88af3c68a1ca" desc="">
+    Statistics
+  </message>
+  <message name="IDS_DEVTOOLS_c9cc8cce247e49bae79f15173ce97354" desc="">
+    Save
+  </message>
+  <message name="IDS_DEVTOOLS_ca0dbad92a874b2f69b549293387925e" desc="">
+    Code
+  </message>
+  <message name="IDS_DEVTOOLS_ca425f84afc5ad77fc9c4d75eed8113f" desc="">
+    ⬆<ph name="CHANGERATETEXT">$1s</ph>/s
+  </message>
+  <message name="IDS_DEVTOOLS_cce99c598cfdb9773ab041d54c3d973a" desc="">
+    Profile
+  </message>
+  <message name="IDS_DEVTOOLS_d06bb9eb4ee32f700ffc1c9f8e5c0a25" desc="">
+    Aggregated self time
+  </message>
+  <message name="IDS_DEVTOOLS_d0f25115288c15321ecf672f0d6a83ea" desc="">
+    Profiles
+  </message>
+  <message name="IDS_DEVTOOLS_d1c7c250869b52eefda57caa7e6eda20" desc="">
+    Stop recording heap profile
+  </message>
+  <message name="IDS_DEVTOOLS_d3d9fdef0e91fc4aba18e57475fd74ea" desc="">
+    VMs
+  </message>
+  <message name="IDS_DEVTOOLS_d46b0b413885c920720180d0af60ff54" desc="">
+    Record JavaScript CPU Profile
+  </message>
+  <message name="IDS_DEVTOOLS_d4bbbe0308b348c609eb4cab28150f80" desc="">
+    Heap snapshot
+  </message>
+  <message name="IDS_DEVTOOLS_d5a263d11105678cc9ae7d29efc4ccc8" desc="">
+    
+        Allocation timelines show instrumented JavaScript memory allocations over time.
+        Once profile is recorded you can select a time interval to see objects that
+        were allocated within it and still alive by the end of recording.
+        Use this profile type to isolate memory leaks.
+  </message>
+  <message name="IDS_DEVTOOLS_d5e5683e3f616ed72e310476bb97f33f" desc="">
+    Size of the object itself in bytes
+  </message>
+  <message name="IDS_DEVTOOLS_d9319f0a43aaf399eebd2d28713e823a" desc="">
+    Select JavaScript VM instance
+  </message>
+  <message name="IDS_DEVTOOLS_d9c86ffece4f8636e406975843aa7b4d" desc="">
+    Live objects
+  </message>
+  <message name="IDS_DEVTOOLS_da9157d11966e9225d29d4f1f0ca3d75" desc="">
+    Total page JS heap size across all VM instances.
+  </message>
+  <message name="IDS_DEVTOOLS_dc1270259e986e0406259be39a53c8ad" desc="">
+    Record allocation stacks (extra performance overhead)
+  </message>
+  <message name="IDS_DEVTOOLS_dce00dbf94282d9be42f7d6a4ebc2c41" desc="">
+    Self time
+  </message>
+  <message name="IDS_DEVTOOLS_ddacdca2bfda777bb0cb7668d76df5f4" desc="">
+    (Anonymous Script <ph name="NODE_CALLFRAME_SCRIPTID">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_df3b67aec764dd36b2a1e94d7f205051" desc="">
+    ALLOCATION TIMELINES
+  </message>
+  <message name="IDS_DEVTOOLS_e098faba9dade304f660aed30940747a" desc="">
+    Start recording heap profile
+  </message>
+  <message name="IDS_DEVTOOLS_e93f994f01c537c4e2f7d8528c3eb5e9" desc="">
+    Count
+  </message>
+  <message name="IDS_DEVTOOLS_ea1c7351fd540546f03ac089a7b41eb3" desc="">
+    Profile loading failed: <ph name="ERROR_MESSAGE">$1s</ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_ed195e39f2d26aed511b5c86c75935d8" desc="">
+    Size Delta
+  </message>
+  <message name="IDS_DEVTOOLS_eec806d119a5eddd61e2ccd21bfd9b20" desc="">
+    Aggregated total time
+  </message>
+  <message name="IDS_DEVTOOLS_ef15fd2f45e6bb5ce57587895ba64f93" desc="">
+    Browser
+  </message>
+  <message name="IDS_DEVTOOLS_f19dbf2edb3a0bd74b0524d960ff21eb" desc="">
+    Load
+  </message>
+  <message name="IDS_DEVTOOLS_f3e40bc520cf1f22e4d36d5c5f8d2618" desc="">
+    User object reachable from window
+  </message>
+  <message name="IDS_DEVTOOLS_f4dab52bcaa99b605451620178a1ee4e" desc="">
+    File &apos;<ph name="READER_FILENAME__">$1s</ph>&apos; read error: <ph name="READER_ERROR___MESSAGE">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_f6c0e3a1c3cfabd32ae8d3ae741fcf0a" desc="">
+    Comparison
+  </message>
+  <message name="IDS_DEVTOOLS_f6ff777632b4b524d2f203c78359eed2" desc="">
+    Saving… <ph name="PERCENTVALUE">$1d</ph>%%
+  </message>
+  <message name="IDS_DEVTOOLS_fbbe8b507083defdbfe14a341551c77b" desc="">
+    CPU profiles show where the execution time is spent in your page&apos;s JavaScript functions.
+  </message>
+  <message name="IDS_DEVTOOLS_fdbd92f0c6b67291ca954b74885d434e" desc="">
+    Profile <ph name="TYPE_NEXTPROFILEUID__">$1d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_ffd4ebcdf9ad139d4cd2c84d853f4f34" desc="">
+    Total Size (bytes)
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/protocol_monitor/protocol_monitor_strings.grdp b/third_party/blink/renderer/devtools/front_end/protocol_monitor/protocol_monitor_strings.grdp
new file mode 100644
index 0000000..b9ce807
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/protocol_monitor/protocol_monitor_strings.grdp
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_02674a4ef33e11c879283629996c8ff8" desc="">
+    Direction
+  </message>
+  <message name="IDS_DEVTOOLS_15c2d85f1fae22a3c3a0594510a1f611" desc="">
+    Request
+  </message>
+  <message name="IDS_DEVTOOLS_2448f896f98bcda2db043a999a00e1d2" desc="">
+    <ph name="THIS_DATA_COLUMNID_">$1s</ph> ms
+  </message>
+  <message name="IDS_DEVTOOLS_5b6cf869265c13af8566f192b4ab3d2a" desc="">
+    Documentation
+  </message>
+  <message name="IDS_DEVTOOLS_6a0d9eaee314c567fd72fb97ee707a36" desc="">
+    Record
+  </message>
+  <message name="IDS_DEVTOOLS_a3d5de3eac8bb00ae86fd1a1005f1500" desc="">
+    Timestamp
+  </message>
+  <message name="IDS_DEVTOOLS_a854885880458e27324853c6f46e28f7" desc="">
+    No message selected
+  </message>
+  <message name="IDS_DEVTOOLS_c2474d3f42e95aac175f984da6d91cf5" desc="">
+    Protocol monitor
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/quick_open/quick_open_strings.grdp b/third_party/blink/renderer/devtools/front_end/quick_open/quick_open_strings.grdp
new file mode 100644
index 0000000..a1176471
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/quick_open/quick_open_strings.grdp
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_06f884351ae9dbaaca31602b74d0ed4e" desc="">
+    Type &apos;?&apos; to see available commands
+  </message>
+  <message name="IDS_DEVTOOLS_22b6fe30dec1ae9c09480cce01cd4065" desc="">
+    Run command
+  </message>
+  <message name="IDS_DEVTOOLS_74540c79e377bea903e1023a46df5574" desc="">
+    Open file
+  </message>
+  <message name="IDS_DEVTOOLS_8bb9829a676055080c3d4507e0b5f201" desc="">
+    No commands found
+  </message>
+  <message name="IDS_DEVTOOLS_af9ccba3ec4346e3acf785f9bea514d8" desc="">
+    Show <ph name="EXTENSION_TITLE__">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_e576c23d915755d83e2d1f47bd9f6c22" desc="">
+    No results found
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
new file mode 100644
index 0000000..55c8211
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
@@ -0,0 +1,398 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_01abfc750a0c942167651c40d088531d" desc="">
+    #
+  </message>
+  <message name="IDS_DEVTOOLS_05b2812ab435addb8ae4853ead93f1b3" desc="">
+    Received <ph name="NEW_DATE_VERSION_SCRIPTRESPONSETIME_________TOLOCALESTRING__">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_06933067aafd48425d67bcb01bba5cb6" desc="">
+    Update
+  </message>
+  <message name="IDS_DEVTOOLS_09428a9282bbb3ffcf3caa7826f0bf83" desc="">
+    An unexpected error <ph name="SQLERROR_CODE">$1s</ph> occurred.
+  </message>
+  <message name="IDS_DEVTOOLS_0d6fa553290eb4bf9eabe203a43b01d9" desc="">
+    Application cache
+  </message>
+  <message name="IDS_DEVTOOLS_0f558243fbf45f1cd840fff01957f57b" desc="">
+    Clients
+  </message>
+  <message name="IDS_DEVTOOLS_1351a3eaa0f925dad980e83905bc1230" desc="">
+    Worker: <ph name="TARGETINFO_URL">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_136e9c46f48bf8798df663c4e786c3e3" desc="">
+    Update on reload
+  </message>
+  <message name="IDS_DEVTOOLS_15fa5e18dc110ac96ef35fd19abc78b3" desc="">
+    Time Cached
+  </message>
+  <message name="IDS_DEVTOOLS_1722d5b7f86af20cbd8d1981f23a15a3" desc="">
+    cookies used by frames from 
+  </message>
+  <message name="IDS_DEVTOOLS_1818d506396d77b3d035f719885c4cd1" desc="">
+    focus
+  </message>
+  <message name="IDS_DEVTOOLS_1bea0f12b50db07ee7f2265b790417ff" desc="">
+    Installability
+  </message>
+  <message name="IDS_DEVTOOLS_1fece652e0dde2e00d1a7662f081dd71" desc="">
+    The &quot;<ph name="THIS_TABLENAME">$1s</ph>&quot;
+table is empty.
+  </message>
+  <message name="IDS_DEVTOOLS_22554c2f3f431fa4243965df392b9ea9" desc="">
+    AppCache
+  </message>
+  <message name="IDS_DEVTOOLS_29104d3ede0231043a4b92a90b9c2873" desc="">
+    Some entries may have been modified
+  </message>
+  <message name="IDS_DEVTOOLS_2b81bb9b3deebcbfa05edad7a845005a" desc="">
+    Frames
+  </message>
+  <message name="IDS_DEVTOOLS_2bf0a735c3ff964861a2b55319edd355" desc="">
+    Unregister service workers
+  </message>
+  <message name="IDS_DEVTOOLS_2e9d8504b07d0bdf4887f27c070cbbb6" desc="">
+    autoIncrement
+  </message>
+  <message name="IDS_DEVTOOLS_301addf2037879acf2a972b157d2f4a7" desc="">
+    Local Files
+  </message>
+  <message name="IDS_DEVTOOLS_3472423ee4037c94e20d64b3aea8aa49" desc="">
+    <ph name="SCOPEURL">$1s</ph> - deleted
+  </message>
+  <message name="IDS_DEVTOOLS_34b6cd75171affba6957e308dcbd92be" desc="">
+    Version
+  </message>
+  <message name="IDS_DEVTOOLS_368d9ac76af05f714092bc808a426bfc" desc="">
+    Background color
+  </message>
+  <message name="IDS_DEVTOOLS_37acadd70183e7ed00222ff38248a6ff" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_RESPONSE_USAGE_">$1s</ph> used out of <ph name="NUMBER_BYTESTOSTRING_RESPONSE_QUOTA_">$2s</ph> storage quota. 
+  </message>
+  <message name="IDS_DEVTOOLS_3a771376134eb624f3e1fdd3d92d9f4c" desc="">
+    Select a cache entry above to preview
+  </message>
+  <message name="IDS_DEVTOOLS_3afd748bcc6315d69cff002ec6c377ed" desc="">
+    <ph name="THIS__REGISTRATION_ERRORS_LENGTH">$1s</ph> registration errors
+  </message>
+  <message name="IDS_DEVTOOLS_3eab5d12656f2f4462f6594019e77355" desc="">
+    Visible columns
+  </message>
+  <message name="IDS_DEVTOOLS_3edf8ca26a1ec14dd6e91dd277ae1de6" desc="">
+    Origin
+  </message>
+  <message name="IDS_DEVTOOLS_458efe6e41ed41835b8d84b28db3f394" desc="">
+    Service workers from other origins
+  </message>
+  <message name="IDS_DEVTOOLS_45e035baf33a8e403766a606457f8b10" desc="">
+    Theme color
+  </message>
+  <message name="IDS_DEVTOOLS_4f36f645368158bc46346b8902fc2566" desc="">
+    #<ph name="ACTIVE_ID">$1s</ph> activated and is <ph name="ACTIVE_RUNNINGSTATUS">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_50868c2251b0c3b64ca50052891aec4c" desc="">
+    Push data
+  </message>
+  <message name="IDS_DEVTOOLS_536cf547cb6ecfe32be6ed3ffdabf893" desc="">
+    Clear site data
+  </message>
+  <message name="IDS_DEVTOOLS_54f664c70c22054ea0d8d26fc3997ce7" desc="">
+    Online
+  </message>
+  <message name="IDS_DEVTOOLS_55bce575c41275ddaf5fbd1beb7d6018" desc="">
+    #<ph name="INSTALLING_ID">$1s</ph> trying to install
+  </message>
+  <message name="IDS_DEVTOOLS_579fdc74b6520ab8906ecddf3e62d951" desc="">
+    Application Cache
+  </message>
+  <message name="IDS_DEVTOOLS_5bb07b23ba5cd47d49a0a030556b21ba" desc="">
+    Background Fetch
+  </message>
+  <message name="IDS_DEVTOOLS_625bee64c286ac3018d57e87162f44e9" desc="">
+    Delete database
+  </message>
+  <message name="IDS_DEVTOOLS_626e8008b253bc4fbef9dca0a3f63903" desc="">
+    Instance ID
+  </message>
+  <message name="IDS_DEVTOOLS_65ebb2ea77ddbe405def1ad1d01e2e7b" desc="">
+    Bypass the service worker and load resources from the network
+  </message>
+  <message name="IDS_DEVTOOLS_6670d47e57b4fe982554519effc771c5" desc="">
+    Object stores
+  </message>
+  <message name="IDS_DEVTOOLS_673eb027e9c056f57140322807351dd5" desc="">
+    unique
+  </message>
+  <message name="IDS_DEVTOOLS_6827a85b76e967f6a129e08f9272e76d" desc="">
+    Local Storage
+  </message>
+  <message name="IDS_DEVTOOLS_6c202452e44c42e323204da2a3a3c24a" desc="">
+    File System
+  </message>
+  <message name="IDS_DEVTOOLS_6e72748dc2f2224dfabb374ca0cc988f" desc="">
+    #<ph name="REDUNDANT_ID">$1s</ph> is redundant
+  </message>
+  <message name="IDS_DEVTOOLS_76c18f2b1e4155961e10434e3d83317f" desc="">
+    Key generator value: <ph name="STRING_METADATA_KEYGENERATORVALUE_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_76c3e002d3c052bd6a909366a8dc3845" desc="">
+    Manifest
+  </message>
+  <message name="IDS_DEVTOOLS_795a813f20a4d4b04f85ddb86287fce4" desc="">
+    Storage quota is limited in Incognito mode
+  </message>
+  <message name="IDS_DEVTOOLS_798a0aef3a97a3eab63d88a0b6c07341" desc="">
+    SW Scope
+  </message>
+  <message name="IDS_DEVTOOLS_7a06b1cc41f027f75bc7be8ee937c344" desc="">
+    Database no longer has expected version.
+  </message>
+  <message name="IDS_DEVTOOLS_7adc295d89feec026fbcee16448c31d8" desc="">
+    <ph name="UNREACHABLEMAINORIGIN">$1s</ph> (failed to load)
+  </message>
+  <message name="IDS_DEVTOOLS_7d52bfc51222e4122623057310cba1ce" desc="">
+    Show previous page
+  </message>
+  <message name="IDS_DEVTOOLS_7e91f7be5e9ff7a50a44d4a7f21afb90" desc="">
+    Start URL
+  </message>
+  <message name="IDS_DEVTOOLS_7ed01b26177166f3627e29da31bc7e28" desc="">
+    No Application Cache information available.
+  </message>
+  <message name="IDS_DEVTOOLS_83b33e9633245178cb76ecb91dd424d8" desc="">
+    Select a value to preview
+  </message>
+  <message name="IDS_DEVTOOLS_86befdf33863a849060cf9f6d2425cdb" desc="">
+    Security origin
+  </message>
+  <message name="IDS_DEVTOOLS_87e059dc2492bd5041581f05daef12f7" desc="">
+    Filter by Path
+  </message>
+  <message name="IDS_DEVTOOLS_886b9bffdebad3a988a8da87b1affa2d" desc="">
+    Filter service worker
+  </message>
+  <message name="IDS_DEVTOOLS_897356954c2cd3d41b221e3f24f99bba" desc="">
+    Key
+  </message>
+  <message name="IDS_DEVTOOLS_8c4aa541ee911e8d80451ef8cc304806" desc="">
+    Storage
+  </message>
+  <message name="IDS_DEVTOOLS_8f67973007158337346584551b093be8" desc="">
+    Icons
+  </message>
+  <message name="IDS_DEVTOOLS_90e4c7584668933aebe8cf1cbccfe82d" desc="">
+    Please confirm delete of &quot;<ph name="THIS__DATABASE_DATABASEID_NAME">$1s</ph>&quot; database.
+  </message>
+  <message name="IDS_DEVTOOLS_9122ec25094d00855f48a500241c6008" desc="">
+    Service Workers
+  </message>
+  <message name="IDS_DEVTOOLS_9350cbb84e8ab827898a783d8f724951" desc="">
+    inspect
+  </message>
+  <message name="IDS_DEVTOOLS_94aa6cfa461998abe42d53963f1344c3" desc="">
+    An error occurred trying to
+read the &quot;<ph name="THIS_TABLENAME">$1s</ph>&quot; table.
+  </message>
+  <message name="IDS_DEVTOOLS_98fc9226c3fe878e39cd048fbd1c3692" desc="">
+    Short name
+  </message>
+  <message name="IDS_DEVTOOLS_9999106349673567ecaf1c32c07301a5" desc="">
+    #<ph name="WAITING_ID">$1s</ph> waiting to activate
+  </message>
+  <message name="IDS_DEVTOOLS_9b2a7456cec10d8b5ab8ce656598320b" desc="">
+    Key path: 
+  </message>
+  <message name="IDS_DEVTOOLS_9b790a1c94937c437f9801d3c970efa7" desc="">
+     (empty)
+  </message>
+  <message name="IDS_DEVTOOLS_9c6a9d9f033001a9b3104984d319563b" desc="">
+    Push
+  </message>
+  <message name="IDS_DEVTOOLS_9d4a8f2dfe3c62f3ce3011035d28fb66" desc="">
+    Unregister
+  </message>
+  <message name="IDS_DEVTOOLS_9dce9dd0f39a17c2e029174f5bc86ef9" desc="">
+    Background Sync
+  </message>
+  <message name="IDS_DEVTOOLS_9ff9f5649294f7200b671d6389d17d9a" desc="">
+    multiEntry
+  </message>
+  <message name="IDS_DEVTOOLS_a049ac2b4afe0fb6e78656082858067e" desc="">
+    Storage Usage
+  </message>
+  <message name="IDS_DEVTOOLS_a274f4d4670213a9045ce258c6c56b80" desc="">
+    Notifications
+  </message>
+  <message name="IDS_DEVTOOLS_a4ecfc70574394990cf17bd83df499f7" desc="">
+    Event
+  </message>
+  <message name="IDS_DEVTOOLS_a7d5ee58baead2f3bc229d3d9c047875" desc="">
+    Unregister service worker
+  </message>
+  <message name="IDS_DEVTOOLS_a89edb1e05005c844b684d1eb0de111a" desc="">
+    Cache Storage
+  </message>
+  <message name="IDS_DEVTOOLS_ab0cf104f39708eabd07b8cb67e149ba" desc="">
+    Cache
+  </message>
+  <message name="IDS_DEVTOOLS_ab6ee23f23d1d5f342588e64297efc1c" desc="">
+    Session Storage
+  </message>
+  <message name="IDS_DEVTOOLS_acb91a6bfd0644c5b90280f9ebf38774" desc="">
+    Response-Type
+  </message>
+  <message name="IDS_DEVTOOLS_ad2669a90d3964bc67a1a34152122845" desc="">
+    Matching entries: <ph name="THIS__RETURNCOUNT">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_ae262bf758227f7e319ba799a861681a" desc="">
+    Local and session storage
+  </message>
+  <message name="IDS_DEVTOOLS_b275ec0eb3bbc2f5ab8988fe15b47817" desc="">
+    Errors and warnings
+  </message>
+  <message name="IDS_DEVTOOLS_b5dc9b0e6beecb1e80f6f1b5b3050f12" desc="">
+    Application panel
+  </message>
+  <message name="IDS_DEVTOOLS_b66b856833f34a841b51c7207dbc601f" desc="">
+    IndexedDB
+  </message>
+  <message name="IDS_DEVTOOLS_b704442a84076f1139cd70604553e8dc" desc="">
+    Clear object store
+  </message>
+  <message name="IDS_DEVTOOLS_b9987a246a537f4fe86f1f2e3d10dbdb" desc="">
+    Display
+  </message>
+  <message name="IDS_DEVTOOLS_ba8d2e1eca2bf52ab3cd0202d4d04c07" desc="">
+    Test push message from DevTools.
+  </message>
+  <message name="IDS_DEVTOOLS_bb8839cf9d324a22591ff426c28c5345" desc="">
+    Show next page
+  </message>
+  <message name="IDS_DEVTOOLS_be8545ae7ab0276e15898aae7acfbd7a" desc="">
+    Resource
+  </message>
+  <message name="IDS_DEVTOOLS_bebc9b50966d8d2bd1913a3c25b707d4" desc="">
+    Data may be stale
+  </message>
+  <message name="IDS_DEVTOOLS_bec9bd97f1cda22686854c090b5133fc" desc="">
+    Save events
+  </message>
+  <message name="IDS_DEVTOOLS_c13af79d63bfcb3f07bc3810418c99f8" desc="">
+    Delete Selected
+  </message>
+  <message name="IDS_DEVTOOLS_c1ea3facd2946b59df718d8dd2737cfa" desc="">
+    Background Services
+  </message>
+  <message name="IDS_DEVTOOLS_c64518704ce0c0d5501a45763f464276" desc="">
+    Usage
+  </message>
+  <message name="IDS_DEVTOOLS_c6661c86882ff82da47747b673907fa2" desc="">
+    No manifest detected
+  </message>
+  <message name="IDS_DEVTOOLS_c76eb02deaa41ffd58d22d08c213fea5" desc="">
+    Select an entry to view metadata
+  </message>
+  <message name="IDS_DEVTOOLS_c7bc3fddddb62d5b656adc30cea77a55" desc="">
+    Presentation
+  </message>
+  <message name="IDS_DEVTOOLS_c9a5ab3cec5c1de816c0f8ea8d7edfed" desc="">
+    Stop recording events
+  </message>
+  <message name="IDS_DEVTOOLS_c9c5c65fb4af9cf90eb99b3b84424189" desc="">
+    Identity
+  </message>
+  <message name="IDS_DEVTOOLS_cc0041e1d664fd87ad1cfaae4265893f" desc="">
+    Bypass for network
+  </message>
+  <message name="IDS_DEVTOOLS_cc8763e24276418641ca188290e3334c" desc="">
+    On page reload, force the service worker to update, and activate it
+  </message>
+  <message name="IDS_DEVTOOLS_ccdc0553283bddb674e37e272a9832f0" desc="">
+    Clearing...
+  </message>
+  <message name="IDS_DEVTOOLS_cd461eb091b5b58a6fe5a1b5edba4c5f" desc="">
+    Refresh Caches
+  </message>
+  <message name="IDS_DEVTOOLS_ce0540adc848ff5957d4d857984592d1" desc="">
+    Cache storage
+  </message>
+  <message name="IDS_DEVTOOLS_ce9d3c5ae4d74bd1e8d01732e264e36b" desc="">
+    No metadata for this event
+  </message>
+  <message name="IDS_DEVTOOLS_cfde4f71aaac6d02831a661f4f9990f1" desc="">
+    App Manifest
+  </message>
+  <message name="IDS_DEVTOOLS_d3b206d196cd6be3a2764c1fb90b200f" desc="">
+    Delete selected
+  </message>
+  <message name="IDS_DEVTOOLS_d4f61ad886e0b169b0394d97bb1a649a" desc="">
+    Recording <ph name="RESOURCES_BACKGROUNDSERVICEVIEW_GETUISTRING_THIS__SERVICENAME_">$1s</ph> activity...
+  </message>
+  <message name="IDS_DEVTOOLS_d6f875d9f87efbee1a96b8c4f5784d47" desc="">
+    Primary key
+  </message>
+  <message name="IDS_DEVTOOLS_d892ca11530be1235ec05eb368ecaf37" desc="">
+    Start from key
+  </message>
+  <message name="IDS_DEVTOOLS_d8e87c0927539672f54462c837be0b7f" desc="">
+    Sync
+  </message>
+  <message name="IDS_DEVTOOLS_da131b6bd53f501c1323af4b198740d2" desc="">
+    Push Messaging
+  </message>
+  <message name="IDS_DEVTOOLS_dc105ac52c237f9f09c74b0767f64f74" desc="">
+    Content-Type
+  </message>
+  <message name="IDS_DEVTOOLS_dc5f1eac93c7e0c1f63f360b24fd8489" desc="">
+    Refresh database
+  </message>
+  <message name="IDS_DEVTOOLS_dc87ca61695d643744b53bca9bf3f1d5" desc="">
+    Total entries: <ph name="STRING_METADATA_ENTRIESCOUNT_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_ddcf50c29294d4414f3f7c1bbc892cb5" desc="">
+    Resources
+  </message>
+  <message name="IDS_DEVTOOLS_e00a7ac13697ba51e098449fd644935b" desc="">
+    Show events from other domains
+  </message>
+  <message name="IDS_DEVTOOLS_e307db07b3975fef922a80d07455ee5e" desc="">
+    Database
+  </message>
+  <message name="IDS_DEVTOOLS_e44ec1cd1aa0128f893c1c815ea1abe0" desc="">
+    Click the record button <ph name="UI_CREATEINLINEBUTTON_LANDINGRECORDBUTTON_">$1s</ph> or hit <ph name="RECORDKEY">$2s</ph> to start recording.
+  </message>
+  <message name="IDS_DEVTOOLS_e498749f3c42246d50b15c81c101d988" desc="">
+    Application
+  </message>
+  <message name="IDS_DEVTOOLS_e60593abb5603af48307343eb862d86a" desc="">
+    DOM Storage
+  </message>
+  <message name="IDS_DEVTOOLS_e918a12b0b4a962aff8b7119643af250" desc="">
+    skipWaiting
+  </message>
+  <message name="IDS_DEVTOOLS_ea2b2676c28c0db26d39331a336c6b92" desc="">
+    start
+  </message>
+  <message name="IDS_DEVTOOLS_ec1e074d56484165315c26a9baebaebd" desc="">
+    Web SQL
+  </message>
+  <message name="IDS_DEVTOOLS_ec3c33fb128aab90f46c922f8f7098f1" desc="">
+    Start recording events
+  </message>
+  <message name="IDS_DEVTOOLS_ed1f65c89bd76342be87bec8edd63904" desc="">
+    Refresh IndexedDB
+  </message>
+  <message name="IDS_DEVTOOLS_eeb6ddeed2e08678ebc14eb39c8340bf" desc="">
+    IDB
+  </message>
+  <message name="IDS_DEVTOOLS_ef34ee8b3523f523efcf74d28fab2b47" desc="">
+    Sync tag
+  </message>
+  <message name="IDS_DEVTOOLS_ef399b2d446bb37b7c32ad2cc1b6045b" desc="">
+    stop
+  </message>
+  <message name="IDS_DEVTOOLS_f31bbdd1b3e85bccd652680e16935819" desc="">
+    Source
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/screencast/screencast_strings.grdp b/third_party/blink/renderer/devtools/front_end/screencast/screencast_strings.grdp
new file mode 100644
index 0000000..6b5de3a
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/screencast/screencast_strings.grdp
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_30b6c2858b9a03c8518c8804bb96067c" desc="">
+    Profiling in progress
+  </message>
+  <message name="IDS_DEVTOOLS_e2ed1dadec9e90b7108be8256808a5c0" desc="">
+    Toggle screencast
+  </message>
+  <message name="IDS_DEVTOOLS_f775403b2922abfa05ae76f922fcacf3" desc="">
+    The tab is inactive
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
new file mode 100644
index 0000000..42af092
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_07553a11db31a4433684be32cc4716e3" desc="">
+    Cross-Origin Read Blocking (CORB) blocked cross-origin response <ph name="NETWORKREQUEST_URL__">$1s</ph> with MIME type <ph name="NETWORKREQUEST_MIMETYPE">$2s</ph>. See https://www.chromestatus.com/feature/5629709824032768 for more details.
+  </message>
+  <message name="IDS_DEVTOOLS_07ba1c48fc0b478df5462700f993fa5e" desc="">
+    <ph name="NETWORKREQUEST_RESOURCETYPE___TITLE__">$1s</ph> failed loading: <ph name="NETWORKREQUEST_REQUESTMETHOD">$2s</ph> &quot;<ph name="NETWORKREQUEST_URL__">$3s</ph>&quot;.
+  </message>
+  <message name="IDS_DEVTOOLS_09cdd6a7321c64bae05b8cca859f1461" desc="">
+    Enable cache
+  </message>
+  <message name="IDS_DEVTOOLS_0fe1f9158e2f164da1332501f9e65702" desc="">
+    Navigated to <ph name="EVENT_DATA_URL">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_1023bf87beb1d2947824df00fa0898ad" desc="">
+    Disable async stack traces
+  </message>
+  <message name="IDS_DEVTOOLS_150fa516787b5e5d1d2723c84574dbee" desc="">
+    HTTP-Based Public Key Pinning is deprecated. Chrome 69 and later will ignore HPKP response headers. (Host: <ph name="PARSED_HOST">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_185bf49541541933dc3fd1fb89d3fea3" desc="">
+    Enable custom formatters
+  </message>
+  <message name="IDS_DEVTOOLS_1985576209a186984f171efd81e91e3d" desc="">
+    &lt;anonymous&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_21070fd87b02611a5426d726a70a5502" desc="">
+    No value found for parameter &quot;<ph name="PARAMNAME">$1s</ph>&quot;.
+  </message>
+  <message name="IDS_DEVTOOLS_212af9ab36fa426a9e52bc1ebfd8665b" desc="">
+    Drag / drop
+  </message>
+  <message name="IDS_DEVTOOLS_23e5a7b7976e412ece233ceaf8257bc2" desc="">
+    Clipboard
+  </message>
+  <message name="IDS_DEVTOOLS_23e8e5daea7e9dc939ae4298a6f74e36" desc="">
+    ServerTiming: <ph name="MSG">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_28e486be4b3bad598b6e9eaa34ebec76" desc="">
+    Set-Cookie header is ignored in response from url: <ph name="RESPONSE_URL">$1s</ph>. Cookie length should be less than or equal to 4096 characters.
+  </message>
+  <message name="IDS_DEVTOOLS_2973958e77629e3a167a1a4c60dd5c5f" desc="">
+    Slow 3G
+  </message>
+  <message name="IDS_DEVTOOLS_3034e08ec0b14678ddb50284eef02ee4" desc="">
+    Do not preserve log upon navigation
+  </message>
+  <message name="IDS_DEVTOOLS_328d7fa69bbeeeefd8b3db6e1f06a17b" desc="">
+    With Block
+  </message>
+  <message name="IDS_DEVTOOLS_3437f1aa3e12219221092a45e62c9d4f" desc="">
+    Profile &apos;<ph name="DATA_TITLE">$1s</ph>&apos; finished.
+  </message>
+  <message name="IDS_DEVTOOLS_365e0172a2e97b0a26274c5bd2e4bad0" desc="">
+    Script Blocked by Content Security Policy
+  </message>
+  <message name="IDS_DEVTOOLS_38fe155e13d77298cfd5688c3378fa85" desc="">
+    Emulate CSS screen media type
+  </message>
+  <message name="IDS_DEVTOOLS_3b563524fdb17b4a86590470d40bef74" desc="">
+    Media
+  </message>
+  <message name="IDS_DEVTOOLS_3d9320c6e796b65a6c91fcd64e27fa88" desc="">
+    Hide frames per second (FPS) meter
+  </message>
+  <message name="IDS_DEVTOOLS_3ff9f750075f426831f71818a8f4ff12" desc="">
+    Request was blocked by DevTools: &quot;<ph name="NETWORKREQUEST_URL__">$1s</ph>&quot;.
+  </message>
+  <message name="IDS_DEVTOOLS_40dba446b661ae69dea3a8e026f76dfd" desc="">
+    Hide paint flashing rectangles
+  </message>
+  <message name="IDS_DEVTOOLS_4249b3308a0ed3e1e90e0a91ca11cf21" desc="">
+    WebGL Error Fired
+  </message>
+  <message name="IDS_DEVTOOLS_4636cded1f8a4578c9e8e7f2fd9cfdee" desc="">
+    (binary)
+  </message>
+  <message name="IDS_DEVTOOLS_4a2c6d480468877dd33d9a5bae54b549" desc="">
+    WebGL Warning Fired
+  </message>
+  <message name="IDS_DEVTOOLS_4bfca2e69ec73893d36ebcdcce35cf36" desc="">
+    Suspend AudioContext
+  </message>
+  <message name="IDS_DEVTOOLS_4cc00b9253c692417d3b0765d5ae5e79" desc="">
+    Unable to parse &quot;<ph name="PARAMNAME">$1s</ph>&quot; value &quot;<ph name="PARAMVALUE">$2s</ph>&quot;.
+  </message>
+  <message name="IDS_DEVTOOLS_4cc6684df7b4a92b1dec6fce3264fac8" desc="">
+    Global
+  </message>
+  <message name="IDS_DEVTOOLS_509820290d57f333403f490dde7316f4" desc="">
+    Local
+  </message>
+  <message name="IDS_DEVTOOLS_509a7f125ba127d220887938738af707" desc="">
+    Failed to save to temp variable.
+  </message>
+  <message name="IDS_DEVTOOLS_52da3e7fcf6abefc2a8807df4b759ef8" desc="">
+    setInterval fired
+  </message>
+  <message name="IDS_DEVTOOLS_599eba19aa93a929cb8589f148b8a6c4" desc="">
+    screen
+  </message>
+  <message name="IDS_DEVTOOLS_5c63ad6312d0451b3d1012e5ea8fd08f" desc="">
+    Emulate CSS print media type
+  </message>
+  <message name="IDS_DEVTOOLS_5fe9d4f417e5f34919b0b6e5f0431ec1" desc="">
+    Hide hit-test borders
+  </message>
+  <message name="IDS_DEVTOOLS_61cf8510205077b6f5491d38cd44c0f7" desc="">
+    Pointer
+  </message>
+  <message name="IDS_DEVTOOLS_62efb9ec331e364b96efe68c8b03ca20" desc="">
+    Worker
+  </message>
+  <message name="IDS_DEVTOOLS_62fce3f916fe47f75c391fd5e4fc3eca" desc="">
+    Consider disabling <ph name="COMMON_UISTRING__CHROME_DATA_SAVER__">$1s</ph> while debugging. For more info see: <ph name="_HTTPS___SUPPORT_GOOGLE_COM_CHROME__P_DATASAVER_">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_6617a779f50f7afb7949c7ea3ad52b28" desc="">
+    Show hit-test borders
+  </message>
+  <message name="IDS_DEVTOOLS_6948a469c79f7dd5426e4f291cba3db1" desc="">
+    Chrome Data Saver
+  </message>
+  <message name="IDS_DEVTOOLS_6ce4d85a628a88bbdb3ac24a8e5a9c2e" desc="">
+    Keyboard
+  </message>
+  <message name="IDS_DEVTOOLS_6e2e8696272b40b7a9745c998a39b177" desc="">
+    Close AudioContext
+  </message>
+  <message name="IDS_DEVTOOLS_6ea5359e01a41842884943125518289b" desc="">
+    Canvas
+  </message>
+  <message name="IDS_DEVTOOLS_6fdb8f0232e380cf106312e7b880d8d2" desc="">
+    Preserve log upon navigation
+  </message>
+  <message name="IDS_DEVTOOLS_7121afd196f5c52bef488d5a0f4c097b" desc="">
+    Script First Statement
+  </message>
+  <message name="IDS_DEVTOOLS_794f64c7f20487f6e13679201deeab3d" desc="">
+    Picture-in-Picture
+  </message>
+  <message name="IDS_DEVTOOLS_7c2bc755363ab11a1611bfa369654ff8" desc="">
+    Show frames per second (FPS) meter
+  </message>
+  <message name="IDS_DEVTOOLS_81961fe251f4d1cb4df131561dedf319" desc="">
+    Hide layer borders
+  </message>
+  <message name="IDS_DEVTOOLS_83f2229658949472d34f78e19475fcdd" desc="">
+    Show layer borders
+  </message>
+  <message name="IDS_DEVTOOLS_9028784c589c0c809700c7fbc66a5d96" desc="">
+    Resource interpreted as <ph name="NETWORKREQUEST_RESOURCETYPE___TITLE__">$1s</ph> but transferred with MIME type <ph name="NETWORKREQUEST_MIMETYPE">$2s</ph>: &quot;<ph name="NETWORKREQUEST_URL__">$3s</ph>&quot;.
+  </message>
+  <message name="IDS_DEVTOOLS_90da6f38945a1c7e7ba11e3adcbe6919" desc="">
+    Pause on exceptions
+  </message>
+  <message name="IDS_DEVTOOLS_9108ae673b7efa9620aec771edc9d4de" desc="">
+    setTimeout fired
+  </message>
+  <message name="IDS_DEVTOOLS_930c85a96d7227c515891c4f86a17c30" desc="">
+    Disable JavaScript
+  </message>
+  <message name="IDS_DEVTOOLS_93dee6366d66361f36985cf5c88346aa" desc="">
+    Create AudioContext
+  </message>
+  <message name="IDS_DEVTOOLS_95bbad55f11c237ed89546e748093ba7" desc="">
+    Request Animation Frame
+  </message>
+  <message name="IDS_DEVTOOLS_96d008db67fc0b5551a926842bbb6a71" desc="">
+    Notification
+  </message>
+  <message name="IDS_DEVTOOLS_9850063efe194af1c63d2aa61ef94c62" desc="">
+    Create canvas context
+  </message>
+  <message name="IDS_DEVTOOLS_9dd7b9f5cc1c19a830f153c3e8f1ad89" desc="">
+    Fast 3G
+  </message>
+  <message name="IDS_DEVTOOLS_9f7d4cb9fefed9daf0e9e4aad0620497" desc="">
+    Do not highlight ad frames
+  </message>
+  <message name="IDS_DEVTOOLS_a155bce1e541d9234013102c2e613941" desc="">
+    ServiceWorkerCacheAgent error deleting cache entry <ph name="CACHE_TOSTRING__">$1s</ph> in cache: <ph name="RESPONSE_PROTOCOL_ERROR_">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_a1595abbb4c3a326636dd178757cd6c1" desc="">
+    Control
+  </message>
+  <message name="IDS_DEVTOOLS_a720fc15eb9ec85751969e8615ace9e1" desc="">
+    Duplicate parameter &quot;<ph name="PARAMNAME">$1s</ph>&quot; ignored.
+  </message>
+  <message name="IDS_DEVTOOLS_ac4aac1ba23d844f4976e9fcd1fd4a61" desc="">
+    WebGL Error Fired (<ph name="ERRORNAME">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_af4bb376939e77df0e7c2332b837a866" desc="">
+    Closure
+  </message>
+  <message name="IDS_DEVTOOLS_b28354b543375bfa94dabaeda722927f" desc="">
+    top
+  </message>
+  <message name="IDS_DEVTOOLS_b43356cdb7bdec437e117bb1ed5625bc" desc="">
+    Capture async stack traces
+  </message>
+  <message name="IDS_DEVTOOLS_b50ec339cd40d39d5ff4ca56379e61c6" desc="">
+    Paused in debugger
+  </message>
+  <message name="IDS_DEVTOOLS_b600e0facf21b7d4a9a769a60b236ba1" desc="">
+    Enable JavaScript
+  </message>
+  <message name="IDS_DEVTOOLS_ba50636875d999db0c9218b48b23027d" desc="">
+    Do not capture async stack traces
+  </message>
+  <message name="IDS_DEVTOOLS_ba5b676ccf658f863ef2e3df082ee262" desc="">
+    Animation Frame Fired
+  </message>
+  <message name="IDS_DEVTOOLS_bc955333ff170762be4971a8fde558cf" desc="">
+    WebAudio
+  </message>
+  <message name="IDS_DEVTOOLS_c432f4e5fdec5760a2f06b38f646168e" desc="">
+    Profile &apos;<ph name="DATA_TITLE">$1s</ph>&apos; started.
+  </message>
+  <message name="IDS_DEVTOOLS_c5d59568835d0760e318829b800ee06d" desc="">
+    Catch
+  </message>
+  <message name="IDS_DEVTOOLS_c64d5c0d5233c7d6e85a41c6dd3c3a3f" desc="">
+    Resume AudioContext
+  </message>
+  <message name="IDS_DEVTOOLS_c89686a387d2b12b3c729ce35a0bcb5b" desc="">
+    Window
+  </message>
+  <message name="IDS_DEVTOOLS_c9ad95228aa735bdda1aebf38da022af" desc="">
+    Parse
+  </message>
+  <message name="IDS_DEVTOOLS_cedab6b9e4e794e93bba797e8aff218a" desc="">
+    <ph name="NETWORKREQUEST_RESOURCETYPE___TITLE__">$1s</ph> finished loading: <ph name="NETWORKREQUEST_REQUESTMETHOD">$2s</ph> &quot;<ph name="NETWORKREQUEST_URL__">$3s</ph>&quot;.
+  </message>
+  <message name="IDS_DEVTOOLS_d24a22fef757966373800aa9795458dc" desc="">
+    ? [sm]
+  </message>
+  <message name="IDS_DEVTOOLS_d31827f0a164d52b636bb1f71ca4a351" desc="">
+    Cancel Animation Frame
+  </message>
+  <message name="IDS_DEVTOOLS_d4fad802fc7bf0248d34c685533bd56f" desc="">
+    Disable request blocking
+  </message>
+  <message name="IDS_DEVTOOLS_d6b6b668dbca9d4fe774bb654226ebe3" desc="">
+    Animation
+  </message>
+  <message name="IDS_DEVTOOLS_dcd9c13c4f8ae68ea7ae92edf9191264" desc="">
+    Deprecated syntax found. Please use: &lt;name&gt;;dur=&lt;duration&gt;;desc=&lt;description&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_df9f4292c1d025cbada13b25744d34e5" desc="">
+    Do not emulate CSS media type
+  </message>
+  <message name="IDS_DEVTOOLS_e1e4c8c9ccd9fc39c391da4bcd093fb2" desc="">
+    Block
+  </message>
+  <message name="IDS_DEVTOOLS_e2b60c37530e144372a7943df0f6017d" desc="">
+    &lt;iframe&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_e402c138065e7eece783dc30c6079192" desc="">
+    XHR
+  </message>
+  <message name="IDS_DEVTOOLS_e485c975c1d6c17d9dfd81128b83f63a" desc="">
+    Set innerHTML
+  </message>
+  <message name="IDS_DEVTOOLS_e55f75a29310d7b60f7ac1d390c8ae42" desc="">
+    Module
+  </message>
+  <message name="IDS_DEVTOOLS_e69d85cd1acdf030f0cede48b61d7720" desc="">
+    Hide scroll performance bottlenecks
+  </message>
+  <message name="IDS_DEVTOOLS_edb020d2175281d94054136e09a3e132" desc="">
+    Do not pause on exceptions
+  </message>
+  <message name="IDS_DEVTOOLS_efa547f7d0b9924fdc7b301838c99fad" desc="">
+    No emulation
+  </message>
+  <message name="IDS_DEVTOOLS_efb4777327e6f704fb1519c1882f93ec" desc="">
+    Timer
+  </message>
+  <message name="IDS_DEVTOOLS_eff8d530f562a22a45fbf1ee83299353" desc="">
+    Show scroll performance bottlenecks
+  </message>
+  <message name="IDS_DEVTOOLS_f107ac919ab14339cdb22cd00fccd215" desc="">
+    Show paint flashing rectangles
+  </message>
+  <message name="IDS_DEVTOOLS_f2a47c6809d88e175dade0ef7b16aa13" desc="">
+    Mouse
+  </message>
+  <message name="IDS_DEVTOOLS_f6137609f4decf877ede5bd3a3125629" desc="">
+    DevTools: CPU profile parser is fixing <ph name="COUNT">$1s</ph> missing samples.
+  </message>
+  <message name="IDS_DEVTOOLS_f7531e2d0ea27233ce00b5f01c5bf335" desc="">
+    print
+  </message>
+  <message name="IDS_DEVTOOLS_f9778c8e7464e4bb037ec2463879588f" desc="">
+    DOM Mutation
+  </message>
+  <message name="IDS_DEVTOOLS_f99d691018d8f37dda14a9cd29c1d1ee" desc="">
+    Script blocked due to Content Security Policy directive: <ph name="AUXDATA__DIRECTIVETEXT__">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_fa79cdac06c8d2166fd5cda17ccbc0ce" desc="">
+    Extraneous trailing characters.
+  </message>
+  <message name="IDS_DEVTOOLS_ff92304ed0236a5f972fd90e9b47fae4" desc="">
+    Unrecognized parameter &quot;<ph name="PARAMNAME">$1s</ph>&quot;.
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/search/search_strings.grdp b/third_party/blink/renderer/devtools/front_end/search/search_strings.grdp
new file mode 100644
index 0000000..ef34ad6
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/search/search_strings.grdp
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_2f04f3e6eee339eeb0ddb6c39606424d" desc="">
+    Use Regular Expression
+  </message>
+  <message name="IDS_DEVTOOLS_44b4e531d5b06deb40b631e4aa3c7e36" desc="">
+    Show <ph name="MATCHESLEFTCOUNT">$1d</ph> more
+  </message>
+  <message name="IDS_DEVTOOLS_617778f774bc264b169dde60f223285f" desc="">
+    Indexing…
+  </message>
+  <message name="IDS_DEVTOOLS_9fc52b55092ab8a92303e86722013b36" desc="">
+    Search interrupted.
+  </message>
+  <message name="IDS_DEVTOOLS_ab29eba562fe20ebb94fed224813c109" desc="">
+    Search finished.
+  </message>
+  <message name="IDS_DEVTOOLS_b20aa447d2e9b2d5065ac0ac4ed7bcc2" desc="">
+    Found <ph name="THIS__SEARCHMATCHESCOUNT">$1d</ph> matching lines in <ph name="THIS__NONEMPTYSEARCHRESULTSCOUNT">$2d</ph> files.
+  </message>
+  <message name="IDS_DEVTOOLS_c440d4f6fc7dd9239ada58277b900249" desc="">
+    Found <ph name="THIS__SEARCHMATCHESCOUNT">$1d</ph> matching lines in 1 file.
+  </message>
+  <message name="IDS_DEVTOOLS_c949475bc69a57c419ac976fe8870652" desc="">
+    Match Case
+  </message>
+  <message name="IDS_DEVTOOLS_d1e64da6074bf343a0a6bd2cd6515189" desc="">
+    Indexing interrupted.
+  </message>
+  <message name="IDS_DEVTOOLS_f4c8ff50c8f0b548e3297aeaf3b304b4" desc="">
+    Found 1 matching line in 1 file.
+  </message>
+  <message name="IDS_DEVTOOLS_f7e2fe044011a710d68a3c4e165ef7bf" desc="">
+    No matches found.
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp b/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
new file mode 100644
index 0000000..46bc61e
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_02e168aecffdbc00d48c952eba310b02" desc="">
+    Main origin
+  </message>
+  <message name="IDS_DEVTOOLS_0f22d804f6720086f2ffe6c4e9d7d3fd" desc="">
+    Unknown / canceled
+  </message>
+  <message name="IDS_DEVTOOLS_1238f21584e6dc18c44c4d96fc9bef13" desc="">
+    Issued at
+  </message>
+  <message name="IDS_DEVTOOLS_1dc6f97ea55d39e862637dcb0298a3ff" desc="">
+    Open full certificate details
+  </message>
+  <message name="IDS_DEVTOOLS_22b29ad1d12365f80273c5b173b2ac6b" desc="">
+    Not secure (broken)
+  </message>
+  <message name="IDS_DEVTOOLS_22fcd09f19102f883a8e337ea1624b20" desc="">
+    Log ID
+  </message>
+  <message name="IDS_DEVTOOLS_278dded33791cce4bb389fdf6a516856" desc="">
+    Your connection to this origin is not secure.
+  </message>
+  <message name="IDS_DEVTOOLS_2ad7083a628c9593e4c538deab838d77" desc="">
+    This page is not secure (broken HTTPS).
+  </message>
+  <message name="IDS_DEVTOOLS_2fae32629d4ef4fc6341f1751b405e45" desc="">
+    Security
+  </message>
+  <message name="IDS_DEVTOOLS_36cc7ab088ee5ea657c3d1e47016901b" desc="">
+    The security details above are from the first inspected response.
+  </message>
+  <message name="IDS_DEVTOOLS_3b878279a04dc47d60932cb294d96259" desc="">
+    Overview
+  </message>
+  <message name="IDS_DEVTOOLS_3bc80b82af3a60973d57e8ff762b87eb" desc="">
+    Your page requested non-secure resources that were blocked.
+  </message>
+  <message name="IDS_DEVTOOLS_4212f531450435498e750858b23cc739" desc="">
+    No security details are available for this origin.
+  </message>
+  <message name="IDS_DEVTOOLS_45c618734371071e7c335c06c1d2852d" desc="">
+    This page is secure (valid HTTPS).
+  </message>
+  <message name="IDS_DEVTOOLS_46b0ab970c4705a3a388a6d86aa831c2" desc="">
+    Main origin (secure)
+  </message>
+  <message name="IDS_DEVTOOLS_472acd9e75af12adb6421b3eedc8f975" desc="">
+    Reload the page to record requests for HTTP resources.
+  </message>
+  <message name="IDS_DEVTOOLS_4b1f76eea8c3d555f4fe2a9efe4513a5" desc="">
+    SCT
+  </message>
+  <message name="IDS_DEVTOOLS_4e6509e12a8c0ebf2b39fd7e158d48c2" desc="">
+    This origin is a non-HTTPS secure origin.
+  </message>
+  <message name="IDS_DEVTOOLS_55c917250d323e0d8572a1c3673bc80d" desc="">
+    The security of this page is unknown.
+  </message>
+  <message name="IDS_DEVTOOLS_5a70e1c7b4fa71a69851e44cda14dff7" desc="">
+    Secure origins
+  </message>
+  <message name="IDS_DEVTOOLS_628b50e6d53ec0d0e9b62971872e6b8b" desc="">
+    Show more (<ph name="SANLIST_LENGTH">$1d</ph> total)
+  </message>
+  <message name="IDS_DEVTOOLS_62a0282d39568be094470486eaf70c4f" desc="">
+    SAN
+  </message>
+  <message name="IDS_DEVTOOLS_6792733f0a02ad04a0a660c9a0bcfad1" desc="">
+    Main origin (non-secure)
+  </message>
+  <message name="IDS_DEVTOOLS_6f5168da8d36a44f8c196c8aa3ff3e40" desc="">
+    Certificate Transparency
+  </message>
+  <message name="IDS_DEVTOOLS_71d09158378f6d5172ac991d5f26a1c1" desc="">
+    Log name
+  </message>
+  <message name="IDS_DEVTOOLS_77ee7df08008633ded94db7e019a2bcc" desc="">
+    Non-secure origins
+  </message>
+  <message name="IDS_DEVTOOLS_7d5b05ab7e3853d483726a5b1a94c475" desc="">
+    Signature algorithm
+  </message>
+  <message name="IDS_DEVTOOLS_7e025c9c2b87ac6d946747ea156b3f37" desc="">
+    Blocked mixed content
+  </message>
+  <message name="IDS_DEVTOOLS_918fa99baa381e2902ad2185ea659228" desc="">
+    Hash algorithm
+  </message>
+  <message name="IDS_DEVTOOLS_9859694a15c021aa640bfda6b3e4423f" desc="">
+    Key exchange group
+  </message>
+  <message name="IDS_DEVTOOLS_98bce0cf672e732251f6f4327749ed18" desc="">
+    (n/a)
+  </message>
+  <message name="IDS_DEVTOOLS_9b30e34fce49de00a7fefd64aa5371fd" desc="">
+    This page is not secure.
+  </message>
+  <message name="IDS_DEVTOOLS_9c5ad7ee1f9630b92891a478327e670f" desc="">
+    View <ph name="FILTERREQUESTCOUNT">$1d</ph> requests in Network Panel
+  </message>
+  <message name="IDS_DEVTOOLS_a10edd2a3893a9ba446fb26e7c27556a" desc="">
+    This request complies with Chrome&apos;s Certificate Transparency policy.
+  </message>
+  <message name="IDS_DEVTOOLS_a2b16c6b3db371738d0e5c3f3ba2b54d" desc="">
+    This response was loaded from cache. Some security details might be missing.
+  </message>
+  <message name="IDS_DEVTOOLS_a574d935216052d2d6ee69b743cd4147" desc="">
+    Show full details
+  </message>
+  <message name="IDS_DEVTOOLS_a5f643359ba723c6f26cc3a3cfc14874" desc="">
+    No security information
+  </message>
+  <message name="IDS_DEVTOOLS_a7ec70e97f31322dc2e9aaa805b102ab" desc="">
+    View <ph name="FILTERREQUESTCOUNT">$1d</ph> request in Network Panel
+  </message>
+  <message name="IDS_DEVTOOLS_b92f649b203ca680d444432324771186" desc="">
+    This request does not comply with Chrome&apos;s Certificate Transparency policy.
+  </message>
+  <message name="IDS_DEVTOOLS_c3db8a95444ea6caa45cc7dcb6e78d64" desc="">
+    Security overview
+  </message>
+  <message name="IDS_DEVTOOLS_c64a179b75e6bf377700187bea8de65e" desc="">
+    Reload to view details
+  </message>
+  <message name="IDS_DEVTOOLS_c8ef68c0fce4f4bfe0219809473d3651" desc="">
+    Cipher
+  </message>
+  <message name="IDS_DEVTOOLS_d42c8bbf1d0f4a65f4dec450f2bc4439" desc="">
+    Key exchange
+  </message>
+  <message name="IDS_DEVTOOLS_decb9f523d4cf5dc037b61cb555e6bf3" desc="">
+    Not secure
+  </message>
+  <message name="IDS_DEVTOOLS_ebcaa68d6546adbf82e22d0384d945cf" desc="">
+    Hide full details
+  </message>
+  <message name="IDS_DEVTOOLS_f701897bd65f88245ebde54a818953a1" desc="">
+    Signature data
+  </message>
+  <message name="IDS_DEVTOOLS_ffb9069b5d779f0a9e37dfbd47d9edfb" desc="">
+    Validation status
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp b/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp
new file mode 100644
index 0000000..594f0c9
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_0f653ae68147586eb189d7baf37a696a" desc="">
+    Restore defaults and reload
+  </message>
+  <message name="IDS_DEVTOOLS_29e9d8e295b1d5b3002d2d3d5baa4c37" desc="">
+    Shortcuts
+  </message>
+  <message name="IDS_DEVTOOLS_2cce43a6505a5d1bdf7ea98bf7fd1bc8" desc="">
+    Blackbox
+  </message>
+  <message name="IDS_DEVTOOLS_331f303e56b067dd84abf94c3bd349c4" desc="">
+    Framework Blackboxing
+  </message>
+  <message name="IDS_DEVTOOLS_4829262cecb9828817b33e0f9c907f91" desc="">
+    Experiments
+  </message>
+  <message name="IDS_DEVTOOLS_57391192dfa1f247ad015a0fe2eca48e" desc="">
+    Pattern
+  </message>
+  <message name="IDS_DEVTOOLS_89bacc9715d030658f2602ce65426a7f" desc="">
+    WARNING:
+  </message>
+  <message name="IDS_DEVTOOLS_ae25bc3ceebf44be4b59d0ade0e53f0b" desc="">
+    No blackboxed patterns
+  </message>
+  <message name="IDS_DEVTOOLS_b39a035a995fc6597c8eb942210d1527" desc="">
+    Behavior
+  </message>
+  <message name="IDS_DEVTOOLS_b791f463c10f80e87e887e1f863f6209" desc="">
+    Add pattern...
+  </message>
+  <message name="IDS_DEVTOOLS_c745d3ac390f120535d734cd14e2f6aa" desc="">
+    Blackbox content scripts (extension scripts in the page)
+  </message>
+  <message name="IDS_DEVTOOLS_c9deece3e6de26d07ef6409b96f21fd0" desc="">
+    Blackboxing
+  </message>
+  <message name="IDS_DEVTOOLS_ce85958ff51e825dcf797d0451b96b71" desc="">
+    Blackbox scripts whose names match
+  </message>
+  <message name="IDS_DEVTOOLS_d0834fcec6337785ee749c8f5464f6f6" desc="">
+    Preferences
+  </message>
+  <message name="IDS_DEVTOOLS_d2ef7dcdede23820e315ca1c3425ba4f" desc="">
+    Debugger will skip through the scripts and will not stop on exceptions thrown by them.
+  </message>
+  <message name="IDS_DEVTOOLS_e3b2f7d0af9c89f5ea504968ffcd2ef0" desc="">
+    These experiments could be dangerous and may require restart.
+  </message>
+  <message name="IDS_DEVTOOLS_ffebb5ad32e2b253aa418b8d7a70028e" desc="">
+    Blackbox content scripts
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/snippets/snippets_strings.grdp b/third_party/blink/renderer/devtools/front_end/snippets/snippets_strings.grdp
new file mode 100644
index 0000000..7f0cb369
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/snippets/snippets_strings.grdp
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_b1d75786654fa93f10c31c3b544bbd03" desc="">
+    Run snippet
+  </message>
+  <message name="IDS_DEVTOOLS_e0fe9b9cadde935642ad15a85c79d7b1" desc="">
+    No snippets found.
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/source_frame/source_frame_strings.grdp b/third_party/blink/renderer/devtools/front_end/source_frame/source_frame_strings.grdp
new file mode 100644
index 0000000..a1d41c7
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/source_frame/source_frame_strings.grdp
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_0818b6e40afc14684d727176bd222aa0" desc="">
+    Set indentation to 2 spaces
+  </message>
+  <message name="IDS_DEVTOOLS_194f5394ae2e9c74dc3c441b92862d1d" desc="">
+    Font
+  </message>
+  <message name="IDS_DEVTOOLS_22a4f3a07431f171506336782f211c61" desc="">
+    Drop image file here
+  </message>
+  <message name="IDS_DEVTOOLS_261e3ec3b2545f38725d7bfe67146cc6" desc="">
+    Set indentation to 8 spaces
+  </message>
+  <message name="IDS_DEVTOOLS_33443fc55632401a517b16878389f2f6" desc="">
+    Set indentation to 4 spaces
+  </message>
+  <message name="IDS_DEVTOOLS_4cfa6c981549e990fe2344e4c805405e" desc="">
+    Find
+  </message>
+  <message name="IDS_DEVTOOLS_59cc7cb8dfb2491cc5dfe8ebc546d797" desc="">
+    Pretty print
+  </message>
+  <message name="IDS_DEVTOOLS_5ada9c9e1dad2e7e20aaa7688d7bd6c7" desc="">
+    Line <ph name="LOCATION_______">$1s</ph>, Column <ph name="LOCATION_______">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_6efb3f8d955fc3395726044acec66129" desc="">
+    <ph name="THIS__IMAGEPREVIEWELEMENT_NATURALWIDTH">$1d</ph> × <ph name="THIS__IMAGEPREVIEWELEMENT_NATURALHEIGHT">$2d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_700a6fe957fe2782a2412e4397bce276" desc="">
+    Open image in new tab
+  </message>
+  <message name="IDS_DEVTOOLS_70a0a440eed4f94a3632d346b03d9a53" desc="">
+    2 spaces
+  </message>
+  <message name="IDS_DEVTOOLS_7894a953b2e5c818b263e862929417f3" desc="">
+    Tab character
+  </message>
+  <message name="IDS_DEVTOOLS_84ce71f0603df8984b0adcf5248189e3" desc="">
+    Copy image URL
+  </message>
+  <message name="IDS_DEVTOOLS_89733eed06340faa760afff3b68a90ba" desc="">
+    <ph name="SELECTEDTEXT_LENGTH">$1d</ph> characters selected
+  </message>
+  <message name="IDS_DEVTOOLS_a04563146775acda156e27c41a068e65" desc="">
+    Nothing to preview
+  </message>
+  <message name="IDS_DEVTOOLS_abd971c026bf77d9ed737cebeed94889" desc="">
+    Default indentation:
+  </message>
+  <message name="IDS_DEVTOOLS_af70cf4b9f56cd69eb56ae32f21e8416" desc="">
+    <ph name="SELECTIONS_LENGTH">$1d</ph> selection regions
+  </message>
+  <message name="IDS_DEVTOOLS_be53a0541a6d36f6ecb879fa2c584b08" desc="">
+    Image
+  </message>
+  <message name="IDS_DEVTOOLS_c144a6b9ff31ca0302b1cf94d4d540a0" desc="">
+    <ph name="TEXTRANGE_ENDLINE___TEXTRANGE_STARTLINE____">$1d</ph> lines, <ph name="SELECTEDTEXT_LENGTH">$2d</ph> characters selected
+  </message>
+  <message name="IDS_DEVTOOLS_d7c4338058e90b2cad7c9ff93588c1ec" desc="">
+    8 spaces
+  </message>
+  <message name="IDS_DEVTOOLS_ea1fe1846b8a6d69ebf101a05a5fc194" desc="">
+    Copy image as data URI
+  </message>
+  <message name="IDS_DEVTOOLS_fb49df2db96d183474fb9e954a9f0e52" desc="">
+    Set indentation to tab character
+  </message>
+  <message name="IDS_DEVTOOLS_fb61758d0f0fda4ba867c3d5a46c16a7" desc="">
+    Sources
+  </message>
+  <message name="IDS_DEVTOOLS_fe81050cdc3adc7c1912fc08b7aec77f" desc="">
+    4 spaces
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/sources/sources_strings.grdp b/third_party/blink/renderer/devtools/front_end/sources/sources_strings.grdp
new file mode 100644
index 0000000..eadd29d
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/sources/sources_strings.grdp
@@ -0,0 +1,483 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_00a2f9d16aa26eb89a776a3d1ed4f9cd" desc="">
+    Expression to check before pausing, e.g. x &gt; 5
+  </message>
+  <message name="IDS_DEVTOOLS_01f0097c3b7f0774139005734eacbae4" desc="">
+    Add property path to watch
+  </message>
+  <message name="IDS_DEVTOOLS_035e70b676c1a7f341f758eb10a3bd1d" desc="">
+    Add conditional breakpoint…
+  </message>
+  <message name="IDS_DEVTOOLS_03bfaee27b3b5cfb28deef84a0f416e5" desc="">
+    Threads
+  </message>
+  <message name="IDS_DEVTOOLS_03c2e7e41ffc181a4e84080b4710e81e" desc="">
+    New
+  </message>
+  <message name="IDS_DEVTOOLS_05184fe2fb6c76214bc56796837f1c9b" desc="">
+    Next call frame
+  </message>
+  <message name="IDS_DEVTOOLS_06a75795f2a3be3930c707d64fd488c0" desc="">
+    Show trailing whitespace characters
+  </message>
+  <message name="IDS_DEVTOOLS_08355a3ae65d425a4a6bac94ce97dcb4" desc="">
+    Edit breakpoint…
+  </message>
+  <message name="IDS_DEVTOOLS_08fb8cd30c88d3b186962bb2c3226479" desc="">
+    Enabled all breakpoints in line
+  </message>
+  <message name="IDS_DEVTOOLS_0ab9daf7d77e9446aeae5f66be24e800" desc="">
+    Never pause here
+  </message>
+  <message name="IDS_DEVTOOLS_0b7328e087472930bcd6e51a840bb454" desc="">
+    Open color picker.
+  </message>
+  <message name="IDS_DEVTOOLS_0c2c743a6d4f5bb5d3689cd87f7f5e1b" desc="">
+    Disable CSS source maps
+  </message>
+  <message name="IDS_DEVTOOLS_0d09bbb2a0380f333de821bf7b3aec61" desc="">
+    Not paused
+  </message>
+  <message name="IDS_DEVTOOLS_1223c0b07c7cf71289fa7f390c192ab5" desc="">
+    Bracket matching
+  </message>
+  <message name="IDS_DEVTOOLS_127c3c0187f2be68a7c19273f4d8a71b" desc="">
+    Delete all watch expressions
+  </message>
+  <message name="IDS_DEVTOOLS_14acf8c40287aedb6124add396f6d1c5" desc="">
+    Local Modifications...
+  </message>
+  <message name="IDS_DEVTOOLS_160023e58c0251b10b570f6b43914bfc" desc="">
+    Drop workspace folder here
+  </message>
+  <message name="IDS_DEVTOOLS_1cd54499835432c724e9654327ca5788" desc="">
+    Code folding
+  </message>
+  <message name="IDS_DEVTOOLS_1ced1baad9bf7cd516caae0aa7558853" desc="">
+    Debugger paused
+  </message>
+  <message name="IDS_DEVTOOLS_1d76fdc8e9a79fa435ee5a2afa4c9bb8" desc="">
+    Paused before potential out-of-memory crash
+  </message>
+  <message name="IDS_DEVTOOLS_1f15646f60fc3c1dea747963f02d51ac" desc="">
+    Group by folder
+  </message>
+  <message name="IDS_DEVTOOLS_216a9080bf22a993b2d7f9331d34cc68" desc="">
+    Don&apos;t pause on exceptions
+  </message>
+  <message name="IDS_DEVTOOLS_23682ad11e062879a37272dce2948fa5" desc="">
+    Clear configuration
+  </message>
+  <message name="IDS_DEVTOOLS_25eeda4a035b17ed5a353480e79dc6fb" desc="">
+    Do not detect indentation
+  </message>
+  <message name="IDS_DEVTOOLS_26fff70cf6b5204fa5154fa62f5b7b69" desc="">
+    Add to watch
+  </message>
+  <message name="IDS_DEVTOOLS_27cd6a52dd9de75342d5115dc6bca6ba" desc="">
+    Associated files are available via file tree or <ph name="UI_SHORTCUTREGISTRY_SHORTCUTTITLEFORACTION__QUICKOPEN_SHOW__">$1s</ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_30af0d86fe286708503f94e65c4da3f6" desc="">
+    Paused on breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_30e71ab6fb45b9773731a0af4d7fb3b4" desc="">
+    Do not show whitespace characters
+  </message>
+  <message name="IDS_DEVTOOLS_327cbc7239d92013006f601d8b9aff8f" desc="">
+    Remove folder from workspace
+  </message>
+  <message name="IDS_DEVTOOLS_33121362971f65350982fbfea136c37d" desc="">
+    Log a message to Console, do not break
+  </message>
+  <message name="IDS_DEVTOOLS_339ba442656bbc8e43e64b427dc598ff" desc="">
+    Type a number to go to that line.
+  </message>
+  <message name="IDS_DEVTOOLS_3486ec2a2dd86270ed12b46a54372d06" desc="">
+    Blackbox all content scripts
+  </message>
+  <message name="IDS_DEVTOOLS_348b4e067738aac6d7d6c30ccdb59beb" desc="">
+    Source Map detected.
+  </message>
+  <message name="IDS_DEVTOOLS_34b75062caaa3ec1d9e1ee9b725ae303" desc="">
+    Do not automatically reveal files in sidebar
+  </message>
+  <message name="IDS_DEVTOOLS_37d2aa45fbe4026fc347be7b07a8a468" desc="">
+    Deactivate breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_38173eb2f7725d12dfcf1a920a77ecb4" desc="">
+    Create new snippet
+  </message>
+  <message name="IDS_DEVTOOLS_3831f64f248899a06a6bbc8b82fccfdf" desc="">
+    Log message, e.g. &apos;x is&apos;, x
+  </message>
+  <message name="IDS_DEVTOOLS_38b6dddcf45b3d2c9fc86ce4dd2592ba" desc="">
+    Add expression
+  </message>
+  <message name="IDS_DEVTOOLS_3a4c2017c43ba6b63be4576d17893645" desc="">
+    Source map found, but ignored for blackboxed file.
+  </message>
+  <message name="IDS_DEVTOOLS_3a9bd88b445fd1f4d4d8c3ce995be74a" desc="">
+    Step over next function call
+  </message>
+  <message name="IDS_DEVTOOLS_3e27fe84bd1fddd585c8d06e6307b67a" desc="">
+    Remove all breakpoints in line
+  </message>
+  <message name="IDS_DEVTOOLS_3fd3dddd9661349b90b648bba2748f1b" desc="">
+    Sources panel
+  </message>
+  <message name="IDS_DEVTOOLS_41c03dfb0ea5890d60756ad55c6e47b1" desc="">
+    Detect indentation
+  </message>
+  <message name="IDS_DEVTOOLS_434a4d8ac15ff124add730be03574f41" desc="">
+    Enable all breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_4541d5953fd7db4d62ef89e9717f845a" desc="">
+    Disable autocompletion
+  </message>
+  <message name="IDS_DEVTOOLS_46e8256224399a2d32a2f988a21d6562" desc="">
+    Select folder for overrides
+  </message>
+  <message name="IDS_DEVTOOLS_47dbdb85aee276cd77a84a067af48aa5" desc="">
+    Enable tab moves focus
+  </message>
+  <message name="IDS_DEVTOOLS_48c7c41b72e1d678923ce3571aa65b2d" desc="">
+    Step
+  </message>
+  <message name="IDS_DEVTOOLS_4b3812a22a58f04fb5a0598658a3bd27" desc="">
+    Go to symbol
+  </message>
+  <message name="IDS_DEVTOOLS_4ba67b3e9f4bf3135a50fa54c133fba6" desc="">
+    Conditional breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_4bc16757f0e30f62902ac7f86f19b23b" desc="">
+    Show function definition
+  </message>
+  <message name="IDS_DEVTOOLS_51eb42e9c7ed7951fea0d51a1e4a2cfc" desc="">
+    Go to line <ph name="POSITION_LINE">$1s</ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_520d0db389f362bf79ef56ca0af3dcab" desc="">
+    Format
+  </message>
+  <message name="IDS_DEVTOOLS_52b3bc5e77aa070243c36cd40a4d6b6d" desc="">
+    Autocompletion
+  </message>
+  <message name="IDS_DEVTOOLS_58943fa26a94bc86f7f45106080e4496" desc="">
+    Resume script execution
+  </message>
+  <message name="IDS_DEVTOOLS_590d0b32e1139c6c3bcb0f99ed978dff" desc="">
+    subtree modifications
+  </message>
+  <message name="IDS_DEVTOOLS_5a0a64ecca63ba9a006b6397cbdc2b8c" desc="">
+    Go to line
+  </message>
+  <message name="IDS_DEVTOOLS_5a3bb6a64da384e59876cd0bc00a6eae" desc="">
+    Unblackbox this script
+  </message>
+  <message name="IDS_DEVTOOLS_5bcc1f50ed61246502d37ce93368dda8" desc="">
+    Paused on XHR or fetch
+  </message>
+  <message name="IDS_DEVTOOLS_5d113f2038d289f391614c39043629e8" desc="">
+    Scope
+  </message>
+  <message name="IDS_DEVTOOLS_5e7d63192de1cf31345f98b0252bc523" desc="">
+    &lt;not available&gt;
+  </message>
+  <message name="IDS_DEVTOOLS_6005268f51bade5b1f650b47b7ff68ff" desc="">
+    Stop blackboxing all content scripts
+  </message>
+  <message name="IDS_DEVTOOLS_610fbde3ea8bddc086a8f50a83f0fedf" desc="">
+    Previous call frame
+  </message>
+  <message name="IDS_DEVTOOLS_6121be69f70b38551bf15d90d0806778" desc="">
+    Automatically reveal files in sidebar
+  </message>
+  <message name="IDS_DEVTOOLS_61cb6bc6751fc6b516847995380fdced" desc="">
+    New snippet
+  </message>
+  <message name="IDS_DEVTOOLS_64c5d2b51d86c5e1d22a77a4ea5ed174" desc="">
+    Disable code folding
+  </message>
+  <message name="IDS_DEVTOOLS_6700b68e0b1cd0ed13778682cc1b376d" desc="">
+    Enable code folding
+  </message>
+  <message name="IDS_DEVTOOLS_681e10aecbafd7dd385fa51798ca0fd6" desc="">
+    New file
+  </message>
+  <message name="IDS_DEVTOOLS_686182a69271ee4d9caa32b17ad56e9f" desc="">
+    Show whitespace characters:
+  </message>
+  <message name="IDS_DEVTOOLS_68b3c10a88384bc903eb0bcb08f5e17c" desc="">
+    Disable JavaScript source maps
+  </message>
+  <message name="IDS_DEVTOOLS_69a309503a6b6e3265981a5db92c82ac" desc="">
+    Paused on promise rejection
+  </message>
+  <message name="IDS_DEVTOOLS_6bb8aa3b54918eeb27dafcace1e5addd" desc="">
+    (no domain)
+  </message>
+  <message name="IDS_DEVTOOLS_6cc39b227b3571623cfa817ed5df1c8b" desc="">
+    Disable all breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_6e0fdbff6068d8b45cac1f4ae39d6798" desc="">
+    Enable autocompletion
+  </message>
+  <message name="IDS_DEVTOOLS_6f92b50cf0c9932b9359e4cfa2a6473b" desc="">
+    Restart frame
+  </message>
+  <message name="IDS_DEVTOOLS_70f2eb480d2c49d1ca88ba9da0af545a" desc="">
+    Debugger will skip stepping through this script, and will not stop on exceptions
+  </message>
+  <message name="IDS_DEVTOOLS_7129472c5eb598faf47e4e877f2618fd" desc="">
+    Breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_74313a1b4f28fd0980e0c7677b38373c" desc="">
+    Show all whitespace characters
+  </message>
+  <message name="IDS_DEVTOOLS_74662a9a81d3f50d9d1f3dddf044b550" desc="">
+    Exclude folder
+  </message>
+  <message name="IDS_DEVTOOLS_74ff4bad28abee3489bd8ca138b80bb6" desc="">
+    No files found
+  </message>
+  <message name="IDS_DEVTOOLS_77898d40c8a26a07d05eedb991359804" desc="">
+    (source mapped from <ph name="COMPONENTS_LINKIFIER_LINKIFYURL_ORIGINURL_">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_7b5c591188e24ed71b8c72b32e86af5e" desc="">
+    Disable all breakpoints in line
+  </message>
+  <message name="IDS_DEVTOOLS_7e469e0762bb1a977747a18457c2e66f" desc="">
+    Show blackboxed frames
+  </message>
+  <message name="IDS_DEVTOOLS_80bd21c435b5e5ba6890161f3bc4bfd7" desc="">
+    Search in folder
+  </message>
+  <message name="IDS_DEVTOOLS_820587f2a3808a99f9ffcc0576b31681" desc="">
+    Make a copy…
+  </message>
+  <message name="IDS_DEVTOOLS_8317db413bca5e9ed3305fdac525ddd2" desc="">
+    No variables
+  </message>
+  <message name="IDS_DEVTOOLS_8560d166d251804230566118358414e5" desc="">
+    Add watch expression
+  </message>
+  <message name="IDS_DEVTOOLS_87837d0ddaa832891236be71b7bf6935" desc="">
+    Pause on caught exceptions
+  </message>
+  <message name="IDS_DEVTOOLS_906646d2d69e110dcd4cfc74e5790f6f" desc="">
+    Paused on event listener
+  </message>
+  <message name="IDS_DEVTOOLS_923dab99a01b578dbf1ec09277a0ed2e" desc="">
+    Rename…
+  </message>
+  <message name="IDS_DEVTOOLS_945efe35b9e3950bd1a796a9310736a6" desc="">
+    Go to line <ph name="POSITION_LINE">$1s</ph> and column <ph name="POSITION_COLUMN">$2s</ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_9757132fffae1fe86f7c3329dc189e8d" desc="">
+    Paused on exception
+  </message>
+  <message name="IDS_DEVTOOLS_97edd5423acfdf412ad9ba7f4eaa6d39" desc="">
+    No file selected.
+  </message>
+  <message name="IDS_DEVTOOLS_9bbb0377ecbf31fdcfcce3283e815311" desc="">
+    Ctrl+Enter
+  </message>
+  <message name="IDS_DEVTOOLS_9ce5614862050d067b2fca22f4190d12" desc="">
+    Source map URL: 
+  </message>
+  <message name="IDS_DEVTOOLS_9db6195620a7743228ce74addf1414d8" desc="">
+    Associated files should be added to the file tree. You can debug these resolved source files as regular JavaScript files.
+  </message>
+  <message name="IDS_DEVTOOLS_9f7753821d867a81273a50de2b1e0cac" desc="">
+    Pause script execution
+  </message>
+  <message name="IDS_DEVTOOLS_a0b40b72aff4851f0c49348f8868b935" desc="">
+    Disable bracket matching
+  </message>
+  <message name="IDS_DEVTOOLS_a17411e9872c82855a0dc0d000e50c22" desc="">
+    Call Stack
+  </message>
+  <message name="IDS_DEVTOOLS_a1942a7798767819b93df38c18d1696d" desc="">
+    Display variable values inline while debugging
+  </message>
+  <message name="IDS_DEVTOOLS_a22d670bd986f8f534fe07a96b38f217" desc="">
+    Add logpoint…
+  </message>
+  <message name="IDS_DEVTOOLS_a271d86904edd95b1e28f6df6c74c9ac" desc="">
+    Disable tab moves focus
+  </message>
+  <message name="IDS_DEVTOOLS_a2c01e3ac442e470bc38437752707518" desc="">
+    Return value
+  </message>
+  <message name="IDS_DEVTOOLS_a2ca04f270c5acfa0cda3aa0fc6976c5" desc="">
+    Do not search in anonymous and content scripts
+  </message>
+  <message name="IDS_DEVTOOLS_a3fec1fa9178602843f79537c66b1496" desc="">
+    Step out of current function
+  </message>
+  <message name="IDS_DEVTOOLS_a570cb902fdec05031ec1a25634bdbdb" desc="">
+    Open a JavaScript or CSS file to see symbols
+  </message>
+  <message name="IDS_DEVTOOLS_a6e75eb31dc77e8d077fb6f92909e191" desc="">
+    Open folder
+  </message>
+  <message name="IDS_DEVTOOLS_a7706b6eb022a90c58cf658d0085d6d1" desc="">
+    Possible ways to cancel this behavior are:
+  </message>
+  <message name="IDS_DEVTOOLS_a95c3c1dc2719e527889096bbd7b662c" desc="">
+    Search in all files
+  </message>
+  <message name="IDS_DEVTOOLS_abe5b14af1d1b81dffc5d6009dead621" desc="">
+    Pause only when the condition is true
+  </message>
+  <message name="IDS_DEVTOOLS_ac52cf637478f3656a1fdee5c02324fd" desc="">
+    Filesystem
+  </message>
+  <message name="IDS_DEVTOOLS_acd2fcf9aed3ee59e77566016a828d14" desc="">
+    Paused on debugged function
+  </message>
+  <message name="IDS_DEVTOOLS_ae76c8d643589ff25d35455a80f12061" desc="">
+    Do not display variable values inline while debugging
+  </message>
+  <message name="IDS_DEVTOOLS_b16d05636bde9fe84392fd4af198e9c1" desc="">
+    Quick source
+  </message>
+  <message name="IDS_DEVTOOLS_b1a3aaa0de38001a39eeaf44c523a98c" desc="">
+    Changes to this file were not saved to file system.
+  </message>
+  <message name="IDS_DEVTOOLS_b2accffe28bb4f4b596c9be64a28d281" desc="">
+    Snippets
+  </message>
+  <message name="IDS_DEVTOOLS_b402b605feaea6029ac976f4963e20cd" desc="">
+    Are you sure you want to close unsaved file: <ph name="UISOURCECODE_NAME__">$1s</ph>?
+  </message>
+  <message name="IDS_DEVTOOLS_b526d5bfa95ab0d53b118425336d52a3" desc="">
+    Are you sure you want to remove this folder?
+  </message>
+  <message name="IDS_DEVTOOLS_b55fd1bae7b054568efa40f60f5b6c43" desc="">
+    Enable CSS source maps
+  </message>
+  <message name="IDS_DEVTOOLS_b7e7eecbc4a9a1151ccae2f9c5aa7b14" desc="">
+    Trailing
+  </message>
+  <message name="IDS_DEVTOOLS_b8f2563abb6a5253a6f11f95be0b9b62" desc="">
+    Open in Sources panel
+  </message>
+  <message name="IDS_DEVTOOLS_b918846bd0a77b2051d8c4019201315f" desc="">
+    Remove other breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_b9947e230aa5ac451e3b0649bdb1c7fc" desc="">
+    DevTools have unsaved changes that will be permanently lost.
+  </message>
+  <message name="IDS_DEVTOOLS_bb9f288b958ce8c8d250cb392e2f1309" desc="">
+    You can click the <ph name="TOOLBAR_ELEMENT">$1s</ph> button on the bottom status bar, and continue debugging with the new formatted source.
+  </message>
+  <message name="IDS_DEVTOOLS_bd1066101df937dfc5704f95ef779f62" desc="">
+    No watch expressions
+  </message>
+  <message name="IDS_DEVTOOLS_c081841ffd8efaef2ee0c7bf0507758a" desc="">
+    Enable bracket matching
+  </message>
+  <message name="IDS_DEVTOOLS_c12de9f9edb401ca79a3291f1831ae6f" desc="">
+    Closure (<ph name="UI_BEAUTIFYFUNCTIONNAME_SCOPENAME_">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_c2e59ab4f410655458110ae524bdc9be" desc="">
+    Go to &quot;<ph name="COMMON_UISTRING__BLACKBOXING__">$1s</ph>&quot; tab in settings
+  </message>
+  <message name="IDS_DEVTOOLS_c322e65e12002c3bd486ff62ff5b1fd6" desc="">
+    node removal
+  </message>
+  <message name="IDS_DEVTOOLS_c42094126c68a61cc5359460681e1d90" desc="">
+    ⌘+Enter
+  </message>
+  <message name="IDS_DEVTOOLS_c5301693c4e792bcd5a479ef38fb8f8d" desc="">
+    Run
+  </message>
+  <message name="IDS_DEVTOOLS_c70f6a5cffc2538db7304f46556f0b30" desc="">
+    Add selected text to watches
+  </message>
+  <message name="IDS_DEVTOOLS_c79e1cbadc868bad54f2eabe113c06f0" desc="">
+    Enable breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_cd26a6a87959e99fdf99f5ccccef0464" desc="">
+    Disable breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_d07d651006c823c6f008158f6eb63056" desc="">
+    Resume with all pauses blocked for 500 ms
+  </message>
+  <message name="IDS_DEVTOOLS_d127dbe7fc7834601145be34349fc60f" desc="">
+    Evaluate selected text in console
+  </message>
+  <message name="IDS_DEVTOOLS_d13c3f7baed576768b11a714ef4d90e2" desc="">
+    paused
+  </message>
+  <message name="IDS_DEVTOOLS_d19dc64c372f09800d8117286c3d0aff" desc="">
+    Breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_d1d71571592bcbb9f99bf726a9d0fe0a" desc="">
+    Delete watch expression
+  </message>
+  <message name="IDS_DEVTOOLS_d280677ba371231189ef4c4fdaa9b4b3" desc="">
+    Pretty-print this minified file?
+  </message>
+  <message name="IDS_DEVTOOLS_d318592071e92890f8046b4eeeecbdf1" desc="">
+    attribute modifications
+  </message>
+  <message name="IDS_DEVTOOLS_d6444e40ec843a5dc64161f9eafe07a4" desc="">
+    Logpoint
+  </message>
+  <message name="IDS_DEVTOOLS_da1c6b95e46b078b40e1a53bc7c44f17" desc="">
+    Step into next function call
+  </message>
+  <message name="IDS_DEVTOOLS_dab88f58b06d64f6f2b0c157580c8d36" desc="">
+    Add source map…
+  </message>
+  <message name="IDS_DEVTOOLS_db49f36ab9f08d646ff4416ab5009ab9" desc="">
+    This script is blackboxed in debugger
+  </message>
+  <message name="IDS_DEVTOOLS_dbcfcc422f1a9f6e817bb7670a93f7d3" desc="">
+    Are you sure you want to exclude this folder?
+  </message>
+  <message name="IDS_DEVTOOLS_de1b7eb1c5cad7712d521bade538de6e" desc="">
+    Search in anonymous and content scripts
+  </message>
+  <message name="IDS_DEVTOOLS_e3de97b1a66edbe3fceddb1d5db2df2d" desc="">
+    Copy value
+  </message>
+  <message name="IDS_DEVTOOLS_e64479370fed877b3ff0b51ee73c95ac" desc="">
+    Reveal in sidebar
+  </message>
+  <message name="IDS_DEVTOOLS_e68f8487d76aeb933b5b06d7999a0e44" desc="">
+    Enable JavaScript source maps
+  </message>
+  <message name="IDS_DEVTOOLS_ed5eceb0fe85ceeeabf4b4b93eeca711" desc="">
+    Continue to here
+  </message>
+  <message name="IDS_DEVTOOLS_f0a47f037ca7a988466a647277ef1134" desc="">
+    Paused on assertion
+  </message>
+  <message name="IDS_DEVTOOLS_f20658650d987d31063b593c05980397" desc="">
+    Watch
+  </message>
+  <message name="IDS_DEVTOOLS_f2e95c3adb199ba9a0ec03526b6126ba" desc="">
+    Drop in a folder to add to workspace
+  </message>
+  <message name="IDS_DEVTOOLS_f395c33fe9c382316b99128e087cb697" desc="">
+    Close All
+  </message>
+  <message name="IDS_DEVTOOLS_f515d5945239e7a61fa267bd058134f1" desc="">
+    Add folder to workspace
+  </message>
+  <message name="IDS_DEVTOOLS_f96985d765ed09d4930d76a3ca869508" desc="">
+    Terminate current JavaScript call
+  </message>
+  <message name="IDS_DEVTOOLS_fd1be3efcf102a4183d9445d789574f4" desc="">
+    Are you sure you want to delete this file?
+  </message>
+  <message name="IDS_DEVTOOLS_fd8c5cf684c7bd30f907a74603e204ac" desc="">
+    Activate breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_fe6e43324287cedf01c16c9eb8d2b121" desc="">
+    Copy stack trace
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/terminal/terminal_strings.grdp b/third_party/blink/renderer/devtools/front_end/terminal/terminal_strings.grdp
new file mode 100644
index 0000000..a47f40c
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/terminal/terminal_strings.grdp
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_514d8a494f087c0d549b9536c2ef3bd9" desc="">
+    Terminal
+  </message>
+  <message name="IDS_DEVTOOLS_69685b2a57646d0d0bc125b5f94f3931" desc="">
+    Terminal service is not available
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/text_editor/text_editor_strings.grdp b/third_party/blink/renderer/devtools/front_end/text_editor/text_editor_strings.grdp
new file mode 100644
index 0000000..06bb435
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/text_editor/text_editor_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_55212da7537e29610e52134f4097f0b2" desc="">
+    Code editor
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp b/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
new file mode 100644
index 0000000..72eb2f7
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
@@ -0,0 +1,942 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_0007333f80790cc7ef6b97ce5e0ac297" desc="">
+    Layout Invalidations
+  </message>
+  <message name="IDS_DEVTOOLS_0060636b449d1da5a8581bfee180f0c2" desc="">
+     and 
+  </message>
+  <message name="IDS_DEVTOOLS_00c3783a786856df24fd85e1093ef516" desc="">
+    DCL
+  </message>
+  <message name="IDS_DEVTOOLS_00eeb588b8336923c7a236079c87c908" desc="">
+    Install Timer
+  </message>
+  <message name="IDS_DEVTOOLS_01b26724a7b11aab03f175512e2c9f0f" desc="">
+    Show recent timeline sessions
+  </message>
+  <message name="IDS_DEVTOOLS_01da20cf88fba2246309c541bddac1b1" desc="">
+    Documents: <ph name="PH1">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_01ea16e89d02c33ef2a6a5db67665e0a" desc="">
+    Main — <ph name="TRACK_URL">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_02a3a357710cc2a5dfdfb74ed012fb59" desc="">
+    Url
+  </message>
+  <message name="IDS_DEVTOOLS_04cbdd8baeb482d9ff2e48b186479a06" desc="">
+    Streaming Wasm Response
+  </message>
+  <message name="IDS_DEVTOOLS_05b8804bd8a147740eb7a35786c71a3c" desc="">
+    Node:
+  </message>
+  <message name="IDS_DEVTOOLS_0638b5f626aa994ad5da95ce95288a66" desc="">
+    [ unknown node ]
+  </message>
+  <message name="IDS_DEVTOOLS_064866c00b4298761dd36ec9df9be72f" desc="">
+    Hit Test
+  </message>
+  <message name="IDS_DEVTOOLS_06a83455a448479eb950ea190d815439" desc="">
+    Malformed CPU profile format
+  </message>
+  <message name="IDS_DEVTOOLS_06b8c6c83d552c894d01ad5c418e566b" desc="">
+    Receive Response
+  </message>
+  <message name="IDS_DEVTOOLS_0712dbe6e0a2a72e3c3bd1fc42b8ccb3" desc="">
+    Style Invalidations
+  </message>
+  <message name="IDS_DEVTOOLS_07535d965500993cdde8e7d0c686c993" desc="">
+    Onload Event
+  </message>
+  <message name="IDS_DEVTOOLS_077569834980c53bd3805bb49d62d910" desc="">
+    - Network throttling is enabled
+  </message>
+  <message name="IDS_DEVTOOLS_07f0007f9cefa79303d1093070975149" desc="">
+    - JavaScript sampling is disabled
+  </message>
+  <message name="IDS_DEVTOOLS_07fa8e7d7b4634db5e410fc35dba40f4" desc="">
+    Long frame
+  </message>
+  <message name="IDS_DEVTOOLS_0877b8db99f11ff69875bf025df75c7f" desc="">
+    Animation Frame Requested
+  </message>
+  <message name="IDS_DEVTOOLS_08790b919dc10c98ef4f4678e4a4cc98" desc="">
+    Fire Idle Callback
+  </message>
+  <message name="IDS_DEVTOOLS_08c1f153a1c6f21e1064581df9b05ee8" desc="">
+    Layer tree
+  </message>
+  <message name="IDS_DEVTOOLS_08d5a8363f4ad65b14c8f9a32dd93944" desc="">
+    JS Frame
+  </message>
+  <message name="IDS_DEVTOOLS_08db5f30f625a406882862b8a29b1bc6" desc="">
+    Major GC
+  </message>
+  <message name="IDS_DEVTOOLS_09d66464ad53e6080988ad30824ea023" desc="">
+    Frame — <ph name="TRACK_URL">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_0d015d96f63a8c12d96b8399482b593f" desc="">
+    Uncategorized
+  </message>
+  <message name="IDS_DEVTOOLS_0d43b55b13285fd05236204c9a884f18" desc="">
+    (<ph name="TIMELINE_TIMELINEHISTORYMANAGER__COARSEAGE_STARTEDAT_">$1s</ph> ago)
+  </message>
+  <message name="IDS_DEVTOOLS_0d58f3789fa2779bb4b2b86f90cab764" desc="">
+    Nodes That Need Layout
+  </message>
+  <message name="IDS_DEVTOOLS_0e6dea9a8306a3ef170cb8c78263debe" desc="">
+    Click the record button <ph name="RECORDBUTTON">$1s</ph> or hit <ph name="RECORDKEY">$2s</ph> to start a new recording.
+Click the reload button <ph name="RELOADBUTTON">$3s</ph> or hit <ph name="RELOADKEY">$4s</ph> to record the page load.
+  </message>
+  <message name="IDS_DEVTOOLS_0e8dfe937d359c7df31b16ea7e9cca81" desc="">
+    Hide chrome frame in Layers view
+  </message>
+  <message name="IDS_DEVTOOLS_10aab9f12eca6bca3a6215914bab2745" desc="">
+    Allotted Time
+  </message>
+  <message name="IDS_DEVTOOLS_121ee86db6c7660edb3864de279ef742" desc="">
+    <ph name="ELAPSED_TOFIXED_PRECISE_________">$1s</ph> sec
+  </message>
+  <message name="IDS_DEVTOOLS_12ddabfc2210fb123f96a03f03816024" desc="">
+    Recurring handler took <ph name="NUMBER_MILLISTOSTRING_EVENT_DURATION__TRUE_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_1572a49e8ab36a669016c48d7fca5d76" desc="">
+    Cache Consume Options
+  </message>
+  <message name="IDS_DEVTOOLS_16bfbf9c462762cf1cba4134ec53c504" desc="">
+    Loading
+  </message>
+  <message name="IDS_DEVTOOLS_177845b60fde99c43c2af204188dd7c6" desc="">
+    No Grouping
+  </message>
+  <message name="IDS_DEVTOOLS_17f2327f2e0ec63f2a2e6300fc1b70a7" desc="">
+    Show memory timeline
+  </message>
+  <message name="IDS_DEVTOOLS_187c6ad3a74cc93ac6c2229d398e383e" desc="">
+    Nodes
+  </message>
+  <message name="IDS_DEVTOOLS_18fe96f0c47fa8c62afe3be31d97b880" desc="">
+    Finish Loading
+  </message>
+  <message name="IDS_DEVTOOLS_1b01d1ef7772f8e0c0e0427e4faad653" desc="">
+    Listeners: <ph name="PH1">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_1b2c363066d5912ae8b27a1527f3d45e" desc="">
+    Group by Domain
+  </message>
+  <message name="IDS_DEVTOOLS_1e3eaaac8d3c9cffea8ee7f03a8a6121" desc="">
+    DOMContentLoaded Event
+  </message>
+  <message name="IDS_DEVTOOLS_205fd38626b73da9d837e61b2c7af71f" desc="">
+    Evaluate Script
+  </message>
+  <message name="IDS_DEVTOOLS_20ec6a94230f361368789ddd8bec855c" desc="">
+    Malformed timeline input, wrong JSON brackets balance
+  </message>
+  <message name="IDS_DEVTOOLS_21263b6755fb99797d30820fd4d2b4f0" desc="">
+    <ph name="URL">$1s</ph> [<ph name="STARTLINE____">$2s</ph>…<ph name="ENDLINE____">$3s</ph>]
+  </message>
+  <message name="IDS_DEVTOOLS_217b22459b85349fbfbb7e797719cc37" desc="">
+    Receive WebSocket Handshake
+  </message>
+  <message name="IDS_DEVTOOLS_2267d2f9ecae3136874e6017d01f4574" desc="">
+    Group by Activity
+  </message>
+  <message name="IDS_DEVTOOLS_232e59f3380381caf0634ab2f035dbf2" desc="">
+     (from service worker)
+  </message>
+  <message name="IDS_DEVTOOLS_237f26bfdd9351cd363bca657ff35551" desc="">
+    . Long frame times are an indication of 
+  </message>
+  <message name="IDS_DEVTOOLS_2383a71629ce9189cb494c3fb8af7ac4" desc="">
+    Heaviest stack
+  </message>
+  <message name="IDS_DEVTOOLS_23ca9d66c8f521090421155382ef94b9" desc="">
+    Key Down
+  </message>
+  <message name="IDS_DEVTOOLS_248307dc00d11b9b67fdbfd34507f0aa" desc="">
+    XHR Load
+  </message>
+  <message name="IDS_DEVTOOLS_24aa4117da86c41684ad25742832dfa6" desc="">
+    Async
+  </message>
+  <message name="IDS_DEVTOOLS_25a5e1f05232eae2d3a9c1ff4cb83869" desc="">
+    Function Call
+  </message>
+  <message name="IDS_DEVTOOLS_277b749cfb8e0389d1b89e7cb671753a" desc="">
+    Context Menu
+  </message>
+  <message name="IDS_DEVTOOLS_2875ec54afbd9922e0553f4b441aa54b" desc="">
+    Painting
+  </message>
+  <message name="IDS_DEVTOOLS_2a0e96bf571559add541036a25ca1d36" desc="">
+    Call Stacks
+  </message>
+  <message name="IDS_DEVTOOLS_2a5021aea912316167342b34cbf358b4" desc="">
+    Compile Module
+  </message>
+  <message name="IDS_DEVTOOLS_2ae3330084b44abd37672d0ca603471e" desc="">
+    <ph name="BEGINDATA__DIRTYOBJECTS__">$1s</ph> of <ph name="BEGINDATA__TOTALOBJECTS__">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2b2195634ae5a0d7c6dada694bdcdc65" desc="">
+    Callback ID
+  </message>
+  <message name="IDS_DEVTOOLS_2b55387dd066c5bac646ac61543d152d" desc="">
+    CPU
+  </message>
+  <message name="IDS_DEVTOOLS_2b66d07a14b9e6da66cde6baab473e92" desc="">
+    <ph name="SELFCATEGORY_TITLE">$1s</ph> (children)
+  </message>
+  <message name="IDS_DEVTOOLS_2d2af0b530003b552681f91e29c509d1" desc="">
+    Recalculation Forced
+  </message>
+  <message name="IDS_DEVTOOLS_2da2415714a5e7450a9a127431c02a41" desc="">
+    Send Request
+  </message>
+  <message name="IDS_DEVTOOLS_2f3e2f48b161c9cadd45dcdffd726c79" desc="">
+    JS Heap: <ph name="PH1">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_2f709c2af7d47a610e3c48a87d1ec429" desc="">
+    Invalidate Layout
+  </message>
+  <message name="IDS_DEVTOOLS_2fc3f5e968c02091e3ba9863088f0651" desc="">
+    Callback Function
+  </message>
+  <message name="IDS_DEVTOOLS_31084046e4a000cc4ac980dc68ea9ce9" desc="">
+    Parse Script
+  </message>
+  <message name="IDS_DEVTOOLS_316853cc3718335f11c048e33b9be98a" desc="">
+    Click
+  </message>
+  <message name="IDS_DEVTOOLS_31c6b3fdfaaa80dba2dbf92a4600524c" desc="">
+    Sign
+  </message>
+  <message name="IDS_DEVTOOLS_3227e479f6541c685b08023affdc8c69" desc="">
+    Consumed Cache Size
+  </message>
+  <message name="IDS_DEVTOOLS_324118a6721dd6b8a9b9f4e327df2bf5" desc="">
+    Input
+  </message>
+  <message name="IDS_DEVTOOLS_33991558a089f674da1c5c26fd0a6161" desc="">
+    jank
+  </message>
+  <message name="IDS_DEVTOOLS_347aadfc505a4bc6b2f9d81363d2331c" desc="">
+    NET
+  </message>
+  <message name="IDS_DEVTOOLS_3581b739d9d4895d0f69d9a897b02a3f" desc="">
+    Verify Reply
+  </message>
+  <message name="IDS_DEVTOOLS_359783360b8522c55aa7a9c7d5593d5d" desc="">
+    Pinch End
+  </message>
+  <message name="IDS_DEVTOOLS_361beede47de122bfb91fe1da6e3f1a7" desc="">
+    Legacy Timeline format is not supported.
+  </message>
+  <message name="IDS_DEVTOOLS_38108192a74ae7b3deda8762ff299109" desc="">
+    <ph name="TITLE">$1s</ph> - Details
+  </message>
+  <message name="IDS_DEVTOOLS_390afae8f87b5024e0c32c4e6ce3e2da" desc="">
+    Run Microtasks
+  </message>
+  <message name="IDS_DEVTOOLS_3b419ceb25fddb807ea9483f65fded06" desc="">
+    Capture settings
+  </message>
+  <message name="IDS_DEVTOOLS_3d1f92a565d3b1a61880236e33c49bf3" desc="">
+    Timeline
+  </message>
+  <message name="IDS_DEVTOOLS_3de9387fc230ce74214446e58c4fefde" desc="">
+    Compile
+  </message>
+  <message name="IDS_DEVTOOLS_3df7062234c9e55d49648289c5b2c80f" desc="">
+    Time spent in rendering
+  </message>
+  <message name="IDS_DEVTOOLS_3e8e23cb13898f3f9760a0f1d9203bd1" desc="">
+    Stack trace:
+  </message>
+  <message name="IDS_DEVTOOLS_3eec15d0ba9a93d3176bab9218bb2570" desc="">
+    Failed to save timeline: <ph name="ERROR_MESSAGE">$1s</ph> (<ph name="ERROR_NAME">$2s</ph>, <ph name="ERROR_CODE">$3s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_3f978efb26e66c6c9af5db34268ccf41" desc="">
+    Compiled Wasm Module
+  </message>
+  <message name="IDS_DEVTOOLS_40a15f22bd3574304d7843344006e0b6" desc="">
+    First Contentful Paint
+  </message>
+  <message name="IDS_DEVTOOLS_41b1478092e85f8c8fa1f98e5efc9b35" desc="">
+    Layout Forced
+  </message>
+  <message name="IDS_DEVTOOLS_427b6d816d7fdd86cabe48d8180a3cc9" desc="">
+    Image URL
+  </message>
+  <message name="IDS_DEVTOOLS_44ff4e8748811d0661f820aeb28ecaf4" desc="">
+    XHR Ready State Change
+  </message>
+  <message name="IDS_DEVTOOLS_46a2a41cc6e552044816a2d04634545d" desc="">
+    State
+  </message>
+  <message name="IDS_DEVTOOLS_46fa564bb1eed5cd3992cac85e8f094b" desc="">
+    Screenshots
+  </message>
+  <message name="IDS_DEVTOOLS_476f68367f02309b39daf298981e30c5" desc="">
+    Minor GC
+  </message>
+  <message name="IDS_DEVTOOLS_4802a5ac6005a6ab9c68a2fb29e30a3e" desc="">
+    Paint
+  </message>
+  <message name="IDS_DEVTOOLS_485ee6de8f9a0e36c182bb56a05b2ca2" desc="">
+    Mouse Wheel
+  </message>
+  <message name="IDS_DEVTOOLS_48c84951d13f2944b6003832cd4ffcba" desc="">
+    <ph name="EVENTDATA__ENCODEDDATALENGTH__">$1s</ph> Bytes
+  </message>
+  <message name="IDS_DEVTOOLS_498f79c4c5bbde77f1bceb6c86fd0f6d" desc="">
+    Show
+  </message>
+  <message name="IDS_DEVTOOLS_4bbc0d1543b5258061fd5c8590fdbe9e" desc="">
+    Encoded Data
+  </message>
+  <message name="IDS_DEVTOOLS_4c2a8fe7eaf24721cc7a9f0175115bd4" desc="">
+    Message
+  </message>
+  <message name="IDS_DEVTOOLS_4dab9fd6bdee5285598246c6ea7cc88f" desc="">
+    Fling Halt
+  </message>
+  <message name="IDS_DEVTOOLS_4ebada6a2af2bcba53ded1d7b414f081" desc="">
+    FP
+  </message>
+  <message name="IDS_DEVTOOLS_4fec5edf718a9ca2c6245167b78f029a" desc="">
+    Scroll End
+  </message>
+  <message name="IDS_DEVTOOLS_50294a317f78ab6caf9ad2558a4b7484" desc="">
+    Invalidations
+  </message>
+  <message name="IDS_DEVTOOLS_504d75604f56770c3c513de2c49ecbe8" desc="">
+    Wasm Module Cache Hit
+  </message>
+  <message name="IDS_DEVTOOLS_5288680dbe452673259047491b87d17d" desc="">
+    CPU time
+  </message>
+  <message name="IDS_DEVTOOLS_52f9ec21735243ad9917cda3ca077d32" desc="">
+    GPU
+  </message>
+  <message name="IDS_DEVTOOLS_53081d6225e780e0f0d94351251be91c" desc="">
+    (changed &quot;<ph name="INVALIDATION_SELECTORPART">$1s</ph>&quot;<ph name="EXTRADATA">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_539dddc574d3cd85ba73df93df7a76d0" desc="">
+    Stack Trace
+  </message>
+  <message name="IDS_DEVTOOLS_5402f18025999ebf70fd56d8ca1b1909" desc="">
+    GC Event
+  </message>
+  <message name="IDS_DEVTOOLS_5481039ec867c9ad2d5a313287124961" desc="">
+    Fling Start
+  </message>
+  <message name="IDS_DEVTOOLS_548babdfd161a8fc2d4b634b682a456d" desc="">
+    (changed attribute to &quot;<ph name="INVALIDATION_CHANGEDATTRIBUTE">$1s</ph>&quot;<ph name="EXTRADATA">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_54fd6de66ec40dc89ec90ba5429094d7" desc="">
+    Group by Category
+  </message>
+  <message name="IDS_DEVTOOLS_5564c00b6210082e536c897014f72361" desc="">
+    Start profiling and reload page
+  </message>
+  <message name="IDS_DEVTOOLS_55b4b48b80795ee9b6a3220ca415786d" desc="">
+    Schedule Style Recalculation
+  </message>
+  <message name="IDS_DEVTOOLS_57f06e0eceb273507ade79baf8d3a2d4" desc="">
+    Tap Begin
+  </message>
+  <message name="IDS_DEVTOOLS_585bc08ce62507965db6ec748c5a251e" desc="">
+    Timer ID
+  </message>
+  <message name="IDS_DEVTOOLS_5878904937ecbc1d0556f79782ab03cd" desc="">
+    Scroll Update
+  </message>
+  <message name="IDS_DEVTOOLS_59cf59caeba8075dec6a745b083edd0c" desc="">
+    Time Waiting for Main Thread
+  </message>
+  <message name="IDS_DEVTOOLS_5a750f86ef41f22f852c43351e3ff383" desc="">
+    Verify
+  </message>
+  <message name="IDS_DEVTOOLS_5af8622e730a34a15962fb8f46df1b5e" desc="">
+    Captures advanced paint instrumentation, introduces significant performance overhead
+  </message>
+  <message name="IDS_DEVTOOLS_5bcd94bc1f71db5659b35ad5deeb94c7" desc="">
+    Fling
+  </message>
+  <message name="IDS_DEVTOOLS_5c593b83b0fca15af4b50ca95c9d3e87" desc="">
+    Sign Reply
+  </message>
+  <message name="IDS_DEVTOOLS_5e8170b28aaa693ca9170bb0d65b3043" desc="">
+    Destroy WebSocket
+  </message>
+  <message name="IDS_DEVTOOLS_5faec6160efeed8f7d29c081b92e8890" desc="">
+    User Timing
+  </message>
+  <message name="IDS_DEVTOOLS_5fbceaa309bba91ee916d46e06ac0080" desc="">
+    Touch Move
+  </message>
+  <message name="IDS_DEVTOOLS_5fe7053d3a0e35c2a3242996e690e160" desc="">
+    Update Layer
+  </message>
+  <message name="IDS_DEVTOOLS_60c21f78ce2dcd2da001fa3221c8ca15" desc="">
+    Nodes:
+  </message>
+  <message name="IDS_DEVTOOLS_60f718c3ba644a198920a74083ccb80f" desc="">
+    Blackboxed
+  </message>
+  <message name="IDS_DEVTOOLS_611d29c748a7931c825a247fcf3f290e" desc="">
+    Encrypt
+  </message>
+  <message name="IDS_DEVTOOLS_61b67ca7c1d8eea5b55d23bbb615d944" desc="">
+    Recalculate Style
+  </message>
+  <message name="IDS_DEVTOOLS_6347d5359bdee6d82717b4ad73aee504" desc="">
+    Frame Started Loading
+  </message>
+  <message name="IDS_DEVTOOLS_63b8e6041614e4130722f79ab66087e6" desc="">
+    Send WebSocket Handshake
+  </message>
+  <message name="IDS_DEVTOOLS_63e467c0720678aa503a5f0c022d56c5" desc="">
+    First Invalidated
+  </message>
+  <message name="IDS_DEVTOOLS_640d76ebf7917e7353ea0444790c9794" desc="">
+    Image Resize
+  </message>
+  <message name="IDS_DEVTOOLS_66f1aed235ade25269a561e81cbbb43a" desc="">
+    Scripting
+  </message>
+  <message name="IDS_DEVTOOLS_67eed98f4bc75c4ab261b79a0e2c52cd" desc="">
+    Cache Rejected
+  </message>
+  <message name="IDS_DEVTOOLS_686155af75a60a0f6e9d80c1f7edd3e9" desc="">
+    JavaScript
+  </message>
+  <message name="IDS_DEVTOOLS_6978c23868116ca3eccb809bc699742e" desc="">
+    - Significant overhead due to paint instrumentation
+  </message>
+  <message name="IDS_DEVTOOLS_6a389f626da11df07508bf251de59c23" desc="">
+    Frame Start
+  </message>
+  <message name="IDS_DEVTOOLS_6aafe894685594a908a8996d68e88028" desc="">
+    Hide records shorter than <ph name="DURATIONMS">$1d</ph> ms
+  </message>
+  <message name="IDS_DEVTOOLS_6c906c5f560faeb38f51683bc22fb212" desc="">
+    Group by Subdomain
+  </message>
+  <message name="IDS_DEVTOOLS_6d12d9901547700bfcefb2a3e5cff2b6" desc="">
+    heaviest stack
+  </message>
+  <message name="IDS_DEVTOOLS_6d1b752b6cdcfb71d098cc271bc133aa" desc="">
+    Embedder Callback
+  </message>
+  <message name="IDS_DEVTOOLS_6d555ca89f5ca9fc68e5d418b4cf732b" desc="">
+    Draw Frame
+  </message>
+  <message name="IDS_DEVTOOLS_6d8a59067ffe0498f3f5f2c03025b799" desc="">
+    Group by URL
+  </message>
+  <message name="IDS_DEVTOOLS_70582905817c902e7a334c24718cba88" desc="">
+    Touch End
+  </message>
+  <message name="IDS_DEVTOOLS_70bac6f11765f731510b0ac659fd26a1" desc="">
+    Malformed timeline data: <ph name="E_TOSTRING__">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_70d68501018d9d6496e55cad8a2c3ba1" desc="">
+    Load profile...
+  </message>
+  <message name="IDS_DEVTOOLS_7125f394de382b13a7e7a5a4b6d23364" desc="">
+    Raster
+  </message>
+  <message name="IDS_DEVTOOLS_71613aba6474d2e2dc12d00cc46743f0" desc="">
+    Touch Start
+  </message>
+  <message name="IDS_DEVTOOLS_7363d05ecc1497f2f8ae05b334b45651" desc="">
+    Disable JavaScript samples
+  </message>
+  <message name="IDS_DEVTOOLS_73cf2f244bc0cb0d205b99c04fa13978" desc="">
+    Tap
+  </message>
+  <message name="IDS_DEVTOOLS_762e3a77ecdd773da2b859e6807529a2" desc="">
+    Decrypt
+  </message>
+  <message name="IDS_DEVTOOLS_76e815d5323a5f3c80a3d186c416e92b" desc="">
+    [Chrome extensions overhead]
+  </message>
+  <message name="IDS_DEVTOOLS_76ecb02d1f339c2dd6f14c41ead368c1" desc="">
+    Handler took <ph name="NUMBER_MILLISTOSTRING_EVENT_DURATION__TRUE_">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_77c18a89217b2a9a2553ac9a0598b99a" desc="">
+    Rasterizer Thread <ph name="RASTERCOUNT">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_7820708244a842157c5fc8186e7b2bfa" desc="">
+    Wasm Module Cache Invalid
+  </message>
+  <message name="IDS_DEVTOOLS_787ddae3fd4c1a93cc28af8bb3940bdc" desc="">
+    Image Decode
+  </message>
+  <message name="IDS_DEVTOOLS_798afebf1a5e9d795a96f0c82cccaae2" desc="">
+    Cached Wasm Module
+  </message>
+  <message name="IDS_DEVTOOLS_7b3a6b02f467b95f2b5267aa94708c07" desc="">
+    Thread <ph name="__THREADINDEX">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_7d341c08fd102f0b86285b5ff2e26ea7" desc="">
+    <ph name="TITLE">$1s</ph>: <ph name="EVENTDATA__MESSAGE__">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_80a4b8755d836d50c19e6eefb976bc2e" desc="">
+    <ph name="SELFCATEGORY_TITLE">$1s</ph> (self)
+  </message>
+  <message name="IDS_DEVTOOLS_81e91be8db96be69e372f0c715ec7444" desc="">
+    Collected
+  </message>
+  <message name="IDS_DEVTOOLS_81ff3427527105233469f158360d8b02" desc="">
+    Select item for details.
+  </message>
+  <message name="IDS_DEVTOOLS_83561bf6ac8333e0d1627887d533907c" desc="">
+    Drop timeline file or URL here
+  </message>
+  <message name="IDS_DEVTOOLS_83e393be9178c404eb013bca48782312" desc="">
+    <ph name="CLIPWIDTH">$1s</ph> × <ph name="CLIPHEIGHT">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_86c8559f4d7d1d74dc3e04baf2b65cc6" desc="">
+    Decoded Body
+  </message>
+  <message name="IDS_DEVTOOLS_871646cb17b6936aa29df33b8d550051" desc="">
+    Update Layer Tree
+  </message>
+  <message name="IDS_DEVTOOLS_87644f7e0957c4bf2990aedfe046b71e" desc="">
+    GPU Memory [KB]: <ph name="PH1">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_87ba2ecc8b6915e8bd6f5089918229fd" desc="">
+    Range
+  </message>
+  <message name="IDS_DEVTOOLS_883b293ae308d8e3caf1614c8a2fda61" desc="">
+    Group by Frame
+  </message>
+  <message name="IDS_DEVTOOLS_88d982014902936a7e7ab24058c88ed0" desc="">
+    Unknown cause for 
+  </message>
+  <message name="IDS_DEVTOOLS_89be1120d42e0cbacbf46eee77766b76" desc="">
+    . 
+  </message>
+  <message name="IDS_DEVTOOLS_8a51f964740c2a7cb6e68404bda36ef1" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_DELTA_">$1s</ph> collected
+  </message>
+  <message name="IDS_DEVTOOLS_8af5f76068f881acfad9d2880a4e2310" desc="">
+    Input Latency
+  </message>
+  <message name="IDS_DEVTOOLS_8b8747bfcec728cdf8d54030e4d28451" desc="">
+    Processing profile…
+  </message>
+  <message name="IDS_DEVTOOLS_8b92f105e7b7e01e4c7ed00d415318f3" desc="">
+    Stylesheet URL
+  </message>
+  <message name="IDS_DEVTOOLS_8c311ee6ad411f13da478e4996907ffa" desc="">
+    After recording, select an area of interest in the overview by dragging.
+Then, zoom and pan the timeline with the mousewheel or <ph name="NAVIGATENODE">$1s</ph> keys.
+<ph name="LEARNMORENODE">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_8cd016d3ad1e59b37133d6c993455d70" desc="">
+    Timer Installed
+  </message>
+  <message name="IDS_DEVTOOLS_8cda3116eb86d6a743edfebd3c2eda15" desc="">
+    Request Idle Callback
+  </message>
+  <message name="IDS_DEVTOOLS_8d98af06b412f3f2455c2786e88aabdb" desc="">
+    Nodes: <ph name="PH1">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_8dc1f1afb0dcd5282b146c8cf5d758e1" desc="">
+    Unexpected entryIndex <ph name="ENTRYINDEX">$1d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_8f25fae98e6ef6ff87364e590df8cee5" desc="">
+    Long task
+  </message>
+  <message name="IDS_DEVTOOLS_8f27b153047f3da699b39221b47b3217" desc="">
+    Initializing profiler…
+  </message>
+  <message name="IDS_DEVTOOLS_91412421a30e87ce15a4f10ea39f6682" desc="">
+    WASD
+  </message>
+  <message name="IDS_DEVTOOLS_920fd776137a582939eac4fc07808754" desc="">
+    <ph name="NUMBER_MILLISTOSTRING_FRAME_ENDTIME___FRAME_STARTTIME__TRUE_">$1s</ph> (at <ph name="NUMBER_MILLISTOSTRING_FRAME_STARTTIMEOFFSET__TRUE_">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_92f0ea95e53565026335b840b8788bf4" desc="">
+    <ph name="NUMBER_MILLISTOSTRING_TOTALTIME__TRUE_">$1s</ph> (self <ph name="NUMBER_MILLISTOSTRING_SELFTIME__TRUE_">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_932cc5549fb2d58c13ce13653469d309" desc="">
+    Mouse Down
+  </message>
+  <message name="IDS_DEVTOOLS_9352d706112654ca7c3f2595bc529cf5" desc="">
+    Paint Image
+  </message>
+  <message name="IDS_DEVTOOLS_9399a34c7d7f4a7c046016e38f80e046" desc="">
+     is a likely performance bottleneck.
+  </message>
+  <message name="IDS_DEVTOOLS_93c8e71d1b0e18d58736d3e255a14533" desc="">
+    Buffer usage
+  </message>
+  <message name="IDS_DEVTOOLS_956f8281d37fdceec7698094b6236870" desc="">
+     (from cache)
+  </message>
+  <message name="IDS_DEVTOOLS_9669f96ca9d2e20feda07d349d3cac86" desc="">
+    Key Up
+  </message>
+  <message name="IDS_DEVTOOLS_976e665bbff13a2a2a2eb4a83b8bb602" desc="">
+    Save profile...
+  </message>
+  <message name="IDS_DEVTOOLS_97c091732895087b8900d81105e54446" desc="">
+    Frame Start (main thread)
+  </message>
+  <message name="IDS_DEVTOOLS_997a8c473db4f81c5fb3d5900030d44d" desc="">
+    (no recordings)
+  </message>
+  <message name="IDS_DEVTOOLS_9a2837081f4b4cd67f625bd8e7567383" desc="">
+    Compile Script
+  </message>
+  <message name="IDS_DEVTOOLS_9a4a3be092449d965b794e48c6f6163c" desc="">
+    Rasterize Paint
+  </message>
+  <message name="IDS_DEVTOOLS_9bedd08947719ccd339c3493e6701d4b" desc="">
+    Request Main Thread Frame
+  </message>
+  <message name="IDS_DEVTOOLS_9c80c1f042e925ffbb94db9a17eb03df" desc="">
+    Dedicated Worker
+  </message>
+  <message name="IDS_DEVTOOLS_9d07f3bb674a88ea26f10e6b7589e000" desc="">
+    Aggregated Time
+  </message>
+  <message name="IDS_DEVTOOLS_9d2ee5f9ab058df4d72a137e13bd4df2" desc="">
+    [ <ph name="INVALIDATION_NODENAME">$1s</ph> ]
+  </message>
+  <message name="IDS_DEVTOOLS_a10717d996573373c8f9a01c49bbb6a5" desc="">
+    Loading profile…
+  </message>
+  <message name="IDS_DEVTOOLS_a3b61900010aa9472919c6c7ac247fed" desc="">
+    (changed id to &quot;<ph name="INVALIDATION_CHANGEDID">$1s</ph>&quot;<ph name="EXTRADATA">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_a45da96d0bf6575970f2d27af22be28a" desc="">
+    System
+  </message>
+  <message name="IDS_DEVTOOLS_a580a76abe8b0912889d80f5c4cb9559" desc="">
+    Group by Product
+  </message>
+  <message name="IDS_DEVTOOLS_a6f22a2373839a1d417d7a4078f0383a" desc="">
+    - CPU throttling is enabled
+  </message>
+  <message name="IDS_DEVTOOLS_a7c815fafe60ae637d38216fa6300395" desc="">
+    FCP
+  </message>
+  <message name="IDS_DEVTOOLS_aa40d845ecb78d5bd01ff8b0cc9e213e" desc="">
+    Receive Data
+  </message>
+  <message name="IDS_DEVTOOLS_aa49e59ec25c5938e9629495e54b913a" desc="">
+    Worker — <ph name="TRACK_URL">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_ab0b54125d50a09e75f3829e7e90b8ea" desc="">
+    WebSocket Protocol
+  </message>
+  <message name="IDS_DEVTOOLS_ac3b5b333d7529f2d6e178a1d326315c" desc="">
+    Disables JavaScript sampling, reduces overhead when running against mobile devices
+  </message>
+  <message name="IDS_DEVTOOLS_ac5bb077c33753116b5e91ff1766e7bc" desc="">
+    Received
+  </message>
+  <message name="IDS_DEVTOOLS_aed0e2bd848c2ac9dddb03d8c21c67b6" desc="">
+    Touch Cancel
+  </message>
+  <message name="IDS_DEVTOOLS_b030f7d30297555390c6d2d23af55fa1" desc="">
+    moments
+  </message>
+  <message name="IDS_DEVTOOLS_b06f8ae307ae18937eff67f286f45a20" desc="">
+    Repeats
+  </message>
+  <message name="IDS_DEVTOOLS_b0f2106d388ba5a6a92ac83146df9770" desc="">
+    Idle callback execution extended beyond deadline by <ph name="EXCEEDEDMS">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_b14bde4124700a984f3840be64c98bf2" desc="">
+    Invoked by Timeout
+  </message>
+  <message name="IDS_DEVTOOLS_b24dc7fd5bcbe91ffc080f0220f50dfe" desc="">
+    DOM GC
+  </message>
+  <message name="IDS_DEVTOOLS_b2ef7b20ebc78fa300ef24c1965033bc" desc="">
+    Pending for
+  </message>
+  <message name="IDS_DEVTOOLS_b357598ce3db447b0138d1a5ea6b76cc" desc="">
+    Layout root
+  </message>
+  <message name="IDS_DEVTOOLS_b3bfefedfa27aa11e79f637b22b16d09" desc="">
+    [V8 Runtime]
+  </message>
+  <message name="IDS_DEVTOOLS_b49fe3f49a656887d446c2da3851a88a" desc="">
+    Network request
+  </message>
+  <message name="IDS_DEVTOOLS_b4c7087912916a68642000d8759baec2" desc="">
+    Scroll Begin
+  </message>
+  <message name="IDS_DEVTOOLS_b53fbbb58656447400f07904e68c0f12" desc="">
+    Create WebSocket
+  </message>
+  <message name="IDS_DEVTOOLS_b59387e4d62a4e3fe4285a1311dcd272" desc="">
+    Event Log
+  </message>
+  <message name="IDS_DEVTOOLS_b6722804f5872cdd88347da194d42087" desc="">
+    HEAP
+  </message>
+  <message name="IDS_DEVTOOLS_b8a4d4c7e6bb7b5534b856ce7a9ccde0" desc="">
+    Drag
+  </message>
+  <message name="IDS_DEVTOOLS_b9048f8879ceb809dd8d8bc0866306b5" desc="">
+    , and <ph name="INVALIDATIONNODES_LENGTH____">$1s</ph> others
+  </message>
+  <message name="IDS_DEVTOOLS_b94c9f4242a696995ec1a85feeb138e2" desc="">
+    Layer Root
+  </message>
+  <message name="IDS_DEVTOOLS_b9749b1e497d9193cd4307bb9e341287" desc="">
+    Decrypt Reply
+  </message>
+  <message name="IDS_DEVTOOLS_b9f3a3a888d3796e0248b3f65b14349e" desc="">
+    Bottom-Up
+  </message>
+  <message name="IDS_DEVTOOLS_bd03acccca092608aac93f5fadb091d5" desc="">
+    Streamed
+  </message>
+  <message name="IDS_DEVTOOLS_be7e368875c5373b1cfbc3b97abc8742" desc="">
+    ≥ <ph name="DURATIONMS">$1d</ph> ms
+  </message>
+  <message name="IDS_DEVTOOLS_be85f6b3661854c85ddbde4c1429b2a2" desc="">
+    (<ph name="CLIP___">$1s</ph>, <ph name="CLIP___">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_be97c5beb33f5316b2c221bfbb95852b" desc="">
+    MIME Type
+  </message>
+  <message name="IDS_DEVTOOLS_bea8bf1ee3b28d900843d6ea1e0440db" desc="">
+    Tap Halt
+  </message>
+  <message name="IDS_DEVTOOLS_bf4bbfdff02c062ac1a0badec650946d" desc="">
+    Async Task
+  </message>
+  <message name="IDS_DEVTOOLS_c123b11aa670647408778a3b81a21d70" desc="">
+    FPS
+  </message>
+  <message name="IDS_DEVTOOLS_c26f31b8ab9b92198caf6894823b14f7" desc="">
+    <ph name="HOURS">$1s</ph> h
+  </message>
+  <message name="IDS_DEVTOOLS_c4030781aba9b3bf12bc9d7c031b586d" desc="">
+    [unattributed]
+  </message>
+  <message name="IDS_DEVTOOLS_c472e98c996b5d0a818722fcbe0af52d" desc="">
+    Key Character
+  </message>
+  <message name="IDS_DEVTOOLS_c5374020999c64f3332f04b296f61a19" desc="">
+    <ph name="WIDTH">$1d</ph> × <ph name="HEIGHT">$2d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_c5c7073f423645e437dd5d02419a911a" desc="">
+    Mouse Up
+  </message>
+  <message name="IDS_DEVTOOLS_c85a251cc457840f1e032f1b733e9398" desc="">
+    Timeout
+  </message>
+  <message name="IDS_DEVTOOLS_c86fd16c7ad825ebab7026a3e8fa7c4e" desc="">
+    Parse Stylesheet
+  </message>
+  <message name="IDS_DEVTOOLS_c91e491bb45df4c3796656945c4b86a2" desc="">
+    (changed pesudo to &quot;<ph name="INVALIDATION_CHANGEDPSEUDO">$1s</ph>&quot;<ph name="EXTRADATA">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_cba3eb593893730dfd61ce641be2502a" desc="">
+    Network:
+  </message>
+  <message name="IDS_DEVTOOLS_cbacb5bbda9c8eeb8e3a7e8e53fbcc59" desc="">
+    Timer Fired
+  </message>
+  <message name="IDS_DEVTOOLS_cbf04be3b9563f7d53998e76488efb4a" desc="">
+    Mouse Move
+  </message>
+  <message name="IDS_DEVTOOLS_cc4950eab5e0aaef9820cc6388a97640" desc="">
+    Related Node
+  </message>
+  <message name="IDS_DEVTOOLS_cdcf2d0d2f648704b8223f9a96ad0b14" desc="">
+    Parse HTML
+  </message>
+  <message name="IDS_DEVTOOLS_cdfb064314e94077caa817714d943c6f" desc="">
+    Elements Affected
+  </message>
+  <message name="IDS_DEVTOOLS_ce5bf551379459c1c61d2a204061c455" desc="">
+    Location
+  </message>
+  <message name="IDS_DEVTOOLS_cf56f8ebb3727b1d4d5a17fb18d4df74" desc="">
+    Paint Setup
+  </message>
+  <message name="IDS_DEVTOOLS_d0369e883465f2849df8b38966901bbf" desc="">
+    CPU:
+  </message>
+  <message name="IDS_DEVTOOLS_d07a2a1ecbb76d84f08a686e6c9639ea" desc="">
+    Evaluate Module
+  </message>
+  <message name="IDS_DEVTOOLS_d0c91aff68287a1a0003af56b91cadde" desc="">
+    [<ph name="MIN">$1s</ph> – <ph name="MAX">$2s</ph>]
+  </message>
+  <message name="IDS_DEVTOOLS_d14e91fe7abe33fc8a05c193fc812d68" desc="">
+    Mime Type
+  </message>
+  <message name="IDS_DEVTOOLS_d20caec3b48a1eef164cb4ca81ba2587" desc="">
+    L
+  </message>
+  <message name="IDS_DEVTOOLS_d24cdb5cd4bcfa9bab80d1fd5dc08b91" desc="">
+    <ph name="URL">$1s</ph> [<ph name="STARTLINE____">$2s</ph>…]
+  </message>
+  <message name="IDS_DEVTOOLS_d3064df90a9cacec6d462ba65032fd08" desc="">
+    CPU profile for a target is not available.
+  </message>
+  <message name="IDS_DEVTOOLS_d47c9bd21b8b13b29cb0757777f54d04" desc="">
+    Idle Callback Requested
+  </message>
+  <message name="IDS_DEVTOOLS_d49a276b363c437fe2446074595fd325" desc="">
+    <ph name="REASON">$1s</ph> for 
+  </message>
+  <message name="IDS_DEVTOOLS_d5b4c38ebb6e58f520e8d1e816ad841e" desc="">
+    Stopping timeline…
+  </message>
+  <message name="IDS_DEVTOOLS_d5e3345f0d0b507c8993b1ca0e0ac2c4" desc="">
+    First Paint
+  </message>
+  <message name="IDS_DEVTOOLS_d84fe3f1d290e4855cc0487b5ea18a4a" desc="">
+    Pinch Begin
+  </message>
+  <message name="IDS_DEVTOOLS_d88a137b62e15eece2884fa04bc36099" desc="">
+    <ph name="THIS__STYLE_TITLE">$1s</ph> at <ph name="STARTTIME">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_d97477d6d8a838ead9348185bb5b6742" desc="">
+    Thread
+  </message>
+  <message name="IDS_DEVTOOLS_da3c710f68f0c891ecad19dcbf2ab349" desc="">
+    Console Time
+  </message>
+  <message name="IDS_DEVTOOLS_da5b09b6bbef223b458f5a5318c1261d" desc="">
+    First Meaningful Paint
+  </message>
+  <message name="IDS_DEVTOOLS_dbcc430b9ebc8e22dc874b108f77e4a9" desc="">
+    Timings
+  </message>
+  <message name="IDS_DEVTOOLS_dc1085228e13a69521f0d08a4f03c304" desc="">
+    Call Tree
+  </message>
+  <message name="IDS_DEVTOOLS_dc16531c312e4d4c024ffd577d123887" desc="">
+    Listeners
+  </message>
+  <message name="IDS_DEVTOOLS_dc63e2b8b0f6f66d3dd2dc5d49483eec" desc="">
+    Show all records
+  </message>
+  <message name="IDS_DEVTOOLS_dd694dc279a5fbefed6a8323c15f4e67" desc="">
+    Cancel Idle Callback
+  </message>
+  <message name="IDS_DEVTOOLS_dd810137d5737309d18208a7be75773f" desc="">
+    Malformed timeline data: Unknown JSON format
+  </message>
+  <message name="IDS_DEVTOOLS_dde38f333e095c2293593f504fd701a2" desc="">
+    JavaScript Profiler
+  </message>
+  <message name="IDS_DEVTOOLS_de1ec1ac3add3d69ffedffd437af7056" desc="">
+    Pinch Update
+  </message>
+  <message name="IDS_DEVTOOLS_df2c48ebe0a4927e0f33ce3adeaa265b" desc="">
+    Range:  <ph name="NUMBER_MILLISTOSTRING_STARTOFFSET_">$1s</ph> – <ph name="NUMBER_MILLISTOSTRING_ENDOFFSET_">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_df839b886d238c3ce6de2fa9f9615518" desc="">
+    FMP
+  </message>
+  <message name="IDS_DEVTOOLS_dfe67b764ba5e3f6b674f049db64e165" desc="">
+    First Layout Invalidation
+  </message>
+  <message name="IDS_DEVTOOLS_e02d2ae03de9d493df2b6b2d2813d302" desc="">
+    Duration
+  </message>
+  <message name="IDS_DEVTOOLS_e0c70bcb54d7f58615eb895c232049ce" desc="">
+    Composite Layers
+  </message>
+  <message name="IDS_DEVTOOLS_e1851406e1e939d1ddaf625e577d6731" desc="">
+    <ph name="MINUTES">$1s</ph> m
+  </message>
+  <message name="IDS_DEVTOOLS_e2daed94bbd6483ac087b1da9a06e3fd" desc="">
+    <ph name="NUMBER_PRECISEMILLISTOSTRING_FRAME_DURATION____">$1s</ph> ~ <ph name="_______FRAME_DURATION">$2.0f</ph> fps
+  </message>
+  <message name="IDS_DEVTOOLS_e599161956d626eda4cb0a5ffb85271c" desc="">
+    Idle
+  </message>
+  <message name="IDS_DEVTOOLS_e5cfc515f312c9e85f367e9854d9963e" desc="">
+    Digest
+  </message>
+  <message name="IDS_DEVTOOLS_e6dd8524d6da96536b22201a9a46285a" desc="">
+    GPU Memory
+  </message>
+  <message name="IDS_DEVTOOLS_e77edad5ae632de2e062ef4bb6c80d8a" desc="">
+     took <ph name="NUMBER_MILLISTOSTRING_EVENT_DURATION__TRUE_">$1s</ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_e88587c08bb701308040c1d45394ecad" desc="">
+    Tap Down
+  </message>
+  <message name="IDS_DEVTOOLS_ea972b3952b64289353946d365786435" desc="">
+    Encrypt Reply
+  </message>
+  <message name="IDS_DEVTOOLS_eaeb30f9f18e0c50b178676f3eaef45f" desc="">
+    Task
+  </message>
+  <message name="IDS_DEVTOOLS_ebd9bec4d70abc789d439c1f136b0538" desc="">
+    Layout
+  </message>
+  <message name="IDS_DEVTOOLS_ec21eddec143497cea80ab45444a5dab" desc="">
+    Owner Element
+  </message>
+  <message name="IDS_DEVTOOLS_ecfc2dffe568c10a67dbc6d3724cfde2" desc="">
+    Activity
+  </message>
+  <message name="IDS_DEVTOOLS_ed3fec59c125d9d20096de997602a46d" desc="">
+    Remove Timer
+  </message>
+  <message name="IDS_DEVTOOLS_ee1532a0110d6e08d330eca2f822a8c1" desc="">
+    Interactions
+  </message>
+  <message name="IDS_DEVTOOLS_eecac07978e7842e41f7dbc578482368" desc="">
+    Produced Cache Size
+  </message>
+  <message name="IDS_DEVTOOLS_ef5db075f5ef10fbe063c7108aa49b25" desc="">
+    <ph name="DOMAIN">$1s</ph> #<ph name="SEQUENCENUMBER">$2d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_f17085a56d199cb15adf7c101d611bcc" desc="">
+    (changed class to &quot;<ph name="INVALIDATION_CHANGEDCLASS">$1s</ph>&quot;<ph name="EXTRADATA">$2s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_f34ebd9ed4156c54fbd77bb3a5b3107a" desc="">
+    Save profile…
+  </message>
+  <message name="IDS_DEVTOOLS_f466ef0697891303497dfbf951a14b1d" desc="">
+    Idle Frame
+  </message>
+  <message name="IDS_DEVTOOLS_f4843c1c797abf1a256c8802b6cd9f51" desc="">
+    Dimensions
+  </message>
+  <message name="IDS_DEVTOOLS_f4af86c1492aed890460419da6d5e5e9" desc="">
+    <ph name="NUMBER_BYTESTOSTRING_MINUSEDHEAPSIZE_">$1s</ph> – <ph name="NUMBER_BYTESTOSTRING_MAXUSEDHEAPSIZE_">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_f5baffc95ebfe1ede488388589db922b" desc="">
+    Other Invalidations
+  </message>
+  <message name="IDS_DEVTOOLS_fa7c73b6eb5f3c3b77e47d8a4557af3e" desc="">
+    Load profile…
+  </message>
+  <message name="IDS_DEVTOOLS_faa313428f0d69de3acffbd44c3df1ec" desc="">
+    Enable advanced paint instrumentation (slow)
+  </message>
+  <message name="IDS_DEVTOOLS_fb29a86861ace71b3d0203222b687dc0" desc="">
+    Digest Reply
+  </message>
+  <message name="IDS_DEVTOOLS_fc763cb31e9938f37737394681228f83" desc="">
+    , 
+  </message>
+  <message name="IDS_DEVTOOLS_fd87735db5aee919b6f95f0413a5f542" desc="">
+    Forced reflow
+  </message>
+  <message name="IDS_DEVTOOLS_feab2dfada0f566072a2a5c3268c2b02" desc="">
+    Subframe
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/timeline_model/timeline_model_strings.grdp b/third_party/blink/renderer/devtools/front_end/timeline_model/timeline_model_strings.grdp
new file mode 100644
index 0000000..8e9b291a
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/timeline_model/timeline_model_strings.grdp
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_3f2fac4932c34252407760c51ae69add" desc="">
+    Two flings at the same time? <ph name="FLINGSTART_STARTTIME">$1s</ph> vs <ph name="EVENT_STARTTIME">$2s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_939f0ea548384887196004acbb969c45" desc="">
+    Two touches at the same time? <ph name="TOUCHSTART_STARTTIME">$1s</ph> vs <ph name="EVENT_STARTTIME">$2s</ph>
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
new file mode 100644
index 0000000..cd35e68
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_00618c7a664449da0ec12190348c685a" desc="">
+    Switch between files with the same name and different extensions.
+  </message>
+  <message name="IDS_DEVTOOLS_0118d8300f0ad71843ef9f47acca779f" desc="">
+    Close others
+  </message>
+  <message name="IDS_DEVTOOLS_02b411e36e8eaa1f554ca176a106a79d" desc="">
+    <ph name="MEGABYTES">$1.0f</ph> MB
+  </message>
+  <message name="IDS_DEVTOOLS_0c89face91afb1a048a34349404d11c0" desc="">
+    Add selection to watch
+  </message>
+  <message name="IDS_DEVTOOLS_0cbca9a34ab68b4be10dd4d09dd61bfd" desc="">
+    Search previous
+  </message>
+  <message name="IDS_DEVTOOLS_0d76841780a2811f3c24a4f8faf129d0" desc="">
+    Show previous/next recording
+  </message>
+  <message name="IDS_DEVTOOLS_0ebe6df8a3ac338e0512acc741823fdb" desc="">
+    Replace
+  </message>
+  <message name="IDS_DEVTOOLS_0f8854fb5bc175b0cd33a1e8518b366f" desc="">
+    Switch to pan mode
+  </message>
+  <message name="IDS_DEVTOOLS_10f0bad7213cafe18fb1cf55180a11da" desc="">
+    (async)
+  </message>
+  <message name="IDS_DEVTOOLS_1299e7f10b9d0110e7fa5368d1307500" desc="">
+    long text was truncated (<ph name="TOTALBYTES">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_16d0b9499edfdb8274382a91686ae1ae" desc="">
+    Performance Panel
+  </message>
+  <message name="IDS_DEVTOOLS_17d440bf27df66479f0fb39cb4f4ad09" desc="">
+    Reset view
+  </message>
+  <message name="IDS_DEVTOOLS_1d5f9d3b8b2be8824c18022f024a43a0" desc="">
+    Decrement value
+  </message>
+  <message name="IDS_DEVTOOLS_21e2320215eb46d452e7494f3dd1c798" desc="">
+    Next/previous property
+  </message>
+  <message name="IDS_DEVTOOLS_2c0924faed417c521b57d553c678ec21" desc="">
+    Step into
+  </message>
+  <message name="IDS_DEVTOOLS_2dfef1e9ae6e4da36eb0bbeea94742b9" desc="">
+    <ph name="MEGABYTES">$1.1f</ph> MB
+  </message>
+  <message name="IDS_DEVTOOLS_2ea54663ae9e3a586132a34a98d85ccf" desc="">
+    Async Call
+  </message>
+  <message name="IDS_DEVTOOLS_3a39eb38e879f66d1c57dedd478272da" desc="">
+    Open in new tab
+  </message>
+  <message name="IDS_DEVTOOLS_3b41735478b3a3fd8bcc06f6c7a6b56c" desc="">
+    Copy link address
+  </message>
+  <message name="IDS_DEVTOOLS_3b5f1c7ab5345e75ee764983f8091933" desc="">
+    Pan or rotate up/down
+  </message>
+  <message name="IDS_DEVTOOLS_3b9dc6454ee96394b3d853e8d597ebc6" desc="">
+    Increment value
+  </message>
+  <message name="IDS_DEVTOOLS_3f0b6654f9648ab6331823837c2b9f73" desc="">
+    Step over
+  </message>
+  <message name="IDS_DEVTOOLS_3f9eccbca028a6949c39933d13f82e56" desc="">
+    Step out
+  </message>
+  <message name="IDS_DEVTOOLS_44ac014b674d7d86a04702e6fd1fa858" desc="">
+    Reconnect when ready by reopening DevTools.
+  </message>
+  <message name="IDS_DEVTOOLS_47df0ee8f6dc4f553c907c49e686216c" desc="">
+    Memory Panel
+  </message>
+  <message name="IDS_DEVTOOLS_47e0270ab1827aab8faa1c85d89bd48f" desc="">
+    <ph name="UI_KEYBOARDSHORTCUT_SHORTCUTTOSTRING_____UI_KEYBOARDSHORTCUT_MODIFIERS_CTRLORMETA_">$1s</ph>Click to select multiple types
+  </message>
+  <message name="IDS_DEVTOOLS_4819fdb79641baf4a4c8a2ca2159a312" desc="">
+    <ph name="BYTES">$1.0f</ph> B
+  </message>
+  <message name="IDS_DEVTOOLS_48f12760504bf8e958a4bdde56d4fa46" desc="">
+    1 match
+  </message>
+  <message name="IDS_DEVTOOLS_4b50fa6fd1a6a30c9b16f71dd2600574" desc="">
+    Jump to next editing location
+  </message>
+  <message name="IDS_DEVTOOLS_4c4306efb0e40e97e5f8693613b1fcfd" desc="">
+    Go to matching bracket
+  </message>
+  <message name="IDS_DEVTOOLS_4f348d8d1f134f0980102a4a6508cd5e" desc="">
+    Pan or rotate left/right
+  </message>
+  <message name="IDS_DEVTOOLS_52ebc26c2921d5b84fc670dff9891821" desc="">
+    Zoom out
+  </message>
+  <message name="IDS_DEVTOOLS_534d1b2eaff704d4888de7c4a5e282c0" desc="">
+    Debugging connection was closed. Reason: 
+  </message>
+  <message name="IDS_DEVTOOLS_5372591390f99dcf8e0467c671abbc23" desc="">
+    Switch to rotate mode
+  </message>
+  <message name="IDS_DEVTOOLS_56797cefb1efc9130f7c48a7d1db0f0c" desc="">
+    Panels
+  </message>
+  <message name="IDS_DEVTOOLS_5ccb2f9327ce0aba7567b62e9e1d15a5" desc="">
+    Close tabs to the right
+  </message>
+  <message name="IDS_DEVTOOLS_5d3d95cb74ad59528ab385023f8a2a39" desc="">
+    Save profile
+  </message>
+  <message name="IDS_DEVTOOLS_63e29f78b2e7788d9234a5790897fa71" desc="">
+    DevTools was disconnected from the page.
+  </message>
+  <message name="IDS_DEVTOOLS_650654cb12c12b00e68b4384e0aede82" desc="">
+    <ph name="THIS__ONLYCOMPLETION_TEXT">$1s</ph>, suggestion
+  </message>
+  <message name="IDS_DEVTOOLS_678b7bbae3b9b67cee8bce1fac3067d7" desc="">
+    Full list of DevTools keyboard shortcuts and gestures
+  </message>
+  <message name="IDS_DEVTOOLS_6a5fa3625d6317cd74f686720d9c411e" desc="">
+    Promise rejected
+  </message>
+  <message name="IDS_DEVTOOLS_6e0e121820384f135321cb766273f4da" desc="">
+    Soft undo
+  </message>
+  <message name="IDS_DEVTOOLS_6ed5ccb02cd39be4a7a98739f5696d43" desc="">
+    Record page reload
+  </message>
+  <message name="IDS_DEVTOOLS_7102e4c0bf381bdbaa934e3dccb5b283" desc="">
+    (anonymous)
+  </message>
+  <message name="IDS_DEVTOOLS_71fc9f874ad2b8e691e9e797eda7d353" desc="">
+    Expand/collapse
+  </message>
+  <message name="IDS_DEVTOOLS_728618db1e2c850d3f7363ff2768d6b6" desc="">
+    Increment CSS unit by 10
+  </message>
+  <message name="IDS_DEVTOOLS_7423ba80f2fff2568d2c642c6be84ede" desc="">
+    Close drawer
+  </message>
+  <message name="IDS_DEVTOOLS_75cfe68daf4ccd62d15e9e0fb5ec1097" desc="">
+    Increment CSS unit by 1
+  </message>
+  <message name="IDS_DEVTOOLS_76f8c7ad696be0b330b46afe79ac279d" desc="">
+    Start/stop recording
+  </message>
+  <message name="IDS_DEVTOOLS_79fb17b82d0adb283dc78efc0d7f73e8" desc="">
+    Temporarily toggle pan/rotate mode while held
+  </message>
+  <message name="IDS_DEVTOOLS_7b91156d64b76a5dda815937fce9119d" desc="">
+    Zoom in
+  </message>
+  <message name="IDS_DEVTOOLS_7cf206a681131cf4d86fc190f2d9ad27" desc="">
+    Reconnect DevTools
+  </message>
+  <message name="IDS_DEVTOOLS_7dce122004969d56ae2e0245cb754d35" desc="">
+    Edit
+  </message>
+  <message name="IDS_DEVTOOLS_84e1d403df34bad00840ee636021698c" desc="">
+    Close <ph name="THIS_TITLE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_860429066968657205b314b6b4143da7" desc="">
+    Layers Panel
+  </message>
+  <message name="IDS_DEVTOOLS_86e8f8f8327d28b74e5bd3ab581f77ee" desc="">
+    e.g. /small[\d]+/ url:a.com/b
+  </message>
+  <message name="IDS_DEVTOOLS_89345110b204ed302eddff2ddbd0f4d9" desc="">
+    Toggle breakpoint enabled
+  </message>
+  <message name="IDS_DEVTOOLS_894dab8359b886762113f6b64ce9f107" desc="">
+    Replace all
+  </message>
+  <message name="IDS_DEVTOOLS_8e44b2127a4fb456a78feced6731a496" desc="">
+    <ph name="CURRENTMATCHINDEX____">$1d</ph> of <ph name="MATCHES">$2d</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_925f3a6eef3896d617fc0ab413f9c123" desc="">
+    <ph name="MATCHES">$1d</ph> matches
+  </message>
+  <message name="IDS_DEVTOOLS_92d347b9338f3ffaa227ee482078bbb7" desc="">
+    Toggle comment
+  </message>
+  <message name="IDS_DEVTOOLS_966f6f045ff62d8c920c755930f4fd7b" desc="">
+    Toggle edit as HTML
+  </message>
+  <message name="IDS_DEVTOOLS_96ec2a08daf01bc10adc6e4ac4c76ceb" desc="">
+    Decrement CSS unit by 10
+  </message>
+  <message name="IDS_DEVTOOLS_96fb9e0d650af5b1fc4ec6c7bc11b49b" desc="">
+    More Tools
+  </message>
+  <message name="IDS_DEVTOOLS_9f52d2760e4266c7904c59fd9d3e7321" desc="">
+    Pick a recording from history
+  </message>
+  <message name="IDS_DEVTOOLS_9f98ff6e7e9fd0475daeb2005234ab5d" desc="">
+    <ph name="KILOBYTES">$1.0f</ph> KB
+  </message>
+  <message name="IDS_DEVTOOLS_a7659dc1b20557ca85c11efc57ace59f" desc="">
+    Toggle all breakpoints
+  </message>
+  <message name="IDS_DEVTOOLS_a950da8e902568f9a6879abe4313efe7" desc="">
+    Once page is reloaded, DevTools will automatically reconnect.
+  </message>
+  <message name="IDS_DEVTOOLS_addec426932e71323700afa1911f8f1c" desc="">
+    more
+  </message>
+  <message name="IDS_DEVTOOLS_ae240dc70adad29732bb1fdf0c746b84" desc="">
+    <ph name="KILOBYTES">$1.1f</ph> KB
+  </message>
+  <message name="IDS_DEVTOOLS_b014fff4db17dba07ddb6ea5efafd32e" desc="">
+    Jump to previous/next frame
+  </message>
+  <message name="IDS_DEVTOOLS_b1402575685f74c0c38dabac64e95cc0" desc="">
+    Navigate elements
+  </message>
+  <message name="IDS_DEVTOOLS_b228e7bd736e688236ab3aa37996bf8f" desc="">
+    Decrement by <ph name="__">$1f</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_b79165bf6163186253ed04568e1aee11" desc="">
+    Pause/ Continue
+  </message>
+  <message name="IDS_DEVTOOLS_b79f0c59be8253d3acb4515071312bda" desc="">
+    Hide <ph name="THIS__SHOWHIDESIDEBARBUTTONTITLE">$1s</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_b7ad6a024765bbfbff22dedf9afe0d17" desc="">
+    Jump to previous editing location
+  </message>
+  <message name="IDS_DEVTOOLS_bc39d641a1e16fb73f2ce5520d250ed0" desc="">
+    Go to member
+  </message>
+  <message name="IDS_DEVTOOLS_bd474df0d4b1bb32aac1b31560b962fb" desc="">
+    Next/previous call frame
+  </message>
+  <message name="IDS_DEVTOOLS_c449b64ade0e464abfc5cb5368775233" desc="">
+    Decrement CSS unit by 1
+  </message>
+  <message name="IDS_DEVTOOLS_d06337cfbcc777e0a7caa50ef1a9986a" desc="">
+    More tabs
+  </message>
+  <message name="IDS_DEVTOOLS_d2923d7d060c61c17d06c22166b2be41" desc="">
+    Promise resolved
+  </message>
+  <message name="IDS_DEVTOOLS_d9f57fb1f634a4e047fd69b574434c7f" desc="">
+    Evaluate selection in console
+  </message>
+  <message name="IDS_DEVTOOLS_dd73c24f4702950125e2fcb7d26ca5fe" desc="">
+    Show more (<ph name="TOTALBYTES">$1s</ph>)
+  </message>
+  <message name="IDS_DEVTOOLS_dd92c3884d43902407a4c2f0d8cc0eb1" desc="">
+    Toggle breakpoint
+  </message>
+  <message name="IDS_DEVTOOLS_e0aa021e21dddbd6d8cecec71e9cf564" desc="">
+    OK
+  </message>
+  <message name="IDS_DEVTOOLS_e0f1691bdb67d9859e5a6a8d7c26e6a1" desc="">
+    Text Editor
+  </message>
+  <message name="IDS_DEVTOOLS_e118bd20c49240fd8387f3bd901cb4f1" desc="">
+    Load profile
+  </message>
+  <message name="IDS_DEVTOOLS_e6290b661dba9591e4c505fb50257d0c" desc="">
+    Search next
+  </message>
+  <message name="IDS_DEVTOOLS_e7a555523728cc11719011fa5a7e77ba" desc="">
+    Select next occurrence
+  </message>
+  <message name="IDS_DEVTOOLS_e81c4e4f2b7b93b481e13a8553c2ae1b" desc="">
+    or
+  </message>
+  <message name="IDS_DEVTOOLS_e9947a82726be2fb77368eb27c082402" desc="">
+    (no item selected)
+  </message>
+  <message name="IDS_DEVTOOLS_f68d3333dcde01f3ef29184f1bcdf1c5" desc="">
+    never show
+  </message>
+  <message name="IDS_DEVTOOLS_f6c46678b34ad4b4c0241c9487a26b59" desc="">
+    Increment by <ph name="__">$1f</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_f8fbc6412a360fc4f854f34a776e2af2" desc="">
+    Another profiler is already active
+  </message>
+  <message name="IDS_DEVTOOLS_fa66e32280e1ee04b8e8cd36626b65f0" desc="">
+    Close editor tab
+  </message>
+  <message name="IDS_DEVTOOLS_fbbbb283efc814825613301c22951ba7" desc="">
+    Close all
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/web_audio_strings.grdp b/third_party/blink/renderer/devtools/front_end/web_audio/web_audio_strings.grdp
new file mode 100644
index 0000000..665fadf
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/web_audio_strings.grdp
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_13d286ca90ba61a4151f3dcb6e353cc8" desc="">
+    OfflineAudioContext
+  </message>
+  <message name="IDS_DEVTOOLS_28fce67b0d0976eaca9b69748126a6d2" desc="">
+    Open a page that uses Web Audio API to start monitoring.
+  </message>
+  <message name="IDS_DEVTOOLS_40097404a926dce6324e58f598bd7dcb" desc="">
+    Render Capacity
+  </message>
+  <message name="IDS_DEVTOOLS_40734192aacef5d658541080315dd644" desc="">
+    Callback Buffer Size
+  </message>
+  <message name="IDS_DEVTOOLS_545e39748691e8a65093c525011a321b" desc="">
+    Max Output Channels
+  </message>
+  <message name="IDS_DEVTOOLS_5f8695ebb8bc9a78c593ee7bfae153a5" desc="">
+    AudioContext
+  </message>
+  <message name="IDS_DEVTOOLS_b78dc7712ef83a078c1c68822d2a1a3f" desc="">
+    BaseAudioContexts
+  </message>
+  <message name="IDS_DEVTOOLS_f047f03535a9154caf9a858b5aed17c0" desc="">
+    Current Time
+  </message>
+  <message name="IDS_DEVTOOLS_f579f693ac9ecb3fb9e822bf32028e13" desc="">
+    Sample Rate
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/workspace/workspace_strings.grdp b/third_party/blink/renderer/devtools/front_end/workspace/workspace_strings.grdp
new file mode 100644
index 0000000..a38a4055
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/workspace/workspace_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_818fc883495655418c9124800c52b7ee" desc="">
+    This file was changed externally. Would you like to reload it?
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/package.json b/third_party/blink/renderer/devtools/package.json
index 4a01845..2c03d53e 100644
--- a/third_party/blink/renderer/devtools/package.json
+++ b/third_party/blink/renderer/devtools/package.json
@@ -15,6 +15,7 @@
     "check-gn": "node scripts/check_gn.js",
     "check-json": "node scripts/json_validator/validate_module_json.js",
     "check-loc": "node scripts/check_localizability.js -a",
+    "check-grdp": "node scripts/check_localizable_resources.js",
     "generate-jsconfig": "node scripts/generate_jsconfig.js"
   },
   "repository": {
diff --git a/third_party/blink/renderer/devtools/scripts/check_localizability.js b/third_party/blink/renderer/devtools/scripts/check_localizability.js
index 963eb49..30102a70 100644
--- a/third_party/blink/renderer/devtools/scripts/check_localizability.js
+++ b/third_party/blink/renderer/devtools/scripts/check_localizability.js
@@ -11,21 +11,11 @@
 // In this case, add it to the excluded errors at the top of the script.
 
 const path = require('path');
+const localizationUtils = require('./localization_utils/localization_utils');
+const esprimaTypes = localizationUtils.esprimaTypes;
+const escodegen = localizationUtils.escodegen;
+const esprima = localizationUtils.esprima;
 
-// Use modules in third_party/node/node_modules
-const THIRD_PARTY_PATH = path.resolve(__dirname, '..', '..', '..', '..');
-const REPO_NODE_MODULES_PATH = path.resolve(THIRD_PARTY_PATH, 'node', 'node_modules');
-const escodegen = require(path.resolve(REPO_NODE_MODULES_PATH, 'escodegen'));
-const esprima = require(path.resolve(REPO_NODE_MODULES_PATH, 'esprima'));
-
-const fs = require('fs');
-const {promisify} = require('util');
-const readDirAsync = promisify(fs.readdir);
-const readFileAsync = promisify(fs.readFile);
-const statAsync = promisify(fs.stat);
-
-const excludeFiles = ['lighthouse-dt-bundle.js', 'Tests.js'];
-const excludeDirs = ['_test_runner', 'Images', 'node_modules'];
 // Exclude known errors
 const excludeErrors = [
   'Common.UIString(view.title())', 'Common.UIString(setting.title() || \'\')', 'Common.UIString(option.text)',
@@ -34,16 +24,6 @@
   'Common.UIString(extension.title())', 'Common.UIString(this._currentValueLabel, value)'
 ];
 
-const esprimaTypes = {
-  BI_EXPR: 'BinaryExpression',
-  CALL_EXPR: 'CallExpression',
-  COND_EXPR: 'ConditionalExpression',
-  IDENTIFIER: 'Identifier',
-  MEMBER_EXPR: 'MemberExpression',
-  TAGGED_TEMP_EXPR: 'TaggedTemplateExpression',
-  TEMP_LITERAL: 'TemplateLiteral'
-};
-
 const usage = `Usage: node ${path.basename(process.argv[0])} [-a | <.js file path>*]
 
 -a: If present, check all devtools frontend .js files
@@ -62,7 +42,7 @@
     let filePaths = [];
     if (process.argv[2] === '-a') {
       const frontendPath = path.resolve(__dirname, '..', 'front_end');
-      await getFilesFromDirectory(frontendPath, filePaths);
+      await localizationUtils.getFilesFromDirectory(frontendPath, filePaths, ['.js']);
     } else {
       filePaths = process.argv.slice(2);
     }
@@ -86,73 +66,15 @@
 
 main();
 
-function verifyIdentifier(node, name) {
-  return node !== undefined && node.type === esprimaTypes.IDENTIFIER && node.name === name;
-}
-
-/**
- * Verify callee of objectName.propertyName(), e.g. Common.UIString().
- */
-function verifyCallExpressionCallee(callee, objectName, propertyName) {
-  return callee !== undefined && callee.type === esprimaTypes.MEMBER_EXPR && callee.computed === false &&
-      verifyIdentifier(callee.object, objectName) && verifyIdentifier(callee.property, propertyName);
-}
-
-function isNodeCallOnObject(node, objectName, propertyName) {
-  return node !== undefined && node.type === esprimaTypes.CALL_EXPR &&
-      verifyCallExpressionCallee(node.callee, objectName, propertyName);
-}
-
-function isNodeCommonUIStringCall(node) {
-  return isNodeCallOnObject(node, 'Common', 'UIString');
-}
-
-function isNodeUIformatLocalized(node) {
-  return isNodeCallOnObject(node, 'UI', 'formatLocalized');
-}
-
-function isNodelsTaggedTemplateExpression(node) {
-  return node !== undefined && node.type === esprimaTypes.TAGGED_TEMP_EXPR && verifyIdentifier(node.tag, 'ls') &&
-      node.quasi !== undefined && node.quasi.type !== undefined && node.quasi.type === esprimaTypes.TEMP_LITERAL;
-}
-
 function includesConditionalExpression(listOfElements) {
   return listOfElements.filter(ele => ele !== undefined && ele.type === esprimaTypes.COND_EXPR).length > 0;
 }
 
-function getLocalizationCase(node) {
-  if (isNodeCommonUIStringCall(node))
-    return 'Common.UIString';
-  else if (isNodelsTaggedTemplateExpression(node))
-    return 'Tagged Template';
-  else if (isNodeUIformatLocalized(node))
-    return 'UI.formatLocalized';
-  else
-    return null;
-}
-
-function isLocalizationCall(node) {
-  return isNodeCommonUIStringCall(node) || isNodelsTaggedTemplateExpression(node) || isNodeUIformatLocalized(node);
-}
-
 function addError(error, errors) {
   if (!errors.includes(error))
     errors.push(error);
 }
 
-function getLocation(node) {
-  if (node !== undefined && node.loc !== undefined && node.loc.start !== undefined && node.loc.end !== undefined &&
-      node.loc.start.line !== undefined && node.loc.end.line !== undefined) {
-    const startLine = node.loc.start.line;
-    const endLine = node.loc.end.line;
-    if (startLine === endLine)
-      return ` Line ${startLine}`;
-    else
-      return ` Line ${node.loc.start.line}-${node.loc.end.line}`;
-  }
-  return '';
-}
-
 function buildConcatenatedNodesList(node, nodes) {
   if (!node)
     return;
@@ -173,7 +95,7 @@
  */
 function checkConcatenation(parentNode, node, filePath, errors) {
   function isWord(node) {
-    return (node.type === 'Literal' && !!node.value.match(/[a-z]/i));
+    return (node.type === esprimaTypes.LITERAL && !!node.value.match(/[a-z]/i));
   }
   function isConcatenation(node) {
     return (node !== undefined && node.type === esprimaTypes.BI_EXPR && node.operator === '+');
@@ -183,16 +105,18 @@
     return;
 
   if (isConcatenation(node)) {
-    let concatenatedNodes = [];
+    const concatenatedNodes = [];
     buildConcatenatedNodesList(node, concatenatedNodes);
-    const hasLocalizationCall = !!concatenatedNodes.find(currentNode => isLocalizationCall(currentNode));
+    const hasLocalizationCall =
+        !!concatenatedNodes.find(currentNode => localizationUtils.isLocalizationCall(currentNode));
     if (hasLocalizationCall) {
       const hasAlphabeticLiteral = !!concatenatedNodes.find(currentNode => isWord(currentNode));
       if (hasAlphabeticLiteral) {
         const code = escodegen.generate(node);
         addError(
             `${filePath}${
-                getLocation(node)}: string concatenation should be changed to variable substitution with ls: ${code}`,
+                localizationUtils.getLocationMessage(
+                    node.loc)}: string concatenation should be changed to variable substitution with ls: ${code}`,
             errors);
       }
     }
@@ -200,26 +124,18 @@
 }
 
 /**
- * Verify if callee is functionName() or object.functionName().
- */
-function verifyFunctionCallee(callee, functionName) {
-  return callee !== undefined &&
-      ((callee.type === esprimaTypes.IDENTIFIER && callee.name === functionName) ||
-       (callee.type === esprimaTypes.MEMBER_EXPR && verifyIdentifier(callee.property, functionName)));
-}
-
-/**
  * Check if an argument of a function is localized.
  */
 function checkFunctionArgument(functionName, argumentIndex, node, filePath, errors) {
-  if (node !== undefined && node.type === esprimaTypes.CALL_EXPR && verifyFunctionCallee(node.callee, functionName) &&
-      node.arguments !== undefined && node.arguments.length > argumentIndex) {
+  if (node !== undefined && node.type === esprimaTypes.CALL_EXPR &&
+      localizationUtils.verifyFunctionCallee(node.callee, functionName) && node.arguments !== undefined &&
+      node.arguments.length > argumentIndex) {
     const arg = node.arguments[argumentIndex];
     // No need to localize empty strings.
-    if (arg.type == 'Literal' && arg.value === '')
+    if (arg.type === esprimaTypes.LITERAL && arg.value === '')
       return;
 
-    if (!isLocalizationCall(arg)) {
+    if (!localizationUtils.isLocalizationCall(arg)) {
       let order = '';
       switch (argumentIndex) {
         case 0:
@@ -235,8 +151,8 @@
           order = `${argumentIndex + 1}th`;
       }
       addError(
-          `${filePath}${getLocation(node)}: ${order} argument to ${functionName}() should be localized: ${
-              escodegen.generate(node)}`,
+          `${filePath}${localizationUtils.getLocationMessage(node.loc)}: ${order} argument to ${
+              functionName}() should be localized: ${escodegen.generate(node)}`,
           errors);
     }
   }
@@ -266,19 +182,22 @@
     return;
   }
 
-  const locCase = getLocalizationCase(node);
+  const locCase = localizationUtils.getLocalizationCase(node);
   const code = escodegen.generate(node);
   switch (locCase) {
     case 'Common.UIString':
     case 'UI.formatLocalized':
       const firstArgType = node.arguments[0].type;
-      if (firstArgType !== 'Literal' && firstArgType !== 'TemplateLiteral' && firstArgType !== 'Identifier' &&
-          !excludeErrors.includes(code)) {
-        addError(`${filePath}${getLocation(node)}: first argument to call should be a string: ${code}`, errors);
+      if (firstArgType !== esprimaTypes.LITERAL && firstArgType !== esprimaTypes.TEMP_LITERAL &&
+          firstArgType !== esprimaTypes.IDENTIFIER && !excludeErrors.includes(code)) {
+        addError(
+            `${filePath}${localizationUtils.getLocationMessage(node.loc)}: first argument to call should be a string: ${
+                code}`,
+            errors);
       }
       if (includesConditionalExpression(node.arguments.slice(1))) {
         addError(
-            `${filePath}${getLocation(node)}: conditional(s) found in ${
+            `${filePath}${localizationUtils.getLocationMessage(node.loc)}: conditional(s) found in ${
                 code}. Please extract conditional(s) out of the localization call.`,
             errors);
       }
@@ -286,7 +205,7 @@
     case 'Tagged Template':
       if (includesConditionalExpression(node.quasi.expressions)) {
         addError(
-            `${filePath}${getLocation(node)}: conditional(s) found in ${
+            `${filePath}${localizationUtils.getLocationMessage(node.loc)}: conditional(s) found in ${
                 code}. Please extract conditional(s) out of the localization call.`,
             errors);
       }
@@ -303,42 +222,11 @@
   }
 }
 
-function getRelativeFilePathFromSrc(fullFilePath) {
-  return path.relative(path.resolve(THIRD_PARTY_PATH, '..'), fullFilePath);
-}
-
 async function auditFileForLocalizability(filePath, errors) {
-  const fileContent = await readFileAsync(filePath);
-  const ast = esprima.parse(fileContent.toString(), {loc: true});
+  const fileContent = await localizationUtils.parseFileContent(filePath);
+  const ast = esprima.parse(fileContent, {loc: true});
 
-  const relativeFilePath = getRelativeFilePathFromSrc(filePath);
+  const relativeFilePath = localizationUtils.getRelativeFilePathFromSrc(filePath);
   for (const node of ast.body)
     analyzeNode(undefined, node, relativeFilePath, errors);
 }
-
-function shouldParseDirectory(directoryName) {
-  return !excludeDirs.reduce((result, dir) => result || directoryName.indexOf(dir) !== -1, false);
-}
-
-function shouldParseFile(filePath) {
-  return (path.extname(filePath) === '.js' && !excludeFiles.includes(path.basename(filePath)));
-}
-
-async function getFilesFromItem(itemPath, filePaths) {
-  const stat = await statAsync(itemPath);
-  if (stat.isDirectory() && shouldParseDirectory(itemPath))
-    return await getFilesFromDirectory(itemPath, filePaths);
-
-  if (shouldParseFile(itemPath))
-    filePaths.push(itemPath);
-}
-
-async function getFilesFromDirectory(directoryPath, filePaths) {
-  const itemNames = await readDirAsync(directoryPath);
-  const promises = [];
-  for (const itemName of itemNames) {
-    const itemPath = path.resolve(directoryPath, itemName);
-    promises.push(getFilesFromItem(itemPath, filePaths));
-  }
-  await Promise.all(promises);
-}
diff --git a/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js b/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
new file mode 100644
index 0000000..946cbcf
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/check_localizable_resources.js
@@ -0,0 +1,240 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright (C) Microsoft Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * This script is part of the presubmit check that parses DevTools frontend .js and
+ * module.json files, collects localizable strings, checks if frontend strings are
+ * in .grd/.grdp files and reports error if present.
+ *
+ * If argument '--autofix' is present, add the new resources to and remove unused
+ * messages from GRDP files.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const {promisify} = require('util');
+const writeFileAsync = promisify(fs.writeFile);
+const appendFileAsync = promisify(fs.appendFile);
+const checkLocalizedStrings = require('./localization_utils/check_localized_strings');
+const localizationUtils = require('./localization_utils/localization_utils');
+
+const grdpFileStart = '<?xml version="1.0" encoding="utf-8"?>\n<grit-part>\n';
+const grdpFileEnd = '</grit-part>';
+
+async function main() {
+  let frontendStrings;
+  let IDSkeys;
+
+  try {
+    [frontendStrings, IDSkeys] = await checkLocalizedStrings.parseLocalizableResourceMaps(false);
+
+    if (process.argv.includes('--autofix')) {
+      await autofix(frontendStrings, IDSkeys);
+    } else {
+      await getErrors(frontendStrings, IDSkeys);
+    }
+  } catch (e) {
+    console.log(`Error: ${e.message}`);
+    process.exit(1);
+  }
+}
+
+main();
+
+async function getErrors(frontendStrings, IDSkeys) {
+  const toAddError = await checkLocalizedStrings.getAndReportResourcesToAdd(frontendStrings, IDSkeys);
+  const toRemoveError = checkLocalizedStrings.getAndReportResourcesToRemove(frontendStrings, IDSkeys);
+  let error = `${toAddError ? toAddError : ''}${toRemoveError ? toRemoveError : ''}`;
+
+  if (error === '') {
+    console.log('DevTools localizable resources checker passed.');
+    return;
+  }
+
+  error += '\nThe errors are potentially fixable with the `--autofix` option.';
+
+  throw new Error(error);
+}
+
+async function autofix(frontendStrings, IDSkeys) {
+  const resourceAdded = await addResourcesToGRDP(frontendStrings, IDSkeys);
+  const resourceRemoved = await removeResourcesFromGRDP(frontendStrings, IDSkeys);
+
+  if (!resourceAdded && !resourceRemoved) {
+    console.log('DevTools localizable resources checker passed.');
+    return;
+  }
+
+  let message =
+      'Found changes to localizable DevTools strings.\nDevTools localizable resources checker has updated the appropriate grdp file(s).';
+  if (resourceAdded)
+    message += '\nManually write a description for any new <message> entries.';
+  message += '\nUse git status to see what has changed.';
+  throw new Error(message);
+}
+
+// Return true if any resources are added
+async function addResourcesToGRDP(frontendStrings, IDSkeys) {
+  const keysToAddToGRD = checkLocalizedStrings.getDifference(IDSkeys, frontendStrings);
+  if (keysToAddToGRD.size === 0)
+    return false;
+
+  const frontendPath = path.resolve(__dirname, '..', 'front_end');
+  const frontendDirs = await localizationUtils.getChildDirectoriesFromDirectory(frontendPath);
+  // Map file path to its parent grdp file path
+  const filePathToGrdpFilePath = new Map();
+  // Map grdp file path to strings to be added to that file so that we only need to
+  // modify every grdp file once
+  const grdpFilePathToStrings = new Map();
+
+  // Get the grdp files that need to be modified
+  for (const [key, stringObj] of keysToAddToGRD) {
+    let grdpFilePath = '';
+    if (filePathToGrdpFilePath.has(stringObj.filepath)) {
+      grdpFilePath = filePathToGrdpFilePath.get(stringObj.filepath);
+    } else {
+      grdpFilePath = localizationUtils.getGRDPFilePath(stringObj.filepath, frontendDirs);
+      filePathToGrdpFilePath.set(stringObj.filepath, grdpFilePath);
+    }
+
+    if (!grdpFilePathToStrings.has(grdpFilePath))
+      grdpFilePathToStrings.set(grdpFilePath, []);
+
+    // Add the IDS key to stringObj so we have access to it later
+    stringObj.ids = key;
+    grdpFilePathToStrings.get(grdpFilePath).push(stringObj);
+  }
+
+  const promises = [];
+
+  const grdpFilePathsToAdd = [];
+  for (let [grdpFilePath, stringsToAdd] of grdpFilePathToStrings) {
+    // The grdp file doesn't exist, so create one.
+    if (!fs.existsSync(grdpFilePath)) {
+      let grdpMessagesToAdd = '';
+      for (const stringObj of stringsToAdd)
+        grdpMessagesToAdd += localizationUtils.createGrdpMessage(stringObj.ids, stringObj);
+
+      // Create a new grdp file and reference it in the parent grd file
+      promises.push(appendFileAsync(grdpFilePath, `${grdpFileStart}${grdpMessagesToAdd}${grdpFileEnd}`));
+      grdpFilePathsToAdd.push(
+          path.relative(path.dirname(localizationUtils.GRD_PATH), grdpFilePath).split(path.sep).join('/'));
+      continue;
+    }
+
+    const grdpFileContent = await localizationUtils.parseFileContent(grdpFilePath);
+    const grdpFileLines = grdpFileContent.split('\n');
+
+    let newGrdpFileContent = '';
+    const IDSRegex = new RegExp(`"(${localizationUtils.IDSPrefix}.*?)"`);
+    for (let i = 0; i < grdpFileLines.length; i++) {
+      const grdpLine = grdpFileLines[i];
+      const match = grdpLine.match(IDSRegex);
+      // match[0]: full match
+      // match[1]: message IDS key
+      if (match) {
+        const ids = match[1];
+        const stringsToAddRemaining = [];
+        for (const stringObj of stringsToAdd) {
+          // Insert the new <message> in sorted order.
+          if (ids > stringObj.ids)
+            newGrdpFileContent += localizationUtils.createGrdpMessage(stringObj.ids, stringObj);
+          else
+            stringsToAddRemaining.push(stringObj);
+        }
+        stringsToAdd = stringsToAddRemaining;
+      } else if (grdpLine.includes(grdpFileEnd)) {
+        // Just hit the end tag, so insert any remaining <message>s.
+        for (const stringObj of stringsToAdd)
+          newGrdpFileContent += localizationUtils.createGrdpMessage(stringObj.ids, stringObj);
+      }
+      newGrdpFileContent += grdpLine;
+      if (i < grdpFileLines.length - 1)
+        newGrdpFileContent += '\n';
+    }
+
+    promises.push(writeFileAsync(grdpFilePath, newGrdpFileContent));
+  }
+  promises.push(addChildGRDPFilePathsToGRD(grdpFilePathsToAdd.sort()));
+  await Promise.all(promises);
+  return true;
+}
+
+async function addChildGRDPFilePathsToGRD(relativeGrdpFilePaths) {
+  function createPartFileEntry(relativeGrdpFilePath) {
+    return `      <part file="${relativeGrdpFilePath}" />\n`;
+  }
+
+  const grdFileContent = await localizationUtils.parseFileContent(localizationUtils.GRD_PATH);
+  const grdLines = grdFileContent.split('\n');
+
+  let newGrdFileContent = '';
+  for (let i = 0; i < grdLines.length; i++) {
+    const grdLine = grdLines[i];
+    // match[0]: full match
+    // match[1]: relative grdp file path
+    const match = grdLine.match(/<part file="(.*?)"/);
+    if (match) {
+      const relativeGrdpFilePathsRemaining = [];
+      for (const relativeGrdpFilePath of relativeGrdpFilePaths) {
+        if (relativeGrdpFilePath < match[1])
+          newGrdFileContent += createPartFileEntry(relativeGrdpFilePath);
+        else
+          relativeGrdpFilePathsRemaining.push(relativeGrdpFilePath);
+      }
+      relativeGrdpFilePaths = relativeGrdpFilePathsRemaining;
+    } else if (grdLine.includes('</messages>')) {
+      for (const relativeGrdpFilePath of relativeGrdpFilePaths)
+        newGrdFileContent += createPartFileEntry(relativeGrdpFilePath);
+    }
+    newGrdFileContent += grdLine;
+    if (i < grdLines.length - 1)
+      newGrdFileContent += '\n';
+  }
+  return writeFileAsync(localizationUtils.GRD_PATH, newGrdFileContent);
+}
+
+// Return true if any resources are removed
+async function removeResourcesFromGRDP(frontendStrings, IDSkeys) {
+  const keysToRemoveFromGRD = checkLocalizedStrings.getDifference(frontendStrings, IDSkeys);
+  if (keysToRemoveFromGRD.size === 0)
+    return false;
+
+  // Map grdp file path to message IDS keys to remove
+  const grdpFilePathToKeys = new Map();
+  for (const [key, messageObj] of keysToRemoveFromGRD) {
+    if (!grdpFilePathToKeys.has(messageObj.filepath))
+      grdpFilePathToKeys.set(messageObj.filepath, []);
+
+    grdpFilePathToKeys.get(messageObj.filepath).push(key);
+  }
+
+  const promises = [];
+  for (const [grdpFilePath, listOfKeys] of grdpFilePathToKeys) {
+    let newGrdpFileContent = '';
+    const grdpFileContent = await localizationUtils.parseFileContent(grdpFilePath);
+    const grdpFileLines = grdpFileContent.split('\n');
+
+    for (let i = 0; i < grdpFileLines.length; i++) {
+      if (!lineContainsIDS(grdpFileLines[i], listOfKeys)) {
+        newGrdpFileContent += grdpFileLines[i];
+        if (i < grdpFileLines.length - 1)
+          newGrdpFileContent += '\n';
+        continue;
+      }
+
+      while (!grdpFileLines[i].includes('</message>'))
+        i++;
+    }
+
+    promises.push(writeFileAsync(grdpFilePath, newGrdpFileContent));
+  }
+  await Promise.all(promises);
+  return true;
+}
+
+function lineContainsIDS(line, listOfIDS) {
+  return listOfIDS.some(ids => line.includes(ids));
+}
diff --git a/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js b/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
new file mode 100644
index 0000000..be79c23
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/localization_utils/check_localized_strings.js
@@ -0,0 +1,384 @@
+// Copyright 2019 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.
+
+/**
+ * Functions in this script parse DevTools frontend .js and module.json files,
+ * collect localizable strings, check if frontend strings are in .grd/.grdp
+ * files and report error if present.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const {promisify} = require('util');
+const writeFileAsync = promisify(fs.writeFile);
+const localizationUtils = require('./localization_utils');
+const escodegen = localizationUtils.escodegen;
+const esprimaTypes = localizationUtils.esprimaTypes;
+const esprima = localizationUtils.esprima;
+const DEVTOOLS_FRONTEND_PATH = path.resolve(__dirname, '..', '..', 'front_end');
+const extensionStringKeys = ['category', 'destination', 'title', 'title-mac'];
+
+// Format of frontendStrings
+// { IDS_md5-hash => {
+//     string: string,
+//     code: string,
+//     filepath: string,
+//     location: {
+//       start: {
+//         line: number, (1-based)
+//         column: number (0-based)
+//       },
+//       end: {
+//         line: number,
+//         column: number
+//       }
+//     },
+//     arguments: string[]
+//   }
+// }
+const frontendStrings = new Map();
+
+// Format
+// {
+//   IDS_KEY => {
+//     filepath: string,
+//     location: {
+//       start: {
+//         line: number
+//       },
+//       end: {
+//         line: number
+//       }
+//     }
+//   }
+// }
+const IDSkeys = new Map();
+
+const devtoolsFrontendPath = path.resolve(__dirname, '..', '..', 'front_end');
+
+async function parseLocalizableResourceMaps(isDebug) {
+  const devtoolsFiles = [];
+  await localizationUtils.getFilesFromDirectory(devtoolsFrontendPath, devtoolsFiles, ['.js', 'module.json']);
+
+  const promises = [parseLocalizableStrings(devtoolsFiles, isDebug), parseIDSKeys(localizationUtils.GRD_PATH, isDebug)];
+  return Promise.all(promises);
+}
+
+/**
+ * The following functions parse localizable strings (wrapped in
+ * Common.UIString, UI.formatLocalized or ls``) from devtools frontend files.
+ */
+
+async function parseLocalizableStrings(devtoolsFiles, isDebug) {
+  const promises = devtoolsFiles.map(filePath => parseLocalizableStringsFromFile(filePath));
+  await Promise.all(promises);
+  if (isDebug)
+    await writeFileAsync(path.resolve(__dirname, 'localizable_strings.json'), JSON.stringify(frontendStrings));
+  return frontendStrings;
+}
+
+async function parseLocalizableStringsFromFile(filePath) {
+  const fileContent = await localizationUtils.parseFileContent(filePath);
+  if (path.basename(filePath) === 'module.json')
+    return parseLocalizableStringFromModuleJson(fileContent, filePath);
+
+  const ast = esprima.parse(fileContent, {loc: true});
+  for (const node of ast.body)
+    parseLocalizableStringFromNode(node, filePath);
+}
+
+function parseLocalizableStringFromModuleJson(fileContent, filePath) {
+  const fileJSON = JSON.parse(fileContent);
+  if (!fileJSON.extensions)
+    return;
+
+  for (const extension of fileJSON.extensions) {
+    for (const key in extension) {
+      if (extensionStringKeys.includes(key)) {
+        addString(extension[key], extension[key], filePath);
+      } else if (key === 'device') {
+        addString(extension.device.title, extension.device.title, filePath);
+      } else if (key === 'options') {
+        for (const option of extension.options) {
+          addString(option.title, option.title, filePath);
+          if (option.text !== undefined)
+            addString(option.text, option.text, filePath);
+        }
+      }
+    }
+  }
+}
+
+function parseLocalizableStringFromNode(node, filePath) {
+  if (!node)
+    return;
+
+  if (Array.isArray(node)) {
+    for (const child of node)
+      parseLocalizableStringFromNode(child, filePath);
+
+    return;
+  }
+
+  const keys = Object.keys(node);
+  const objKeys = keys.filter(key => key !== 'loc' && typeof node[key] === 'object');
+  if (objKeys.length === 0) {
+    // base case: all values are non-objects -> node is a leaf
+    return;
+  }
+
+  const locCase = localizationUtils.getLocalizationCase(node);
+  switch (locCase) {
+    case 'Common.UIString':
+      handleCommonUIString(node, filePath);
+      break;
+    case 'UI.formatLocalized':
+      if (node.arguments !== undefined && node.arguments[1] !== undefined && node.arguments[1].elements !== undefined)
+        handleCommonUIString(node, filePath, node.arguments[1].elements);
+      break;
+    case 'Tagged Template':
+      handleTemplateLiteral(node.quasi, escodegen.generate(node), filePath);
+      break;
+    case null:
+      break;
+    default:
+      throw new Error(
+          `${filePath}${localizationUtils.getLocationMessage(node.loc)}: unexpected localization case for node: ${
+              escodegen.generate(node)}`);
+  }
+
+  for (const key of objKeys) {
+    // recursively parse all the child nodes
+    parseLocalizableStringFromNode(node[key], filePath);
+  }
+}
+
+function handleCommonUIString(node, filePath, argumentNodes) {
+  if (argumentNodes === undefined)
+    argumentNodes = node.arguments.slice(1);
+  const firstArgType = node.arguments[0].type;
+  switch (firstArgType) {
+    case esprimaTypes.LITERAL:
+      const message = node.arguments[0].value;
+      addString(message, escodegen.generate(node), filePath, node.loc, argumentNodes);
+      break;
+    case esprimaTypes.TEMP_LITERAL:
+      handleTemplateLiteral(node.arguments[0], escodegen.generate(node), filePath, argumentNodes);
+      break;
+    default:
+      break;
+  }
+}
+
+function handleTemplateLiteral(node, code, filePath, argumentNodes) {
+  if (node.expressions.length === 0) {
+    // template literal does not contain any variables, parse the value
+    addString(node.quasis[0].value.cooked, code, filePath, node.loc, argumentNodes);
+    return;
+  }
+
+  argumentNodes = node.expressions;
+  let processedMsg = '';
+  for (let i = 0; i < node.quasis.length; i++) {
+    processedMsg += node.quasis[i].value.cooked;
+    if (i < node.expressions.length) {
+      // add placeholder for variable so that
+      // the ph tag gets generated
+      processedMsg += '%s';
+    }
+  }
+  addString(processedMsg, code, filePath, node.loc, argumentNodes);
+}
+
+function addString(str, code, filePath, location, argumentNodes) {
+  const currentString = {
+    string: str,
+    code: code,
+    filepath: filePath,
+  };
+  if (location)
+    currentString.location = location;
+  if (argumentNodes && argumentNodes.length > 0)
+    currentString.arguments = argumentNodes.map(argNode => escodegen.generate(argNode));
+
+  // In the case of duplicates, to enforce that entries are added to
+  // a consistent GRDP file, we use the file path that sorts lowest as
+  // the winning entry into frontendStrings.
+  const ids = localizationUtils.getIDSKey(str);
+  if (frontendStrings.has(ids) && frontendStrings.get(ids).filepath <= filePath)
+    return;
+  frontendStrings.set(ids, currentString);
+}
+
+/**
+ * The following functions parse <message>s and their IDS keys from
+ * devtools frontend grdp files.
+ */
+
+async function parseIDSKeys(grdFilePath, isDebug) {
+  // NOTE: this function assumes that no <message> tags are present in the parent
+  const grdpFilePaths = await parseGRDFile(grdFilePath);
+  await parseGRDPFiles(grdpFilePaths);
+  if (isDebug)
+    await writeFileAsync(path.resolve(__dirname, 'IDS_Keys.json'), JSON.stringify(IDSkeys));
+  return IDSkeys;
+}
+
+async function parseGRDFile(grdFilePath) {
+  const fileContent = await localizationUtils.parseFileContent(grdFilePath);
+  const grdFileDir = path.dirname(grdFilePath);
+  const partFileRegex = /<part file="(.*?)"/g;
+
+  let match;
+  const grdpFilePaths = new Set();
+  while ((match = partFileRegex.exec(fileContent)) !== null) {
+    if (match.index === partFileRegex.lastIndex)
+      partFileRegex.lastIndex++;
+    // match[0]: full match
+    // match[1]: part file path
+    grdpFilePaths.add(path.resolve(grdFileDir, match[1]));
+  }
+  return grdpFilePaths;
+}
+
+function parseGRDPFiles(grdpFilePaths) {
+  const promises = Array.from(grdpFilePaths, grdpFilePath => parseGRDPFile(grdpFilePath));
+  return Promise.all(promises);
+}
+
+function trimGrdpPlaceholder(placeholder) {
+  const exampleRegex = new RegExp('<ex>.*?<\/ex>', 'gms');
+  // $1s<ex>my example</ex> -> $1s
+  return placeholder.replace(exampleRegex, '').trim();
+}
+
+function convertToFrontendPlaceholders(message) {
+  // <ph name="phname">$1s<ex>my example</ex></ph> and <ph name="phname2">$2.3f</ph>
+  // match[0]: <ph name="phname1">$1s</ph>
+  // match[1]: $1s<ex>my example</ex>
+  let placeholderRegex = new RegExp('<ph[^>]*>(.*?)<\/ph>', 'gms');
+  let match;
+  while ((match = placeholderRegex.exec(message)) !== null) {
+    const placeholder = match[0];
+    const placeholderValue = trimGrdpPlaceholder(match[1]);
+    const newPlaceholderValue = placeholderValue.replace(/\$[1-9]/, '%');
+    message =
+        message.substring(0, match.index) + newPlaceholderValue + message.substring(match.index + placeholder.length);
+    // Modified the message, so search from the beginning of the string again.
+    placeholderRegex.lastIndex = 0;
+  }
+  return message;
+}
+
+function trimGrdpMessage(message) {
+  // '    Message text \n  ' trims to ' Message text '.
+  const fixedLeadingWhitespace = 4;  // GRDP encoding uses 4 leading spaces.
+  const trimmedMessage = message.substring(4);
+  return trimmedMessage.substring(0, trimmedMessage.lastIndexOf('\n'));
+}
+
+async function parseGRDPFile(filePath) {
+  const fileContent = await localizationUtils.parseFileContent(filePath);
+
+  function lineNumberOfIndex(str, index) {
+    const stringToIndex = str.substr(0, index);
+    return stringToIndex.split('\n').length;
+  }
+
+  // Example:
+  //  <message name="IDS_*" desc="*">
+  //      Message text here with optional placeholders <ph name="phname">$1s</ph>
+  //  </message>
+  // match[0]: the entire '<message>...</message>' block.
+  // match[1]: '     Message text here with optional placeholders <ph name="phname">$1s</ph>\n  '
+  const messageRegex = new RegExp('<message[^>]*>\s*\n(.*?)<\/message>', 'gms');
+  let match;
+  while ((match = messageRegex.exec(fileContent)) !== null) {
+    const line = lineNumberOfIndex(fileContent, match.index);
+
+    let message = match[1];
+    message = trimGrdpMessage(message);
+    message = convertToFrontendPlaceholders(message);
+    message = localizationUtils.sanitizeStringIntoFrontendFormat(message);
+
+    const ids = localizationUtils.getIDSKey(message);
+    IDSkeys.set(ids, {filepath: filePath, location: {start: {line}, end: {line}}});
+  }
+}
+
+/**
+ * The following functions compare frontend localizable strings
+ * with grdp <message>s and report error of resources to add or
+ * remove.
+ */
+async function getAndReportResourcesToAdd(frontendStrings, IDSkeys) {
+  const keysToAddToGRD = getDifference(IDSkeys, frontendStrings);
+  if (keysToAddToGRD.size === 0)
+    return;
+
+  let errorStr = 'The following frontend string(s) need to be added to GRD/GRDP file(s).\n';
+  errorStr += 'Please refer to auto-generated message(s) below and modify as needed.\n\n';
+
+  const frontendDirs = await localizationUtils.getChildDirectoriesFromDirectory(DEVTOOLS_FRONTEND_PATH);
+  const fileToGRDPMap = new Map();
+
+  // Example error message:
+  // third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js Line 973: ls`(disk cache)`
+  // Add a new message tag for this string to third_party\blink\renderer\devtools\front_end\network\network_strings.grdp
+  // <message name="IDS_DEVTOOLS_ad86890fb40822a3b12627efaca4ecd7" desc="Fill in the description.">
+  //   (disk cache)
+  // </message>
+  for (const [key, stringObj] of keysToAddToGRD) {
+    let relativeGRDPFilePath = '';
+    if (fileToGRDPMap.has(stringObj.filepath)) {
+      relativeGRDPFilePath = fileToGRDPMap.get(stringObj.filepath);
+    } else {
+      relativeGRDPFilePath = localizationUtils.getRelativeFilePathFromSrc(
+          localizationUtils.getGRDPFilePath(stringObj.filepath, frontendDirs));
+      fileToGRDPMap.set(stringObj.filepath, relativeGRDPFilePath);
+    }
+    errorStr += `${localizationUtils.getRelativeFilePathFromSrc(stringObj.filepath)}${
+        localizationUtils.getLocationMessage(stringObj.location)}: ${stringObj.code}\n`;
+    errorStr += `Add a new message tag for this string to ${
+        localizationUtils.getRelativeFilePathFromSrc(
+            localizationUtils.getGRDPFilePath(stringObj.filepath, frontendDirs))}\n\n`;
+    errorStr += localizationUtils.createGrdpMessage(key, stringObj);
+  }
+  return errorStr;
+}
+
+function getAndReportResourcesToRemove(frontendStrings, IDSkeys) {
+  const keysToRemoveFromGRD = getDifference(frontendStrings, IDSkeys);
+  if (keysToRemoveFromGRD.size === 0)
+    return;
+
+  let errorStr =
+      '\nThe message(s) associated with the following IDS key(s) should be removed from its GRD/GRDP file(s):\n';
+  // Example error message:
+  // third_party/blink/renderer/devtools/front_end/help/help_strings.grdp Line 18: IDS_DEVTOOLS_7d0ee6fed10d3d4e5c9ee496729ab519
+  for (const [key, keyObj] of keysToRemoveFromGRD) {
+    errorStr += `${localizationUtils.getRelativeFilePathFromSrc(keyObj.filepath)}${
+        localizationUtils.getLocationMessage(keyObj.location)}: ${key}\n\n`;
+  }
+  return errorStr;
+}
+
+/**
+ * Output a Map containing entries that are in @comparison but not @reference in sorted order.
+ */
+function getDifference(reference, comparison) {
+  const difference = [];
+  for (const [key, value] of comparison) {
+    if (!reference.has(key))
+      difference.push([key, value]);
+  }
+  return new Map(difference.sort());
+}
+
+module.exports = {
+  parseLocalizableResourceMaps,
+  getAndReportResourcesToAdd,
+  getAndReportResourcesToRemove,
+  getDifference
+};
diff --git a/third_party/blink/renderer/devtools/scripts/localization_utils/localization_utils.js b/third_party/blink/renderer/devtools/scripts/localization_utils/localization_utils.js
new file mode 100644
index 0000000..11119c5
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/localization_utils/localization_utils.js
@@ -0,0 +1,269 @@
+// Copyright 2019 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.
+
+const fs = require('fs');
+const md5 = require('./md5');
+const {promisify} = require('util');
+const path = require('path');
+const readFileAsync = promisify(fs.readFile);
+const readDirAsync = promisify(fs.readdir);
+const statAsync = promisify(fs.stat);
+
+const esprimaTypes = {
+  BI_EXPR: 'BinaryExpression',
+  CALL_EXPR: 'CallExpression',
+  COND_EXPR: 'ConditionalExpression',
+  IDENTIFIER: 'Identifier',
+  LITERAL: 'Literal',
+  MEMBER_EXPR: 'MemberExpression',
+  TAGGED_TEMP_EXPR: 'TaggedTemplateExpression',
+  TEMP_LITERAL: 'TemplateLiteral'
+};
+
+const excludeFiles = ['lighthouse-dt-bundle.js', 'Tests.js'];
+const excludeDirs = ['test_runner', 'Images', 'langpacks', 'node_modules'];
+const cppSpecialCharactersMap = {
+  '"': '\\"',
+  '\\': '\\\\',
+  '\n': '\\n'
+};
+const IDSPrefix = 'IDS_DEVTOOLS_';
+
+const THIRD_PARTY_PATH = path.resolve(__dirname, '..', '..', '..', '..', '..');
+const SRC_PATH = path.resolve(THIRD_PARTY_PATH, '..');
+const GRD_PATH = path.resolve(__dirname, '..', '..', 'front_end', 'langpacks', 'devtools_ui_strings.grd');
+const REPO_NODE_MODULES_PATH = path.resolve(THIRD_PARTY_PATH, 'node', 'node_modules');
+const escodegen = require(path.resolve(REPO_NODE_MODULES_PATH, 'escodegen'));
+const esprima = require(path.resolve(REPO_NODE_MODULES_PATH, 'esprima'));
+
+function getRelativeFilePathFromSrc(filePath) {
+  return path.relative(SRC_PATH, filePath);
+}
+
+function shouldParseDirectory(directoryName) {
+  return !excludeDirs.some(dir => directoryName.includes(dir));
+}
+
+/**
+ * @filepath can be partial path or full path, as long as it contains the file name.
+ */
+function shouldParseFile(filepath) {
+  return !excludeFiles.includes(path.basename(filepath));
+}
+
+async function parseFileContent(filePath) {
+  const fileContent = await readFileAsync(filePath);
+  return fileContent.toString();
+}
+
+function isNodeCallOnObject(node, objectName, propertyName) {
+  return node !== undefined && node.type === esprimaTypes.CALL_EXPR &&
+      verifyCallExpressionCallee(node.callee, objectName, propertyName);
+}
+
+function isNodeCommonUIStringCall(node) {
+  return isNodeCallOnObject(node, 'Common', 'UIString');
+}
+
+function isNodeUIformatLocalized(node) {
+  return isNodeCallOnObject(node, 'UI', 'formatLocalized');
+}
+
+function isNodelsTaggedTemplateExpression(node) {
+  return node !== undefined && node.type === esprimaTypes.TAGGED_TEMP_EXPR && verifyIdentifier(node.tag, 'ls') &&
+      node.quasi !== undefined && node.quasi.type !== undefined && node.quasi.type === esprimaTypes.TEMP_LITERAL;
+}
+
+/**
+ * Verify callee of objectName.propertyName(), e.g. Common.UIString().
+ */
+function verifyCallExpressionCallee(callee, objectName, propertyName) {
+  return callee !== undefined && callee.type === esprimaTypes.MEMBER_EXPR && callee.computed === false &&
+      verifyIdentifier(callee.object, objectName) && verifyIdentifier(callee.property, propertyName);
+}
+
+function verifyIdentifier(node, name) {
+  return node !== undefined && node.type === esprimaTypes.IDENTIFIER && node.name === name;
+}
+
+function getLocalizationCase(node) {
+  if (isNodeCommonUIStringCall(node))
+    return 'Common.UIString';
+  else if (isNodelsTaggedTemplateExpression(node))
+    return 'Tagged Template';
+  else if (isNodeUIformatLocalized(node))
+    return 'UI.formatLocalized';
+  else
+    return null;
+}
+
+function isLocalizationCall(node) {
+  return isNodeCommonUIStringCall(node) || isNodelsTaggedTemplateExpression(node) || isNodeUIformatLocalized(node);
+}
+
+/**
+ * Verify if callee is functionName() or object.functionName().
+ */
+function verifyFunctionCallee(callee, functionName) {
+  return callee !== undefined &&
+      ((callee.type === esprimaTypes.IDENTIFIER && callee.name === functionName) ||
+       (callee.type === esprimaTypes.MEMBER_EXPR && verifyIdentifier(callee.property, functionName)));
+}
+
+function getLocationMessage(location) {
+  if (location !== undefined && location.start !== undefined && location.end !== undefined &&
+      location.start.line !== undefined && location.end.line !== undefined) {
+    const startLine = location.start.line;
+    const endLine = location.end.line;
+    if (startLine === endLine)
+      return ` Line ${startLine}`;
+    else
+      return ` Line ${location.start.line}-${location.end.line}`;
+  }
+  return '';
+}
+
+function sanitizeStringIntoGRDFormat(str) {
+  return str.replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/"/g, '&quot;')
+      .replace(/'/g, '&apos;')
+}
+
+function sanitizeStringIntoFrontendFormat(str) {
+  return str.replace(/&apos;/g, '\'')
+      .replace(/&quot;/g, '"')
+      .replace(/&gt;/g, '>')
+      .replace(/&lt;/g, '<')
+      .replace(/&amp;/g, '&');
+}
+
+function sanitizeString(str, specialCharactersMap) {
+  let sanitizedStr = '';
+  for (let i = 0; i < str.length; i++) {
+    let currChar = str.charAt(i);
+    if (specialCharactersMap[currChar] !== undefined)
+      currChar = specialCharactersMap[currChar];
+
+    sanitizedStr += currChar;
+  }
+  return sanitizedStr;
+}
+
+function sanitizeStringIntoCppFormat(str) {
+  return sanitizeString(str, cppSpecialCharactersMap);
+}
+
+async function getFilesFromItem(itemPath, filePaths, acceptedFileEndings) {
+  const stat = await statAsync(itemPath);
+  if (stat.isDirectory() && shouldParseDirectory(itemPath))
+    return await getFilesFromDirectory(itemPath, filePaths, acceptedFileEndings);
+
+  const hasAcceptedEnding =
+      acceptedFileEndings.some(acceptedEnding => itemPath.toLowerCase().endsWith(acceptedEnding.toLowerCase()));
+  if (hasAcceptedEnding && shouldParseFile(itemPath))
+    filePaths.push(itemPath);
+}
+
+async function getFilesFromDirectory(directoryPath, filePaths, acceptedFileEndings) {
+  const itemNames = await readDirAsync(directoryPath);
+  const promises = [];
+  for (const itemName of itemNames) {
+    const itemPath = path.resolve(directoryPath, itemName);
+    promises.push(getFilesFromItem(itemPath, filePaths, acceptedFileEndings));
+  }
+  return Promise.all(promises);
+}
+
+async function getChildDirectoriesFromDirectory(directoryPath) {
+  const dirPaths = [];
+  const itemNames = await readDirAsync(directoryPath);
+  for (const itemName of itemNames) {
+    const itemPath = path.resolve(directoryPath, itemName);
+    const stat = await statAsync(itemPath);
+    if (stat.isDirectory() && shouldParseDirectory(itemName))
+      dirPaths.push(itemPath);
+  }
+  return dirPaths;
+}
+
+/**
+ * Get the parent grdp file path for the input frontend file path.
+ * NOTE: Naming convention of a grdp file is the name of the child directory under
+ * devtools/front_end plus _strings.grdp
+ */
+function getGRDPFilePath(frontendFilepath, frontendDirs) {
+  const frontendDirsLowerCase = frontendDirs.map(dir => dir.toLowerCase());
+  const dirpath = path.dirname(frontendFilepath);
+  if (frontendDirsLowerCase.includes(dirpath.toLowerCase()))
+    return path.resolve(dirpath, `${path.basename(dirpath)}_strings.grdp`);
+}
+
+function modifyStringIntoGRDFormat(str, args) {
+  let sanitizedStr = sanitizeStringIntoGRDFormat(str);
+
+  const phRegex = /%d|%f|%s|%.[0-9]f/gm;
+  if (!str.match(phRegex))
+    return sanitizedStr;
+
+  let phNames;
+  if (args !== undefined)
+    phNames = args.map(arg => arg.replace(/[^a-zA-Z]/gm, '_').toUpperCase());
+  else
+    phNames = ['PH1', 'PH2', 'PH3', 'PH4', 'PH5', 'PH6', 'PH7', 'PH8', 'PH9'];
+
+  // It replaces all placeholders with <ph> tags.
+  let match;
+  let count = 1;
+  while ((match = phRegex.exec(sanitizedStr)) !== null) {
+    // This is necessary to avoid infinite loops with zero-width matches
+    if (match.index === phRegex.lastIndex)
+      phRegex.lastIndex++;
+
+    // match[0]: the placeholder (e.g. %d, %s, %.2f, etc.)
+    const ph = match[0];
+    // e.g. $1s, $1d, $1.2f
+    const newPh = `$${count}` + ph.substr(1);
+
+    const i = sanitizedStr.indexOf(ph);
+    sanitizedStr = `${sanitizedStr.substring(0, i)}<ph name="${phNames[count - 1]}">${newPh}</ph>${
+        sanitizedStr.substring(i + ph.length)}`;
+    count++;
+  }
+  return sanitizedStr;
+}
+
+function createGrdpMessage(ids, stringObj) {
+  let message = `  <message name="${ids}" desc="">\n`;
+  message += `    ${modifyStringIntoGRDFormat(stringObj.string, stringObj.arguments)}\n`;
+  message += '  </message>\n';
+  return message;
+}
+
+function getIDSKey(str) {
+  return `${IDSPrefix}${md5(str)}`
+}
+
+module.exports = {
+  createGrdpMessage,
+  escodegen,
+  esprima,
+  esprimaTypes,
+  getChildDirectoriesFromDirectory,
+  getFilesFromDirectory,
+  getGRDPFilePath,
+  getIDSKey,
+  getLocalizationCase,
+  getLocationMessage,
+  getRelativeFilePathFromSrc,
+  GRD_PATH,
+  IDSPrefix,
+  isLocalizationCall,
+  modifyStringIntoGRDFormat,
+  parseFileContent,
+  sanitizeStringIntoCppFormat,
+  sanitizeStringIntoFrontendFormat,
+  verifyFunctionCallee
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/scripts/localization_utils/md5.js b/third_party/blink/renderer/devtools/scripts/localization_utils/md5.js
new file mode 100644
index 0000000..3f59f37
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/localization_utils/md5.js
@@ -0,0 +1,157 @@
+// Copyright 2019 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.
+
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+function md5(s) {
+  return binl2hex(core_md5(str2binl(s), s.length * 8));
+}
+
+function core_md5(x, len) {
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a = 1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d = 271733878;
+
+  for (var i = 0; i < x.length; i += 16) {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
+    d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
+    b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
+    d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
+    c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
+    d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
+    d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+    a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
+    d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
+    c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
+    b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
+    d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
+    c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
+    d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
+    c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
+    a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
+    d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
+    c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
+    b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
+    d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
+    b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
+    d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
+    c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
+    d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
+    a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
+    d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
+    b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
+    d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
+    c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
+    d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
+    d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
+    a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
+    d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
+    b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+}
+
+function md5_cmn(q, a, b, x, s, t) {
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
+}
+
+function md5_ff(a, b, c, d, x, s, t) {
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+
+function md5_gg(a, b, c, d, x, s, t) {
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+
+function md5_hh(a, b, c, d, x, s, t) {
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+
+function md5_ii(a, b, c, d, x, s, t) {
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+function safe_add(x, y) {
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+function bit_rol(num, cnt) {
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+function str2binl(str) {
+  var bin = Array();
+  var mask = (1 << 8) - 1;
+  for (var i = 0; i < str.length * 8; i += 8)
+    bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << (i % 32);
+  return bin;
+}
+
+function binl2hex(binarray) {
+  var hex_tab = '0123456789abcdef';
+  var str = '';
+  for (var i = 0; i < binarray.length * 4; i++) {
+    str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) +
+        hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF);
+  }
+  return str;
+}
+
+module.exports = md5;
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc
index 111b6a9..1181fe0f 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_loader.cc
@@ -75,7 +75,7 @@
 
   ExecutionContext* exection_context =
       queue_item_->Request()->GetExecutionContext();
-  // The execution context was town down. The loader will eventually get a
+  // The execution context was torn down. The loader will eventually get a
   // Cancel() call.
   if (!exection_context)
     return;
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index b13eb0b..a62f61a 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -77,6 +77,9 @@
   return XRSession::kModeInline;
 }
 
+// When updating this list, also update XRRuntimeManager's
+// AreArFeaturesEnabled() until https://crbug.com/966647 is fixed.
+// TODO(https://crbug.com/966647) remove the above comment when fixed.
 bool AreArRuntimeFeaturesEnabled(const FeatureContext* context) {
   return RuntimeEnabledFeatures::WebXRHitTestEnabled(context) ||
          RuntimeEnabledFeatures::WebXRPlaneDetectionEnabled(context);
diff --git a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
index f370641..7cbf81a 100644
--- a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
+++ b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
@@ -53,8 +53,11 @@
   filter_operations_.Append(cc::FilterOperation::CreateOpacityFilter(amount));
 }
 
-void CompositorFilterOperations::AppendBlurFilter(float amount) {
-  filter_operations_.Append(cc::FilterOperation::CreateBlurFilter(amount));
+void CompositorFilterOperations::AppendBlurFilter(
+    float amount,
+    SkBlurImageFilter::TileMode tile_mode) {
+  filter_operations_.Append(
+      cc::FilterOperation::CreateBlurFilter(amount, tile_mode));
 }
 
 void CompositorFilterOperations::AppendDropShadowFilter(IntPoint offset,
diff --git a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
index bfb58a14..b6755ec 100644
--- a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
+++ b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
@@ -30,7 +30,9 @@
   void AppendBrightnessFilter(float amount);
   void AppendContrastFilter(float amount);
   void AppendOpacityFilter(float amount);
-  void AppendBlurFilter(float amount);
+  void AppendBlurFilter(float amount,
+                        SkBlurImageFilter::TileMode tile_mode =
+                            SkBlurImageFilter::kClampToBlack_TileMode);
   void AppendDropShadowFilter(IntPoint offset, float std_deviation, Color);
   void AppendColorMatrixFilter(const cc::FilterOperation::Matrix&);
   void AppendZoomFilter(float amount, int inset);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index d8717c7..445778a 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -375,7 +375,7 @@
     },
     {
       name: "CSSPictureInPicture",
-      status: "stable",
+      status: "experimental",
       depends_on: ["PictureInPictureAPI"],
     },
     {
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
index 9cc40d8..d417982 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -392,7 +392,7 @@
 crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-non-composited-scroll.html [ Failure ]
 
 # Backdrop filter
-crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Failure ]
+crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Timeout Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-browser-zoom.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-border-radius.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-clip-rect-zoom.html [ Failure ]
@@ -400,6 +400,7 @@
 crbug.com/923429 css3/filters/backdrop-filter-rendering.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-rendering-no-background.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-transform.html [ Failure ]
+crbug.com/923429 css3/filters/backdrop-filter-edge-pixels.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 00022d5..47f62ea 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2771,7 +2771,6 @@
 # needs implementation of test_driver_internal.action_sequence
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/elementTiming.html [ Timeout ]
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/multiDevice.html [ Failure Timeout ]
-crbug.com/893480 external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html [ Failure Timeout ]
 
 # Hit a DCHECK
 crbug.com/918664 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-table-1a.html [ Failure Pass ]
@@ -2782,6 +2781,16 @@
 crbug.com/965409 external/wpt/css/css-font-loading/fontface-descriptor-updates.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.12 ] external/wpt/preload/download-resources.html [ Failure Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/preload/download-resources.html [ Failure Timeout ]
+crbug.com/626703 [ Retina ] external/wpt/preload/download-resources.html [ Failure Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/preload/download-resources.html [ Failure Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/preload/onerror-event.html [ Failure Timeout ]
+crbug.com/626703 [ Win ] external/wpt/preload/onerror-event.html [ Failure Timeout ]
+crbug.com/626703 [ Mac10.12 ] external/wpt/preload/onerror-event.html [ Failure Timeout ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/preload/onerror-event.html [ Failure Timeout ]
+crbug.com/626703 [ Retina ] external/wpt/preload/onerror-event.html [ Timeout ]
+crbug.com/626703 [ Win7 ] external/wpt/service-workers/service-worker/fetch-event-async-respond-with.https.html [ Timeout ]
 crbug.com/626703 virtual/layout_ng_experimental/external/wpt/css/css-contain/contain-layout-button-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-contain/contain-layout-button-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-001-manual.html [ Skip ]
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-basic-blur-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-basic-blur-expected.png
index 71307d03..d3be83b 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-basic-blur-expected.png
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-basic-blur-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..7e810e0
--- /dev/null
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels.html
new file mode 100644
index 0000000..89b1114
--- /dev/null
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-pixels.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
+
+<div class="red_square"></div>
+<div class="filter"></div>
+
+<style>
+div {
+  width: 100px;
+  position: absolute;
+}
+body { overflow: hidden; margin: 0;}
+.red_square {
+  height: 100px;
+  left:30px;
+  top: 20px;
+  background: red;
+  transform: rotate(30deg);
+}
+.filter {
+  height: 200px;
+  background: transparent;
+  backdrop-filter: blur(20px);
+}
+</style>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index afde87d..00f086fb 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -69189,6 +69189,18 @@
      {}
     ]
    ],
+   "css/css-text/text-transform/text-transform-upperlower-044.html": [
+    [
+     "css/css-text/text-transform/text-transform-upperlower-044.html",
+     [
+      [
+       "/css/css-text/text-transform/reference/text-transform-upperlower-044-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/text-transform/text-transform-upperlower-101.html": [
     [
      "css/css-text/text-transform/text-transform-upperlower-101.html",
@@ -94427,6 +94439,18 @@
      {}
     ]
    ],
+   "css/css-writing-modes/percentage-padding-in-shrink-to-fit.html": [
+    [
+     "css/css-writing-modes/percentage-padding-in-shrink-to-fit.html",
+     [
+      [
+       "/css/reference/nothing.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-writing-modes/relpos-inline-overflowing-block-vrl.html": [
     [
      "css/css-writing-modes/relpos-inline-overflowing-block-vrl.html",
@@ -112067,18 +112091,6 @@
      {}
     ]
    ],
-   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html": [
-    [
-     "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html",
-     [
-      [
-       "/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit.html": [
     [
      "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit.html",
@@ -133681,6 +133693,11 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-position-computed-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-position-x-computed-expected.txt": [
     [
      {}
@@ -153046,6 +153063,11 @@
      {}
     ]
    ],
+   "css/css-text/text-transform/reference/text-transform-upperlower-044-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-text/text-transform/reference/text-transform-upperlower-101-ref.html": [
     [
      {}
@@ -178856,11 +178878,6 @@
      {}
     ]
    ],
-   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue-ref.html": [
-    [
-     {}
-    ]
-   ],
    "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit-ref.html": [
     [
      {}
@@ -178871,11 +178888,26 @@
      {}
     ]
    ],
+   "html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/media-elements/track/track-element/track-helpers.js": [
     [
      {}
     ]
    ],
+   "html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list-expected.txt": [
     [
      {}
@@ -182506,6 +182538,11 @@
      {}
     ]
    ],
+   "imagebitmap-renderingcontext/context-creation-with-alpha-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "images/META.yml": [
     [
      {}
@@ -183051,11 +183088,6 @@
      {}
     ]
    ],
-   "infrastructure/metadata/infrastructure/testdriver/actions/actionsWithKeyPressed.html.ini": [
-    [
-     {}
-    ]
-   ],
    "infrastructure/metadata/infrastructure/testdriver/actions/elementPosition.html.ini": [
     [
      {}
@@ -194511,6 +194543,11 @@
      {}
     ]
    ],
+   "service-workers/service-worker/resources/update-during-installation-worker.py": [
+    [
+     {}
+    ]
+   ],
    "service-workers/service-worker/resources/update-fetch-worker.py": [
     [
      {}
@@ -203551,6 +203588,11 @@
      {}
     ]
    ],
+   "wake-lock/wakelock-screen-type-on-worker.https.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "wake-lock/wakelock-state-is-global.https-expected.txt": [
     [
      {}
@@ -203561,6 +203603,11 @@
      {}
     ]
    ],
+   "wake-lock/wakelock-type.https.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "wasm/META.yml": [
     [
      {}
@@ -203906,11 +203953,6 @@
      {}
     ]
    ],
-   "web-animations/interfaces/Animation/constructor-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "web-animations/interfaces/Animation/finished-expected.txt": [
     [
      {}
@@ -204036,6 +204078,11 @@
      {}
     ]
    ],
+   "web-animations/timing-model/animations/setting-the-start-time-of-an-animation-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "web-animations/timing-model/animations/setting-the-target-effect-of-an-animation-expected.txt": [
     [
      {}
@@ -231578,6 +231625,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-attachment-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-attachment-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-attachment-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-attachment-invalid.html",
@@ -231590,6 +231643,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-clip-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-clip-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-clip-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-clip-invalid.html",
@@ -231602,6 +231661,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-color-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-color-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-color-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-color-invalid.html",
@@ -231614,6 +231679,18 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-computed.html",
+     {}
+    ]
+   ],
+   "css/css-backgrounds/parsing/background-image-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-image-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-image-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-image-invalid.html",
@@ -231632,6 +231709,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-origin-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-origin-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-origin-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-origin-invalid.html",
@@ -231644,6 +231727,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-position-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-position-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-position-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-position-invalid.html",
@@ -231692,6 +231781,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-repeat-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-repeat-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-repeat-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-repeat-invalid.html",
@@ -231704,6 +231799,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/parsing/background-size-computed.html": [
+    [
+     "css/css-backgrounds/parsing/background-size-computed.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-size-invalid.html": [
     [
      "css/css-backgrounds/parsing/background-size-invalid.html",
@@ -235616,6 +235717,60 @@
      {}
     ]
    ],
+   "css/css-logical/parsing/border-block-color-computed.html": [
+    [
+     "css/css-logical/parsing/border-block-color-computed.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-color-invalid.html": [
+    [
+     "css/css-logical/parsing/border-block-color-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-color-valid.html": [
+    [
+     "css/css-logical/parsing/border-block-color-valid.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-style-computed.html": [
+    [
+     "css/css-logical/parsing/border-block-style-computed.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-style-invalid.html": [
+    [
+     "css/css-logical/parsing/border-block-style-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-style-valid.html": [
+    [
+     "css/css-logical/parsing/border-block-style-valid.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-width-computed.html": [
+    [
+     "css/css-logical/parsing/border-block-width-computed.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-width-invalid.html": [
+    [
+     "css/css-logical/parsing/border-block-width-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-logical/parsing/border-block-width-valid.html": [
+    [
+     "css/css-logical/parsing/border-block-width-valid.html",
+     {}
+    ]
+   ],
    "css/css-logical/parsing/inline-size-invalid.html": [
     [
      "css/css-logical/parsing/inline-size-invalid.html",
@@ -269204,6 +269359,12 @@
      {}
     ]
    ],
+   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html": [
+    [
+     "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html",
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange.html": [
     [
      "html/semantics/embedded-content/media-elements/track/track-element/track-cues-cuechange.html",
@@ -269400,6 +269561,12 @@
      {}
     ]
    ],
+   "html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html": [
+    [
+     "html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html",
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list.html": [
     [
      "html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list.html",
@@ -278065,18 +278232,6 @@
      {}
     ]
    ],
-   "imagebitmap-renderingcontext/context-creation-offscreen-with-alpha.html": [
-    [
-     "imagebitmap-renderingcontext/context-creation-offscreen-with-alpha.html",
-     {}
-    ]
-   ],
-   "imagebitmap-renderingcontext/context-creation-offscreen.html": [
-    [
-     "imagebitmap-renderingcontext/context-creation-offscreen.html",
-     {}
-    ]
-   ],
    "imagebitmap-renderingcontext/context-creation-with-alpha.html": [
     [
      "imagebitmap-renderingcontext/context-creation-with-alpha.html",
@@ -278095,30 +278250,6 @@
      {}
     ]
    ],
-   "imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub.html": [
-    [
-     "imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub.html",
-     {}
-    ]
-   ],
-   "imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html": [
-    [
-     "imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html",
-     {}
-    ]
-   ],
-   "imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen.html": [
-    [
-     "imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen.html",
-     {}
-    ]
-   ],
-   "imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen.html": [
-    [
-     "imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen.html",
-     {}
-    ]
-   ],
    "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html": [
     [
      "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html",
@@ -278612,14 +278743,6 @@
      {}
     ]
    ],
-   "infrastructure/testdriver/actions/actionsWithKeyPressed.html": [
-    [
-     "infrastructure/testdriver/actions/actionsWithKeyPressed.html",
-     {
-      "testdriver": true
-     }
-    ]
-   ],
    "infrastructure/testdriver/actions/elementPosition.html": [
     [
      "infrastructure/testdriver/actions/elementPosition.html",
@@ -295370,6 +295493,12 @@
      {}
     ]
    ],
+   "portals/portal-non-http-navigation.html": [
+    [
+     "portals/portal-non-http-navigation.html",
+     {}
+    ]
+   ],
    "portals/portal-onload-event.html": [
     [
      "portals/portal-onload-event.html",
@@ -296015,6 +296144,12 @@
      {}
     ]
    ],
+   "referrer-policy/generic/iframe-src-change.html": [
+    [
+     "referrer-policy/generic/iframe-src-change.html",
+     {}
+    ]
+   ],
    "referrer-policy/generic/link-rel-prefetch.html": [
     [
      "referrer-policy/generic/link-rel-prefetch.html",
@@ -317669,16 +317804,41 @@
      {}
     ]
    ],
-   "wake-lock/wakelock-insecure-context.html": [
+   "wake-lock/wakelock-insecure-context.any.js": [
     [
-     "wake-lock/wakelock-insecure-context.html",
-     {}
+     "wake-lock/wakelock-insecure-context.any.html",
+     {
+      "script_metadata": [
+       [
+        "title",
+        "Wake Lock API is not exposed in an insecure context"
+       ]
+      ]
+     }
+    ],
+    [
+     "wake-lock/wakelock-insecure-context.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "title",
+        "Wake Lock API is not exposed in an insecure context"
+       ]
+      ]
+     }
     ]
    ],
-   "wake-lock/wakelock-state-is-global.https.html": [
+   "wake-lock/wakelock-screen-type-on-worker.https.worker.js": [
     [
-     "wake-lock/wakelock-state-is-global.https.html",
-     {}
+     "wake-lock/wakelock-screen-type-on-worker.https.worker.html",
+     {
+      "script_metadata": [
+       [
+        "title",
+        "Screen wake lock should not be allowed in dedicated worker"
+       ]
+      ]
+     }
     ]
    ],
    "wake-lock/wakelock-supported-by-feature-policy.html": [
@@ -317687,10 +317847,28 @@
      {}
     ]
    ],
-   "wake-lock/wakelock-type.https.html": [
+   "wake-lock/wakelock-type.https.any.js": [
     [
-     "wake-lock/wakelock-type.https.html",
-     {}
+     "wake-lock/wakelock-type.https.any.html",
+     {
+      "script_metadata": [
+       [
+        "title",
+        "WakeLock.request() with invalid type"
+       ]
+      ]
+     }
+    ],
+    [
+     "wake-lock/wakelock-type.https.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "title",
+        "WakeLock.request() with invalid type"
+       ]
+      ]
+     }
     ]
    ],
    "wasm/create_multiple_memory.worker.js": [
@@ -349483,7 +349661,7 @@
    "testharness"
   ],
   "bluetooth/characteristic/writeValue/buffer-is-detached.https.html": [
-   "762735c320b1a7f626b448933e2fd0a0dac4793d",
+   "b8a1abfa5afe3d514d6884b58268dc47cfc15f5e",
    "testharness"
   ],
   "bluetooth/characteristic/writeValue/characteristic-is-removed.https.html": [
@@ -349511,7 +349689,7 @@
    "testharness"
   ],
   "bluetooth/descriptor/writeValue/buffer-is-detached.https.html": [
-   "2402d89440e6f011e9eab37519faefb0643d29bf",
+   "2ab990de61c5603976d3dc915d078a59f11d30a2",
    "testharness"
   ],
   "bluetooth/descriptor/writeValue/gen-service-is-removed.https.html": [
@@ -374390,6 +374568,10 @@
    "5ed794153083a7e06eb2e98b7ebdb5bec2676d28",
    "visual"
   ],
+  "css/css-backgrounds/parsing/background-attachment-computed.html": [
+   "ceb76c2cdae213b5dad7e1f770590e0b4b054f12",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-attachment-invalid.html": [
    "30757f3a4d02a9b1d1fc82c19255804ee2b20ae9",
    "testharness"
@@ -374398,6 +374580,10 @@
    "14d72c606ba52e85d296a5404c2546486765cd42",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-clip-computed.html": [
+   "9e8b7631bbb37c20fe7bdf5b43206a1e9aa20ce7",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-clip-invalid.html": [
    "96831e06fc9cce1c9c45e4d44c83f04d2350d0ab",
    "testharness"
@@ -374406,14 +374592,26 @@
    "e262a788bd0296deb0429fd4fc4257390776680a",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-color-computed.html": [
+   "7fbeac81a9f328be670c81be4b645e0e37461e91",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-color-invalid.html": [
-   "b94875402d89839fdb717883f11eff7f880036bf",
+   "bf315fdb4e49038dae3eb16d3df7308840b51ca3",
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-color-valid.html": [
    "3859b932c82287c0e103f1a7aebb1ed190b735a0",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-computed.html": [
+   "cb7d10998e0f46c2553fe161cb215179c4bab4c7",
+   "testharness"
+  ],
+  "css/css-backgrounds/parsing/background-image-computed.html": [
+   "af14f6cf6e8e23de029f7079fe653144218b0ecf",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-image-invalid.html": [
    "eaba64a715ce71a9f828569a24df51f1476664dd",
    "testharness"
@@ -374426,6 +374624,10 @@
    "4ea58da801961ba9c8a315ebb0b1de846e16f889",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-origin-computed.html": [
+   "4e5280135d8d2d5bf3ea73cd92505f78c7e09f52",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-origin-invalid.html": [
    "5da00dabe33473254830990c43375ac349eea330",
    "testharness"
@@ -374434,8 +374636,16 @@
    "7e3b0fffcad133948567c5a31b9dcb232891e610",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-position-computed-expected.txt": [
+   "b49d3c27b9c4354dd2725115560d41aa9e6108e6",
+   "support"
+  ],
+  "css/css-backgrounds/parsing/background-position-computed.html": [
+   "c5e30745f476027170420039873f1911908676df",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-position-invalid.html": [
-   "bb974afd9f41d8ca4f0ac82db10e57e4bf6d74b7",
+   "966db4c31a9b1b9d55afbce41ec31bbb98dfc69f",
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-position-valid.html": [
@@ -374443,11 +374653,11 @@
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-position-x-computed-expected.txt": [
-   "777dc05f71398e2ff86c17fbd43010519f07e4ca",
+   "2f99244a7c09ca63b8ee31c6c08ee5eeb364ad6a",
    "support"
   ],
   "css/css-backgrounds/parsing/background-position-x-computed.html": [
-   "6e997c95ae0b632713bed16a84d07c3ac6379d1b",
+   "f64d2658637ae013997dcdac15d090149304d6df",
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-position-x-invalid.html": [
@@ -374463,11 +374673,11 @@
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-position-y-computed-expected.txt": [
-   "86634ba52c136e54a4a635d9de6baa8d41dc4bd1",
+   "f96b97f83ea9cb15609b98a3ff7ae5f207559047",
    "support"
   ],
   "css/css-backgrounds/parsing/background-position-y-computed.html": [
-   "ceff11f624d2d04fdca3201af09023081c3bf759",
+   "103a26037b7115d09472bd58dc49349e698ef020",
    "testharness"
   ],
   "css/css-backgrounds/parsing/background-position-y-invalid.html": [
@@ -374482,6 +374692,10 @@
    "1413a2e959a4cf00b64fecf06932a083e1480e54",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-repeat-computed.html": [
+   "f58ab577791a869c837b08c39514d3ee8b17eba6",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-repeat-invalid.html": [
    "abb153378f4ce02507d39c5a5a979689df58c8ba",
    "testharness"
@@ -374490,6 +374704,10 @@
    "2f72e03c73332dcf4356796ea3164fe170a21620",
    "testharness"
   ],
+  "css/css-backgrounds/parsing/background-size-computed.html": [
+   "01091f541ff3494d9949d620521ea6e91d9d9f97",
+   "testharness"
+  ],
   "css/css-backgrounds/parsing/background-size-invalid.html": [
    "01a0322002bc4fecc9c48d2266e39d1c2da3e500",
    "testharness"
@@ -393422,6 +393640,42 @@
    "47170e48f15f90488542daf7ede743cd55f75172",
    "testharness"
   ],
+  "css/css-logical/parsing/border-block-color-computed.html": [
+   "dc9e44edb1a11c374074c266c2b0929a5f13580b",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-color-invalid.html": [
+   "1c25f125fe32fe138d4ef1a656c4a249cd981136",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-color-valid.html": [
+   "aefe0f268b0f7a91d46fb40c4b051587de1f2ac7",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-style-computed.html": [
+   "beb3869863ccf016457b03bf4a8c401b7a69c47c",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-style-invalid.html": [
+   "680e510aae6853fc48ad65f2eb3a0fabdb436936",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-style-valid.html": [
+   "860a1052b8346aadbbbf18faf818ab9248f35535",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-width-computed.html": [
+   "3b4d934f8b23b29cd5d7b3aae039ac51ac4ab410",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-width-invalid.html": [
+   "65990fe930d2b8b69a2863d675bbfd993291ce5a",
+   "testharness"
+  ],
+  "css/css-logical/parsing/border-block-width-valid.html": [
+   "98987df9a703ef38d20c6bc1581b2ca136342615",
+   "testharness"
+  ],
   "css/css-logical/parsing/inline-size-invalid.html": [
    "d3d5d3f84d9d01392533e787b9e6755d592ec96e",
    "testharness"
@@ -407235,7 +407489,7 @@
    "support"
   ],
   "css/css-text/text-transform/reference/text-transform-upperlower-039-ref.html": [
-   "0e243edde50eeddae6b52604051ade41194cf447",
+   "f4fb6804434e02f283fe036e8a110a5652fc8cb3",
    "support"
   ],
   "css/css-text/text-transform/reference/text-transform-upperlower-040-ref.html": [
@@ -407254,6 +407508,10 @@
    "5c5e18c2f7451fb5a236c9fae6344786a65336b3",
    "support"
   ],
+  "css/css-text/text-transform/reference/text-transform-upperlower-044-ref.html": [
+   "3eed1edb80e628c28f6a483db4a6980a4b36064e",
+   "support"
+  ],
   "css/css-text/text-transform/reference/text-transform-upperlower-101-ref.html": [
    "0105b021d468598eec8e48de849d4764a9e517af",
    "support"
@@ -407595,7 +407853,7 @@
    "reftest"
   ],
   "css/css-text/text-transform/text-transform-upperlower-039.html": [
-   "40f36e0be3cd440d8f8820e5636455e90b5b9e68",
+   "470a4f6fb549f29d9a29f43b68df63b304c851e2",
    "reftest"
   ],
   "css/css-text/text-transform/text-transform-upperlower-040.html": [
@@ -407614,6 +407872,10 @@
    "d56f6b3ee3bf1cd0d692d36a782c0040129b31a4",
    "reftest"
   ],
+  "css/css-text/text-transform/text-transform-upperlower-044.html": [
+   "6d81c4540a132fc5db5a8361154c3f04f1087f32",
+   "reftest"
+  ],
   "css/css-text/text-transform/text-transform-upperlower-101.html": [
    "e4f2ca7d9f510359541f974e4f648c89de6e1561",
    "reftest"
@@ -423858,6 +424120,10 @@
    "187d875cd5115cec781d18bf4fca715441d5b2d7",
    "reftest"
   ],
+  "css/css-writing-modes/percentage-padding-in-shrink-to-fit.html": [
+   "2d4364c886c48c3ef7ebddfb43d2210bdf75e094",
+   "reftest"
+  ],
   "css/css-writing-modes/reference/available-size-001-ref.html": [
    "84252946946d4e444f8b0dd69381a1bd04554c80",
    "support"
@@ -437471,11 +437737,11 @@
    "testharness"
   ],
   "dom/events/EventListener-handleEvent-expected.txt": [
-   "5269d0c706742beeafd9b5be98bf7f4b08e1c4ac",
+   "ee188fd998810a6b4b6f2ea6230fd4c32abe91f3",
    "support"
   ],
   "dom/events/EventListener-handleEvent.html": [
-   "6630f273fff4b450d1fcc425828b5f1f53357e54",
+   "1eb80ccb15f0bc6fda7d04d4fc01555ea6b8ab04",
    "testharness"
   ],
   "dom/events/EventListener-incumbent-global-1.sub.html": [
@@ -438939,7 +439205,7 @@
    "testharness"
   ],
   "dom/traversal/TreeWalker-acceptNode-filter.html": [
-   "1446f40f68dd25f13b97bdce2a425cd9f36104d1",
+   "e2110aff3b3dec517f70866f908de2f919c089a4",
    "testharness"
   ],
   "dom/traversal/TreeWalker-basic.html": [
@@ -444775,7 +445041,7 @@
    "testharness"
   ],
   "fetch/corb/script-resource-with-nonsniffable-types.tentative.sub.html": [
-   "62fd9ca389da9fda5e7c6655ae88be57b7688d5b",
+   "e599bf26f9445e9e96ee98e4bbab2895e2283fe2",
    "testharness"
   ],
   "fetch/corb/style-css-mislabeled-as-html-nosniff.sub.html": [
@@ -450635,7 +450901,7 @@
    "testharness"
   ],
   "html/dom/interfaces.https_exclude=(Document_Window_HTML._)-expected.txt": [
-   "d9a59c802f3368cc0d818e3f221e9e1e60e545e0",
+   "efc72ac6e85df27db2bee1d3de2d0524ece4421c",
    "support"
   ],
   "html/dom/interfaces.https_include=(Document_Window)-expected.txt": [
@@ -450643,11 +450909,11 @@
    "support"
   ],
   "html/dom/interfaces.https_include=HTML._-expected.txt": [
-   "9eb12ff6ce99e7f2e5058c918bde46ba2ca12322",
+   "ff83da82f19174e6967a57bac2261f4daabd9c64",
    "support"
   ],
   "html/dom/interfaces.worker-expected.txt": [
-   "f31faed6e03fe737629ad68d4b9c83b5bd599850",
+   "c2e30c2ea7e56bf6e4864ca4bb552e331e16ea48",
    "support"
   ],
   "html/dom/interfaces.worker.js": [
@@ -457519,7 +457785,7 @@
    "testharness"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html": [
-   "67ab3bdb138230fa64f0a04c0cc2bd780a23ec65",
+   "501cf3f92a2796c2685a046829593821d7414b2c",
    "testharness"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-add-remove-cue.html": [
@@ -457598,16 +457864,12 @@
    "76019c9b41e4fd4c56f4252d399f088b7b3e4470",
    "reftest"
   ],
-  "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue-ref.html": [
-   "837c4fd7833d569f2949bf1c2a677836ac851597",
-   "support"
-  ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-empty-cue.html": [
-   "c2d300999eeaa207365b5c0b42f9b033e6b96446",
-   "reftest"
+   "427189f6fc78074ae13c58e94ae3d02bc0e513b2",
+   "testharness"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit-ref.html": [
-   "c4c14bc2a394ff19c14bfa76c4b59ad5d6ddb618",
+   "8354041eb2a0aa57b283e12d9c0390f16327ac80",
    "support"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-line-doesnt-fit.html": [
@@ -457615,7 +457877,7 @@
    "reftest"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video-ref.html": [
-   "c3ee804c485b762d8b69614136558bbf2ed833a8",
+   "39461350b089b9041a71a6ca9aad7f1cfc078d68",
    "support"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-transformed-video.html": [
@@ -457670,12 +457932,20 @@
    "ff447f33f2e85d77dbfc1d84e46e4a2c2ec245ac",
    "testharness"
   ],
+  "html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error-expected.txt": [
+   "4c3f1e5fdded729f49f8dbd82a4953d060002a38",
+   "support"
+  ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html": [
-   "ffc8ec0682dedc522f4713dcf3ba58b31debbb8b",
+   "dd97d0522d781f685178763df67dbb3145290ca3",
    "testharness"
   ],
+  "html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-expected.txt": [
+   "4c3f1e5fdded729f49f8dbd82a4953d060002a38",
+   "support"
+  ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html": [
-   "34a53d153190afe44357d5b53e184050de092b18",
+   "f3c78668b4ac6862dc60eb9d4f6c30e149fa07af",
    "testharness"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-helpers.js": [
@@ -457711,7 +457981,7 @@
    "testharness"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html": [
-   "2902ba90bc97355bea3902a1a6dcb81d4e4e9e41",
+   "3ec47a39e2f229e48c3ae14ec6428c88931013a7",
    "testharness"
   ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-mode-triggers-loading.html": [
@@ -457754,6 +458024,14 @@
    "1f7df3b68232eef2010b7c8df665ec39b68af08e",
    "testharness"
   ],
+  "html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order-expected.txt": [
+   "4fddcc451df5ef1673936383c4f029e69842815f",
+   "support"
+  ],
+  "html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html": [
+   "522d067adfc500a4e773c267c663f331d86c57cc",
+   "testharness"
+  ],
   "html/semantics/embedded-content/media-elements/track/track-element/track-text-track-cue-list-expected.txt": [
    "040deeef2e98886f03913317283e22c787880673",
    "support"
@@ -461367,7 +461645,7 @@
    "support"
   ],
   "html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html": [
-   "97320090cf039c7352d3f302a42c4bee8765bbd7",
+   "73eebaff70f09bb98a3b7dd6b59f9357a3ab0668",
    "testharness"
   ],
   "html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base-expected.txt": [
@@ -461375,7 +461653,7 @@
    "support"
   ],
   "html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html": [
-   "8d1b54baccdfd49403c6d192d4c287d3d8818636",
+   "3da6a49ef866e7c048461d60b398ad619e72d986",
    "testharness"
   ],
   "html/semantics/links/linktypes/alternate-css-ref.html": [
@@ -461407,11 +461685,11 @@
    "support"
   ],
   "html/semantics/rellist-feature-detection-expected.txt": [
-   "a00858d5f893aa6d6e1bff2e145b72ea2c1fcf4d",
+   "8e4fd0837cfd3d013c37ab0bb1b37439f7a26b7a",
    "support"
   ],
   "html/semantics/rellist-feature-detection.html": [
-   "d290439d8ee647b2f8d95ddc45ac390dccde9843",
+   "45debcc49a487d43437b04e6e8a429f7db321db1",
    "testharness"
   ],
   "html/semantics/scripting-1/META.yml": [
@@ -466370,16 +466648,12 @@
    "21c5ea48ad4cadff96c401972594083958f447f3",
    "testharness"
   ],
-  "imagebitmap-renderingcontext/context-creation-offscreen-with-alpha.html": [
-   "b565def964d0eb422702bc73054b6c4dfa0a3cb3",
-   "testharness"
-  ],
-  "imagebitmap-renderingcontext/context-creation-offscreen.html": [
-   "41cc6dc02a239cf26ea37bbdca1a252f19917ee7",
-   "testharness"
+  "imagebitmap-renderingcontext/context-creation-with-alpha-expected.txt": [
+   "f4a8ae2398153c9cb61dcc17e9ada6959624c221",
+   "support"
   ],
   "imagebitmap-renderingcontext/context-creation-with-alpha.html": [
-   "2c8fa08b9d340c9c0f1cb446a7a445fdc08cfff0",
+   "4354b9ac4188cd904f7e941001e724f78b0bde54",
    "testharness"
   ],
   "imagebitmap-renderingcontext/context-creation.html": [
@@ -466390,24 +466664,8 @@
    "eca7afe9ddd18f23e14f9cb2b16208713af7974b",
    "testharness"
   ],
-  "imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub.html": [
-   "0a0e203249f13f45198cbc05316b81694377d588",
-   "testharness"
-  ],
-  "imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html": [
-   "6a555fe25aad6b823a87282472345acb7d9dbf78",
-   "testharness"
-  ],
-  "imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen.html": [
-   "9f9c3395733a8a39de61ce8a616c3871e20e7e4f",
-   "testharness"
-  ],
-  "imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen.html": [
-   "e05a623a2fe1d1e290391ca4688285b14b9537b5",
-   "testharness"
-  ],
   "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html": [
-   "19d2f17ed3aff43da03836b8a62272e20bf12e55",
+   "c12a8c93fdb0f83c371982958838b8fa29c4f91d",
    "testharness"
   ],
   "imagebitmap-renderingcontext/transferFromImageBitmap-detached.html": [
@@ -467010,10 +467268,6 @@
    "cbae6b15410e13433c4a9fadd8c2a8cc5fbc4fdc",
    "support"
   ],
-  "infrastructure/metadata/infrastructure/testdriver/actions/actionsWithKeyPressed.html.ini": [
-   "f62bf62a9713c2c12e4d572e0701efde494224c0",
-   "support"
-  ],
   "infrastructure/metadata/infrastructure/testdriver/actions/elementPosition.html.ini": [
    "9ae71a6e73e22a855c69d3269936d71c17d6e9e5",
    "support"
@@ -467214,10 +467468,6 @@
    "ea7973a62e0ee9cdc874879fd844b2309e944e61",
    "testharness"
   ],
-  "infrastructure/testdriver/actions/actionsWithKeyPressed.html": [
-   "74e939f5fde4773aade6ce4f7bbee573e39ae8ec",
-   "testharness"
-  ],
   "infrastructure/testdriver/actions/elementPosition.html": [
    "145852e7b51bd0cdc9e7b4ef5ebddcbf1c0235c5",
    "testharness"
@@ -467603,7 +467853,7 @@
    "support"
   ],
   "interfaces/html.idl": [
-   "a9bd85aff37c761c03c9135f12c71f9c66a4ce77",
+   "5ae46f742922f4c455380745f124769399086eb9",
    "support"
   ],
   "interfaces/image-capture.idl": [
@@ -467651,7 +467901,7 @@
    "support"
   ],
   "interfaces/mediacapture-streams.idl": [
-   "427aa92dd1f750f305bccb0b9514820f5bc1d29b",
+   "c07b7e3301248f4945aacbbbe1a0c84330bbe086",
    "support"
   ],
   "interfaces/mediasession.idl": [
@@ -467835,7 +468085,7 @@
    "support"
   ],
   "interfaces/wake-lock.idl": [
-   "863cef13f3c39e852920fdf3763848562fe7360c",
+   "21a3406f5894629ab5838dcf85474043c5138867",
    "support"
   ],
   "interfaces/wasm-js-api.idl": [
@@ -467863,7 +468113,7 @@
    "support"
   ],
   "interfaces/webaudio.idl": [
-   "4679558023ae87655f46b10d5a57d39269c6e8c9",
+   "aa137c12f01e0ceee5af4435f38187d19c2acfcb",
    "support"
   ],
   "interfaces/webauthn.idl": [
@@ -467895,7 +468145,7 @@
    "support"
   ],
   "interfaces/webrtc.idl": [
-   "6ae34167ded31a5927791915eb56bc55f09c59dd",
+   "4b5d1c1c581869cdfcefab4584c2b05b2dcb1f86",
    "support"
   ],
   "interfaces/webusb.idl": [
@@ -467907,7 +468157,7 @@
    "support"
   ],
   "interfaces/webxr.idl": [
-   "2d1bd9fe5f5702d69051fe00b2f958a9a3a4b82c",
+   "63327a3f55d79f1acdc2cbd53a98b0a089aade2d",
    "support"
   ],
   "interfaces/worklets.idl": [
@@ -481335,7 +481585,7 @@
    "testharness"
   ],
   "pointerevents/pointerevent_pointerout_pen.html": [
-   "972f99d95080ab702da79f8125119a2b27778a84",
+   "a718468c49a965eefee134cc6e6ec4051042cb49",
    "testharness"
   ],
   "pointerevents/pointerevent_pointerout_received_once.html": [
@@ -481403,7 +481653,7 @@
    "testharness"
   ],
   "pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html": [
-   "5bab6e66121cebc47213914fb07f7995d8b0722d",
+   "6ed75ab4cbcda3c673b3328a826019bdade4db5d",
    "testharness"
   ],
   "pointerevents/pointerevent_touch-action-auto-css_touch.html": [
@@ -481419,7 +481669,7 @@
    "testharness"
   ],
   "pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html": [
-   "f05aadf39e8435dea374386a796c15fc747f15b0",
+   "bdd2809354c7a39a7b9acd75c5208cba324fecf1",
    "testharness"
   ],
   "pointerevents/pointerevent_touch-action-inherit_child-none_touch.html": [
@@ -481650,6 +481900,10 @@
    "780e8b58af98cc910e3739e3a9a2e5567d5613d0",
    "testharness"
   ],
+  "portals/portal-non-http-navigation.html": [
+   "3b79df3c230e1128574d4a6eb81d407cd9303e2c",
+   "testharness"
+  ],
   "portals/portal-onload-event.html": [
    "c9f07fcc889043cd523206728f7daaaefa653409",
    "testharness"
@@ -481839,7 +482093,7 @@
    "support"
   ],
   "preload/download-resources.html": [
-   "510ebb480457e9e1b0d6ea788a8bd36c825bc634",
+   "557c9656b1813c52e373ee70e565a44e966389bf",
    "testharness"
   ],
   "preload/dynamic-adding-preload-imagesrcset.html": [
@@ -481859,7 +482113,7 @@
    "testharness"
   ],
   "preload/link-header-on-subresource.html": [
-   "087a3429e649348d2b35fd92bf03ebc6307c72dc",
+   "7047115a1caf4989ea5e80e80db35bc2dcdf2a6b",
    "testharness"
   ],
   "preload/link-header-preload-delay-onload.html": [
@@ -481903,7 +482157,7 @@
    "support"
   ],
   "preload/onerror-event.html": [
-   "8190be87a4b7caf69ba07d07d239ff8143be3b05",
+   "2038e06e941cf7b146fcb744c6c5a2ded3072abc",
    "testharness"
   ],
   "preload/onload-event-expected.txt": [
@@ -481915,11 +482169,11 @@
    "testharness"
   ],
   "preload/preload-csp.sub.html": [
-   "65e9b3a2a6d350598e88cd2c8a7e4fa8d10d7533",
+   "62d0c71039b7c06a4299ac71e055ff3786de5c41",
    "testharness"
   ],
   "preload/preload-default-csp.sub.html": [
-   "923431456cf7e01d66556fda7241be0e660dacc0",
+   "9fc11945866834c70c9ad270fb11d37a443ac3d0",
    "testharness"
   ],
   "preload/preload-strict-dynamic.html": [
@@ -482570,6 +482824,10 @@
    "28e83df6c2ab9b27417b15948929d044095fdceb",
    "testharness"
   ],
+  "referrer-policy/generic/iframe-src-change.html": [
+   "15202a76a1ea3b7ad8deb35cdb33826414d7a95b",
+   "testharness"
+  ],
   "referrer-policy/generic/link-rel-prefetch.html": [
    "914ddfc4b225d26c675c61ecf623fdebb10e4524",
    "testharness"
@@ -492123,7 +492381,7 @@
    "support"
   ],
   "resources/chromium/webxr-test.js": [
-   "8816273705806dd8efb9c33a52a61689f802ecf2",
+   "5406b6745bdf021b59aa21f669e0cdbc6e858045",
    "support"
   ],
   "resources/chromium/webxr-test.js.headers": [
@@ -495787,7 +496045,7 @@
    "support"
   ],
   "service-workers/service-worker/resources/test-helpers.sub.js": [
-   "14101319fa9d9e4c76f33a471098d280f117a15f",
+   "af8dad3a5be8aa831900b183ad7ee9a10417ba1a",
    "support"
   ],
   "service-workers/service-worker/resources/test-request-headers-worker.js": [
@@ -495815,7 +496073,11 @@
    "support"
   ],
   "service-workers/service-worker/resources/update-during-installation-worker.js": [
-   "dabeec077f77d5e8d1924eb5f3bd5d8667b129f5",
+   "3f89881c04384590b8b132c392977256bfb847ed",
+   "support"
+  ],
+  "service-workers/service-worker/resources/update-during-installation-worker.py": [
+   "95e4522007c1d2be14045f7582ebe2b5347abd87",
    "support"
   ],
   "service-workers/service-worker/resources/update-fetch-worker.py": [
@@ -496063,7 +496325,7 @@
    "testharness"
   ],
   "service-workers/service-worker/update-not-allowed.https.html": [
-   "71fe1730e0d68e4e7e0949cfa408d3c2d4ed9d39",
+   "0a54aa9350382bb082f407a1ea30b265575baae9",
    "testharness"
   ],
   "service-workers/service-worker/update-on-navigation.https.html": [
@@ -504783,7 +505045,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/browsers/firefox.py": [
-   "454b08612aaca41e439fc7a9011019e7c4655c26",
+   "3b6949a42f5df564284eea15cd110e8c1a7763ee",
    "support"
   ],
   "tools/wptrunner/wptrunner/browsers/ie.py": [
@@ -506662,18 +506924,22 @@
    "6f05d23550e2b0c967fac5b436a5e3336222ffe2",
    "support"
   ],
-  "wake-lock/wakelock-insecure-context.html": [
-   "2c61f41311db3f9edf0502ea752ca27970eed3fb",
+  "wake-lock/wakelock-insecure-context.any.js": [
+   "f32cc3c354354f99852bb198760cdbd9229355a4",
+   "testharness"
+  ],
+  "wake-lock/wakelock-screen-type-on-worker.https.worker-expected.txt": [
+   "7019eb31819a0309d864b0bae9112977e6bbe04b",
+   "support"
+  ],
+  "wake-lock/wakelock-screen-type-on-worker.https.worker.js": [
+   "28e3394279c0b4311a80aad43ced0f9ea79e029f",
    "testharness"
   ],
   "wake-lock/wakelock-state-is-global.https-expected.txt": [
    "08b692aa24612df2abc368d9afd1075449409b65",
    "support"
   ],
-  "wake-lock/wakelock-state-is-global.https.html": [
-   "2eee31982498caab44b5160237c7ce7f8ce9a569",
-   "testharness"
-  ],
   "wake-lock/wakelock-supported-by-feature-policy.html": [
    "d6289fff43c8717ac6e9ace59713e6fd70bd249d",
    "testharness"
@@ -506682,10 +506948,14 @@
    "409d107ad314e8b92d69fd27e40f2e6a89c9c63c",
    "support"
   ],
-  "wake-lock/wakelock-type.https.html": [
-   "df7a68a040bef273efcd2584dfe3612adc05d85b",
+  "wake-lock/wakelock-type.https.any.js": [
+   "cc37c768272512ec412fb749cebd77afa8662ac3",
    "testharness"
   ],
+  "wake-lock/wakelock-type.https.any.worker-expected.txt": [
+   "9d738db325137ae52ec28c20e296b2ece04fede7",
+   "support"
+  ],
   "wasm/META.yml": [
    "3ea02ee7c9e41831aafbd37436f59b51244b142e",
    "support"
@@ -507266,10 +507536,6 @@
    "9005db9e9f2a8bcf3c9bf90bcea0ca882398442f",
    "testharness"
   ],
-  "web-animations/interfaces/Animation/constructor-expected.txt": [
-   "aee33fa5df746c6835fcff3325d2236fb6edfe85",
-   "support"
-  ],
   "web-animations/interfaces/Animation/constructor.html": [
    "fcbaab1e8dbad3a64852c67931ba7be97b94b0d3",
    "testharness"
@@ -507558,6 +507824,10 @@
    "9d07d53df46164d53b1a3c8f4a10787608c5f014",
    "testharness"
   ],
+  "web-animations/timing-model/animations/setting-the-start-time-of-an-animation-expected.txt": [
+   "5677028f1a13bc273443b1d8f1c989cf7f2ded47",
+   "support"
+  ],
   "web-animations/timing-model/animations/setting-the-start-time-of-an-animation.html": [
    "454a294239aa61130dc56af910fe1aa8faf4900b",
    "testharness"
@@ -507571,7 +507841,7 @@
    "testharness"
   ],
   "web-animations/timing-model/animations/setting-the-timeline-of-an-animation-expected.txt": [
-   "a1f4b2ba477fadf6a05988b605e0996944e9face",
+   "1b685b0566ee9e911c2ff10658344aa0d25c7ea4",
    "support"
   ],
   "web-animations/timing-model/animations/setting-the-timeline-of-an-animation.html": [
@@ -507587,7 +507857,7 @@
    "testharness"
   ],
   "web-animations/timing-model/time-transformations/transformed-progress-expected.txt": [
-   "756e479dd16b564fa648b7c0fa829dcfb591d151",
+   "7f4f1efda60a77468f935161dd930411cd3c654e",
    "support"
   ],
   "web-animations/timing-model/time-transformations/transformed-progress.html": [
@@ -510731,11 +511001,11 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addIceCandidate-expected.txt": [
-   "bd31c0bae79f5f15c96716a48a7103f551ba535a",
+   "1ca662a424c727cd2876fe769d45a9da4100d487",
    "support"
   ],
   "webrtc/RTCPeerConnection-addIceCandidate.html": [
-   "c51c11fccbfae40de733b882503be402cbf7aa7a",
+   "ed52ace59e60c1ea11285052ab2e1efa17a4d941",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addTrack.https.html": [
@@ -510823,7 +511093,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-helper.js": [
-   "fde1c6f0374a02c0998fdfe3a1ba5e7046d314f4",
+   "d859ac736ae6bf0187640dbe2148d8e0d64d9345",
    "support"
   ],
   "webrtc/RTCPeerConnection-iceConnectionState-disconnected.https.html": [
@@ -518759,7 +519029,7 @@
    "support"
   ],
   "xhr/resources/access-control-basic-options-not-supported.py": [
-   "0c69c76293eaeebc1e9c18fed7904c3bdcdc0d8e",
+   "77b274e957ca87270926ad4622d2c79ec9600fed",
    "support"
   ],
   "xhr/resources/access-control-basic-preflight-cache-invalidation.py": [
@@ -519215,11 +519485,11 @@
    "testharness"
   ],
   "xhr/responsexml-document-properties-expected.txt": [
-   "a81e1945acb11ce4a31d375186db53782d88913a",
+   "c7661c57fd9a9d4a5c6cf0bd78d914d8eaccffc1",
    "support"
   ],
   "xhr/responsexml-document-properties.htm": [
-   "b008348e58f0f8751d9d1b83531e843af8239a6d",
+   "9071fab121fc8e55d885c4fc89636380b66c226c",
    "testharness"
   ],
   "xhr/responsexml-get-twice.htm": [
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/writeValue/buffer-is-detached.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/writeValue/buffer-is-detached.https.html
index 762735c..b8a1abfa 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/writeValue/buffer-is-detached.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/writeValue/buffer-is-detached.https.html
@@ -11,26 +11,18 @@
   window.postMessage('', '*', [buffer]);
 }
 
-bluetooth_test(async () => {
+bluetooth_test(async (t) => {
   let {characteristic, fake_characteristic} =
       await getMeasurementIntervalCharacteristic();
 
   let typed_array = Uint8Array.of(1, 2);
   detachBuffer(typed_array.buffer);
-  try {
-    await characteristic.writeValue(typed_array);
-    assert_unreached();
-  } catch (e) {
-    assert_equals(e.code, DOMException.INVALID_STATE_ERR, e.toString());
-  }
+  await promise_rejects(
+    t, 'InvalidStateError', characteristic.writeValue(typed_array));
 
   let array_buffer = Uint8Array.of(3, 4).buffer;
   detachBuffer(array_buffer);
-  try {
-    await characteristic.writeValue(array_buffer);
-    assert_unreached();
-  } catch (e) {
-    assert_equals(e.code, DOMException.INVALID_STATE_ERR, e.toString());
-  }
+  await promise_rejects(
+    t, 'InvalidStateError', characteristic.writeValue(array_buffer));
 }, 'writeValue() fails when passed a detached buffer');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/descriptor/writeValue/buffer-is-detached.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/descriptor/writeValue/buffer-is-detached.https.html
index 2402d89..2ab990d 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/descriptor/writeValue/buffer-is-detached.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/descriptor/writeValue/buffer-is-detached.https.html
@@ -11,26 +11,18 @@
   window.postMessage('', '*', [buffer]);
 }
 
-bluetooth_test(async () => {
+bluetooth_test(async (t) => {
   let {descriptor, fake_descriptor} =
       await getUserDescriptionDescriptor();
 
   let typed_array = Uint8Array.of(1, 2);
   detachBuffer(typed_array.buffer);
-  try {
-    await descriptor.writeValue(typed_array);
-    assert_unreached();
-  } catch (e) {
-    assert_equals(e.code, DOMException.INVALID_STATE_ERR, e.toString());
-  }
+  await promise_rejects(
+    t, 'InvalidStateError', descriptor.writeValue(typed_array));
 
   let array_buffer = Uint8Array.of(3, 4).buffer;
   detachBuffer(array_buffer);
-  try {
-    await descriptor.writeValue(array_buffer);
-    assert_unreached();
-  } catch (e) {
-    assert_equals(e.code, DOMException.INVALID_STATE_ERR, e.toString());
-  }
+  await promise_rejects(
+    t, 'InvalidStateError', descriptor.writeValue(array_buffer));
 }, 'writeValue() fails when passed a detached buffer');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-039-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-039-ref.html
index 0e243ed..f4fb680 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-039-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-039-ref.html
@@ -17,7 +17,10 @@
 </head>
 <body>
 <p class="instructions">Test passes if both characters in each pair match. If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
-<div class="test" lang="lt"><span>&#x69;&#x307;&#x300; &#x69;&#x307;&#x300;</span> <span>&#x69;&#x307;&#x301; &#x69;&#x307;&#x301;</span> <span>&#x69;&#x307;&#x303; &#x69;&#x307;&#x303;</span></div>
+<div class="test" lang="lt">
+  <span>&#x69;&#x307;&#x300; &#x69;&#x307;&#x300;</span> <span>&#x69;&#x307;&#x301; &#x69;&#x307;&#x301;</span> <span>&#x69;&#x307;&#x303; &#x69;&#x307;&#x303;</span>
+  <span>&#x69;&#x307;&#x300; &#x69;&#x307;&#x300;</span> <span>&#x6A;&#x307;&#x301; &#x6A;&#x307;&#x301;</span> <span>&#x12F;&#x307;&#x303; &#x12F;&#x307;&#x303;</span>
+</div>
 <!--Notes:
 The language of the test box is set to Lithuanian (lt)
 -->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-044-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-044-ref.html
new file mode 100644
index 0000000..3eed1ed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-044-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, text transform: Lithuanian, uppercase</title>
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<style type='text/css'>
+@font-face {
+	font-family: 'webfont';
+	src: url('/fonts/DoulosSIL-R.woff') format('woff');
+	font-weight: normal;
+	font-style: normal;
+	}
+.test, .ref { font-size: 200%; line-height: 2.5em; font-family: webfont, serif; }
+.test span, .ref span { margin-right: 1em; white-space: nowrap; }
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if both characters in each pair match. If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
+<div class="test" lang="lt">
+  <span>&#x49;&#x300; &#x49;&#x300;</span>
+  <span>&#x49;&#x301; &#x49;&#x301;</span>
+  <span>&#x49;&#x303; &#x49;&#x303;</span>
+  <span>&#x49; &#x49;</span>
+  <span>&#x4A; &#x4A;</span>
+  <span>&#x12E; &#x12E;</span>
+  <span>X&#x307; X&#x307;</span>
+</div>
+<!--Notes:
+The language of the test box is set to Lithuanian (lt)
+-->
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-039.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-039.html
index 40f36e0..470a4f6f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-039.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-039.html
@@ -22,7 +22,10 @@
 </head>
 <body>
 <p class="instructions">Test passes if both characters in each pair match. If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
-<div class="test" lang="lt"><span>&#xCC; &#x69;&#x307;&#x300;</span> <span>&#xCD; &#x69;&#x307;&#x301;</span> <span>&#x128; &#x69;&#x307;&#x303;</span></div>
+<div class="test" lang="lt">
+  <span>&#xCC; &#x69;&#x307;&#x300;</span> <span>&#xCD; &#x69;&#x307;&#x301;</span> <span>&#x128; &#x69;&#x307;&#x303;</span>
+  <span>&#x49;&#x300; &#x69;&#x307;&#x300;</span> <span>&#x4A;&#x301; &#x6A;&#x307;&#x301;</span> <span>&#x12E;&#x303; &#x12F;&#x307;&#x303;</span>
+</div>
 <!--Notes:
 The language of the test box is set to Lithuanian (lt)
 -->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-044.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-044.html
new file mode 100644
index 0000000..6d81c45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-044.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, text transform: Lithuanian, uppercase</title>
+<meta name="assert" content="text-transform: uppercase will uppercase Lithuanian as described in Unicode's SpecialCasing.txt .">
+<link rel='author' title='Jonathan Kew' href='mailto:jkew@mozilla.com'>
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#text-transform'>
+<link rel="match" href="reference/text-transform-upperlower-044-ref.html">
+<style type='text/css'>
+@font-face {
+	font-family: 'webfont';
+	src: url('/fonts/DoulosSIL-R.woff') format('woff');
+	font-weight: normal;
+	font-style: normal;
+	}
+.test, .ref { font-size: 200%; line-height: 2.5em; font-family: webfont, serif; }
+.test span, .ref span { margin-right: 1em; white-space: nowrap; }
+/* the CSS above is not part of the test */
+.test { text-transform: uppercase; }
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if both characters in each pair match. If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
+<div class="test" lang="lt">
+  <span>&#x69;&#x307;&#x300; &#x49;&#x300;</span>
+  <span>&#x69;&#x307;&#x301; &#x49;&#x301;</span>
+  <span>&#x69;&#x307;&#x303; &#x49;&#x303;</span>
+  <span>&#x69;&#x307; &#x49;</span>
+  <span>&#x6A;&#x307; &#x4A;</span>
+  <span>&#x12F;&#x307; &#x12E;</span>
+  <span>x&#x307; X&#x307;</span> <!-- check that dot isn't deleted in other contexts -->
+</div>
+<!--Notes:
+The language of the test box is set to Lithuanian (lt)
+-->
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent-expected.txt b/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent-expected.txt
index 5269d0c..ee188fd9 100644
--- a/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent-expected.txt
@@ -2,6 +2,8 @@
 PASS calls `handleEvent` method of `EventListener`
 PASS rethrows errors when getting `handleEvent`
 PASS performs `Get` every time event is dispatched
-FAIL throws if `handleEvent` is not callable assert_true: expected true got false
+PASS doesn't call `handleEvent` method on callable `EventListener`
+FAIL throws if `handleEvent` is falsy and not callable assert_true: expected true got false
+FAIL throws if `handleEvent` is thruthy and not callable assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent.html b/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent.html
index 6630f27..1eb80cc 100644
--- a/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent.html
+++ b/third_party/blink/web_tests/external/wpt/dom/events/EventListener-handleEvent.html
@@ -5,13 +5,12 @@
 <script src="/resources/testharnessreport.js"></script>
 <link rel="help" href="https://dom.spec.whatwg.org/#callbackdef-eventlistener">
 <div id=log></div>
-<div id=target></div>
 <script>
 setup({ allow_uncaught_exception: true });
 
 test(function(t) {
     var type = "foo";
-    var target = document.getElementById("target");
+    var target = document.createElement("div");
     var eventListener = {
         handleEvent: function(evt) {
             var that = this;
@@ -30,12 +29,16 @@
 
 test(function(t) {
     var type = "foo";
-    var target = document.getElementById("target");
-    var thrownError = new Error();
+    var target = document.createElement("div");
+    var thrownError = { name: "test" };
     var uncaughtError;
-
-    window.addEventListener("error", function(event) {
+    var errorHandler = function(event) {
         uncaughtError = event.error;
+    };
+
+    window.addEventListener("error", errorHandler);
+    t.add_cleanup(function() {
+        window.removeEventListener("error", errorHandler);
     });
 
     target.addEventListener(type, {
@@ -50,7 +53,7 @@
 
 test(function(t) {
     var type = "foo";
-    var target = document.getElementById("target");
+    var target = document.createElement("div");
     var calls = 0;
 
     target.addEventListener(type, {
@@ -68,11 +71,27 @@
 
 test(function(t) {
     var type = "foo";
-    var target = document.getElementById("target");
-    var uncaughtError;
+    var target = document.createElement("div");
+    var calls = 0;
+    var eventListener = function() { calls++; };
+    eventListener.handleEvent = t.unreached_func("`handleEvent` method should not be called on functions");
 
-    window.addEventListener("error", function(event) {
+    target.addEventListener(type, eventListener);
+    target.dispatchEvent(new Event(type));
+    assert_equals(calls, 1);
+}, "doesn't call `handleEvent` method on callable `EventListener`");
+
+test(function(t) {
+    var type = "foo";
+    var target = document.createElement("div");
+    var uncaughtError;
+    var errorHandler = function(event) {
         uncaughtError = event.error;
+    };
+
+    window.addEventListener("error", errorHandler);
+    t.add_cleanup(function() {
+        window.removeEventListener("error", errorHandler);
     });
 
     target.addEventListener(type, {
@@ -81,5 +100,26 @@
 
     target.dispatchEvent(new Event(type));
     assert_true(uncaughtError instanceof TypeError);
-}, "throws if `handleEvent` is not callable");
+}, "throws if `handleEvent` is falsy and not callable");
+
+test(function(t) {
+    var type = "foo";
+    var target = document.createElement("div");
+    var uncaughtError;
+    var errorHandler = function(event) {
+        uncaughtError = event.error;
+    };
+
+    window.addEventListener("error", errorHandler);
+    t.add_cleanup(function() {
+        window.removeEventListener("error", errorHandler);
+    });
+
+    target.addEventListener(type, {
+        handleEvent: 1,
+    });
+
+    target.dispatchEvent(new Event(type));
+    assert_true(uncaughtError instanceof TypeError);
+}, "throws if `handleEvent` is thruthy and not callable");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/dom/traversal/TreeWalker-acceptNode-filter.html b/third_party/blink/web_tests/external/wpt/dom/traversal/TreeWalker-acceptNode-filter.html
index 1446f40f..e2110aff 100644
--- a/third_party/blink/web_tests/external/wpt/dom/traversal/TreeWalker-acceptNode-filter.html
+++ b/third_party/blink/web_tests/external/wpt/dom/traversal/TreeWalker-acceptNode-filter.html
@@ -8,6 +8,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="traversal-support.js"></script>
+<link rel="help" href="https://dom.spec.whatwg.org/#callbackdef-nodefilter">
 <div id=log></div>
 </head>
 <body>
@@ -102,10 +103,10 @@
     assert_node(walker.currentNode, { type: Element, id: 'root' });
 }, 'Testing with object with non-function acceptNode property');
 
-test(function()
+test(function(t)
 {
     var filter = function() { return NodeFilter.FILTER_ACCEPT; };
-    filter.acceptNode = function(node) { return NodeFilter.FILTER_SKIP; };
+    filter.acceptNode = t.unreached_func("`acceptNode` method should not be called on functions");
     var walker = document.createTreeWalker(testElement, NodeFilter.SHOW_ELEMENT, filter);
     assert_node(walker.firstChild(), { type: Element, id: 'A1' });
     assert_node(walker.nextNode(), { type: Element, id: 'B1' });
@@ -113,17 +114,6 @@
 
 test(function()
 {
-    var filter = {
-        acceptNode: function(node) {
-            return NodeFilter.FILTER_ACCEPT;
-        }
-    };
-    var walker = document.createTreeWalker(testElement, NodeFilter.SHOW_ELEMENT, filter);
-    assert_node(walker.firstChild(), { type: Element, id: 'A1' });
-}, 'Testing acceptNode callee');
-
-test(function()
-{
     var test_error = { name: "test" };
     var walker = document.createTreeWalker(testElement, NodeFilter.SHOW_ELEMENT,
                                            function(node) {
@@ -135,6 +125,38 @@
     assert_node(walker.currentNode, { type: Element, id: 'root' });
 }, 'Testing with filter function that throws');
 
+test(function() {
+    var testError = { name: "test" };
+    var filter = {
+        get acceptNode() {
+            throw testError;
+        },
+    };
+
+    var walker = document.createTreeWalker(testElement, NodeFilter.SHOW_ELEMENT, filter);
+    assert_throws(testError, function() { walker.firstChild(); });
+    assert_node(walker.currentNode, { type: Element, id: 'root' });
+    assert_throws(testError, function() { walker.nextNode(); });
+    assert_node(walker.currentNode, { type: Element, id: 'root' });
+}, "rethrows errors when getting `acceptNode`");
+
+test(function() {
+    var calls = 0;
+    var walker = document.createTreeWalker(testElement, NodeFilter.SHOW_ELEMENT, {
+        get acceptNode() {
+            calls++;
+            return function() {
+                return NodeFilter.FILTER_ACCEPT;
+            };
+        },
+    });
+
+    assert_equals(calls, 0);
+    walker.nextNode();
+    walker.nextNode();
+    assert_equals(calls, 2);
+}, "performs `Get` on every traverse");
+
 test(function()
 {
     var test_error = { name: "test" };
diff --git "a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt" "b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt"
index d9a59c8..efc72ac6 100644
--- "a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt"
+++ "b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt"
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 1148 tests; 1131 PASS, 17 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 1163 tests; 1146 PASS, 17 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Document: original interface defined
 PASS Partial interface mixin NavigatorID: original interface mixin defined
@@ -643,6 +643,21 @@
 PASS CustomElementRegistry interface: operation get(DOMString)
 PASS CustomElementRegistry interface: operation whenDefined(DOMString)
 PASS CustomElementRegistry interface: operation upgrade(Node)
+PASS ElementInternals interface: existence and properties of interface object
+PASS ElementInternals interface object length
+PASS ElementInternals interface object name
+PASS ElementInternals interface: existence and properties of interface prototype object
+PASS ElementInternals interface: existence and properties of interface prototype object's "constructor" property
+PASS ElementInternals interface: existence and properties of interface prototype object's @@unscopables property
+PASS ElementInternals interface: operation setFormValue([object Object],[object Object],[object Object], [object Object],[object Object],[object Object])
+PASS ElementInternals interface: attribute form
+PASS ElementInternals interface: operation setValidity(ValidityStateFlags, DOMString, HTMLElement)
+PASS ElementInternals interface: attribute willValidate
+PASS ElementInternals interface: attribute validity
+PASS ElementInternals interface: attribute validationMessage
+PASS ElementInternals interface: operation checkValidity()
+PASS ElementInternals interface: operation reportValidity()
+PASS ElementInternals interface: attribute labels
 PASS DataTransfer interface: existence and properties of interface object
 PASS DataTransfer interface object length
 PASS DataTransfer interface object name
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_include=HTML._-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_include=HTML._-expected.txt
index 9eb12ff..ff83da82f 100644
--- a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_include=HTML._-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_include=HTML._-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 3613 tests; 3572 PASS, 41 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 3618 tests; 3577 PASS, 41 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Document: original interface defined
 PASS Partial interface mixin NavigatorID: original interface mixin defined
@@ -100,6 +100,7 @@
 PASS HTMLElement interface: attribute spellcheck
 PASS HTMLElement interface: attribute autocapitalize
 PASS HTMLElement interface: attribute innerText
+PASS HTMLElement interface: operation attachInternals()
 PASS HTMLElement interface: attribute onabort
 PASS HTMLElement interface: attribute onauxclick
 PASS HTMLElement interface: attribute onblur
@@ -189,6 +190,7 @@
 PASS HTMLElement interface: document.createElement("noscript") must inherit property "spellcheck" with the proper type
 PASS HTMLElement interface: document.createElement("noscript") must inherit property "autocapitalize" with the proper type
 PASS HTMLElement interface: document.createElement("noscript") must inherit property "innerText" with the proper type
+PASS HTMLElement interface: document.createElement("noscript") must inherit property "attachInternals()" with the proper type
 PASS HTMLElement interface: document.createElement("noscript") must inherit property "onabort" with the proper type
 PASS HTMLElement interface: document.createElement("noscript") must inherit property "onauxclick" with the proper type
 PASS HTMLElement interface: document.createElement("noscript") must inherit property "onblur" with the proper type
@@ -1479,6 +1481,7 @@
 PASS HTMLFormElement interface: attribute elements
 PASS HTMLFormElement interface: attribute length
 PASS HTMLFormElement interface: operation submit()
+PASS HTMLFormElement interface: operation requestSubmit(HTMLElement)
 PASS HTMLFormElement interface: operation reset()
 PASS HTMLFormElement interface: operation checkValidity()
 PASS HTMLFormElement interface: operation reportValidity()
@@ -1498,6 +1501,8 @@
 PASS HTMLFormElement interface: document.createElement("form") must inherit property "elements" with the proper type
 PASS HTMLFormElement interface: document.createElement("form") must inherit property "length" with the proper type
 PASS HTMLFormElement interface: document.createElement("form") must inherit property "submit()" with the proper type
+PASS HTMLFormElement interface: document.createElement("form") must inherit property "requestSubmit(HTMLElement)" with the proper type
+PASS HTMLFormElement interface: calling requestSubmit(HTMLElement) on document.createElement("form") with too few arguments must throw TypeError
 PASS HTMLFormElement interface: document.createElement("form") must inherit property "reset()" with the proper type
 PASS HTMLFormElement interface: document.createElement("form") must inherit property "checkValidity()" with the proper type
 PASS HTMLFormElement interface: document.createElement("form") must inherit property "reportValidity()" with the proper type
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.worker-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.worker-expected.txt
index e00efd4..c2e30c2e 100644
--- a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 652 tests; 611 PASS, 41 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 660 tests; 611 PASS, 49 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Document: original interface defined
 PASS Partial interface mixin NavigatorID: original interface mixin defined
@@ -188,7 +188,14 @@
 PASS Path2D interface: operation rect(unrestricted double, unrestricted double, unrestricted double, unrestricted double)
 PASS Path2D interface: operation arc(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, boolean)
 PASS Path2D interface: operation ellipse(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, boolean)
-PASS ImageBitmapRenderingContext interface: existence and properties of interface object
+FAIL ImageBitmapRenderingContext interface: existence and properties of interface object assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
+FAIL ImageBitmapRenderingContext interface object length assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
+FAIL ImageBitmapRenderingContext interface object name assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
+FAIL ImageBitmapRenderingContext interface: existence and properties of interface prototype object assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
+FAIL ImageBitmapRenderingContext interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
+FAIL ImageBitmapRenderingContext interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
+FAIL ImageBitmapRenderingContext interface: attribute canvas assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
+FAIL ImageBitmapRenderingContext interface: operation transferFromImageBitmap(ImageBitmap) assert_own_property: self does not have own property "ImageBitmapRenderingContext" expected property "ImageBitmapRenderingContext" missing
 PASS OffscreenCanvas interface: existence and properties of interface object
 PASS OffscreenCanvas interface object length
 PASS OffscreenCanvas interface object name
@@ -278,6 +285,7 @@
 PASS OffscreenCanvasRenderingContext2D interface: operation arc(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, boolean)
 PASS OffscreenCanvasRenderingContext2D interface: operation ellipse(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, boolean)
 PASS CustomElementRegistry interface: existence and properties of interface object
+PASS ElementInternals interface: existence and properties of interface object
 PASS DataTransfer interface: existence and properties of interface object
 PASS DataTransferItemList interface: existence and properties of interface object
 PASS DataTransferItem interface: existence and properties of interface object
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html
index 67ab3bdb..501cf3f92 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-active-cues.html
@@ -17,6 +17,9 @@
 
     var video = document.createElement('video');
     video.src = getVideoURI('/media/movie_5');
+    // uanset media element's `show-poster` flag in order to run `time marches on`
+    // when we add new cues into media element's cues list.
+    video.play();
     var trackElement = document.createElement('track');
 
     trackElement.onload = t.step_func(eventCallback);
@@ -36,4 +39,4 @@
     trackElement.default = true;
     video.appendChild(trackElement);
 });
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error-expected.txt
new file mode 100644
index 0000000..4c3f1e5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL HTMLTrackElement 'src' attribute mutations assert_equals: cues list is reset immediately after 'src' mutation with the new URL expected 0 but got 4
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html
index ffc8ec0..dd97d05 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-error.html
@@ -21,7 +21,7 @@
                     break;
                 case 1:
                 case 3:
-                case 5:
+                case 4:
                     assert_unreached("'error' event did not fire, stage = " + stage);
                     break;
                 case 2:
@@ -30,21 +30,10 @@
                     assert_equals(cues[cues.length-1].text, 'I said Bear is coming now!!!! Tab separators.', "Last cue content check");
                     ++stage;
                     testTrack.src = ""; // this should fail
-                    // CuesList will be cleared in the next tick. Spec claims that this should happen immediately,
-                    // but all implementations are doing this asynchronously.
-                    assert_equals(cues.length, 4, "Number of cues immediately after 'src' mutation with the empty URL");
+                    assert_equals(cues.length, 0, "cues list is reset immediately after 'src' mutation with the new URL");
                     // This should raise onError event. If no, we'll know about this after some time.
                     timer = t.step_timeout(t.unreached_func("'error' event is not fired when an empty URL is set"), 100);
                     break;
-                case 4:
-                    assert_equals(testTrack.readyState, HTMLTrackElement.LOADED, "readyState after loading of the second track");
-                    assert_equals(cues.length, 4, "Number of cues after loading of the second track");
-                    assert_equals(cues[cues.length-1].text, 'I said Bear is coming now!!!! Tab separators.', "Last cue content check");
-                    ++stage;
-                    testTrack.removeAttribute('src');
-                    // This should raise onError event, so we'll wait for it for some time
-                    timer = t.step_timeout(t.unreached_func("'error' event is not fired when an empty URL is set"), 100);
-                    break;
                 default:
                     assert_unreached("unexpected stage number = " + stage);
                     break;
@@ -55,7 +44,6 @@
             switch (stage) {
                 case 0:
                 case 2:
-                case 4:
                     assert_unreached("'error' event fired, stage = " + stage);
                     break;
                 case 1:
@@ -72,8 +60,14 @@
                     assert_equals(cues.length, 0, "Number of cues with an empty URL set");
                     ++stage;
                     testTrack.src = "resources/settings.vtt";
+                    // error should happen when we remove `src` during loading, so we have to wait a task because loading starts asynchronously.
+                    t.step_timeout(() => {
+                        testTrack.removeAttribute('src');
+                        // This should raise onError event, so we'll wait for it for some time
+                        timer = t.step_timeout(t.unreached_func("'error' event is not fired when an empty URL is set"), 100);
+                    }, 0);
                     break;
-                case 5:
+                case 4:
                     clearTimeout(timer);
                     assert_equals(testTrack.readyState, HTMLTrackElement.ERROR, "readyState after removing 'src' attr");
                     assert_equals(cues.length, 0, "Number of cues after removing 'src' attr");
@@ -89,4 +83,4 @@
         testTrack.onerror = t.step_func(step_onError);
     });
     </script>
-</video>
\ No newline at end of file
+</video>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-expected.txt
new file mode 100644
index 0000000..4c3f1e5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL HTMLTrackElement 'src' attribute mutations assert_equals: cues list is reset immediately after 'src' mutation with the new URL expected 0 but got 4
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html
index 34a53d15..f3c78668 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-element-src-change.html
@@ -18,9 +18,7 @@
                     assert_equals(cues[cues.length-1].text, 'I said Bear is coming now!!!! Tab separators.', "Last cue content check");
                     ++stage;
                     testTrack.src = "resources/entities.vtt";
-                    // CuesList will be cleared in a microtask. Spec claims that this should happen immediately,
-                    // but all known implementations are doing this asynchronously.
-                    assert_equals(cues.length, 4, "Number of cues immediately after 'src' mutation with the new URL");
+                    assert_equals(cues.length, 0, "cues list is reset immediately after 'src' mutation with the new URL");
                     break;
                 case 1:
                     assert_equals(testTrack.readyState, HTMLTrackElement.LOADED), "readyState after loading of the second track";
@@ -54,4 +52,4 @@
         testTrack.onerror = t.unreached_func("'error' event should not fire");
     });
     </script>
-</video>
\ No newline at end of file
+</video>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html
index 2902ba9..3ec47a3 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode-not-changed-by-new-track.html
@@ -33,11 +33,12 @@
         assert_equals(track1.track.cues.length, 12);
         assert_equals(track1.track.cues[11].startTime, 22);
 
-        // Add a caption track, configured to load automatically.
+        // Add a caption track, and explicitly enable it.
         track2 = document.createElement('track');
         track2.setAttribute('kind', 'captions');
         track2.setAttribute('default', 'default');
         track2.setAttribute('src', 'resources/webvtt-file.vtt');
+        track2.track.mode = 'showing';
         track2.onload = t.step_func(captionsTrackLoaded);
         video.appendChild(track2);
     }
@@ -56,7 +57,7 @@
         track3.mode = 'showing';
     }
 
-    function trackAdded() {
+    function trackAdded(event) {
         // Check that metadata track state has not changed.
         assert_equals(track1.readyState, HTMLTrackElement.LOADED);
         assert_equals(track1.track.mode, 'hidden');
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order-expected.txt
new file mode 100644
index 0000000..4fddcc4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL HTMLTrackElement Text Track Selection Task Order assert_equals: Text track selection hasn't started yet. expected "disabled" but got "showing"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html
new file mode 100644
index 0000000..522d067a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-selection-task-order.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>HTMLTrackElement Text Track Selection Task Order</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+/**
+ * This test is used to ensure that we queue 'honor user preferences for automatic
+ * text track selection' as a macro task, not a micro task. In this test, we
+ * trigger a media event before queuing a text track selection task, and check
+ * the text track's mode to know whether the text track selection runs after the
+ * task for media event.
+ */
+async_test(function(t) {
+    let video = document.createElement("video");
+    video.play();
+    video.onplay = t.step_func(startedPlay);
+
+    // When we create a text track element, it queue a task to run automatic
+    // text track selection later.
+    let track = document.createElement("track");
+    track.default = true;
+    video.appendChild(track);
+    assert_equals(track.track.mode, "disabled", "Text track's mode is disabled by default.");
+
+    function startedPlay() {
+        assert_equals(track.track.mode, "disabled", "Text track selection hasn't started yet.");
+        track.onerror = t.step_func_done(trackError);
+    }
+
+    function trackError() {
+        assert_equals(track.track.mode, "showing", "Text track selection modified track's mode.");
+        t.done();
+    }
+});
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html b/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html
index 9732009..73eebaf 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html
@@ -17,21 +17,12 @@
   <a href="support/target_blank_implicit_noopener.html?a7" id="a7" rel="opener noreferrer" target="_blank">Click me</a>
   <a href="support/target_blank_implicit_noopener.html?a8" id="a8" rel="noopener opener noreferrer" target="_blank">Click me</a>
 
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area1" id="area1" rel="noopener" target="_blank" />
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area2" id="area2" rel="opener" target="_blank" />
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area3" id="area3" target="_blank" />
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area4" id="area4" rel="opener noopener" target="_blank" />
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area5" id="area5" rel="noopener opener" target="_blank" />
-  </img>
+  <!-- Although this is not valid, per the processing model of area it ought to work -->
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area1" id="area1" rel="noopener" target="_blank">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area2" id="area2" rel="opener" target="_blank">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area3" id="area3" target="_blank">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area4" id="area4" rel="opener noopener" target="_blank">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area5" id="area5" rel="noopener opener" target="_blank">
 
   <script>
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html b/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html
index 8d1b54b..3da6a49e 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html
@@ -18,21 +18,12 @@
   <a href="support/target_blank_implicit_noopener.html?a7" id="a7" rel="opener noreferrer">Click me</a>
   <a href="support/target_blank_implicit_noopener.html?a8" id="a8" rel="noopener opener noreferrer">Click me</a>
 
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area1" id="area1" rel="noopener">
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area2" id="area2" rel="opener">
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area3" id="area3">
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area4" id="area4" rel="opener noopener">
-  </img>
-  <img src="/images/threecolors.png" />
-    <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area5" id="area5" rel="noopener opener">
-  </img>
+  <!-- Although this is not valid, per the processing model of area it ought to work -->
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area1" id="area1" rel="noopener">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area2" id="area2" rel="opener">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area3" id="area3">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area4" id="area4" rel="opener noopener">
+  <area shape="rect" coords="0,0,99,50" href="support/target_blank_implicit_noopener.html?area5" id="area5" rel="noopener opener">
 
   <script>
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection-expected.txt
index a00858d5f..8e4fd083 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection-expected.txt
@@ -1,4 +1,7 @@
 This is a testharness.js-based test.
-FAIL Make sure that relList based feature detection is working assert_true: tag = A, link type = opener must be supported expected true got false
+PASS Make sure that relList based feature detection is working for <link>
+FAIL Make sure that relList based feature detection is working for <a> assert_true: tag = A, link type = opener must be supported expected true got false
+FAIL Make sure that relList based feature detection is working for <area> assert_true: tag = AREA, link type = opener must be supported expected true got false
+FAIL Make sure that relList based feature detection is working for <form> Cannot read property 'contains' of undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection.html b/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection.html
index d290439..45debcc 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/rellist-feature-detection.html
@@ -24,10 +24,11 @@
                  'apple-touch-icon-precomposed', 'canonical']
 };
 link_support_table['area'] = link_support_table['a'];
-link_support_table['form'] = link_support_table['form'];
+link_support_table['form'] = link_support_table['a'];
 
-function test_rellist(tag_name, rel_table) {
-  let element = document.createElement(tag_name);
+function test_rellist(tag_name) {
+  const rel_table = link_support_table[tag_name];
+  const element = document.createElement(tag_name);
   let tag = element.tagName;
   // Test that setting rel is also setting relList, for both
   // valid and invalid values.
@@ -74,10 +75,10 @@
   }
 }
 
-test(function() {
-  test_rellist('LINK', link_support_table['link']);
-  test_rellist('A', link_support_table['a']);
-  test_rellist('AREA', link_support_table['area']);
-  test_rellist('FORM', link_support_table['form']);
-}, 'Make sure that relList based feature detection is working');
+['link', 'a', 'area', 'form'].forEach(tag_name => {
+  test(
+    () => test_rellist(tag_name),
+    `Make sure that relList based feature detection is working for <${tag_name}>`
+  );
+});
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/text-level-semantics/the-ruby-element/rt-without-ruby-crash.html b/third_party/blink/web_tests/external/wpt/html/semantics/text-level-semantics/the-ruby-element/rt-without-ruby-crash.html
new file mode 100644
index 0000000..3caed3a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/text-level-semantics/the-ruby-element/rt-without-ruby-crash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=967255">
+<rt style="display:block;">
+  <div></div>
+</rt>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  test(()=> { }, "No crash");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/actionsWithKeyPressed.html.ini b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/actionsWithKeyPressed.html.ini
deleted file mode 100644
index f62bf62a..0000000
--- a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/actionsWithKeyPressed.html.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[actionsWithKeyPressed.html]
-  expected:
-    if product == "safari" or product == "firefox": ERROR
-
-
-  [TestDriver actions: actions with key pressed]
-    expected:
-      if product == "chrome": FAIL
-
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html
deleted file mode 100644
index 74e939f5..0000000
--- a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/actions/actionsWithKeyPressed.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>TestDriver actions: actions with key pressed</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-actions.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-
-<style>
-div#test1, div#test2 {
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100px;
-  height: 100px;
-  background-color: blue;
-}
-
-div#test2 {
-  position: fixed;
-  top: 100px;
-  left: 0;
-  width: 100px;
-  height: 100px;
-  background-color: green;
-}
-</style>
-
-<div id="test1">
-</div>
-
-<div id="test2">
-</div>
-
-<script>
-let keys = [];
-
-async_test(t => {
-  let test1 = document.getElementById("test1");
-  let test2 = document.getElementById("test2");
-  document.getElementById("test1").addEventListener("click",
-    e => {keys.push(e.getModifierState("Control"))});
-  document.getElementById("test2").addEventListener("click",
-    e => {keys.push(e.getModifierState("Control"))});
-
-  let actions = new test_driver.Actions()
-    .keyDown("\uE009")
-    .addTick()
-    .pointerMove(0, 0, {origin: test1})
-    .pointerDown()
-    .pointerUp()
-    .pointerMove(0, 0, {origin: test2})
-    .pointerDown()
-    .pointerUp()
-    .addTick()
-    .keyUp("\uE009")
-    .addTick()
-    .pointerMove(0, 0, {origin: test1})
-    .pointerDown()
-    .pointerUp();
-
-  actions.send()
-    .then(t.step_func_done(() => assert_array_equals(keys, [true, true, false])))
-    .catch(e => t.step_func(() => assert_unreached("Actions sequence failed " + e)));
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/html.idl b/third_party/blink/web_tests/external/wpt/interfaces/html.idl
index f44f5da..5ae46f74 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/html.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/html.idl
@@ -116,6 +116,8 @@
   [CEReactions] attribute DOMString autocapitalize;
 
   [CEReactions] attribute [TreatNullAs=EmptyString] DOMString innerText;
+
+  ElementInternals attachInternals();
 };
 
 HTMLElement includes GlobalEventHandlers;
@@ -795,6 +797,7 @@
   getter (RadioNodeList or Element) (DOMString name);
 
   void submit();
+  void requestSubmit(optional HTMLElement submitter);
   [CEReactions] void reset();
   boolean checkValidity();
   boolean reportValidity();
@@ -1412,9 +1415,9 @@
 };
 Path2D includes CanvasPath;
 
-[Exposed=Window]
+[Exposed=(Window,Worker)]
 interface ImageBitmapRenderingContext {
-  readonly attribute HTMLCanvasElement canvas;
+  readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;
   void transferFromImageBitmap(ImageBitmap? bitmap);
 };
 
@@ -1422,14 +1425,14 @@
   boolean alpha = true;
 };
 
-typedef (OffscreenCanvasRenderingContext2D or WebGLRenderingContext or WebGL2RenderingContext) OffscreenRenderingContext;
+typedef (OffscreenCanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext) OffscreenRenderingContext;
 
 dictionary ImageEncodeOptions {
   DOMString type = "image/png";
   unrestricted double quality;
 };
 
-enum OffscreenRenderingContextId { "2d", "webgl", "webgl2" };
+enum OffscreenRenderingContextId { "2d", "bitmaprenderer", "webgl", "webgl2" };
 
 [Constructor([EnforceRange] unsigned long long width, [EnforceRange] unsigned long long height), Exposed=(Window,Worker), Transferable]
 interface OffscreenCanvas : EventTarget {
@@ -1477,6 +1480,40 @@
   DOMString extends;
 };
 
+[Exposed=Window]
+interface ElementInternals {
+  // Form-associated custom elements
+
+  void setFormValue((File or USVString or FormData)? value,
+                    optional (File or USVString or FormData)? state);
+
+  readonly attribute HTMLFormElement? form;
+
+  void setValidity(ValidityStateFlags flags,
+                   optional DOMString message,
+                   optional HTMLElement anchor);
+  readonly attribute boolean willValidate;
+  readonly attribute ValidityState validity;
+  readonly attribute DOMString validationMessage;
+  boolean checkValidity();
+  boolean reportValidity();
+
+  readonly attribute NodeList labels;
+};
+
+dictionary ValidityStateFlags {
+  boolean valueMissing = false;
+  boolean typeMismatch = false;
+  boolean patternMismatch = false;
+  boolean tooLong = false;
+  boolean tooShort = false;
+  boolean rangeUnderflow = false;
+  boolean rangeOverflow = false;
+  boolean stepMismatch = false;
+  boolean badInput = false;
+  boolean customError = false;
+};
+
 dictionary FocusOptions {
   boolean preventScroll = false;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-streams.idl b/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-streams.idl
index 427aa92..c07b7e3 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-streams.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-streams.idl
@@ -52,7 +52,6 @@
              boolean frameRate = true;
              boolean facingMode = true;
              boolean resizeMode = true;
-             boolean volume = true;
              boolean sampleRate = true;
              boolean sampleSize = true;
              boolean echoCancellation = true;
@@ -71,7 +70,6 @@
              DoubleRange frameRate;
              sequence<DOMString> facingMode;
              sequence<DOMString> resizeMode;
-             DoubleRange volume;
              ULongRange sampleRate;
              ULongRange sampleSize;
              sequence<boolean> echoCancellation;
@@ -94,7 +92,6 @@
              ConstrainDouble frameRate;
              ConstrainDOMString facingMode;
              ConstrainDOMString resizeMode;
-             ConstrainDouble volume;
              ConstrainULong sampleRate;
              ConstrainULong sampleSize;
              ConstrainBoolean echoCancellation;
@@ -113,7 +110,6 @@
              double frameRate;
              DOMString facingMode;
              DOMString resizeMode;
-             double volume;
              long sampleRate;
              long sampleSize;
              boolean echoCancellation;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl b/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl
index 863cef1..21a3406 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl
@@ -16,5 +16,5 @@
 };
 
 dictionary WakeLockRequestOptions {
-  AbortSignal? signal = null;
+  AbortSignal signal;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webaudio.idl b/third_party/blink/web_tests/external/wpt/interfaces/webaudio.idl
index 4679558..aa137c1 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webaudio.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webaudio.idl
@@ -568,7 +568,8 @@
 
 [Exposed=Window,
  SecureContext,
- Constructor (BaseAudioContext context, DOMString name, optional AudioWorkletNodeOptions options)]
+ Constructor (BaseAudioContext context, DOMString name,
+              optional AudioWorkletNodeOptions options)]
 interface AudioWorkletNode : AudioNode {
   readonly attribute AudioParamMap parameters;
   readonly attribute MessagePort port;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl b/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
index 6ae3416..4b5d1c1c 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
@@ -382,7 +382,7 @@
     required DOMHighResTimeStamp timestamp;
     required unsigned long source;
              double audioLevel;
-             unsigned long rtpTimestamp;
+    required unsigned long rtpTimestamp;
 };
 
 dictionary RTCRtpSynchronizationSource : RTCRtpContributingSource {
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl b/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
index 2d1bd9fe5..63327a3 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
@@ -32,7 +32,7 @@
   // Attributes
   readonly attribute XREnvironmentBlendMode environmentBlendMode;
   [SameObject] readonly attribute XRRenderState renderState;
-  readonly attribute XRInputSourceArray inputSources;
+  [SameObject] readonly attribute XRInputSourceArray inputSources;
 
   // Methods
   void updateRenderState(optional XRRenderStateInit state);
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html
index 5bab6e66..6ed75ab 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse.html
@@ -46,7 +46,6 @@
           var target_list = ["target0", "target1"];
           var pointer_event_list = ["pointerdown" , "pointermove", "pointerup"];
           var mouse_event_list = ["mousedown", "mouseup", "mousemove"];
-          var last_pointer_event = null;
 
           target_list.forEach(function(targetId) {
               var target = document.getElementById(targetId);
@@ -64,8 +63,6 @@
 
                       if (label === "pointerdown@target0")
                           event.preventDefault();
-
-                      last_pointer_event = event;
                   });
               });
 
@@ -77,11 +74,6 @@
                       event_log.push(event.type + "@" + targetId);
 
                       include_next_mousemove = (event.type == "mousedown");
-                      test(function() {
-                          test(function () {
-                              assert_equals(event.timeStamp, last_pointer_event.timeStamp, "The time stamp of the compat mouse event should be the same as its pointerevent");
-                          });
-                      }, event.type + "'s time stamp should be the same as " + last_pointer_event.type + "'s time stamp.");
                   });
               });
           });
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html
index bdd2809..f05aadf 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html
@@ -92,7 +92,7 @@
             function run() {
                 var target0 = document.getElementById("target0");
                 var btnComplete = document.getElementById("btnComplete");
-                var actions_promise;
+                var clickIsReceived = false;
 
                 // Check if touch-action attribute works properly for embedded divs
                 // Scrollable-Parent, Child: `auto`, Grand-Child: `none`
@@ -103,11 +103,7 @@
                         assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
                         assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
                     });
-
-                    // Make sure the test finishes after all the input actions are completed.
-                    actions_promise.then( () => {
-                      test_touchaction.done();
-                    });
+                    clickIsReceived = true;
                     updateDescriptionComplete();
                 });
 
@@ -116,11 +112,15 @@
                 });
 
                 // Inject touch inputs.
-                actions_promise = touchScrollInTarget(scrollTarget, 'down');
-                actions_promise.then(function() {
-                    touchScrollInTarget(scrollTarget, 'right');
+                touchScrollInTarget(scrollTarget, 'down').then(function() {
+                    return touchScrollInTarget(scrollTarget, 'right');
                 }).then(function() {
-                    clickInTarget("touch", btnComplete);
+                    return clickInTarget("touch", btnComplete);
+                }).then(function() {
+                    test_touchaction.step(function () {
+                        assert_true(clickIsReceived, "click should be received before the test finishes");
+                    }, "click should be received before the test finishes");
+                    test_touchaction.done();
                 });
             }
         </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/download-resources.html b/third_party/blink/web_tests/external/wpt/preload/download-resources.html
index 510ebb4..557c965 100644
--- a/third_party/blink/web_tests/external/wpt/preload/download-resources.html
+++ b/third_party/blink/web_tests/external/wpt/preload/download-resources.html
@@ -1,10 +1,8 @@
 <!DOCTYPE html>
+<title>Makes sure that preloaded resources are downloaded</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/preload/resources/preload_helper.js"></script>
-<script>
-    var t = async_test('Makes sure that preloaded resources are downloaded');
-</script>
 <link rel=preload href="resources/dummy.js" as=script>
 <link rel=preload href="resources/dummy.css" as=style>
 <link rel=preload href="resources/square.png" as=image>
@@ -17,9 +15,25 @@
 <link rel=preload href="resources/dummy.xml" as="fetch">
 <body>
 <script>
-    window.addEventListener("load", t.step_func(function() {
-        t.step_timeout(function() {
-            verifyPreloadAndRTSupport()
+    setup({explicit_done: true});
+
+    var iterations = 0;
+
+    function check_finished() {
+        if (numberOfResourceTimingEntries("resources/dummy.js") == 1 &&
+            numberOfResourceTimingEntries("resources/dummy.css") == 1 &&
+            numberOfResourceTimingEntries("/fonts/CanvasTest.ttf") == 1 &&
+            numberOfResourceTimingEntries("resources/white.mp4") == 1 &&
+            numberOfResourceTimingEntries("resources/sound_5.oga") == 1 &&
+            numberOfResourceTimingEntries("resources/foo.vtt") == 1 &&
+            numberOfResourceTimingEntries("resources/dummy.xml?foo=bar") == 0 &&
+            numberOfResourceTimingEntries("resources/dummy.xml?novalue") == 0 &&
+            numberOfResourceTimingEntries("resources/dummy.xml") == 1) {
+            done();
+        }
+        iterations++;
+        if (iterations == 10) {
+            // At least one is expected to fail, but this should give details to the exact failure(s).
             verifyNumberOfResourceTimingEntries("resources/dummy.js", 1);
             verifyNumberOfResourceTimingEntries("resources/dummy.css", 1);
             verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 1);
@@ -29,8 +43,15 @@
             verifyNumberOfResourceTimingEntries("resources/dummy.xml?foo=bar", 0);
             verifyNumberOfResourceTimingEntries("resources/dummy.xml?novalue", 0);
             verifyNumberOfResourceTimingEntries("resources/dummy.xml", 1);
-            t.done();
-        }, 5000);
-    }));
+            done();
+        } else {
+            step_timeout(check_finished, 500);
+        }
+    }
+
+    window.addEventListener("load", function() {
+        verifyPreloadAndRTSupport();
+        step_timeout(check_finished, 500);
+    });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html b/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html
index 087a342..7047115 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html
@@ -1,18 +1,31 @@
 <!DOCTYPE html>
+<title>Makes sure that Link headers on subresources preload resources</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/preload/resources/preload_helper.js"></script>
-<script>
-    var t = async_test('Makes sure that Link headers on subresources preload resources');
-</script>
 <link rel=stylesheet href="resources/dummy-preloads-subresource.css?link-header-on-subresource">
 <script>
-    window.addEventListener("load", t.step_func(function() {
-        t.step_timeout(function() {
-            verifyPreloadAndRTSupport();
+    setup({explicit_done: true});
+
+    var iterations = 0;
+
+    function check_finished() {
+        if (numberOfResourceTimingEntries("/fonts/CanvasTest.ttf?link-header-on-subresource") == 1) {
+            done();
+        }
+        iterations++;
+        if (iterations == 10) {
+             // This is expected to fail, but this should give details to the exact failure.
             verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf?link-header-on-subresource", 1);
-            t.done();
-        }, 5000);
-    }));
+            done();
+        } else {
+            step_timeout(check_finished, 500);
+        }
+    }
+
+    window.addEventListener("load", function() {
+        verifyPreloadAndRTSupport();
+        step_timeout(check_finished, 500);
+    });
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/preload/onerror-event.html b/third_party/blink/web_tests/external/wpt/preload/onerror-event.html
index 8190be87..2038e06 100644
--- a/third_party/blink/web_tests/external/wpt/preload/onerror-event.html
+++ b/third_party/blink/web_tests/external/wpt/preload/onerror-event.html
@@ -1,12 +1,10 @@
 <!DOCTYPE html>
 <html>
-<head></head>
-<body>
+<title>Makes sure that preloaded resources trigger the onerror event</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/preload/resources/preload_helper.js"></script>
 <script>
-    var t = async_test('Makes sure that preloaded resources trigger the onerror event');
     var scriptFailed = false;
     var styleFailed = false;
     var imageFailed = false;
@@ -28,10 +26,20 @@
 <link rel=preload href="non-existent/dummy.xml?foo" as=foobarxmlthing onerror="gibberishFailed = true;">
 <link rel=preload href="non-existent/dummy.xml?fetch" as=fetch onerror="fetchFailed = true;">
 <link rel=preload href="non-existent/dummy.xml?empty" onerror="emptyFailed = true;">
+<body>
 <script>
-    window.onload = t.step_func(function() {
-        t.step_timeout(function() {
-            verifyPreloadAndRTSupport();
+    setup({explicit_done: true});
+
+    var iterations = 0;
+
+    function check_finished() {
+        if (styleFailed && scriptFailed && imageFailed && fontFailed && videoFailed && audioFailed &&
+            trackFailed && !gibberishFailed && fetchFailed && !emptyFailed) {
+            done();
+        }
+        iterations++;
+        if (iterations == 10) {
+            // At least one is expected to fail, but this should give details to the exact failure(s).
             assert_true(styleFailed, "style triggered error event");
             assert_true(scriptFailed, "script triggered error event");
             assert_true(imageFailed, "image triggered error event");
@@ -42,8 +50,15 @@
             assert_false(gibberishFailed, "gibberish as value did not trigger error event");
             assert_true(fetchFailed, "fetch as triggered error event");
             assert_false(emptyFailed, "empty as triggered error event");
-            t.done();
-        }, 5000);
+            done();
+        } else {
+            step_timeout(check_finished, 500);
+        }
+    }
+
+    window.addEventListener("load", function() {
+        verifyPreloadAndRTSupport();
+        step_timeout(check_finished, 500);
     });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html b/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
index 65e9b3a..62d0c710 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
@@ -15,41 +15,41 @@
 <link rel=preload href="resources/dummy.xml">
 <body>
 <script>
-setup({explicit_done: true});
+    setup({explicit_done: true});
 
-var iterations = 0;
+    var iterations = 0;
 
-function check_finished() {
-    if (numberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js") == 0 &&
-        numberOfResourceTimingEntries("resources/dummy.css") == 0 &&
-        numberOfResourceTimingEntries("resources/square.png") == 0 &&
-        numberOfResourceTimingEntries("/fonts/CanvasTest.ttf") == 0 &&
-        numberOfResourceTimingEntries("resources/white.mp4") == 0 &&
-        numberOfResourceTimingEntries("resources/sound_5.oga") == 0 &&
-        numberOfResourceTimingEntries("resources/foo.vtt") == 0 &&
-        numberOfResourceTimingEntries("resources/dummy.xml") == 0) {
-        done();
+    function check_finished() {
+        if (numberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js") == 0 &&
+            numberOfResourceTimingEntries("resources/dummy.css") == 0 &&
+            numberOfResourceTimingEntries("resources/square.png") == 0 &&
+            numberOfResourceTimingEntries("/fonts/CanvasTest.ttf") == 0 &&
+            numberOfResourceTimingEntries("resources/white.mp4") == 0 &&
+            numberOfResourceTimingEntries("resources/sound_5.oga") == 0 &&
+            numberOfResourceTimingEntries("resources/foo.vtt") == 0 &&
+            numberOfResourceTimingEntries("resources/dummy.xml") == 0) {
+            done();
+        }
+        iterations++;
+        if (iterations == 10) {
+            // At least one is expected to fail, but this should give details to the exact failure(s).
+            verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
+            verifyNumberOfResourceTimingEntries("resources/square.png", 0);
+            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
+            verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
+            verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
+            verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
+            done();
+        } else {
+            step_timeout(check_finished, 500);
+        }
     }
-    iterations++;
-    if (iterations == 10) {
-        // At least one is expected to fail, but this should give details to the exact failure(s).
-        verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
-        verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
-        verifyNumberOfResourceTimingEntries("resources/square.png", 0);
-        verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
-        verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
-        verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
-        verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
-        verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
-        done();
-    } else {
+
+    window.addEventListener("load", function() {
+        verifyPreloadAndRTSupport();
         step_timeout(check_finished, 500);
-    }
-}
-
-window.addEventListener("load", function() {
-    verifyPreloadAndRTSupport();
-    step_timeout(check_finished, 500);
-});
+    });
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html b/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
index 92343145..9fc1194 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
@@ -15,41 +15,41 @@
 <link rel=preload href="resources/dummy.xml">
 <body>
 <script>
-setup({explicit_done: true});
+    setup({explicit_done: true});
 
-var iterations = 0;
+    var iterations = 0;
 
-function check_finished() {
-    if (numberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js") == 0 &&
-        numberOfResourceTimingEntries("resources/dummy.css") == 0 &&
-        numberOfResourceTimingEntries("resources/square.png") == 0 &&
-        numberOfResourceTimingEntries("/fonts/CanvasTest.ttf") == 0 &&
-        numberOfResourceTimingEntries("resources/white.mp4") == 0 &&
-        numberOfResourceTimingEntries("resources/sound_5.oga") == 0 &&
-        numberOfResourceTimingEntries("resources/foo.vtt") == 0 &&
-        numberOfResourceTimingEntries("resources/dummy.xml") == 0) {
-        done();
+    function check_finished() {
+        if (numberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js") == 0 &&
+            numberOfResourceTimingEntries("resources/dummy.css") == 0 &&
+            numberOfResourceTimingEntries("resources/square.png") == 0 &&
+            numberOfResourceTimingEntries("/fonts/CanvasTest.ttf") == 0 &&
+            numberOfResourceTimingEntries("resources/white.mp4") == 0 &&
+            numberOfResourceTimingEntries("resources/sound_5.oga") == 0 &&
+            numberOfResourceTimingEntries("resources/foo.vtt") == 0 &&
+            numberOfResourceTimingEntries("resources/dummy.xml") == 0) {
+            done();
+        }
+        iterations++;
+        if (iterations == 10) {
+            // At least one is expected to fail, but this should give details to the exact failure(s).
+            verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
+            verifyNumberOfResourceTimingEntries("resources/square.png", 0);
+            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
+            verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
+            verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
+            verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
+            done();
+        } else {
+            step_timeout(check_finished, 500);
+        }
     }
-    iterations++;
-    if (iterations == 10) {
-        // At least one is expected to fail, but this should give details to the exact failure(s).
-        verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
-        verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
-        verifyNumberOfResourceTimingEntries("resources/square.png", 0);
-        verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
-        verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
-        verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
-        verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
-        verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
-        done();
-    } else {
+
+    window.addEventListener("load", function() {
+        verifyPreloadAndRTSupport();
         step_timeout(check_finished, 500);
-    }
-}
-
-window.addEventListener("load", function() {
-    verifyPreloadAndRTSupport();
-    step_timeout(check_finished, 500);
-});
+    });
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/iframe-src-change.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/iframe-src-change.html
new file mode 100644
index 0000000..15202a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/iframe-src-change.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+function nextMessage() {
+  return new Promise((resolve, reject) => {
+    window.addEventListener('message', e => resolve(e.data), {once: true});
+  });
+}
+
+promise_test(async () => {
+  let iframe = document.createElement('iframe');
+  iframe.setAttribute('src', '/common/security-features/subresource/document.py?first')
+  iframe.setAttribute('referrerpolicy', 'no-referrer');
+  document.body.appendChild(iframe);
+
+  try {
+    {
+      let {referrer: documentReferrer, headers: {referer: httpReferrer}} = await nextMessage();
+      assert_equals(httpReferrer, undefined, 'expected no HTTP Referer header on initial load');
+      assert_equals(documentReferrer, undefined, 'expected no document.referrer on initial load');
+    }
+
+    iframe.setAttribute('src', '/common/security-features/subresource/document.py?second');
+
+    {
+      let {referrer: documentReferrer, headers: {referer: httpReferrer}} = await nextMessage();
+      assert_equals(httpReferrer, undefined, 'expected no HTTP Referer header on src change');
+      assert_equals(documentReferrer, undefined, 'expected no document.referrer on src change');
+    }
+  } finally {
+    iframe.remove();
+  }
+}, "Checks that referrerpolicy is respected when an iframe's src changes.");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
index 454b086..3b6949a 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -161,6 +161,7 @@
           "wasm": kwargs.get("wasm", True),
           "verify": kwargs["verify"],
           "headless": "MOZ_HEADLESS" in os.environ,
+          "fission": get_bool_pref("fission.autostart"),
           "sw-e10s": get_bool_pref("dom.serviceWorkers.parent_intercept")}
     rv.update(run_info_browser_version(kwargs["binary"]))
     return rv
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-insecure-context.any.js b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-insecure-context.any.js
new file mode 100644
index 0000000..f32cc3c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-insecure-context.any.js
@@ -0,0 +1,5 @@
+//META: title=Wake Lock API is not exposed in an insecure context
+
+test(() => {
+  assert_false("WakeLock" in self, "'WakeLock' must not be exposed");
+}, "Wake Lock API is not exposed in an insecure context");
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-insecure-context.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-insecure-context.html
deleted file mode 100644
index 2c61f41..0000000
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-insecure-context.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>Wake Lock API is not exposed in an insecure context</title>
-<link rel="help" href="https://w3c.github.io/wake-lock/#wake-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-
-test(t => {
-  assert_false("WakeLock" in self, "'WakeLock' must not be exposed");
-}, "Wake Lock API is not exposed in an insecure context");
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker-expected.txt
new file mode 100644
index 0000000..7019eb3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Screen wake lock should not be allowed in dedicated worker WakeLock is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker.js b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker.js
new file mode 100644
index 0000000..28e3394
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-screen-type-on-worker.https.worker.js
@@ -0,0 +1,8 @@
+//META: title=Screen wake lock should not be allowed in dedicated worker
+importScripts("/resources/testharness.js");
+
+promise_test(t => {
+  return promise_rejects(t, "NotAllowedError", WakeLock.request('screen'));
+}, "Screen wake lock should not be allowed in dedicated worker");
+
+done();
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-state-is-global.https.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-state-is-global.https.html
deleted file mode 100644
index 2eee319..0000000
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-state-is-global.https.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>wake lock state should be global</title>
-<link rel="help" href="https://w3c.github.io/wake-lock/">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<body>
-<script id="iframe" type="text/plain">
-let iframeWakeLock;
-const controller = new AbortController();
-const signal = controller.signal;
-window.onmessage = async message => {
-  switch(message.data) {
-  case "ACQUIRED":
-    iframeWakeLock = new WakeLock("screen");
-    await iframeWakeLock.request({ signal });
-    parent.postMessage(iframeWakeLock.active, "*");
-    break;
-  case "RELEASED":
-    controller.abort();
-    parent.postMessage(iframeWakeLock.active, "*");
-    break;
-  default:
-    parent.postMessage("unknown operation", "*");
-  }
-}
-</script>
-
-<script>
-function load_iframe() {
-  return new Promise(resolve => {
-    const iframe = document.createElement("iframe");
-    iframe.onload = () => { resolve(iframe); };
-    iframe.srcdoc = "<script>" +
-                    document.getElementById('iframe').textContent +
-                    "<\/script>";
-    document.body.appendChild(iframe);
-  });
-}
-
-function wait_for_message(iframe) {
-  return new Promise(resolve => {
-    self.addEventListener("message", function listener(e) {
-      if (e.source === iframe.contentWindow) {
-        resolve(e.data);
-        self.removeEventListener("message", listener);
-      }
-    });
-  });
-}
-
-promise_test(async t => {
-  const wakeLock = await new WakeLock("screen");
-  const iframe = await load_iframe();
-  const eventWatcher = new EventWatcher(t, wakeLock, "activechange");
-
-  assert_false(wakeLock.active, "wakeLock is initially false");
-
-  //when iframe wake lock is acquired, parent wake lock should be actived
-  iframe.contentWindow.postMessage("ACQUIRED", "*");
-  const isActive1 = await wait_for_message(iframe);
-  await eventWatcher.wait_for("activechange");
-  assert_true(isActive1, "the iframe wake lock state is actived when iframe wake lock is acquired");
-  assert_true(wakeLock.active, "the wake lock state is actived when iframe wake lock is acquired");
-
-  //when iframe wake lock is released, parent wake lock should be inactived
-  iframe.contentWindow.postMessage("RELEASED", "*");
-  const isActive2 = await wait_for_message(iframe);
-  eventWatcher.wait_for("activechange");
-  assert_false(isActive2, "the iframe wake lock state is inactived when iframe wake lock is released");
-  assert_false(wakeLock.active, "the wake lock state is inactived when iframe wake lock is released");
-}, "Test that wake lock state should be global");
-</script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.js b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.js
new file mode 100644
index 0000000..cc37c76
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.js
@@ -0,0 +1,19 @@
+//META: title=WakeLock.request() with invalid type
+
+promise_test(async t => {
+  await promise_rejects(t, new TypeError(), WakeLock.request());
+}, "'TypeError' is thrown when set an empty wake lock type");
+
+promise_test(t => {
+  const invalidTypes = [
+    "invalid",
+    null,
+    123,
+    {},
+    "",
+    true
+  ];
+  return Promise.all(invalidTypes.map(invalidType => {
+    return promise_rejects(t, new TypeError(), WakeLock.request(invalidType));
+  }));
+}, "'TypeError' is thrown when set an invalid wake lock type");
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.worker-expected.txt
new file mode 100644
index 0000000..9d738db3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.any.worker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL 'TypeError' is thrown when set an empty wake lock type promise_test: Unhandled rejection with value: object "ReferenceError: WakeLock is not defined"
+FAIL 'TypeError' is thrown when set an invalid wake lock type WakeLock is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.html b/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.html
deleted file mode 100644
index df7a68a0..0000000
--- a/third_party/blink/web_tests/external/wpt/wake-lock/wakelock-type.https.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>WakeLockType Test</title>
-<link rel="help" href="https://w3c.github.io/wake-lock/">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<script>
-
-test(() => {
-  const wakeLock = new WakeLock("screen");
-  assert_equals(wakeLock.type, "screen");
-}, "Test that wakeLock.type is 'screen' when screen wake lock is invoked");
-
-test(() => {
-  const wakeLock = new WakeLock("system");
-  assert_equals(wakeLock.type, "system");
-}, "Test that wakeLock.type is 'system' when system wake lock is invoked");
-
-test(() => {
-  assert_throws(new TypeError(), () => new WakeLock());
-}, "'TypeError' is thrown when set an empty wake lock type");
-
-test(() => {
-  const invalidTypes = [
-    "invalid",
-    null,
-    123,
-    {},
-    "",
-    true
-  ];
-  invalidTypes.map(invalidType => {
-    assert_throws(new TypeError(), () => new WakeLock(invalidType));
-  })
-}, "'TypeError' is thrown when set an invalid wake lock type");
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/xhr/resources/access-control-basic-options-not-supported.py b/third_party/blink/web_tests/external/wpt/xhr/resources/access-control-basic-options-not-supported.py
index 0c69c76..77b274e9 100644
--- a/third_party/blink/web_tests/external/wpt/xhr/resources/access-control-basic-options-not-supported.py
+++ b/third_party/blink/web_tests/external/wpt/xhr/resources/access-control-basic-options-not-supported.py
@@ -3,7 +3,10 @@
 
     # Allow simple requests, but deny preflight
     if request.method != "OPTIONS":
-        response.headers.set("Access-Control-Allow-Credentials", "true")
-        response.headers.set("Access-Control-Allow-Origin", request.headers.get("origin"))
+        if "origin" in request.headers:
+            response.headers.set("Access-Control-Allow-Credentials", "true")
+            response.headers.set("Access-Control-Allow-Origin", request.headers["origin"])
+        else:
+            response.status = 500
     else:
         response.status = 400
diff --git a/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties-expected.txt b/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties-expected.txt
index a81e194..c7661c5 100644
--- a/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL domain assert_equals: expected (undefined) undefined but got (string) "web-platform.test"
+PASS domain
 PASS URL
 PASS documentURI
 PASS baseURI
diff --git a/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties.htm b/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties.htm
index b008348..9071fab 100644
--- a/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties.htm
+++ b/third_party/blink/web_tests/external/wpt/xhr/responsexml-document-properties.htm
@@ -12,9 +12,11 @@
       var client = new XMLHttpRequest()
       client.open("GET", "resources/well-formed.xml", false)
       client.send(null)
-      var responseURL = new URL('resources/well-formed.xml', location.href).href
+      var responseURLObject = new URL('resources/well-formed.xml', location.href);
+      var responseURL = responseURLObject.href
+      var responseDomain = responseURLObject.hostname
       var expected = {
-        domain:undefined,
+        domain:responseDomain,
         URL:responseURL,
         documentURI:responseURL,
         baseURI:responseURL,
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt
deleted file mode 100644
index 0d88251..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt
+++ /dev/null
@@ -1,358 +0,0 @@
-
-
-container{
-  "paths": [
-    {
-      "path": [
-        "M",
-        80,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        80,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        80,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        80,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        80,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        80,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        0,
-        0,
-        "L",
-        800,
-        0,
-        "L",
-        800,
-        420,
-        "L",
-        0,
-        420,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "tagName": "div",
-    "idValue": "container",
-    "nodeWidth": "300",
-    "nodeHeight": "300"
-  }
-}
-child{
-  "paths": [
-    {
-      "path": [
-        "M",
-        220,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        200,
-        "L",
-        220,
-        200,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        140,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        270,
-        "L",
-        140,
-        270,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        140,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        270,
-        "L",
-        140,
-        270,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        140,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        140,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "tagName": "div",
-    "idValue": "child",
-    "nodeWidth": "240",
-    "nodeHeight": "220"
-  }
-}
-span{
-  "paths": [
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "tagName": "span",
-    "idValue": "span",
-    "nodeWidth": "10",
-    "nodeHeight": "70"
-  }
-}
-TEXT{
-  "paths": [
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "nodeWidth": "10",
-    "nodeHeight": "70",
-    "tagName": "#text"
-  }
-}
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt
deleted file mode 100644
index 19c8be8..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl-expected.txt
+++ /dev/null
@@ -1,358 +0,0 @@
-
-
-container{
-  "paths": [
-    {
-      "path": [
-        "M",
-        80,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        80,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        80,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        80,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        80,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        80,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        0,
-        0,
-        "L",
-        440,
-        0,
-        "L",
-        440,
-        420,
-        "L",
-        0,
-        420,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "tagName": "div",
-    "idValue": "container",
-    "nodeWidth": "300",
-    "nodeHeight": "300"
-  }
-}
-child{
-  "paths": [
-    {
-      "path": [
-        "M",
-        220,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        200,
-        "L",
-        220,
-        200,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        140,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        270,
-        "L",
-        140,
-        270,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        140,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        270,
-        "L",
-        140,
-        270,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        140,
-        50,
-        "L",
-        380,
-        50,
-        "L",
-        380,
-        350,
-        "L",
-        140,
-        350,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "tagName": "div",
-    "idValue": "child",
-    "nodeWidth": "240",
-    "nodeHeight": "220"
-  }
-}
-span{
-  "paths": [
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "tagName": "span",
-    "idValue": "span",
-    "nodeWidth": "10",
-    "nodeHeight": "70"
-  }
-}
-TEXT{
-  "paths": [
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 0, 0, 0)",
-      "outlineColor": "rgba(128, 0, 0, 0)",
-      "name": "content"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 255, 0, 0)",
-      "name": "padding"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(0, 0, 255, 0)",
-      "name": "border"
-    },
-    {
-      "path": [
-        "M",
-        310,
-        100,
-        "L",
-        320,
-        100,
-        "L",
-        320,
-        170,
-        "L",
-        310,
-        170,
-        "Z"
-      ],
-      "fillColor": "rgba(255, 255, 255, 0)",
-      "name": "margin"
-    }
-  ],
-  "showRulers": true,
-  "showExtensionLines": true,
-  "elementInfo": {
-    "nodeWidth": "10",
-    "nodeHeight": "70",
-    "tagName": "#text"
-  }
-}
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl.js b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl.js
deleted file mode 100644
index 372151e8..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-node-vertical-rl.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2019 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.addResult(`\n`);
-  await TestRunner.loadModule('elements_test_runner');
-  await TestRunner.loadModule('console_test_runner');
-  await TestRunner.showPanel('elements');
-  await TestRunner.loadHTML(`
-    <script src="/js-test-resources/ahem.js"></script>
-    <style>
-    body {
-      margin: 0;
-      font: 10px/10px Ahem;
-    }
-    #container {
-      margin: 50px 60px 70px 80px;
-      width: 300px;
-      height: 300px;
-      writing-mode: vertical-rl;
-    }
-    #child {
-      padding: 50px 60px 70px 80px;
-      width: 100px;
-      height: 100px;
-    }
-    </style>
-    <div id="container">
-      <div id="child">
-        <span id="span">ABCDEFG</span>
-      </div>
-    </div>
-  `);
-
-  await TestRunner.evaluateInPagePromise('');
-  function dumpHighlight(id) {
-    return new Promise(resolve => ElementsTestRunner.dumpInspectorHighlightJSON(id, resolve));
-  }
-  await dumpHighlight('container');
-  await dumpHighlight('child');
-  await dumpHighlight('span');
-
-  let textNode = await ElementsTestRunner.findNodePromise(node => node.nodeValue() == 'ABCDEFG');
-  let result = await TestRunner.OverlayAgent.getHighlightObjectForTest(textNode.id);
-  TestRunner.addResult('TEXT' + JSON.stringify(result, null, 2));
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/preload/download-resources-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/preload/download-resources-expected.txt
new file mode 100644
index 0000000..066ba1f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/preload/download-resources-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources are downloaded Uncaught Error: assert_equals: resources/white.mp4 expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..aa88efb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event Uncaught Error: assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..aa88efb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event Uncaught Error: assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..aa88efb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event Uncaught Error: assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..aa88efb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event Uncaught Error: assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/mac-retina/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..0ef49ad
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/preload/download-resources-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/preload/download-resources-expected.txt
new file mode 100644
index 0000000..066ba1f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/preload/download-resources-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources are downloaded Uncaught Error: assert_equals: resources/white.mp4 expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..aa88efb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event Uncaught Error: assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..aa88efb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event Uncaught Error: assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win7/external/wpt/preload/onerror-event-expected.txt b/third_party/blink/web_tests/platform/win7/external/wpt/preload/onerror-event-expected.txt
new file mode 100644
index 0000000..aa88efb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/external/wpt/preload/onerror-event-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Makes sure that preloaded resources trigger the onerror event Uncaught Error: assert_true: video triggered error event expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/webxr_test_pages/webxr-samples/js/xrray-module.js b/third_party/webxr_test_pages/webxr-samples/js/xrray-module.js
index ea5b134..218967e 100644
--- a/third_party/webxr_test_pages/webxr-samples/js/xrray-module.js
+++ b/third_party/webxr_test_pages/webxr-samples/js/xrray-module.js
@@ -42,7 +42,7 @@
   for(let col = 0; col < 4; ++col) {
     for(let row = 0; row < 4; ++row) {
       for(let k = 0; k < 4; ++k) {
-        result[col * 4 + row] += rhs[col][k] * lhs[k][row];
+        result[col * 4 + row] += rhs[col * 4 + k] * lhs[k * 4 + row];
       }
     }
   }
@@ -89,7 +89,7 @@
       // second column
       axis.x * axis.y * one_minus_cos_angle - axis.z * sin_angle,
       cos_angle + axis.y * axis.y * one_minus_cos_angle,
-      axis.z * axis.y * one_minus_cos_angle + x * sin_angle,
+      axis.z * axis.y * one_minus_cos_angle + axis.x * sin_angle,
       0,
       // third column
       axis.x * axis.z * one_minus_cos_angle + axis.y * sin_angle,
@@ -108,7 +108,6 @@
 
 export class XRRay {
   constructor() {
-
     if (arguments.length > 0 && arguments[0] instanceof XRRigidTransform) {
       if(arguments.length != 1)
         throw new Error("Invalid number of arguments!");
@@ -139,7 +138,7 @@
       ]);
 
       const initial_ray_direction = [0, 0, -1];
-      const desired_ray_direction = [direction.x, direction.y, direction.z];
+      const desired_ray_direction = [this.direction_.x, this.direction_.y, this.direction_.z];
 
       const cos_angle = dot(initial_ray_direction, desired_ray_direction);
 
@@ -153,14 +152,14 @@
         const axis = new DOMPointReadOnly(1, 0, 0, 0);
         cos_angle = -1;
 
-        this.matrix_ = rotate(this.matrix_, axis, Math.acos(angle));
+        this.matrix_ = rotate(this.matrix_, axis, Math.acos(cos_angle));
       } else {
         // Rotation needed - create it from axis-angle.
-        const cross_initial_desired = cross(initialRayDirection, desiredRayDirection);
+        const cross_initial_desired = cross(initial_ray_direction, desired_ray_direction);
         const axis = normalizeLength(new DOMPointReadOnly(
           cross_initial_desired[0], cross_initial_desired[1], cross_initial_desired[2], 0));
 
-        this.matrix_ = rotate(this.matrix_, axis, Math.acos(angle));
+        this.matrix_ = rotate(this.matrix_, axis, Math.acos(cos_angle));
       }
     }
 
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
index d97424d2..59b0e43f 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
@@ -294,7 +294,7 @@
       }
 
       function DOMPointFromVec4(vector) {
-        return { x : vector[0], y : vector[1], z : vector[2], w : vector[3]}
+        return new DOMPointReadOnly(vector[0], vector[1], vector[2], vector[3]);
       }
 
       let rayOrigin = vec4.create();
diff --git a/tools/gritsettings/translation_expectations.pyl b/tools/gritsettings/translation_expectations.pyl
index 83c03e53..dbe60d3 100644
--- a/tools/gritsettings/translation_expectations.pyl
+++ b/tools/gritsettings/translation_expectations.pyl
@@ -83,6 +83,7 @@
     "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": "Separate release process",
     "components/components_locale_settings.grd": "Not UI strings; localized separately",
     "components/omnibox/resources/omnibox_resources.grd": "Not UI strings; localized separately",
+    "third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd" : "Devtools UI strings that are optionally enabled by browser.",
     "tools/grit/grit/testdata/buildinfo.grd": "Test data",
     "tools/grit/grit/testdata/chrome/app/generated_resources.grd": "Test data",
     "tools/grit/grit/testdata/substitute.grd": "Test data",
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5833ef05..c487a20 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -50749,6 +50749,9 @@
 </enum>
 
 <enum name="SCTCanBeChecked">
+  <obsolete>
+    Deprecated after 2019-04.
+  </obsolete>
   <int value="0" label="No valid STH"/>
   <int value="1" label="Requires newer STH"/>
   <int value="2" label="Can be checked for inclusion"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 154a5fa..f1a3731 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -5111,8 +5111,8 @@
   </summary>
 </histogram>
 
-<histogram name="Arc.CupsPrinting.PageCount" expires_after="2019-06-20">
-  <owner>skau@google.com</owner>
+<histogram name="Arc.CupsPrinting.PageCount" expires_after="2020-01-20">
+  <owner>skau@chromium.org</owner>
   <owner>vkuzkokov@google.com</owner>
   <summary>
     The number of pages sent to a native printer by ARC. Reported for every
@@ -22239,7 +22239,7 @@
 </histogram>
 
 <histogram name="CrosUsb.NotificationClosed" enum="CrosUsbNotificationClosed"
-    expires_after="2019-07-01">
+    expires_after="2021-07-01">
   <owner>benwells@chromium.org</owner>
   <owner>nverne@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
@@ -51809,7 +51809,7 @@
 </histogram>
 
 <histogram base="true" name="MachineLearningService.CpuTimeMicrosec"
-    units="microseconds">
+    units="microseconds" expires_after="2020-02-01">
 <!-- Name completed by histogram_suffixes
      name="MachineLearningServiceRequests" -->
 
@@ -51821,7 +51821,8 @@
 </histogram>
 
 <histogram name="MachineLearningService.CpuUsageMilliPercent"
-    units="1/1000ths of %" expires_after="2019-07-01">
+    units="1/1000ths of %" expires_after="2020-02-01">
+  <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
     Fraction of total CPU resources used by Chrome OS ML Service, sampled every
@@ -51830,7 +51831,8 @@
 </histogram>
 
 <histogram name="MachineLearningService.CreateGraphExecutorResult.Event"
-    enum="MachineLearningServiceCreateGraphExecutorResultEvent">
+    enum="MachineLearningServiceCreateGraphExecutorResultEvent"
+    expires_after="2020-02-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
@@ -51839,7 +51841,7 @@
 </histogram>
 
 <histogram base="true" name="MachineLearningService.ElapsedTimeMicrosec"
-    units="microseconds">
+    units="microseconds" expires_after="2020-02-01">
 <!-- Name completed by histogram_suffixes
      name="MachineLearningServiceRequests" -->
 
@@ -51849,7 +51851,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.ExecuteResult.Event"
-    enum="MachineLearningServiceExecuteResultEvent">
+    enum="MachineLearningServiceExecuteResultEvent" expires_after="2020-02-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
@@ -51858,7 +51860,8 @@
 </histogram>
 
 <histogram name="MachineLearningService.LoadModelResult.Event"
-    enum="MachineLearningServiceLoadModelResultEvent">
+    enum="MachineLearningServiceLoadModelResultEvent"
+    expires_after="2020-02-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
@@ -51867,7 +51870,8 @@
 </histogram>
 
 <histogram name="MachineLearningService.MojoConnectionEvent"
-    enum="MachineLearningServiceMojoConnectionEvent" expires_after="2019-07-01">
+    enum="MachineLearningServiceMojoConnectionEvent" expires_after="2020-02-01">
+  <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
     Events related to the connection and disconnection of the Mojo IPC channel
@@ -51876,7 +51880,8 @@
 </histogram>
 
 <histogram name="MachineLearningService.PeakPrivateMemoryKb" units="KB"
-    expires_after="2019-07-01">
+    expires_after="2020-02-01">
+  <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
     Peak private (non-shared) memory used by Chrome OS ML Service over the last
@@ -51885,7 +51890,7 @@
 </histogram>
 
 <histogram base="true" name="MachineLearningService.PrivateMemoryDeltaKb"
-    units="KB">
+    units="KB" expires_after="2020-02-01">
 <!-- Name completed by histogram_suffixes
      name="MachineLearningServiceRequests" -->
 
@@ -51897,7 +51902,8 @@
 </histogram>
 
 <histogram name="MachineLearningService.PrivateMemoryKb" units="KB"
-    expires_after="2019-07-01">
+    expires_after="2020-02-01">
+  <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
     Private (non-shared) memory used by Chrome OS ML Service, sampled every 5
@@ -53202,10 +53208,10 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.StreamBrokerDisconnectReason2"
-    enum="AudioOutputStreamDisconnectReason2" expires_after="2019-06-01">
-  <owner>jonasolsson@chromium.org</owner>
-  <owner>maxmorin@chromium.org</owner>
+    enum="AudioOutputStreamDisconnectReason2" expires_after="2019-09-01">
   <owner>olka@chromium.org</owner>
+  <owner>grunell@chromium.org</owner>
+  <owner>maxmorin@chromium.org</owner>
   <summary>
     Describes why and in which state an audio output stream ended.
   </summary>
@@ -65464,7 +65470,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.CanInclusionCheckSCT"
-    enum="SCTCanBeChecked" expires_after="M78">
+    enum="SCTCanBeChecked" expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>estark@chromium.org</owner>
   <owner>rsleevi@chromium.org</owner>
   <summary>
@@ -65599,7 +65608,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.DnsQueryAuditProofError"
-    enum="NetErrorCodes" expires_after="M78">
+    enum="NetErrorCodes" expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>robpercival@chromium.org</owner>
   <summary>
     Counts of specific error codes returned by LogDnsClient at the end of an
@@ -65609,7 +65621,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.DnsQueryAuditProofRcode"
-    enum="AsyncDNSRcode">
+    enum="AsyncDNSRcode" expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>robpercival@chromium.org</owner>
   <summary>
     Counts of specific DNS response codes returned by LogDnsClient at the end of
@@ -65620,7 +65635,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.DnsQueryDuration" units="ms"
-    expires_after="M78">
+    expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>robpercival@chromium.org</owner>
   <summary>
     The time taken attempting to obtain an inclusion proof from a Certificate
@@ -65630,7 +65648,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.DnsQueryDuration.Success"
-    units="ms" expires_after="M78">
+    units="ms" expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>robpercival@chromium.org</owner>
   <summary>
     The time taken to successfully obtain an inclusion proof from a Certificate
@@ -65640,7 +65661,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.DnsQueryLeafIndexError"
-    enum="NetErrorCodes">
+    enum="NetErrorCodes" expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>robpercival@chromium.org</owner>
   <summary>
     Counts of specific error codes returned by LogDnsClient at the end of an
@@ -65650,7 +65674,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.DnsQueryLeafIndexRcode"
-    enum="AsyncDNSRcode" expires_after="M78">
+    enum="AsyncDNSRcode" expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>robpercival@chromium.org</owner>
   <summary>
     Counts of specific DNS response codes returned by LogDnsClient at the end of
@@ -65720,7 +65747,10 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.InclusionCheckResult"
-    enum="CTLogEntryInclusionCheckResult" expires_after="M78">
+    enum="CTLogEntryInclusionCheckResult" expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>estark@chromium.org</owner>
   <owner>rsleevi@chromium.org</owner>
   <summary>
@@ -65744,7 +65774,11 @@
   </summary>
 </histogram>
 
-<histogram name="Net.CertificateTransparency.PilotSTHAge" units="ms">
+<histogram name="Net.CertificateTransparency.PilotSTHAge" units="ms"
+    expires_after="M76">
+  <obsolete>
+    Removed in Chrome 76.
+  </obsolete>
   <owner>estark@chromium.org</owner>
   <owner>rsleevi@chromium.org</owner>
   <summary>
@@ -73600,7 +73634,7 @@
 </histogram>
 
 <histogram name="Net.SSLTLS13Downgrade" enum="BooleanDowngrade"
-    expires_after="2019-06-30">
+    expires_after="2019-12-31">
   <owner>svaldez@chromium.org</owner>
   <owner>davidben@chromium.org</owner>
   <summary>
@@ -73611,7 +73645,7 @@
 </histogram>
 
 <histogram name="Net.SSLTLS13DowngradeTLS13Experiment" enum="BooleanDowngrade"
-    expires_after="2019-06-30">
+    expires_after="2019-12-31">
   <owner>svaldez@chromium.org</owner>
   <owner>davidben@chromium.org</owner>
   <summary>
@@ -73623,7 +73657,7 @@
 </histogram>
 
 <histogram name="Net.SSLTLS13DowngradeType" enum="TLS13DowngradeType"
-    expires_after="2019-06-30">
+    expires_after="2019-12-31">
   <owner>svaldez@chromium.org</owner>
   <owner>davidben@chromium.org</owner>
   <summary>
@@ -73633,7 +73667,7 @@
 </histogram>
 
 <histogram name="Net.SSLTLS13DowngradeTypeTLS13Experiment"
-    enum="TLS13DowngradeType" expires_after="2019-06-30">
+    enum="TLS13DowngradeType" expires_after="2019-12-31">
   <owner>svaldez@chromium.org</owner>
   <owner>davidben@chromium.org</owner>
   <summary>
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 5551a94..208b227 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -37,6 +37,9 @@
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d.html [ Skip ]
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-sub-texture.html [ Skip ]
 crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-texture.html [ Skip ]
+crbug.com/967809 [ Android_Webview ] blink_perf.canvas/draw-video-to-hw-accelerated-canvas-2d_RAF.html?RAF [ Skip ]
+crbug.com/967809 [ Android_Webview ] blink_perf.canvas/upload-video-to-sub-texture_RAF.html?RAF [ Skip ]
+crbug.com/967809 [ Android_Webview ] blink_perf.canvas/upload-video-to-texture_RAF.html?RAF [ Skip ]
 
 # Benchmark: blink_perf.css
 crbug.com/891878 [ Nexus5X_Webview ] blink_perf.css/CustomPropertiesVarAlias.html [ Skip ]
@@ -248,6 +251,7 @@
 crbug.com/954948 [ Android_Webview ] rendering.mobile/cnn_mobile_pinch_2018 [ Skip ]
 crbug.com/966637 [ Pixel2_Webview ] rendering.mobile/google_news_mobile_2018 [ Skip ]
 crbug.com/966631 [ Pixel_2 ] rendering.mobile/* [ Skip ] # mass disabling to set up the bot for pixel 2
+crbug.com/967809 [ Android_Webview ] rendering.mobile/microsoft_video_city [ Skip ]
 
 # Benchmark: rasterize_and_record_micro.top_25
 crbug.com/764543 [ All ] rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ]
diff --git a/ui/base/clipboard/test/test_clipboard.cc b/ui/base/clipboard/test/test_clipboard.cc
index 6fa3c0e..6e299a4 100644
--- a/ui/base/clipboard/test/test_clipboard.cc
+++ b/ui/base/clipboard/test/test_clipboard.cc
@@ -8,6 +8,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "skia/ext/skia_utils_base.h"
 #include "ui/base/clipboard/clipboard_constants.h"
 #include "ui/base/clipboard/clipboard_monitor.h"
@@ -39,6 +40,12 @@
 
 bool TestClipboard::IsFormatAvailable(const ClipboardFormatType& format,
                                       ClipboardType type) const {
+#if defined(OS_LINUX)
+  // The linux clipboard treats the presence of text on the clipboard
+  // as the url format being available.
+  if (format.Equals(ClipboardFormatType::GetUrlType()))
+    return IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), type);
+#endif  // OS_LINUX
   const DataStore& store = GetStore(type);
   return store.data.find(format) != store.data.end();
 }
diff --git a/ui/gfx/mojo/rrect_f_struct_traits.h b/ui/gfx/mojo/rrect_f_struct_traits.h
index 46f97d2..017abcb 100644
--- a/ui/gfx/mojo/rrect_f_struct_traits.h
+++ b/ui/gfx/mojo/rrect_f_struct_traits.h
@@ -89,9 +89,10 @@
     gfx::Vector2dF upper_left;
     if (!data.ReadUpperLeft(&upper_left))
       return false;
-    *out = gfx::RRectF(rect, upper_left.x(), upper_left.y());
-    if (type <= gfx::RRectF::Type::kSimple)
+    if (type <= gfx::RRectF::Type::kSimple) {
+      *out = gfx::RRectF(rect, upper_left.x(), upper_left.y());
       return true;
+    }
     gfx::Vector2dF upper_right;
     gfx::Vector2dF lower_right;
     gfx::Vector2dF lower_left;
@@ -100,9 +101,9 @@
         !data.ReadLowerLeft(&lower_left)) {
       return false;
     }
-    out->SetCornerRadii(gfx::RRectF::Corner::kUpperRight, upper_right);
-    out->SetCornerRadii(gfx::RRectF::Corner::kLowerRight, lower_right);
-    out->SetCornerRadii(gfx::RRectF::Corner::kLowerLeft, lower_left);
+    *out = gfx::RRectF(rect, upper_left.x(), upper_left.y(), upper_right.x(),
+                       upper_right.y(), lower_right.x(), lower_right.y(),
+                       lower_left.x(), lower_left.y());
     return true;
   }
 };
diff --git a/ui/gfx/mojo/struct_traits_unittest.cc b/ui/gfx/mojo/struct_traits_unittest.cc
index dcc1add1..d0102eb 100644
--- a/ui/gfx/mojo/struct_traits_unittest.cc
+++ b/ui/gfx/mojo/struct_traits_unittest.cc
@@ -273,6 +273,12 @@
   EXPECT_EQ(input.GetType(), RRectF::Type::kOval);
   proxy->EchoRRectF(input, &output);
   EXPECT_EQ(input, output);
+  input.SetCornerRadii(RRectF::Corner::kUpperLeft, 50, 50);
+  input.SetCornerRadii(RRectF::Corner::kUpperRight, 20, 20);
+  input.SetCornerRadii(RRectF::Corner::kLowerRight, 0, 0);
+  input.SetCornerRadii(RRectF::Corner::kLowerLeft, 0, 0);
+  proxy->EchoRRectF(input, &output);
+  EXPECT_EQ(input, output);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/rrect_f.cc b/ui/gfx/rrect_f.cc
index 7d3a6255..4f08af2 100644
--- a/ui/gfx/rrect_f.cc
+++ b/ui/gfx/rrect_f.cc
@@ -42,11 +42,19 @@
                float lower_right_x,
                float lower_right_y,
                float lower_left_x,
-               float lower_left_y)
-    : RRectF(x, y, width, height, upper_left_x, upper_left_y) {
-  SetCornerRadii(RRectF::Corner::kUpperRight, upper_right_x, upper_right_y);
-  SetCornerRadii(RRectF::Corner::kLowerRight, lower_right_x, lower_right_y);
-  SetCornerRadii(RRectF::Corner::kLowerLeft, lower_left_x, lower_left_y);
+               float lower_left_y) {
+  SkVector radii[4] = {
+      {upper_left_x, upper_left_y},
+      {upper_right_x, upper_right_y},
+      {lower_right_x, lower_right_y},
+      {lower_left_x, lower_left_y},
+  };
+  skrrect_.setRectRadii(SkRect::MakeXYWH(x, y, width, height), radii);
+  if (IsEmpty()) {
+    // Make sure that empty rects are created fully empty, not with some
+    // non-zero dimensions.
+    skrrect_ = SkRRect::MakeEmpty();
+  }
 }
 
 gfx::Vector2dF RRectF::GetSimpleRadii() const {
diff --git a/ui/gl/direct_composition_surface_win.cc b/ui/gl/direct_composition_surface_win.cc
index ceb798c..b749445e 100644
--- a/ui/gl/direct_composition_surface_win.cc
+++ b/ui/gl/direct_composition_surface_win.cc
@@ -31,11 +31,6 @@
 
 namespace gl {
 namespace {
-struct OverlaySupportInfo {
-  DXGI_FORMAT format;
-  UINT flags;
-};
-
 // Indicates support for either NV12 or YUY2 hardware overlays.
 bool g_supports_overlays = false;
 
@@ -47,12 +42,15 @@
 // overlays using command line flags.
 DXGI_FORMAT g_overlay_format_used = DXGI_FORMAT_NV12;
 
-// This is the raw support info, which shouldn't depend on field trial state, or
-// command line flags. Ordered by most preferred to least preferred format.
-OverlaySupportInfo g_overlay_support_info[] = {
-    {DXGI_FORMAT_NV12, 0},
-    {DXGI_FORMAT_YUY2, 0},
-};
+// These are the raw support info, which shouldn't depend on field trial state,
+// or command line flags.
+UINT g_nv12_overlay_support_flags = 0;
+UINT g_yuy2_overlay_support_flags = 0;
+
+bool FlagsSupportsOverlays(UINT flags) {
+  return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
+                   DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
+}
 
 void InitializeHardwareOverlaySupport() {
   static bool overlay_support_initialized = false;
@@ -95,7 +93,6 @@
     return;
   }
 
-  bool supports_nv12_rec709 = false;
   unsigned int i = 0;
   while (true) {
     Microsoft::WRL::ComPtr<IDXGIOutput> output;
@@ -106,62 +103,47 @@
     if (FAILED(output.As(&output3)))
       continue;
     DCHECK(output3);
+    output3->CheckOverlaySupport(DXGI_FORMAT_NV12, d3d11_device.Get(),
+                                 &g_nv12_overlay_support_flags);
+    output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, d3d11_device.Get(),
+                                 &g_yuy2_overlay_support_flags);
+    if (FlagsSupportsOverlays(g_nv12_overlay_support_flags) &&
+        base::FeatureList::IsEnabled(
+            features::kDirectCompositionPreferNV12Overlays)) {
+      // NV12 format is preferred if it's supported.
 
-    // TODO(zmo): Get rid of the loop and just set two global vars for NV12
-    // and YUY2.
-    for (auto& info : g_overlay_support_info) {
-      if (FAILED(output3->CheckOverlaySupport(info.format, d3d11_device.Get(),
-                                              &info.flags))) {
-        continue;
-      }
       // Per Intel's request, use NV12 only when
       // COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 is also supported. Rec 709 is
       // commonly used for H.264 and HEVC. At least one Intel Gen9 SKU will not
       // support NV12 overlays.
-      if (info.format == DXGI_FORMAT_NV12) {
-        UINT color_space_support_flags = 0;
-        Microsoft::WRL::ComPtr<IDXGIOutput4> output4;
-        if (FAILED(output.As(&output4)))
-          continue;
-
-        if (FAILED(output4->CheckOverlayColorSpaceSupport(
-                info.format, DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
-                d3d11_device.Get(), &color_space_support_flags))) {
-          continue;
-        }
-        supports_nv12_rec709 =
-            !!(color_space_support_flags &
-               DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
-      }
-
-      // Formats are ordered by most preferred to least preferred. Don't choose
-      // a less preferred format, but keep going so that we can record overlay
-      // support for all formats in UMA.
-      if (g_supports_overlays)
-        continue;
-      // Overlays are supported for NV12 only if the feature flag to prefer NV12
-      // over YUY2 is enabled.
-      bool prefer_nv12 = base::FeatureList::IsEnabled(
-          features::kDirectCompositionPreferNV12Overlays);
-      if (info.format == DXGI_FORMAT_NV12 &&
-          (!prefer_nv12 || !supports_nv12_rec709))
-        continue;
-      // Some new Intel drivers only claim to support unscaled overlays, but
-      // scaled overlays still work. It's possible DWM works around it by
-      // performing an extra scaling Blt before calling the driver. Even when
-      // scaled overlays aren't actually supported, presentation using the
-      // overlay path should be relatively efficient.
-      if (info.flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
-                        DXGI_OVERLAY_SUPPORT_FLAG_SCALING)) {
-        g_overlay_format_used = info.format;
-
+      UINT color_space_support_flags = 0;
+      Microsoft::WRL::ComPtr<IDXGIOutput4> output4;
+      if (SUCCEEDED(output.As(&output4)) &&
+          SUCCEEDED(output4->CheckOverlayColorSpaceSupport(
+              DXGI_FORMAT_NV12, DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
+              d3d11_device.Get(), &color_space_support_flags)) &&
+          (color_space_support_flags &
+           DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) {
+        // Some new Intel drivers only claim to support unscaled overlays, but
+        // scaled overlays still work. It's possible DWM works around it by
+        // performing an extra scaling Blt before calling the driver. Even when
+        // scaled overlays aren't actually supported, presentation using the
+        // overlay path should be relatively efficient.
+        g_overlay_format_used = DXGI_FORMAT_NV12;
         g_supports_overlays = true;
-
-        DXGI_OUTPUT_DESC monitor_desc = {};
-        if (SUCCEEDED(output3->GetDesc(&monitor_desc))) {
-          g_overlay_monitor_size =
-              gfx::Rect(monitor_desc.DesktopCoordinates).size();
-        }
+      }
+    }
+    if (!g_supports_overlays &&
+        FlagsSupportsOverlays(g_yuy2_overlay_support_flags)) {
+      // If NV12 isn't supported, fallback to YUY2 if it's supported.
+      g_overlay_format_used = DXGI_FORMAT_YUY2;
+      g_supports_overlays = true;
+    }
+    if (g_supports_overlays) {
+      DXGI_OUTPUT_DESC monitor_desc = {};
+      if (SUCCEEDED(output3->GetDesc(&monitor_desc))) {
+        g_overlay_monitor_size =
+            gfx::Rect(monitor_desc.DesktopCoordinates).size();
       }
     }
     // Early out after the first output that reports overlay support. All
@@ -278,30 +260,20 @@
 
 // static
 bool DirectCompositionSurfaceWin::AreScaledOverlaysSupported() {
-  for (const auto& info : g_overlay_support_info) {
-    if (info.format == g_overlay_format_used)
-      return !!(info.flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING);
-  }
-  NOTREACHED();
-  return false;
+  InitializeHardwareOverlaySupport();
+  if (g_overlay_format_used == DXGI_FORMAT_NV12)
+    return !!(g_nv12_overlay_support_flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING);
+  DCHECK_EQ(DXGI_FORMAT_YUY2, g_overlay_format_used);
+  return !!(g_yuy2_overlay_support_flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING);
 }
 
 // static
-bool DirectCompositionSurfaceWin::SupportsOverlayFormat(
-    DXGI_FORMAT format,
-    bool* supports_scaling) {
+UINT DirectCompositionSurfaceWin::GetOverlaySupportFlags(DXGI_FORMAT format) {
   InitializeHardwareOverlaySupport();
-  DCHECK(supports_scaling);
-  *supports_scaling = false;
-  for (const auto& info : g_overlay_support_info) {
-    if (info.flags) {
-      if (format == info.format) {
-        *supports_scaling = !!(info.flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING);
-        return true;
-      }
-    }
-  }
-  return false;
+  if (format == DXGI_FORMAT_NV12)
+    return g_nv12_overlay_support_flags;
+  DCHECK_EQ(DXGI_FORMAT_YUY2, format);
+  return g_yuy2_overlay_support_flags;
 }
 
 // static
@@ -317,17 +289,18 @@
 // static
 void DirectCompositionSurfaceWin::SetScaledOverlaysSupportedForTesting(
     bool supported) {
-  for (auto& info : g_overlay_support_info) {
-    if (supported)
-      info.flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
-    else
-      info.flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
+  if (supported) {
+    g_nv12_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
+    g_yuy2_overlay_support_flags |= DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
+  } else {
+    g_nv12_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
+    g_yuy2_overlay_support_flags &= ~DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
   }
 }
 
 // static
-void DirectCompositionSurfaceWin::SetPreferNV12OverlaysForTesting() {
-  g_overlay_format_used = DXGI_FORMAT_NV12;
+void DirectCompositionSurfaceWin::SetPreferYUY2OverlaysForTesting() {
+  g_overlay_format_used = DXGI_FORMAT_YUY2;
 }
 
 // static
diff --git a/ui/gl/direct_composition_surface_win.h b/ui/gl/direct_composition_surface_win.h
index 18277e7d..6cf4ca5a 100644
--- a/ui/gl/direct_composition_surface_win.h
+++ b/ui/gl/direct_composition_surface_win.h
@@ -64,17 +64,17 @@
   // Returns monitor size.
   static gfx::Size GetOverlayMonitorSize();
 
-  // Returns if the given format is supported for hardware overlays.
-  // Also, if |supports_scaling| is set to true, SCALING mode is supported for
-  // the given format; otherwise, only DIRECT mode is supported.
-  static bool SupportsOverlayFormat(DXGI_FORMAT format, bool* supports_scaling);
+  // Returns overlay support flags for the given format.
+  // Caller should check for DXGI_OVERLAY_SUPPORT_FLAG_DIRECT and
+  // DXGI_OVERLAY_SUPPORT_FLAG_SCALING bits.
+  static UINT GetOverlaySupportFlags(DXGI_FORMAT format);
 
   // Returns true if there is an HDR capable display connected.
   static bool IsHDRSupported();
 
   static void SetScaledOverlaysSupportedForTesting(bool value);
 
-  static void SetPreferNV12OverlaysForTesting();
+  static void SetPreferYUY2OverlaysForTesting();
 
   bool InitializeNativeWindow();
 
diff --git a/ui/gl/direct_composition_surface_win_unittest.cc b/ui/gl/direct_composition_surface_win_unittest.cc
index 6315b63..e09996b 100644
--- a/ui/gl/direct_composition_surface_win_unittest.cc
+++ b/ui/gl/direct_composition_surface_win_unittest.cc
@@ -600,6 +600,48 @@
     DirectCompositionSurfaceTest::TearDown();
   }
 
+  void InitializeForPixelTest(const gfx::Size& window_size,
+                              const gfx::Size& texture_size,
+                              const gfx::Rect& content_rect,
+                              const gfx::Rect& quad_rect) {
+    EXPECT_TRUE(surface_->Resize(window_size, 1.0,
+                                 GLSurface::ColorSpace::UNSPECIFIED, true));
+    EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
+
+    glClearColor(0.0, 0.0, 0.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
+        QueryD3D11DeviceObjectFromANGLE();
+
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
+        CreateNV12Texture(d3d11_device, texture_size, true);
+    Microsoft::WRL::ComPtr<IDXGIResource1> resource;
+    texture.As(&resource);
+    HANDLE handle = 0;
+    resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr,
+                                 &handle);
+    // The format doesn't matter, since we aren't binding.
+    scoped_refptr<GLImageDXGI> image_dxgi(
+        new GLImageDXGI(texture_size, nullptr));
+    ASSERT_TRUE(image_dxgi->InitializeHandle(base::win::ScopedHandle(handle), 0,
+                                             gfx::BufferFormat::RGBA_8888));
+
+    // Pass content rect with odd with and height.  Surface should round up
+    // width and height when creating swap chain.
+    ui::DCRendererLayerParams params;
+    params.y_image = image_dxgi;
+    params.uv_image = image_dxgi;
+    params.content_rect = content_rect;
+    params.quad_rect = quad_rect;
+    surface_->ScheduleDCLayer(params);
+
+    EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
+              surface_->SwapBuffers(base::DoNothing()));
+
+    Sleep(1000);
+  }
+
   void PixelTestSwapChain(bool layers_enabled) {
     if (!surface_)
       return;
@@ -782,36 +824,10 @@
     return;
 
   gfx::Size window_size(100, 100);
-  EXPECT_TRUE(surface_->Resize(window_size, 1.0,
-                               GLSurface::ColorSpace::UNSPECIFIED, true));
-
-  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
-      QueryD3D11DeviceObjectFromANGLE();
-
   gfx::Size texture_size(50, 50);
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
-      CreateNV12Texture(d3d11_device, texture_size, true);
-  Microsoft::WRL::ComPtr<IDXGIResource1> resource;
-  texture.As(&resource);
-  HANDLE handle = 0;
-  resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr,
-                               &handle);
-  // The format doesn't matter, since we aren't binding.
-  scoped_refptr<GLImageDXGI> image_dxgi(new GLImageDXGI(texture_size, nullptr));
-  ASSERT_TRUE(image_dxgi->InitializeHandle(base::win::ScopedHandle(handle), 0,
-                                           gfx::BufferFormat::RGBA_8888));
-
-  ui::DCRendererLayerParams params;
-  params.y_image = image_dxgi;
-  params.uv_image = image_dxgi;
-  params.content_rect = gfx::Rect(texture_size);
-  params.quad_rect = gfx::Rect(window_size);
-  surface_->ScheduleDCLayer(params);
-
-  EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
-            surface_->SwapBuffers(base::DoNothing()));
-
-  Sleep(1000);
+  gfx::Rect content_rect(texture_size);
+  gfx::Rect quad_rect(window_size);
+  InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
 
   SkColor expected_color = SkColorSetRGB(0xe1, 0x90, 0xeb);
   SkColor actual_color =
@@ -826,40 +842,10 @@
     return;
 
   gfx::Size window_size(100, 100);
-  EXPECT_TRUE(surface_->Resize(window_size, 1.0,
-                               GLSurface::ColorSpace::UNSPECIFIED, true));
-  EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
-
-  glClearColor(0.0, 0.0, 0.0, 1.0);
-  glClear(GL_COLOR_BUFFER_BIT);
-
-  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
-      QueryD3D11DeviceObjectFromANGLE();
-
   gfx::Size texture_size(50, 50);
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
-      CreateNV12Texture(d3d11_device, texture_size, true);
-  Microsoft::WRL::ComPtr<IDXGIResource1> resource;
-  texture.As(&resource);
-  HANDLE handle = 0;
-  resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr,
-                               &handle);
-  // The format doesn't matter, since we aren't binding.
-  scoped_refptr<GLImageDXGI> image_dxgi(new GLImageDXGI(texture_size, nullptr));
-  ASSERT_TRUE(image_dxgi->InitializeHandle(base::win::ScopedHandle(handle), 0,
-                                           gfx::BufferFormat::RGBA_8888));
-
-  // Layer with empty bounds rect.
-  ui::DCRendererLayerParams params;
-  params.y_image = image_dxgi;
-  params.uv_image = image_dxgi;
-  params.content_rect = gfx::Rect(texture_size);
-  surface_->ScheduleDCLayer(params);
-
-  EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
-            surface_->SwapBuffers(base::DoNothing()));
-
-  Sleep(1000);
+  gfx::Rect content_rect(texture_size);
+  gfx::Rect quad_rect;  // Layer with empty bounds rect.
+  InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
 
   // No color is written since the visual committed to DirectComposition has no
   // content.
@@ -927,45 +913,14 @@
 TEST_F(DirectCompositionPixelTest, NV12SwapChain) {
   if (!surface_)
     return;
-  DirectCompositionSurfaceWin::SetPreferNV12OverlaysForTesting();
 
   gfx::Size window_size(100, 100);
-  EXPECT_TRUE(surface_->Resize(window_size, 1.0,
-                               GLSurface::ColorSpace::UNSPECIFIED, true));
-  EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
-
-  glClearColor(0.0, 0.0, 0.0, 1.0);
-  glClear(GL_COLOR_BUFFER_BIT);
-
-  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
-      QueryD3D11DeviceObjectFromANGLE();
-
   gfx::Size texture_size(50, 50);
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
-      CreateNV12Texture(d3d11_device, texture_size, true);
-  Microsoft::WRL::ComPtr<IDXGIResource1> resource;
-  texture.As(&resource);
-  HANDLE handle = 0;
-  resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr,
-                               &handle);
-  // The format doesn't matter, since we aren't binding.
-  scoped_refptr<GLImageDXGI> image_dxgi(new GLImageDXGI(texture_size, nullptr));
-  ASSERT_TRUE(image_dxgi->InitializeHandle(base::win::ScopedHandle(handle), 0,
-                                           gfx::BufferFormat::RGBA_8888));
-
-  // Pass content rect with odd with and height.  Surface should round up width
-  // and height when creating swap chain.
-  ui::DCRendererLayerParams params;
-  params.y_image = image_dxgi;
-  params.uv_image = image_dxgi;
-  params.content_rect = gfx::Rect(0, 0, 49, 49);
-  params.quad_rect = gfx::Rect(window_size);
-  surface_->ScheduleDCLayer(params);
-
-  EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
-            surface_->SwapBuffers(base::DoNothing()));
-
-  Sleep(1000);
+  // Pass content rect with odd with and height.  Surface should round up
+  // width and height when creating swap chain.
+  gfx::Rect content_rect(0, 0, 49, 49);
+  gfx::Rect quad_rect(window_size);
+  InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
 
   Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
       surface_->GetLayerSwapChainForTesting(0);
@@ -993,40 +948,10 @@
   DirectCompositionSurfaceWin::SetScaledOverlaysSupportedForTesting(true);
 
   gfx::Size window_size(100, 100);
-  EXPECT_TRUE(surface_->Resize(window_size, 1.0,
-                               GLSurface::ColorSpace::UNSPECIFIED, true));
-  EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
-
-  glClearColor(0.0, 0.0, 0.0, 1.0);
-  glClear(GL_COLOR_BUFFER_BIT);
-
-  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
-      QueryD3D11DeviceObjectFromANGLE();
-
   gfx::Size texture_size(50, 50);
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
-      CreateNV12Texture(d3d11_device, texture_size, true);
-  Microsoft::WRL::ComPtr<IDXGIResource1> resource;
-  texture.As(&resource);
-  HANDLE handle = 0;
-  resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr,
-                               &handle);
-  // The format doesn't matter, since we aren't binding.
-  scoped_refptr<GLImageDXGI> image_dxgi(new GLImageDXGI(texture_size, nullptr));
-  ASSERT_TRUE(image_dxgi->InitializeHandle(base::win::ScopedHandle(handle), 0,
-                                           gfx::BufferFormat::RGBA_8888));
-
-  ui::DCRendererLayerParams params;
-  params.y_image = image_dxgi;
-  params.uv_image = image_dxgi;
-  params.content_rect = gfx::Rect(texture_size);
-  params.quad_rect = gfx::Rect(gfx::Point(25, 25), texture_size);
-  surface_->ScheduleDCLayer(params);
-
-  EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
-            surface_->SwapBuffers(base::DoNothing()));
-
-  Sleep(1000);
+  gfx::Rect content_rect(texture_size);
+  gfx::Rect quad_rect(gfx::Point(25, 25), texture_size);
+  InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
 
   SkColor video_color = SkColorSetRGB(0xe1, 0x90, 0xeb);
   struct {
diff --git a/ui/gl/gl_surface_egl_surface_control.h b/ui/gl/gl_surface_egl_surface_control.h
index cd31529..3a4b9d9 100644
--- a/ui/gl/gl_surface_egl_surface_control.h
+++ b/ui/gl/gl_surface_egl_surface_control.h
@@ -193,7 +193,7 @@
   bool transaction_ack_pending_ = false;
 
   gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;
-  EGLSurface offscreen_surface_;
+  EGLSurface offscreen_surface_ = nullptr;
   base::CancelableOnceClosure check_pending_presentation_callback_queue_task_;
 
   scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc
index f60d1c9..56ba44e 100644
--- a/ui/views/controls/editable_combobox/editable_combobox.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -291,6 +291,7 @@
 }
 
 EditableCombobox::~EditableCombobox() {
+  CloseMenu();
   textfield_->set_controller(nullptr);
   textfield_->RemoveObserver(this);
 }
diff --git a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
index 1114ea17..c9f42e5 100644
--- a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
@@ -110,6 +110,10 @@
 };
 
 void EditableComboboxTest::TearDown() {
+  if (IsMenuOpen()) {
+    combobox_->GetMenuRunnerForTest()->Cancel();
+    WaitForMenuClosureAnimation();
+  }
   if (widget_)
     widget_->Close();
   ViewsTestBase::TearDown();
@@ -142,8 +146,6 @@
   dummy_focusable_view_->SetID(2);
 
   InitWidget();
-  combobox_->GetTextfieldForTest()->RequestFocus();
-  combobox_->RequestFocus();
 }
 
 // Initializes the widget where the combobox and the dummy control live.
@@ -237,8 +239,6 @@
 
 TEST_F(EditableComboboxTest, FocusOnTextfieldOpensMenu) {
   InitEditableCombobox();
-  dummy_focusable_view_->RequestFocus();
-  WaitForMenuClosureAnimation();
   EXPECT_FALSE(IsMenuOpen());
   combobox_->GetTextfieldForTest()->RequestFocus();
   EXPECT_TRUE(IsMenuOpen());
diff --git a/ui/webui/resources/cr_elements/cr_icons_css.html b/ui/webui/resources/cr_elements/cr_icons_css.html
index ca9a5cf..2b4f7d0 100644
--- a/ui/webui/resources/cr_elements/cr_icons_css.html
+++ b/ui/webui/resources/cr_elements/cr_icons_css.html
@@ -98,7 +98,7 @@
                                          .cr-icon).icon-more-vert {
         background-image: url(../images/dark/icon_more_vert.svg);
       }
-      :-webkit-any(paper-n-button, .cr-icon).icon-more-vert-light-mode {
+      :-webkit-any(cr-icon-button, .cr-icon).icon-more-vert-light-mode {
         background-image: url(../images/icon_more_vert.svg);
       }
 
diff --git a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
index 4432178..3c8530df 100644
--- a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
+++ b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
@@ -39,7 +39,10 @@
     placeholder: String,
 
     /** @type {!Array<string>} */
-    items: Array,
+    items: {
+      type: Array,
+      observer: 'onItemsChanged_',
+    },
 
     /** @type {string} */
     value: {
@@ -58,6 +61,37 @@
 
     /** @type {boolean} */
     updateValueOnInput: Boolean,
+
+    /** @private {boolean} */
+    dropdownRefitPending_: Boolean,
+  },
+
+  /**
+   * Enqueue a task to refit the iron-dropdown if it is open.
+   * @suppress {missingProperties} Property refit never defined on dropdown
+   * @private
+   */
+  enqueueDropdownRefit_: function() {
+    const dropdown = this.$$('iron-dropdown');
+    if (!this.dropdownRefitPending_ && dropdown.opened) {
+      this.dropdownRefitPending_ = true;
+      setTimeout(() => {
+        dropdown.refit();
+        this.dropdownRefitPending_ = false;
+      }, 0);
+    }
+  },
+
+  /**
+   * @param {!Array<string>} oldValue
+   * @param {!Array<string>} newValue
+   * @private
+   */
+  onItemsChanged_: function(oldValue, newValue) {
+    // Refit the iron-dropdown so that it can expand as neccessary to
+    // accommodate new items. Refitting is done on a new task because the change
+    // notification might not yet have propagated to the iron-dropdown.
+    this.enqueueDropdownRefit_();
   },
 
   /** @private */
@@ -72,6 +106,15 @@
     if (this.updateValueOnInput) {
       this.value = this.$.search.value;
     }
+
+    // iron-dropdown sets its max-height when it is opened. If the current value
+    // results in no filtered items in the drop down list, the iron-dropdown
+    // will have a max-height for 0 items. If the user then clears the input
+    // field, a non-zero number of items might be displayed in the drop-down,
+    // but the height is still limited based on 0 items. This results in a tiny,
+    // but scollable dropdown. Refitting the dropdown allows it to expand to
+    // accommodate the new items.
+    this.enqueueDropdownRefit_();
   },
 
   /**
@@ -118,4 +161,4 @@
     return errorMessage;
   },
 
-});
\ No newline at end of file
+});