diff --git a/DEPS b/DEPS
index 85b0e6e1..b951f26 100644
--- a/DEPS
+++ b/DEPS
@@ -52,7 +52,7 @@
   # 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': 'cabdd1a0314054c7e41ba1f475900d70fc52a39c',
+  'angle_revision': 'fb05bcbacca837fe9cebfe6c7649c5885db3fbbb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '6500c6faf82f636d55c9ca5682711022890bef1d',
+  'pdfium_revision': '957480c17682008ae2a14723868fcdcab89b6577',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -366,7 +366,7 @@
       Var('chromium_git') + '/external/github.com/swisspol/GCDWebServer.git' + '@' + '43555c66627f6ed44817855a0f6d465f559d30e0',
 
     'src/ios/third_party/material_components_ios/src':
-      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6bc1117ad8d9779e3d2f4006f28c675cfeada9eb',
+      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6d5844642b004594ba1607916032616f85c7c045',
 
     'src/ios/third_party/material_font_disk_loader_ios/src':
       Var('chromium_git') + '/external/github.com/material-foundation/material-font-disk-loader-ios.git' + '@' + '8e30188777b016182658fbaa0a4a020a48183224',
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
index 8625175..fdbc2148 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
@@ -82,9 +82,7 @@
         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
 
         String[] acceptLanguages = getAcceptLanguages(
-                JSUtils.executeJavaScriptAndWaitForResult(
-                        this, mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                        "document.body.textContent"));
+                getJavaScriptResultBodyTextContent(mAwContents, mContentsClient));
         assertEquals(LocaleUtils.getDefaultLocaleString(), acceptLanguages[0]);
 
         String[] acceptLanguagesJs = getAcceptLanguages(
@@ -104,9 +102,7 @@
         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
 
         acceptLanguages = getAcceptLanguages(
-                JSUtils.executeJavaScriptAndWaitForResult(
-                        this, mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                        "document.body.textContent"));
+                getJavaScriptResultBodyTextContent(mAwContents, mContentsClient));
         assertEquals(LocaleUtils.getDefaultLocaleString(), acceptLanguages[0]);
     }
 
@@ -127,9 +123,8 @@
         String url = mTestServer.getURL("/echoheader?Accept-Language");
         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
 
-        String[] acceptLanguages = getAcceptLanguages(JSUtils.executeJavaScriptAndWaitForResult(
-                this, mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                "document.body.textContent"));
+        String[] acceptLanguages = getAcceptLanguages(
+                getJavaScriptResultBodyTextContent(mAwContents, mContentsClient));
         assertEquals(
                 LocaleUtils.getDefaultLocaleListString(), TextUtils.join(",", acceptLanguages));
 
@@ -148,9 +143,8 @@
 
         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
 
-        acceptLanguages = getAcceptLanguages(JSUtils.executeJavaScriptAndWaitForResult(this,
-                mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                "document.body.textContent"));
+        acceptLanguages = getAcceptLanguages(
+                getJavaScriptResultBodyTextContent(mAwContents, mContentsClient));
         assertEquals(
                 LocaleUtils.getDefaultLocaleListString(), TextUtils.join(",", acceptLanguages));
 
@@ -161,9 +155,8 @@
 
         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
 
-        acceptLanguages = getAcceptLanguages(JSUtils.executeJavaScriptAndWaitForResult(this,
-                mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                "document.body.textContent"));
+        acceptLanguages = getAcceptLanguages(
+                getJavaScriptResultBodyTextContent(mAwContents, mContentsClient));
         assertEquals(
                 LocaleUtils.getDefaultLocaleListString(), TextUtils.join(",", acceptLanguages));
 
@@ -174,9 +167,8 @@
 
         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
 
-        acceptLanguages = getAcceptLanguages(JSUtils.executeJavaScriptAndWaitForResult(this,
-                mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                "document.body.textContent"));
+        acceptLanguages = getAcceptLanguages(
+                getJavaScriptResultBodyTextContent(mAwContents, mContentsClient));
         String[] acceptLangs = Arrays.copyOfRange(acceptLanguages, 0, acceptLanguages.length - 1);
         assertEquals(LocaleUtils.getDefaultLocaleListString(), TextUtils.join(",", acceptLangs));
     }
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 f682f148..4dd1894a 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
@@ -452,9 +452,7 @@
             extraHeaders.put("X-foo", "bar");
             loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders);
 
-            String xfoo = maybeStripDoubleQuotes(JSUtils.executeJavaScriptAndWaitForResult(this,
-                    awContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                    "document.body.textContent"));
+            String xfoo = getJavaScriptResultBodyTextContent(awContents, mContentsClient);
             assertEquals("bar", xfoo);
 
             url = testServer.getURL("/echoheader?Referer");
@@ -463,9 +461,7 @@
             extraHeaders.put("Referer", "http://www.example.com/");
             loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders);
 
-            String referer = maybeStripDoubleQuotes(JSUtils.executeJavaScriptAndWaitForResult(this,
-                    awContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                    "document.body.textContent"));
+            String referer = getJavaScriptResultBodyTextContent(awContents, mContentsClient);
             assertEquals("http://www.example.com/", referer);
         } finally {
             testServer.stopAndDestroyServer();
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 f3c772b..6c4fe83 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
@@ -1776,9 +1776,7 @@
             loadUrlSync(awContents,
                         contentClient.getOnPageFinishedHelper(),
                         url);
-            String userAgent = maybeStripDoubleQuotes(JSUtils.executeJavaScriptAndWaitForResult(
-                    this, awContents, contentClient.getOnEvaluateJavaScriptResultHelper(),
-                    "document.body.textContent"));
+            String userAgent = getJavaScriptResultBodyTextContent(awContents, contentClient);
             assertEquals(customUserAgentString, userAgent);
         } finally {
             testServer.stopAndDestroyServer();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
index 4e3165e0..92ade85 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
@@ -572,6 +572,17 @@
     }
 
     /**
+     * Executes JavaScript code within the given ContentView to get the text content in
+     * document body. Returns the result string without double quotes.
+     */
+    protected String getJavaScriptResultBodyTextContent(
+            final AwContents awContents, final TestAwContentsClient viewClient) throws Exception {
+        String raw = executeJavaScriptAndWaitForResult(
+                awContents, viewClient, "document.body.textContent");
+        return maybeStripDoubleQuotes(raw);
+    }
+
+    /**
      * Wrapper around CriteriaHelper.pollInstrumentationThread. This uses AwTestBase-specifc
      * timeouts and treats timeouts and exceptions as test failures automatically.
      */
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
index 9f402a79..5ac1e250 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
@@ -7,8 +7,10 @@
 import android.support.test.filters.SmallTest;
 import android.util.Pair;
 
+import org.json.JSONArray;
+import org.json.JSONObject;
+
 import org.chromium.android_webview.AwContents;
-import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.test.util.CommonResources;
 import org.chromium.android_webview.test.util.JSUtils;
 import org.chromium.base.annotations.SuppressFBWarnings;
@@ -17,6 +19,8 @@
 import org.chromium.content.browser.test.util.HistoryUtils;
 import org.chromium.net.test.util.TestWebServer;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -27,6 +31,21 @@
  * Test suite for loadUrl().
  */
 public class LoadUrlTest extends AwTestBase {
+    private AwEmbeddedTestServer mTestServer;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mTestServer =
+                AwEmbeddedTestServer.createAndStartServer(getInstrumentation().getTargetContext());
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mTestServer.stopAndDestroyServer();
+        super.tearDown();
+    }
+
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testDataUrl() throws Throwable {
@@ -111,20 +130,34 @@
         return result;
     }
 
-    private void validateRequestHeaders(String[] refNamesAndValues, List<String> request) {
-        List<String> matchingHeaders;
-        for (int i = 0; i < refNamesAndValues.length; i += 2) {
-            matchingHeaders = TestWebServer.getMatchingHeadersValues(request, refNamesAndValues[i]);
-            assertEquals(1, matchingHeaders.size());
-            assertEquals(refNamesAndValues[i + 1], matchingHeaders.get(0));
+    private void validateHeadersValue(final AwContents awContents,
+            final TestAwContentsClient contentsClient, String[] extraHeader,
+            boolean shouldHeaderExist) throws Exception {
+        String textContent = getJavaScriptResultBodyTextContent(awContents, contentsClient);
+        String[] header_values = textContent.split("\\\\n");
+        for (int i = 0; i < extraHeader.length; i += 2) {
+            assertEquals(shouldHeaderExist ? extraHeader[i + 1] : "None", header_values[i / 2]);
         }
     }
 
-    private void validateNoRequestHeaders(String[] refNamesAndValues, List<String> request) {
-        List<String> matchingHeaders;
-        for (int i = 0; i < refNamesAndValues.length; i += 2) {
-            matchingHeaders = TestWebServer.getMatchingHeadersValues(request, refNamesAndValues[i]);
-            assertEquals(0, matchingHeaders.size());
+    private void validateHeadersFromJson(final AwContents awContents,
+            final TestAwContentsClient contentsClient, String[] extraHeader, String jsonName,
+            boolean shouldHeaderExist) throws Exception {
+        String textContent = getJavaScriptResultBodyTextContent(awContents, contentsClient)
+                                     .replaceAll("\\\\\"", "\"");
+        JSONObject jsonObject = new JSONObject(textContent);
+        JSONArray jsonArray = jsonObject.getJSONArray(jsonName);
+        for (int i = 0; i < extraHeader.length; i += 2) {
+            String header = jsonArray.getString(i / 2);
+            assertEquals(shouldHeaderExist ? extraHeader[i + 1] : "None", header);
+        }
+    }
+
+    private final String encodeUrl(String url) {
+        try {
+            return URLEncoder.encode(url, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError(e);
         }
     }
 
@@ -135,32 +168,26 @@
         final AwTestContainerView testContainerView =
                 createAwTestContainerViewOnMainSync(contentsClient);
         final AwContents awContents = testContainerView.getAwContents();
+        enableJavaScriptOnUiThread(awContents);
 
-        TestWebServer webServer = TestWebServer.start();
-        try {
-            final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
-            webServer.setResponseBase64(imagePath,
-                    CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
-            final String path = "/load_url_with_extra_headers_test.html";
-            final String url = webServer.setResponse(
-                    path,
-                    CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
-                    null);
-            String[] extraHeaders = {
-                "X-ExtraHeaders1", "extra-header-data1",
-                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
-            };
+        String[] extraHeaders = {
+                "X-ExtraHeaders1", "extra-header-data1", "x-extraHeaders2", "EXTRA-HEADER-DATA2"};
 
-            loadUrlWithExtraHeadersSync(awContents,
-                                        contentsClient.getOnPageFinishedHelper(),
-                                        url,
-                                        createHeadersMap(extraHeaders));
-            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
-            // Verify that extra headers are only passed for the main resource.
-            validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(imagePath));
-        } finally {
-            webServer.shutdown();
-        }
+        final String url1 = mTestServer.getURL("/image-response-if-header-not-exists?resource="
+                + encodeUrl(CommonResources.FAVICON_DATA_BASE64) + "&header=" + extraHeaders[0]
+                + "&header=" + extraHeaders[2]);
+        final String url2 = mTestServer.getURL("/image-onload-html?imagesrc=" + encodeUrl(url1)
+                + "&header=" + extraHeaders[0] + "&header=" + extraHeaders[2]);
+
+        TestAwContentsClient.OnReceivedTitleHelper onReceivedTitleHelper =
+                contentsClient.getOnReceivedTitleHelper();
+        int onReceivedTitleCallCount = onReceivedTitleHelper.getCallCount();
+        loadUrlWithExtraHeadersSync(awContents, contentsClient.getOnPageFinishedHelper(), url2,
+                createHeadersMap(extraHeaders));
+        // Verify that extra headers are passed to the loaded url.
+        validateHeadersValue(awContents, contentsClient, extraHeaders, true);
+        onReceivedTitleHelper.waitForCallback(onReceivedTitleCallCount);
+        assertEquals("5", onReceivedTitleHelper.getTitle());
     }
 
     @SmallTest
@@ -171,28 +198,17 @@
                 createAwTestContainerViewOnMainSync(contentsClient);
         final AwContents awContents = testContainerView.getAwContents();
 
-        TestWebServer webServer = TestWebServer.start();
-        try {
-            final String path = "/no_overriding_of_existing_headers_test.html";
-            final String url = webServer.setResponse(
-                    path,
-                    "<html><body>foo</body></html>",
-                    null);
-            String[] extraHeaders = {"user-agent", "Borewicz 07 & Bond 007"};
+        enableJavaScriptOnUiThread(awContents);
 
-            loadUrlWithExtraHeadersSync(awContents,
-                                        contentsClient.getOnPageFinishedHelper(),
-                                        url,
-                                        createHeadersMap(extraHeaders));
-            List<String> matchingHeaders = TestWebServer.getMatchingHeadersValues(
-                    webServer.getLastRequest(path), extraHeaders[0]);
-            assertEquals(1, matchingHeaders.size());
-            // Just check that the value is there, and it's not the one we provided.
-            assertTrue(matchingHeaders.get(0).length() > 0);
-            assertFalse(extraHeaders[1].equals(matchingHeaders.get(0)));
-        } finally {
-            webServer.shutdown();
-        }
+        final String url = mTestServer.getURL("/echoheader?user-agent");
+        String[] extraHeaders = {"user-agent", "Borewicz 07 & Bond 007"};
+
+        loadUrlWithExtraHeadersSync(awContents, contentsClient.getOnPageFinishedHelper(), url,
+                createHeadersMap(extraHeaders));
+        String header = getJavaScriptResultBodyTextContent(awContents, contentsClient);
+        // Just check that the value is there, and it's not the one we provided.
+        assertFalse(header.isEmpty());
+        assertFalse(extraHeaders[1].equals(header));
     }
 
     @SmallTest
@@ -202,30 +218,17 @@
         final AwTestContainerView testContainerView =
                 createAwTestContainerViewOnMainSync(contentsClient);
         final AwContents awContents = testContainerView.getAwContents();
+        enableJavaScriptOnUiThread(awContents);
+        String[] extraHeaders = {
+                "X-ExtraHeaders1", "extra-header-data1", "x-extraHeaders2", "EXTRA-HEADER-DATA2"};
+        final String url =
+                mTestServer.getURL("/echoheader?" + extraHeaders[0] + "&" + extraHeaders[2]);
 
-        TestWebServer webServer = TestWebServer.start();
-        try {
-            final String path = "/reload_with_extra_headers_test.html";
-            final String url = webServer.setResponse(path,
-                    "<html><body>foo</body></html>",
-                    createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
-            String[] extraHeaders = {
-                "X-ExtraHeaders1", "extra-header-data1",
-                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
-            };
-
-            loadUrlWithExtraHeadersSync(awContents,
-                                        contentsClient.getOnPageFinishedHelper(),
-                                        url,
-                                        createHeadersMap(extraHeaders));
-            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
-
-            reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
-            assertEquals(2, webServer.getRequestCount(path));
-            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
-        } finally {
-            webServer.shutdown();
-        }
+        loadUrlWithExtraHeadersSync(awContents, contentsClient.getOnPageFinishedHelper(), url,
+                createHeadersMap(extraHeaders));
+        validateHeadersValue(awContents, contentsClient, extraHeaders, true);
+        reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
+        validateHeadersValue(awContents, contentsClient, extraHeaders, true);
     }
 
     @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
@@ -236,35 +239,30 @@
         final AwTestContainerView testContainerView =
                 createAwTestContainerViewOnMainSync(contentsClient);
         final AwContents awContents = testContainerView.getAwContents();
+        final String echoRedirectedUrlHeader = "echo header";
+        final String echoInitialUrlHeader = "data content";
 
-        TestWebServer webServer = TestWebServer.start();
-        try {
-            final String path = "/redirect_and_reload_with_extra_headers_test.html";
-            final String url = webServer.setResponse(path,
-                    "<html><body>foo</body></html>",
-                    createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
-            final String redirectedPath = "/redirected.html";
-            final String redirectedUrl = webServer.setRedirect(redirectedPath, path);
-            String[] extraHeaders = {
-                "X-ExtraHeaders1", "extra-header-data1",
-                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
-            };
+        enableJavaScriptOnUiThread(awContents);
 
-            loadUrlWithExtraHeadersSync(awContents,
-                                        contentsClient.getOnPageFinishedHelper(),
-                                        redirectedUrl,
-                                        createHeadersMap(extraHeaders));
-            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
-            validateRequestHeaders(extraHeaders, webServer.getLastRequest(redirectedPath));
+        String[] extraHeaders = {
+                "X-ExtraHeaders1", "extra-header-data1", "x-extraHeaders2", "EXTRA-HEADER-DATA2"};
+        final String redirectedUrl = mTestServer.getURL("/echoheader-and-set-data?header="
+                + extraHeaders[0] + "&header=" + extraHeaders[2]);
+        final String initialUrl =
+                mTestServer.getURL("/server-redirect-echoheader?url=" + encodeUrl(redirectedUrl)
+                        + "&header=" + extraHeaders[0] + "&header=" + extraHeaders[2]);
+        loadUrlWithExtraHeadersSync(awContents, contentsClient.getOnPageFinishedHelper(),
+                initialUrl, createHeadersMap(extraHeaders));
+        validateHeadersFromJson(
+                awContents, contentsClient, extraHeaders, echoRedirectedUrlHeader, true);
+        validateHeadersFromJson(
+                awContents, contentsClient, extraHeaders, echoInitialUrlHeader, true);
 
-            // WebView will only reload the main page.
-            reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
-            assertEquals(2, webServer.getRequestCount(path));
-            // No extra headers. This is consistent with legacy behavior.
-            validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(path));
-        } finally {
-            webServer.shutdown();
-        }
+        // WebView will only reload the main page.
+        reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
+        // No extra headers. This is consistent with legacy behavior.
+        validateHeadersFromJson(
+                awContents, contentsClient, extraHeaders, echoRedirectedUrlHeader, false);
     }
 
     @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
@@ -275,48 +273,31 @@
         final AwTestContainerView testContainerView =
                 createAwTestContainerViewOnMainSync(contentsClient);
         final AwContents awContents = testContainerView.getAwContents();
-        final AwSettings settings = getAwSettingsOnUiThread(awContents);
-        settings.setJavaScriptEnabled(true);
+        enableJavaScriptOnUiThread(awContents);
 
-        TestWebServer webServer = TestWebServer.start();
-        try {
-            final String nextPath = "/next.html";
-            final String nextUrl = webServer.setResponse(nextPath,
-                    "<html><body>Next!</body></html>",
-                    null);
-            final String path = "/renderer_nav_and_go_back_with_extra_headers_test.html";
-            final String url = webServer.setResponse(path,
-                    "<html><body><a id=\"next\" href=\"next.html\">Next!</a></body></html>",
-                    createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
-            String[] extraHeaders = {
-                "X-ExtraHeaders1", "extra-header-data1",
-                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
-            };
+        String[] extraHeaders = {
+                "X-ExtraHeaders1", "extra-header-data1", "x-extraHeaders2", "EXTRA-HEADER-DATA2"};
+        final String redirectedUrl =
+                mTestServer.getURL("/echoheader?" + extraHeaders[0] + "&" + extraHeaders[2]);
+        final String initialUrl =
+                mTestServer.getURL("/click-redirect?url=" + encodeUrl(redirectedUrl)
+                        + "&header=" + extraHeaders[0] + "&header=" + extraHeaders[2]);
 
-            loadUrlWithExtraHeadersSync(awContents,
-                                        contentsClient.getOnPageFinishedHelper(),
-                                        url,
-                                        createHeadersMap(extraHeaders));
-            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
+        loadUrlWithExtraHeadersSync(awContents, contentsClient.getOnPageFinishedHelper(),
+                initialUrl, createHeadersMap(extraHeaders));
+        validateHeadersValue(awContents, contentsClient, extraHeaders, true);
 
-            int currentCallCount = contentsClient.getOnPageFinishedHelper().getCallCount();
-            JSUtils.clickOnLinkUsingJs(this,
-                                       awContents,
-                                       contentsClient.getOnEvaluateJavaScriptResultHelper(),
-                                       "next");
-            contentsClient.getOnPageFinishedHelper().waitForCallback(
-                    currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            // No extra headers for the page navigated via clicking.
-            validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(nextPath));
+        int currentCallCount = contentsClient.getOnPageFinishedHelper().getCallCount();
+        JSUtils.clickOnLinkUsingJs(
+                this, awContents, contentsClient.getOnEvaluateJavaScriptResultHelper(), "click");
+        contentsClient.getOnPageFinishedHelper().waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        // No extra headers for the page navigated via clicking.
+        validateHeadersValue(awContents, contentsClient, extraHeaders, false);
 
-            HistoryUtils.goBackSync(getInstrumentation(),
-                                    awContents.getWebContents(),
-                                    contentsClient.getOnPageFinishedHelper());
-            assertEquals(2, webServer.getRequestCount(path));
-            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
-        } finally {
-            webServer.shutdown();
-        }
+        HistoryUtils.goBackSync(getInstrumentation(), awContents.getWebContents(),
+                contentsClient.getOnPageFinishedHelper());
+        validateHeadersValue(awContents, contentsClient, extraHeaders, true);
     }
 
     private static class OnReceivedTitleClient extends TestAwContentsClient {
@@ -340,8 +321,7 @@
         final AwTestContainerView testContainerView =
                 createAwTestContainerViewOnMainSync(contentsClient);
         final AwContents awContents = testContainerView.getAwContents();
-        final AwSettings settings = getAwSettingsOnUiThread(awContents);
-        settings.setJavaScriptEnabled(true);
+        enableJavaScriptOnUiThread(awContents);
 
         contentsClient.setOnReceivedTitleCallback(new Runnable() {
             @Override
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 5f84c9b..0a0b40e0 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -107,6 +107,7 @@
     "//android_webview:android_webview_crash_services_java",
     "//android_webview:android_webview_java",
     "//android_webview:android_webview_platform_services_java",
+    "//android_webview/test/embedded_test_server:aw_net_java_test_support",
     "//base:base_java",
     "//base:base_java_test_support",
     "//components/minidump_uploader:minidump_uploader_java",
@@ -223,7 +224,10 @@
   data = [
     "data/",
   ]
-  additional_apks = [ "//net/android:net_test_support_apk" ]
+  additional_apks = [
+    "//android_webview/test/embedded_test_server:aw_net_test_support_apk",
+    "//net/android:net_test_support_apk",
+  ]
 }
 
 test("android_webview_unittests") {
diff --git a/android_webview/test/embedded_test_server/BUILD.gn b/android_webview/test/embedded_test_server/BUILD.gn
new file mode 100644
index 0000000..ba86f9a
--- /dev/null
+++ b/android_webview/test/embedded_test_server/BUILD.gn
@@ -0,0 +1,72 @@
+# 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.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+
+android_library("aw_net_java_test_support") {
+  testonly = true
+  java_files = [
+    "java/src/org/chromium/android_webview/test/AwEmbeddedTestServer.java",
+    "java/src/org/chromium/android_webview/test/AwEmbeddedTestServerImpl.java",
+    "java/src/org/chromium/android_webview/test/AwEmbeddedTestServerService.java",
+  ]
+  deps = [
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//net/android:net_java",
+    "//net/android:net_java_test_support",
+  ]
+}
+
+generate_jni("aw_net_jni_headers") {
+  sources = [
+    "java/src/org/chromium/android_webview/test/AwEmbeddedTestServerImpl.java",
+  ]
+  jni_package = "android_webview/test"
+}
+
+source_set("aw_java_test_native_support") {
+  testonly = true
+  sources = [
+    "../../../net/test/android/net_test_jni_onload.cc",
+    "../../../net/test/android/net_test_jni_onload.h",
+    "../../../net/test/embedded_test_server/android/embedded_test_server_android.cc",
+    "../../../net/test/embedded_test_server/android/embedded_test_server_android.h",
+    "aw_embedded_test_server.cc",
+    "aw_embedded_test_server.h",
+    "aw_test_entry_point.cc",
+    "aw_test_jni_onload.cc",
+    "aw_test_jni_onload.h",
+  ]
+  deps = [
+    "//net:test_support",
+  ]
+  public_deps = [
+    ":aw_net_jni_headers",
+    "//net:net_test_jni_headers",
+  ]
+}
+
+shared_library("aw_net_java_test_native_support") {
+  testonly = true
+  deps = [
+    ":aw_java_test_native_support",
+    "//net:test_support",
+  ]
+}
+
+android_apk("aw_net_test_support_apk") {
+  testonly = true
+
+  # Used as an additional_apk in test scripts.
+  never_incremental = true
+  deps = [
+    ":aw_net_java_test_support",
+    "//base:base_java",
+  ]
+  android_manifest = "java/AndroidManifest.xml"
+  apk_name = "ChromiumNetTestAwSupport"
+  shared_libraries = [ ":aw_net_java_test_native_support" ]
+}
diff --git a/android_webview/test/embedded_test_server/aw_embedded_test_server.cc b/android_webview/test/embedded_test_server/aw_embedded_test_server.cc
new file mode 100644
index 0000000..73b96b3
--- /dev/null
+++ b/android_webview/test/embedded_test_server/aw_embedded_test_server.cc
@@ -0,0 +1,240 @@
+// 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 "android_webview/test/embedded_test_server/aw_embedded_test_server.h"
+
+#include <jni.h>
+
+#include "android_webview/test/jni/AwEmbeddedTestServerImpl_jni.h"
+#include "base/android/jni_array.h"
+#include "base/base64.h"
+#include "base/strings/stringprintf.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
+
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
+using net::test_server::BasicHttpResponse;
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+using net::test_server::ParseQuery;
+using net::test_server::RequestQuery;
+
+namespace android_webview {
+namespace test {
+
+namespace {
+// /click-redirect?url=URL&header=HEADER
+// Returns a href redirect to URL.
+// Responds in the message body with the headers echoed on the current request.
+std::unique_ptr<HttpResponse> HandleClickRedirect(const HttpRequest& request) {
+  if (!ShouldHandle(request, "/click-redirect"))
+    return nullptr;
+  GURL request_url = request.GetURL();
+  RequestQuery query = ParseQuery(request_url);
+
+  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  http_response->set_content_type("text/html");
+  http_response->AddCustomHeader("Cache-Control", "no-cache, no-store");
+
+  std::string url;
+  if (query.find("url") != query.end()) {
+    url = query.at("url").front();
+  }
+
+  std::string content;
+  if (query.find("header") != query.end()) {
+    for (const auto& header : query.at("header")) {
+      std::string header_value = "None";
+      if (request.headers.find(header) != request.headers.end())
+        header_value = request.headers.at(header);
+      content += header_value + "\n";
+    }
+  }
+
+  http_response->set_content(base::StringPrintf(
+      "<html><body><div>%s</div>"
+      "<a id=\"click\" href=\"%s\">Click to redirect to %s</a></body></html>",
+      content.c_str(), url.c_str(), url.c_str()));
+
+  return std::move(http_response);
+}
+
+// /echoheader-and-set-data?header=HEADER1&data=DATA
+// Responds as json in the message body with the headers echoed on the current
+// request and a content that matches DATA.
+std::unique_ptr<HttpResponse> HandleEchoHeaderAndSetData(
+    const HttpRequest& request) {
+  if (!ShouldHandle(request, "/echoheader-and-set-data"))
+    return nullptr;
+  GURL request_url = request.GetURL();
+  RequestQuery query = ParseQuery(request_url);
+
+  std::string header_content;
+  if (query.find("header") != query.end()) {
+    for (const auto& header : query.at("header")) {
+      std::string header_value = "None";
+      if (request.headers.find(header) != request.headers.end())
+        header_value = request.headers.at(header);
+      if (!header_content.empty())
+        header_content += ",";
+      header_content += "\"" + header_value + "\"";
+    }
+  }
+
+  std::string data_content;
+  if (query.find("data") != query.end()) {
+    for (const auto& data : query.at("data")) {
+      if (!data_content.empty())
+        data_content += ", ";
+      data_content += "\"" + data + "\"";
+    }
+  }
+
+  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  http_response->set_content_type("application/json");
+  http_response->AddCustomHeader("Cache-Control", "no-cache, no-store");
+  http_response->set_content(
+      base::StringPrintf("{"
+                         "  \"echo header\": [%s],"
+                         "  \"data content\": [%s]"
+                         "}",
+                         header_content.c_str(), data_content.c_str()));
+
+  return std::move(http_response);
+}
+
+// /server-redirect-echoheader?url=URL&header=HEADER
+// Returns a server-redirect (301) to URL. Pass the headers echoed on the
+// current request into the URL request.
+std::unique_ptr<HttpResponse> HandleServerRedirectEchoHeader(
+    const HttpRequest& request) {
+  if (!ShouldHandle(request, "/server-redirect-echoheader"))
+    return nullptr;
+  GURL request_url = request.GetURL();
+  RequestQuery query = ParseQuery(request_url);
+
+  std::string url;
+  if (query.find("url") != query.end()) {
+    url = query.at("url").front();
+  }
+
+  std::string url_suffix;
+  if (query.find("header") != query.end()) {
+    for (const auto& header : query.at("header")) {
+      std::string header_value = "None";
+      if (request.headers.find(header) != request.headers.end())
+        header_value = request.headers.at(header);
+      url_suffix += "&data=" + header_value;
+    }
+  }
+  url.append(url_suffix);
+
+  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  http_response->AddCustomHeader("Location", url);
+  http_response->set_content_type("text/html");
+  http_response->set_content(base::StringPrintf(
+      "<html><body>Redirecting to %s</body></html>", url.c_str()));
+  return std::move(http_response);
+}
+
+// /image-response-if-header-not-exists?resource=RESOURCE&header=HEADER
+// Returns the response with the base64 encoded image resource in the request.
+std::unique_ptr<HttpResponse> HandleSetImageResponse(
+    const HttpRequest& request) {
+  if (!ShouldHandle(request, "/image-response-if-header-not-exists"))
+    return nullptr;
+  GURL request_url = request.GetURL();
+  RequestQuery query = ParseQuery(request_url);
+
+  std::string resource;
+  if (query.find("resource") != query.end()) {
+    resource = query.at("resource").front();
+  }
+
+  bool header_exist = false;
+  if (query.find("header") != query.end()) {
+    for (const auto& header : query.at("header")) {
+      if (request.headers.find(header) != request.headers.end())
+        header_exist = true;
+      break;
+    }
+  }
+
+  std::string decoded_resource;
+  if (!base::Base64Decode(resource, &decoded_resource))
+    return nullptr;
+
+  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  http_response->set_content_type("image/png");
+  http_response->AddCustomHeader("Cache-Control", "no-store");
+  if (!header_exist)
+    http_response->set_content(decoded_resource);
+  else {
+    http_response->set_code(net::HTTP_NOT_FOUND);
+    http_response->set_content("Found Extra Header. Validation Failed.");
+  }
+  return std::move(http_response);
+}
+
+// /image-onload-html?imagesrc=URL&header=HEADER
+// Returns the response with the base64 encoded image resource in the request.
+std::unique_ptr<HttpResponse> HandleImageOnloadHtml(
+    const HttpRequest& request) {
+  if (!ShouldHandle(request, "/image-onload-html"))
+    return nullptr;
+  GURL request_url = request.GetURL();
+  RequestQuery query = ParseQuery(request_url);
+
+  std::string image_url;
+  if (query.find("imagesrc") != query.end()) {
+    image_url = query.at("imagesrc").front();
+  }
+
+  std::string content;
+  if (query.find("header") != query.end()) {
+    for (const auto& header : query.at("header")) {
+      std::string header_value = "None";
+      if (request.headers.find(header) != request.headers.end())
+        header_value = request.headers.at(header);
+      content += header_value + "\n";
+    }
+  }
+
+  std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
+  http_response->set_content_type("text/html");
+  http_response->set_content(base::StringPrintf(
+      "<html><head><script>function updateTitle() "
+      "{ document.title=document.getElementById('img').naturalHeight } "
+      "</script></head><body><div>%s</div>"
+      "<div onload='updateTitle();'><img id='img' onload='updateTitle();' "
+      "src='%s'></div></body></html>",
+      content.c_str(), image_url.c_str()));
+  return std::move(http_response);
+}
+
+}  // namespace
+
+// static
+ScopedJavaLocalRef<jlongArray> GetHandlers(JNIEnv* env,
+                                           const JavaParamRef<jclass>&) {
+  std::vector<int64_t> handlers = {
+      reinterpret_cast<int64_t>(&HandleClickRedirect),
+      reinterpret_cast<int64_t>(&HandleEchoHeaderAndSetData),
+      reinterpret_cast<int64_t>(&HandleServerRedirectEchoHeader),
+      reinterpret_cast<int64_t>(&HandleSetImageResponse),
+      reinterpret_cast<int64_t>(&HandleImageOnloadHtml)};
+  return base::android::ToJavaLongArray(env, handlers);
+}
+
+// static
+bool RegisterCustomHandlers(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace test
+}  // namespace android_webview
diff --git a/android_webview/test/embedded_test_server/aw_embedded_test_server.h b/android_webview/test/embedded_test_server/aw_embedded_test_server.h
new file mode 100644
index 0000000..77fd9445
--- /dev/null
+++ b/android_webview/test/embedded_test_server/aw_embedded_test_server.h
@@ -0,0 +1,18 @@
+// 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 ANDROID_WEBVIEW_TEST_EMBEDDED_TEST_SERVER_AW_EMBEDDED_TEST_SERVER_H_
+#define ANDROID_WEBVIEW_TEST_EMBEDDED_TEST_SERVER_AW_EMBEDDED_TEST_SERVER_H_
+
+#include <jni.h>
+
+namespace android_webview {
+namespace test {
+
+bool RegisterCustomHandlers(JNIEnv* env);
+
+}  // namespace test
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_TEST_EMBEDDED_TEST_SERVER_AW_EMBEDDED_TEST_SERVER_H_
diff --git a/android_webview/test/embedded_test_server/aw_test_entry_point.cc b/android_webview/test/embedded_test_server/aw_test_entry_point.cc
new file mode 100644
index 0000000..de530ba
--- /dev/null
+++ b/android_webview/test/embedded_test_server/aw_test_entry_point.cc
@@ -0,0 +1,17 @@
+// 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 "android_webview/test/embedded_test_server/aw_test_jni_onload.h"
+#include "base/android/jni_android.h"
+
+// This is called by the VM when the shared library is first loaded.
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  base::android::InitVM(vm);
+  JNIEnv* env = base::android::AttachCurrentThread();
+  if (!android_webview::test::OnJNIOnLoadRegisterJNI(env) ||
+      !android_webview::test::OnJNIOnLoadInit()) {
+    return -1;
+  }
+  return JNI_VERSION_1_4;
+}
diff --git a/android_webview/test/embedded_test_server/aw_test_jni_onload.cc b/android_webview/test/embedded_test_server/aw_test_jni_onload.cc
new file mode 100644
index 0000000..3edd223
--- /dev/null
+++ b/android_webview/test/embedded_test_server/aw_test_jni_onload.cc
@@ -0,0 +1,35 @@
+// 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 "android_webview/test/embedded_test_server/aw_test_jni_onload.h"
+
+#include "android_webview/test/embedded_test_server/aw_embedded_test_server.h"
+#include "base/android/base_jni_onload.h"
+#include "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/bind.h"
+#include "net/test/android/net_test_jni_onload.h"
+#include "net/test/embedded_test_server/android/embedded_test_server_android.h"
+
+namespace android_webview {
+namespace test {
+
+namespace {
+
+bool RegisterJNI(JNIEnv* env) {
+  return android_webview::test::RegisterCustomHandlers(env);
+}
+
+}  // namesapce
+
+bool OnJNIOnLoadRegisterJNI(JNIEnv* env) {
+  return net::test::OnJNIOnLoadRegisterJNI(env) && RegisterJNI(env);
+}
+
+bool OnJNIOnLoadInit() {
+  return base::android::OnJNIOnLoadInit();
+}
+
+}  // namespace test
+}  // namespace android_webview
diff --git a/android_webview/test/embedded_test_server/aw_test_jni_onload.h b/android_webview/test/embedded_test_server/aw_test_jni_onload.h
new file mode 100644
index 0000000..4193779
--- /dev/null
+++ b/android_webview/test/embedded_test_server/aw_test_jni_onload.h
@@ -0,0 +1,19 @@
+// 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 ANDROID_WEBVIEW_TEST_EMBEDDED_TEST_SERVER_AW_TEST_JNI_ONLOAD_H_
+#define ANDROID_WEBVIEW_TEST_EMBEDDED_TEST_SERVER_AW_TEST_JNI_ONLOAD_H_
+
+#include <jni.h>
+
+namespace android_webview {
+namespace test {
+
+bool OnJNIOnLoadRegisterJNI(JNIEnv* env);
+bool OnJNIOnLoadInit();
+
+}  // namespace test
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_TEST_EMBEDDED_TEST_SERVER_AW_TEST_JNI_ONLOAD_H_
diff --git a/android_webview/test/embedded_test_server/java/AndroidManifest.xml b/android_webview/test/embedded_test_server/java/AndroidManifest.xml
new file mode 100644
index 0000000..aee9ecb9
--- /dev/null
+++ b/android_webview/test/embedded_test_server/java/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<!-- package name must be unique. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="org.chromium.android_webview.test.support">
+
+    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <application android:label="ChromiumNetTestAwSupport">
+
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name="org.chromium.android_webview.test.AwEmbeddedTestServerService"
+                android:exported="true"
+                tools:ignore="ExportedService">
+            <intent-filter android:action="org.chromium.net.test.EMBEDDED_TEST_SERVER_SERVICE" />
+        </service>
+
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServer.java b/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServer.java
new file mode 100644
index 0000000..0cb2dbbb
--- /dev/null
+++ b/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServer.java
@@ -0,0 +1,49 @@
+// 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.
+
+package org.chromium.android_webview.test;
+
+import android.content.Context;
+import android.content.Intent;
+
+import org.chromium.net.test.EmbeddedTestServer;
+
+/** A simple file server for java android webview tests which extends
+ *  from class EmbeddedTestServer. It is able to add custom handlers
+ *  which registers with net::test_server::EmbeddedTestServer native code.
+ *
+ * An example use:
+ *   AwEmbeddedTestServer s = AwEmbeddedTestServer.createAndStartServer(context);
+ *
+ *   // serve requests...
+ *   s.getURL("/foo/bar.txt");
+ *
+ *   s.stopAndDestroyServer();
+ *
+ * Note that this runs net::test_server::EmbeddedTestServer in a service in a separate APK.
+ */
+public class AwEmbeddedTestServer extends EmbeddedTestServer {
+    /** Set intent package and class name that will pass to the service.
+     *
+     *  @param intent The intent to use to pass into the service.
+     */
+    @Override
+    protected void setIntentClassName(Intent intent) {
+        intent.setClassName("org.chromium.android_webview.test.support",
+                "org.chromium.android_webview.test.AwEmbeddedTestServerService");
+    }
+
+    /** Create and initialize a server with the default and custom handlers.
+     *
+     *  This handles native object initialization, server configuration, and server initialization.
+     *  On returning, the server is ready for use.
+     *
+     *  @param context The context in which the server will run.
+     *  @return The created server.
+     */
+    public static AwEmbeddedTestServer createAndStartServer(Context context)
+            throws InterruptedException {
+        return initializeAndStartServer(new AwEmbeddedTestServer(), context);
+    }
+}
diff --git a/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServerImpl.java b/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServerImpl.java
new file mode 100644
index 0000000..4fa6711
--- /dev/null
+++ b/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServerImpl.java
@@ -0,0 +1,40 @@
+// 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.
+
+package org.chromium.android_webview.test;
+
+import android.content.Context;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.net.test.EmbeddedTestServerImpl;
+
+/**
+ * Java bindings for running a net::test_server::EmbeddedTestServer.
+ *
+ * This should not be used directly. Use {@link EmbeddedTestServer} instead.
+ */
+@JNINamespace("android_webview::test")
+public class AwEmbeddedTestServerImpl extends EmbeddedTestServerImpl {
+    /** Create an uninitialized EmbeddedTestServer. */
+    public AwEmbeddedTestServerImpl(Context context) {
+        super(context);
+    }
+
+    /** Add the default handlers and serve files from the provided directory relative to the
+     *  external storage directory.
+     *
+     *  @param directoryPath The path of the directory from which files should be served, relative
+     *      to the external storage directory.
+     */
+    @Override
+    public void addDefaultHandlers(final String directoryPath) {
+        super.addDefaultHandlers(directoryPath);
+        long[] handlers = nativeGetHandlers();
+        for (long handler : handlers) {
+            super.registerRequestHandler(handler);
+        }
+    }
+
+    private static native long[] nativeGetHandlers();
+}
diff --git a/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServerService.java b/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServerService.java
new file mode 100644
index 0000000..c50f1d9
--- /dev/null
+++ b/android_webview/test/embedded_test_server/java/src/org/chromium/android_webview/test/AwEmbeddedTestServerService.java
@@ -0,0 +1,19 @@
+// 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.
+
+package org.chromium.android_webview.test;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * A {@link android.app.Service} that creates a new {@link EmbeddedTestServer} when bound.
+ */
+public class AwEmbeddedTestServerService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new AwEmbeddedTestServerImpl(this);
+    }
+}
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index ec6cb5c..aef72a6 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -424,12 +424,12 @@
 // Utilities for extracting icon images.
 
 ImageType ImageTypeForNetworkType(const std::string& type) {
-  if (type == shill::kTypeWifi) {
+  if (NetworkTypePattern::WiFi().MatchesType(type))
     return ARCS;
-  } else if (type == shill::kTypeCellular || type == shill::kTypeWimax ||
-             type == chromeos::kTypeTether) {
+
+  if (NetworkTypePattern::Mobile().MatchesType(type))
     return BARS;
-  }
+
   return NONE;
 }
 
diff --git a/ash/system/power/tray_power.cc b/ash/system/power/tray_power.cc
index 8482046..ec8c87de5 100644
--- a/ash/system/power/tray_power.cc
+++ b/ash/system/power/tray_power.cc
@@ -127,6 +127,7 @@
     gfx::ImageSkiaRep GetImageForScale(float scale) override {
       return PowerStatus::Get()->GetBatteryImage(info_, scale);
     }
+    bool HasRepresentationAtAllScales() const override { return true; }
 
     const PowerStatus::BatteryImageInfo& info() const { return info_; }
 
diff --git a/ash/system/tray/system_tray_notifier.cc b/ash/system/tray/system_tray_notifier.cc
index 661c9f6..4f76c26 100644
--- a/ash/system/tray/system_tray_notifier.cc
+++ b/ash/system/tray/system_tray_notifier.cc
@@ -17,6 +17,7 @@
 #include "ash/system/session/logout_button_observer.h"
 #include "ash/system/status_area_focus_observer.h"
 #include "ash/system/tray_tracing.h"
+#include "ash/system/update/update_observer.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_observer.h"
 
 namespace ash {
@@ -256,6 +257,19 @@
     observer.OnTracingModeChanged(value);
 }
 
+void SystemTrayNotifier::AddUpdateObserver(UpdateObserver* observer) {
+  update_observers_.AddObserver(observer);
+}
+
+void SystemTrayNotifier::RemoveUpdateObserver(UpdateObserver* observer) {
+  update_observers_.RemoveObserver(observer);
+}
+
+void SystemTrayNotifier::NotifyUpdateOverCellularTargetSet(bool success) {
+  for (auto& observer : update_observers_)
+    observer.OnUpdateOverCellularTargetSet(success);
+}
+
 void SystemTrayNotifier::AddVirtualKeyboardObserver(
     VirtualKeyboardObserver* observer) {
   virtual_keyboard_observers_.AddObserver(observer);
diff --git a/ash/system/tray/system_tray_notifier.h b/ash/system/tray/system_tray_notifier.h
index 0373448..89ff134b 100644
--- a/ash/system/tray/system_tray_notifier.h
+++ b/ash/system/tray/system_tray_notifier.h
@@ -31,6 +31,7 @@
 class StatusAreaFocusObserver;
 class TracingObserver;
 class VirtualKeyboardObserver;
+class UpdateObserver;
 
 namespace mojom {
 enum class UpdateSeverity;
@@ -120,6 +121,11 @@
   void RemoveTracingObserver(TracingObserver* observer);
   void NotifyTracingModeChanged(bool value);
 
+  // Update.
+  void AddUpdateObserver(UpdateObserver* observer);
+  void RemoveUpdateObserver(UpdateObserver* observer);
+  void NotifyUpdateOverCellularTargetSet(bool success);
+
   // Virtual keyboard.
   void AddVirtualKeyboardObserver(VirtualKeyboardObserver* observer);
   void RemoveVirtualKeyboardObserver(VirtualKeyboardObserver* observer);
@@ -140,6 +146,7 @@
   base::ObserverList<ScreenShareObserver> screen_share_observers_;
   base::ObserverList<StatusAreaFocusObserver> status_area_focus_observers_;
   base::ObserverList<TracingObserver> tracing_observers_;
+  base::ObserverList<UpdateObserver> update_observers_;
   base::ObserverList<VirtualKeyboardObserver> virtual_keyboard_observers_;
 
   DISALLOW_COPY_AND_ASSIGN(SystemTrayNotifier);
diff --git a/ash/system/update/tray_update.cc b/ash/system/update/tray_update.cc
index 5a1058a9..1f2ca3d 100644
--- a/ash/system/update/tray_update.cc
+++ b/ash/system/update/tray_update.cc
@@ -14,6 +14,7 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_controller.h"
 #include "ash/system/tray/system_tray_delegate.h"
+#include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_item_style.h"
 #include "ash/system/tray/tray_popup_utils.h"
@@ -24,6 +25,7 @@
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
 
 namespace ash {
 namespace {
@@ -137,9 +139,13 @@
 };
 
 TrayUpdate::TrayUpdate(SystemTray* system_tray)
-    : TrayImageItem(system_tray, kSystemTrayUpdateIcon, UMA_UPDATE) {}
+    : TrayImageItem(system_tray, kSystemTrayUpdateIcon, UMA_UPDATE) {
+  Shell::Get()->system_tray_notifier()->AddUpdateObserver(this);
+}
 
-TrayUpdate::~TrayUpdate() {}
+TrayUpdate::~TrayUpdate() {
+  Shell::Get()->system_tray_notifier()->RemoveUpdateObserver(this);
+}
 
 bool TrayUpdate::GetInitialVisibility() {
   // If chrome tells ash there is an update available before this item's system
@@ -159,6 +165,16 @@
   update_view_ = nullptr;
 }
 
+void TrayUpdate::OnUpdateOverCellularTargetSet(bool success) {
+  if (!success)
+    return;
+
+  tray_view()->SetVisible(false);
+  update_over_cellular_available_ = false;
+  if (update_view_)
+    update_view_->GetWidget()->Close();
+}
+
 void TrayUpdate::ShowUpdateIcon(mojom::UpdateSeverity severity,
                                 bool factory_reset_required,
                                 mojom::UpdateType update_type) {
diff --git a/ash/system/update/tray_update.h b/ash/system/update/tray_update.h
index c723d6fc..6dedddc 100644
--- a/ash/system/update/tray_update.h
+++ b/ash/system/update/tray_update.h
@@ -7,6 +7,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/system/tray/tray_image_item.h"
+#include "ash/system/update/update_observer.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
@@ -26,7 +27,7 @@
 // The system update tray item. The tray icon stays visible once an update
 // notification is received. The icon only disappears after a reboot to apply
 // the update. Exported for test.
-class ASH_EXPORT TrayUpdate : public TrayImageItem {
+class ASH_EXPORT TrayUpdate : public TrayImageItem, public UpdateObserver {
  public:
   explicit TrayUpdate(SystemTray* system_tray);
   ~TrayUpdate() override;
@@ -54,6 +55,9 @@
   views::View* CreateDefaultView(LoginStatus status) override;
   void OnDefaultViewDestroyed() override;
 
+  // Overridden from UpdateObserver.
+  void OnUpdateOverCellularTargetSet(bool success) override;
+
   // Expose label information for testing.
   views::Label* GetLabelForTesting();
   UpdateView* update_view_;
diff --git a/ash/system/update/update_observer.h b/ash/system/update/update_observer.h
new file mode 100644
index 0000000..186402b
--- /dev/null
+++ b/ash/system/update/update_observer.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_UPDATE_UPDATE_OBSERVER_H_
+#define ASH_SYSTEM_UPDATE_UPDATE_OBSERVER_H_
+
+namespace ash {
+
+class UpdateObserver {
+ public:
+  virtual ~UpdateObserver() {}
+
+  virtual void OnUpdateOverCellularTargetSet(bool success) = 0;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_UPDATE_UPDATE_OBSERVER_H_
diff --git a/base/compiler_specific.h b/base/compiler_specific.h
index 34b0c2d..dbff842 100644
--- a/base/compiler_specific.h
+++ b/base/compiler_specific.h
@@ -111,21 +111,29 @@
 // Use like:
 //   class ALIGNAS(16) MyClass { ... }
 //   ALIGNAS(16) int array[4];
+//
+// In most places you can use the C++11 keyword "alignas", which is preferred.
+//
+// But compilers have trouble mixing __attribute__((...)) syntax with
+// alignas(...) syntax.
+//
+// Doesn't work in clang or gcc:
+//   struct alignas(16) __attribute__((packed)) S { char c; };
+// Works in clang but not gcc:
+//   struct __attribute__((packed)) alignas(16) S2 { char c; };
+// Works in clang and gcc:
+//   struct alignas(16) S3 { char c; } __attribute__((packed));
+//
+// There are also some attributes that must be specified *before* a class
+// definition: visibility (used for exporting functions/classes) is one of
+// these attributes. This means that it is not possible to use alignas() with a
+// class that is marked as exported.
 #if defined(COMPILER_MSVC)
 #define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
 #elif defined(COMPILER_GCC)
 #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
 #endif
 
-// Return the byte alignment of the given type (available at compile time).
-// Use like:
-//   ALIGNOF(int32_t)  // this would be 4
-#if defined(COMPILER_MSVC)
-#define ALIGNOF(type) __alignof(type)
-#elif defined(COMPILER_GCC)
-#define ALIGNOF(type) __alignof__(type)
-#endif
-
 // Annotate a function indicating the caller must examine the return value.
 // Use like:
 //   int foo() WARN_UNUSED_RESULT;
diff --git a/base/containers/stack_container.h b/base/containers/stack_container.h
index 5090aaf..c77574430 100644
--- a/base/containers/stack_container.h
+++ b/base/containers/stack_container.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/aligned_memory.h"
 #include "build/build_config.h"
 
 namespace base {
@@ -47,17 +46,17 @@
     }
 
     // Casts the buffer in its right type.
-    T* stack_buffer() { return stack_buffer_.template data_as<T>(); }
+    T* stack_buffer() { return reinterpret_cast<T*>(stack_buffer_); }
     const T* stack_buffer() const {
-      return stack_buffer_.template data_as<T>();
+      return reinterpret_cast<const T*>(&stack_buffer_);
     }
 
     // The buffer itself. It is not of type T because we don't want the
     // constructors and destructors to be automatically called. Define a POD
     // buffer of the right size instead.
-    base::AlignedMemory<sizeof(T[stack_capacity]), ALIGNOF(T)> stack_buffer_;
+    alignas(T) char stack_buffer_[sizeof(T[stack_capacity])];
 #if defined(__GNUC__) && !defined(ARCH_CPU_X86_FAMILY)
-    static_assert(ALIGNOF(T) <= 16, "http://crbug.com/115612");
+    static_assert(alignof(T) <= 16, "http://crbug.com/115612");
 #endif
 
     // Set when the stack buffer is used for an allocation. We do not track
diff --git a/base/containers/stack_container_unittest.cc b/base/containers/stack_container_unittest.cc
index 05c733a..2bb95379 100644
--- a/base/containers/stack_container_unittest.cc
+++ b/base/containers/stack_container_unittest.cc
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 
-#include "base/memory/aligned_memory.h"
 #include "base/memory/ref_counted.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -107,9 +106,9 @@
 template <size_t alignment>
 class AlignedData {
  public:
-  AlignedData() { memset(data_.void_data(), 0, alignment); }
+  AlignedData() { memset(data_, 0, alignment); }
   ~AlignedData() {}
-  base::AlignedMemory<alignment, alignment> data_;
+  alignas(alignment) char data_[alignment];
 };
 
 }  // anonymous namespace
@@ -120,11 +119,11 @@
 TEST(StackContainer, BufferAlignment) {
   StackVector<wchar_t, 16> text;
   text->push_back(L'A');
-  EXPECT_ALIGNED(&text[0], ALIGNOF(wchar_t));
+  EXPECT_ALIGNED(&text[0], alignof(wchar_t));
 
   StackVector<double, 1> doubles;
   doubles->push_back(0.0);
-  EXPECT_ALIGNED(&doubles[0], ALIGNOF(double));
+  EXPECT_ALIGNED(&doubles[0], alignof(double));
 
   StackVector<AlignedData<16>, 1> aligned16;
   aligned16->push_back(AlignedData<16>());
diff --git a/base/lazy_instance.h b/base/lazy_instance.h
index aaad3f7..99a000ed7 100644
--- a/base/lazy_instance.h
+++ b/base/lazy_instance.h
@@ -41,7 +41,6 @@
 #include "base/base_export.h"
 #include "base/debug/leak_annotations.h"
 #include "base/logging.h"
-#include "base/memory/aligned_memory.h"
 #include "base/threading/thread_restrictions.h"
 
 // LazyInstance uses its own struct initializer-list style static
@@ -55,7 +54,7 @@
 template <typename Type>
 struct LazyInstanceTraitsBase {
   static Type* New(void* instance) {
-    DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (ALIGNOF(Type) - 1), 0u);
+    DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (alignof(Type) - 1), 0u);
     // Use placement new to initialize our instance in our preallocated space.
     // The parenthesis is very important here to force POD type initialization.
     return new (instance) Type();
@@ -195,7 +194,7 @@
 #endif
     return static_cast<Type*>(internal::GetOrCreateLazyPointer(
         &private_instance_,
-        [this]() { return Traits::New(private_buf_.void_data()); },
+        [this]() { return Traits::New(private_buf_); },
         Traits::kRegisterOnExit ? OnExit : nullptr, this));
   }
 
@@ -204,19 +203,31 @@
       case 0:
         return p == NULL;
       case internal::kLazyInstanceStateCreating:
-        return static_cast<void*>(p) == private_buf_.void_data();
+        return static_cast<void*>(p) == private_buf_;
       default:
         return p == instance();
     }
   }
 
+  // MSVC gives a warning that the alignment expands the size of the
+  // LazyInstance struct to make the size a multiple of the alignment. This
+  // is expected in this case.
+#if defined(OS_WIN)
+#pragma warning(push)
+#pragma warning(disable: 4324)
+#endif
+
   // Effectively private: member data is only public to allow the linker to
   // statically initialize it and to maintain a POD class. DO NOT USE FROM
   // OUTSIDE THIS CLASS.
-
   subtle::AtomicWord private_instance_;
+
   // Preallocated space for the Type instance.
-  base::AlignedMemory<sizeof(Type), ALIGNOF(Type)> private_buf_;
+  alignas(Type) char private_buf_[sizeof(Type)];
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
 
  private:
   Type* instance() {
diff --git a/base/lazy_instance_unittest.cc b/base/lazy_instance_unittest.cc
index 0aa46594..ba18ddd 100644
--- a/base/lazy_instance_unittest.cc
+++ b/base/lazy_instance_unittest.cc
@@ -7,8 +7,8 @@
 #include "base/at_exit.h"
 #include "base/atomic_sequence_num.h"
 #include "base/lazy_instance.h"
-#include "base/memory/aligned_memory.h"
 #include "base/threading/simple_thread.h"
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -152,10 +152,10 @@
  public:
   AlignedData() {}
   ~AlignedData() {}
-  base::AlignedMemory<alignment, alignment> data_;
+  alignas(alignment) char data_[alignment];
 };
 
-}  // anonymous namespace
+}  // namespace
 
 #define EXPECT_ALIGNED(ptr, align) \
     EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
diff --git a/base/memory/aligned_memory.h b/base/memory/aligned_memory.h
index d8290115..e302f45 100644
--- a/base/memory/aligned_memory.h
+++ b/base/memory/aligned_memory.h
@@ -2,24 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// AlignedMemory is a POD type that gives you a portable way to specify static
-// or local stack data of a given alignment and size. For example, if you need
-// static storage for a class, but you want manual control over when the object
-// is constructed and destructed (you don't want static initialization and
-// destruction), use AlignedMemory:
-//
-//   static AlignedMemory<sizeof(MyClass), ALIGNOF(MyClass)> my_class;
-//
-//   // ... at runtime:
-//   new(my_class.void_data()) MyClass();
-//
-//   // ... use it:
-//   MyClass* mc = my_class.data_as<MyClass>();
-//
-//   // ... later, to destruct my_class:
-//   my_class.data_as<MyClass>()->MyClass::~MyClass();
-//
-// Alternatively, a runtime sized aligned allocation can be created:
+#include <type_traits>
+
+// A runtime sized aligned allocation can be created:
 //
 //   float* my_array = static_cast<float*>(AlignedAlloc(size, alignment));
 //
@@ -48,52 +33,7 @@
 
 namespace base {
 
-// AlignedMemory is specialized for all supported alignments.
-// Make sure we get a compiler error if someone uses an unsupported alignment.
-template <size_t Size, size_t ByteAlignment>
-struct AlignedMemory {};
-
-#define BASE_DECL_ALIGNED_MEMORY(byte_alignment)                              \
-  template <size_t Size>                                                      \
-  class AlignedMemory<Size, byte_alignment> {                                 \
-   public:                                                                    \
-    ALIGNAS(byte_alignment) uint8_t data_[Size];                              \
-    void* void_data() { return static_cast<void*>(data_); }                   \
-    const void* void_data() const { return static_cast<const void*>(data_); } \
-    template <typename Type>                                                  \
-    Type* data_as() {                                                         \
-      return static_cast<Type*>(void_data());                                 \
-    }                                                                         \
-    template <typename Type>                                                  \
-    const Type* data_as() const {                                             \
-      return static_cast<const Type*>(void_data());                           \
-    }                                                                         \
-                                                                              \
-   private:                                                                   \
-    void* operator new(size_t);                                               \
-    void operator delete(void*);                                              \
-  }
-
-// Specialization for all alignments is required because MSVC (as of VS 2008)
-// does not understand ALIGNAS(ALIGNOF(Type)) or ALIGNAS(template_param).
-// Greater than 4096 alignment is not supported by some compilers, so 4096 is
-// the maximum specified here.
-BASE_DECL_ALIGNED_MEMORY(1);
-BASE_DECL_ALIGNED_MEMORY(2);
-BASE_DECL_ALIGNED_MEMORY(4);
-BASE_DECL_ALIGNED_MEMORY(8);
-BASE_DECL_ALIGNED_MEMORY(16);
-BASE_DECL_ALIGNED_MEMORY(32);
-BASE_DECL_ALIGNED_MEMORY(64);
-BASE_DECL_ALIGNED_MEMORY(128);
-BASE_DECL_ALIGNED_MEMORY(256);
-BASE_DECL_ALIGNED_MEMORY(512);
-BASE_DECL_ALIGNED_MEMORY(1024);
-BASE_DECL_ALIGNED_MEMORY(2048);
-BASE_DECL_ALIGNED_MEMORY(4096);
-
-#undef BASE_DECL_ALIGNED_MEMORY
-
+// This can be replaced with std::aligned_malloc when we have C++17.
 BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment);
 
 inline void AlignedFree(void* ptr) {
diff --git a/base/memory/aligned_memory_unittest.cc b/base/memory/aligned_memory_unittest.cc
index 892c50e..e354f38 100644
--- a/base/memory/aligned_memory_unittest.cc
+++ b/base/memory/aligned_memory_unittest.cc
@@ -12,84 +12,35 @@
 #define EXPECT_ALIGNED(ptr, align) \
     EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
 
-namespace {
-
-using base::AlignedMemory;
-
-TEST(AlignedMemoryTest, StaticAlignment) {
-  static AlignedMemory<8, 8> raw8;
-  static AlignedMemory<8, 16> raw16;
-  static AlignedMemory<8, 256> raw256;
-  static AlignedMemory<8, 4096> raw4096;
-
-  EXPECT_EQ(8u, ALIGNOF(raw8));
-  EXPECT_EQ(16u, ALIGNOF(raw16));
-  EXPECT_EQ(256u, ALIGNOF(raw256));
-  EXPECT_EQ(4096u, ALIGNOF(raw4096));
-
-  EXPECT_ALIGNED(raw8.void_data(), 8);
-  EXPECT_ALIGNED(raw16.void_data(), 16);
-  EXPECT_ALIGNED(raw256.void_data(), 256);
-  EXPECT_ALIGNED(raw4096.void_data(), 4096);
-}
-
-TEST(AlignedMemoryTest, StackAlignment) {
-  AlignedMemory<8, 8> raw8;
-  AlignedMemory<8, 16> raw16;
-  AlignedMemory<8, 128> raw128;
-
-  EXPECT_EQ(8u, ALIGNOF(raw8));
-  EXPECT_EQ(16u, ALIGNOF(raw16));
-  EXPECT_EQ(128u, ALIGNOF(raw128));
-
-  EXPECT_ALIGNED(raw8.void_data(), 8);
-  EXPECT_ALIGNED(raw16.void_data(), 16);
-  EXPECT_ALIGNED(raw128.void_data(), 128);
-
-  // NaCl x86-64 compiler emits non-validating instructions for >128
-  // bytes alignment.
-  // http://www.chromium.org/nativeclient/design-documents/nacl-sfi-model-on-x86-64-systems
-  // TODO(hamaji): Ideally, NaCl compiler for x86-64 should workaround
-  // this limitation and this #if should be removed.
-  // https://code.google.com/p/nativeclient/issues/detail?id=3463
-#if !(defined(OS_NACL) && defined(ARCH_CPU_X86_64))
-  AlignedMemory<8, 256> raw256;
-  EXPECT_EQ(256u, ALIGNOF(raw256));
-  EXPECT_ALIGNED(raw256.void_data(), 256);
-
-  AlignedMemory<8, 4096> raw4096;
-  EXPECT_EQ(4096u, ALIGNOF(raw4096));
-  EXPECT_ALIGNED(raw4096.void_data(), 4096);
-#endif  // !(defined(OS_NACL) && defined(ARCH_CPU_X86_64))
-}
+namespace base {
 
 TEST(AlignedMemoryTest, DynamicAllocation) {
-  void* p = base::AlignedAlloc(8, 8);
+  void* p = AlignedAlloc(8, 8);
   EXPECT_TRUE(p);
   EXPECT_ALIGNED(p, 8);
-  base::AlignedFree(p);
+  AlignedFree(p);
 
-  p = base::AlignedAlloc(8, 16);
+  p = AlignedAlloc(8, 16);
   EXPECT_TRUE(p);
   EXPECT_ALIGNED(p, 16);
-  base::AlignedFree(p);
+  AlignedFree(p);
 
-  p = base::AlignedAlloc(8, 256);
+  p = AlignedAlloc(8, 256);
   EXPECT_TRUE(p);
   EXPECT_ALIGNED(p, 256);
-  base::AlignedFree(p);
+  AlignedFree(p);
 
-  p = base::AlignedAlloc(8, 4096);
+  p = AlignedAlloc(8, 4096);
   EXPECT_TRUE(p);
   EXPECT_ALIGNED(p, 4096);
-  base::AlignedFree(p);
+  AlignedFree(p);
 }
 
 TEST(AlignedMemoryTest, ScopedDynamicAllocation) {
-  std::unique_ptr<float, base::AlignedFreeDeleter> p(
-      static_cast<float*>(base::AlignedAlloc(8, 8)));
+  std::unique_ptr<float, AlignedFreeDeleter> p(
+      static_cast<float*>(AlignedAlloc(8, 8)));
   EXPECT_TRUE(p.get());
   EXPECT_ALIGNED(p.get(), 8);
 }
 
-}  // namespace
+}  // namespace base
diff --git a/base/memory/manual_constructor.h b/base/memory/manual_constructor.h
index f401f62d..e968d04 100644
--- a/base/memory/manual_constructor.h
+++ b/base/memory/manual_constructor.h
@@ -34,17 +34,15 @@
   // Support users creating arrays of ManualConstructor<>s.  This ensures that
   // the array itself has the correct alignment.
   static void* operator new[](size_t size) {
-    return AlignedAlloc(size, ALIGNOF(Type));
+    return AlignedAlloc(size, alignof(Type));
   }
   static void operator delete[](void* mem) {
     AlignedFree(mem);
   }
 
-  inline Type* get() {
-    return space_.template data_as<Type>();
-  }
+  inline Type* get() { return reinterpret_cast<Type*>(space_); }
   inline const Type* get() const  {
-    return space_.template data_as<Type>();
+    return reinterpret_cast<const Type*>(space_);
   }
 
   inline Type* operator->() { return get(); }
@@ -55,7 +53,7 @@
 
   template <typename... Ts>
   inline void Init(Ts&&... params) {
-    new(space_.void_data()) Type(std::forward<Ts>(params)...);
+    new (space_) Type(std::forward<Ts>(params)...);
   }
 
   inline void InitFromMove(ManualConstructor<Type>&& o) {
@@ -67,7 +65,7 @@
   }
 
  private:
-  AlignedMemory<sizeof(Type), ALIGNOF(Type)> space_;
+  alignas(Type) char space_[sizeof(Type)];
 };
 
 }  // namespace base
diff --git a/base/memory/singleton.h b/base/memory/singleton.h
index 5c58d5fe..d604910 100644
--- a/base/memory/singleton.h
+++ b/base/memory/singleton.h
@@ -24,7 +24,6 @@
 #include "base/base_export.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/memory/aligned_memory.h"
 #include "base/threading/thread_restrictions.h"
 
 namespace base {
@@ -115,7 +114,7 @@
     if (subtle::NoBarrier_AtomicExchange(&dead_, 1))
       return NULL;
 
-    return new(buffer_.void_data()) Type();
+    return new (buffer_) Type();
   }
 
   static void Delete(Type* p) {
@@ -130,14 +129,13 @@
   static void Resurrect() { subtle::NoBarrier_Store(&dead_, 0); }
 
  private:
-  static AlignedMemory<sizeof(Type), ALIGNOF(Type)> buffer_;
+  alignas(Type) static char buffer_[sizeof(Type)];
   // Signal the object was already deleted, so it is not revived.
   static subtle::Atomic32 dead_;
 };
 
 template <typename Type>
-AlignedMemory<sizeof(Type), ALIGNOF(Type)>
-    StaticMemorySingletonTraits<Type>::buffer_;
+alignas(Type) char StaticMemorySingletonTraits<Type>::buffer_[sizeof(Type)];
 template <typename Type>
 subtle::Atomic32 StaticMemorySingletonTraits<Type>::dead_ = 0;
 
diff --git a/base/memory/singleton_unittest.cc b/base/memory/singleton_unittest.cc
index a15145c..e129215 100644
--- a/base/memory/singleton_unittest.cc
+++ b/base/memory/singleton_unittest.cc
@@ -16,6 +16,14 @@
 
 typedef void (*CallbackFunc)();
 
+template <size_t alignment>
+class AlignedData {
+ public:
+  AlignedData() {}
+  ~AlignedData() {}
+  alignas(alignment) char data_[alignment];
+};
+
 class IntSingleton {
  public:
   static IntSingleton* GetInstance() {
@@ -269,19 +277,17 @@
     EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
 
 TEST_F(SingletonTest, Alignment) {
-  using base::AlignedMemory;
-
   // Create some static singletons with increasing sizes and alignment
   // requirements. By ordering this way, the linker will need to do some work to
   // ensure proper alignment of the static data.
   AlignedTestSingleton<int32_t>* align4 =
       AlignedTestSingleton<int32_t>::GetInstance();
-  AlignedTestSingleton<AlignedMemory<32, 32> >* align32 =
-      AlignedTestSingleton<AlignedMemory<32, 32> >::GetInstance();
-  AlignedTestSingleton<AlignedMemory<128, 128> >* align128 =
-      AlignedTestSingleton<AlignedMemory<128, 128> >::GetInstance();
-  AlignedTestSingleton<AlignedMemory<4096, 4096> >* align4096 =
-      AlignedTestSingleton<AlignedMemory<4096, 4096> >::GetInstance();
+  AlignedTestSingleton<AlignedData<32>>* align32 =
+      AlignedTestSingleton<AlignedData<32>>::GetInstance();
+  AlignedTestSingleton<AlignedData<128>>* align128 =
+      AlignedTestSingleton<AlignedData<128>>::GetInstance();
+  AlignedTestSingleton<AlignedData<4096>>* align4096 =
+      AlignedTestSingleton<AlignedData<4096>>::GetInstance();
 
   EXPECT_ALIGNED(align4, 4);
   EXPECT_ALIGNED(align32, 32);
diff --git a/base/metrics/persistent_memory_allocator.h b/base/metrics/persistent_memory_allocator.h
index 79eb96d4..14f7a507 100644
--- a/base/metrics/persistent_memory_allocator.h
+++ b/base/metrics/persistent_memory_allocator.h
@@ -513,7 +513,7 @@
         const_cast<void*>(GetBlockData(ref, T::kPersistentTypeId, size));
     if (!mem)
       return nullptr;
-    DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (ALIGNOF(T) - 1));
+    DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (alignof(T) - 1));
     return new (mem) T();
   }
   template <typename T>
@@ -541,7 +541,7 @@
       return nullptr;
     // Ensure the allocator's internal alignment is sufficient for this object.
     // This protects against coding errors in the allocator.
-    DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (ALIGNOF(T) - 1));
+    DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (alignof(T) - 1));
     // Change the type, clearing the memory if so desired. The new type is
     // "transitioning" so that there is no race condition with the construction
     // of the object should another thread be simultaneously iterating over
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index b841dee3..ed5ef96 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -726,7 +726,7 @@
   // internal pid cache. The libc interface unfortunately requires
   // specifying a new stack, so we use setjmp/longjmp to emulate
   // fork-like behavior.
-  char stack_buf[PTHREAD_STACK_MIN] ALIGNAS(16);
+  alignas(16) char stack_buf[PTHREAD_STACK_MIN];
 #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
     defined(ARCH_CPU_MIPS_FAMILY)
   // The stack grows downward.
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index f85a308..45e3854 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -450,17 +450,6 @@
     cflags += [ "-fdebug-prefix-map=$absolute_path=." ]
   }
 
-  # Tells the compiler not to use absolute paths when passing the default
-  # paths to the tools it invokes. We don't want this because we don't
-  # really need it and it can mess up the goma cache entries. It would
-  # be nice if it was on by default in clang, but it isn't.
-  #
-  # TODO(thakis): Figure out if this should be the default, and expose in
-  # clang-cl if not.
-  if (is_clang && !is_win && !is_nacl) {
-    cflags += [ "-no-canonical-prefixes" ]
-  }
-
   # C++11 compiler flags setup.
   # ---------------------------
   if (is_linux || is_android || (is_nacl && is_clang) || current_os == "aix") {
diff --git a/cc/base/list_container.h b/cc/base/list_container.h
index 93a6a67..bafee79 100644
--- a/cc/base/list_container.h
+++ b/cc/base/list_container.h
@@ -101,7 +101,7 @@
   // Allocate().
   template <typename DerivedElementType>
   DerivedElementType* AllocateAndConstruct() {
-    return new (helper_.Allocate(ALIGNOF(DerivedElementType),
+    return new (helper_.Allocate(alignof(DerivedElementType),
                                  sizeof(DerivedElementType)))
         DerivedElementType;
   }
@@ -110,7 +110,7 @@
   // Allocate().
   template <typename DerivedElementType>
   DerivedElementType* AllocateAndCopyFrom(const DerivedElementType* source) {
-    return new (helper_.Allocate(ALIGNOF(DerivedElementType),
+    return new (helper_.Allocate(alignof(DerivedElementType),
                                  sizeof(DerivedElementType)))
         DerivedElementType(*source);
   }
@@ -154,7 +154,7 @@
   template <typename DerivedElementType>
   DerivedElementType* AppendByMoving(DerivedElementType* item) {
     size_t max_size_for_derived_class = helper_.MaxSizeForDerivedClass();
-    void* new_item = helper_.Allocate(ALIGNOF(DerivedElementType),
+    void* new_item = helper_.Allocate(alignof(DerivedElementType),
                                       max_size_for_derived_class);
     memcpy(new_item, static_cast<void*>(item), max_size_for_derived_class);
     // Construct a new element in-place so it can be destructed safely.
diff --git a/cc/base/list_container_unittest.cc b/cc/base/list_container_unittest.cc
index 31ef300..9753b35 100644
--- a/cc/base/list_container_unittest.cc
+++ b/cc/base/list_container_unittest.cc
@@ -51,7 +51,7 @@
 };
 
 const size_t kLargestDerivedElementSize = sizeof(DerivedElement3);
-const size_t kLargestDerivedElementAlign = ALIGNOF(DerivedElement3);
+const size_t kLargestDerivedElementAlign = alignof(DerivedElement3);
 
 static_assert(sizeof(DerivedElement1) <= kLargestDerivedElementSize,
               "Largest Derived Element size needs update. DerivedElement1 is "
@@ -59,10 +59,10 @@
 static_assert(sizeof(DerivedElement2) <= kLargestDerivedElementSize,
               "Largest Derived Element size needs update. DerivedElement2 is "
               "currently largest.");
-static_assert(ALIGNOF(DerivedElement1) <= kLargestDerivedElementSize,
+static_assert(alignof(DerivedElement1) <= kLargestDerivedElementSize,
               "Largest Derived Element align needs update. DerivedElement1 is "
               "currently largest.");
-static_assert(ALIGNOF(DerivedElement2) <= kLargestDerivedElementSize,
+static_assert(alignof(DerivedElement2) <= kLargestDerivedElementSize,
               "Largest Derived Element align needs update. DerivedElement2 is "
               "currently largest.");
 
@@ -137,7 +137,7 @@
 const size_t kCurrentLargestDerivedElementSize =
     std::max(kLargestDerivedElementSize, sizeof(MockDerivedElementSubclass));
 const size_t kCurrentLargestDerivedElementAlign =
-    std::max(kLargestDerivedElementAlign, ALIGNOF(MockDerivedElementSubclass));
+    std::max(kLargestDerivedElementAlign, alignof(MockDerivedElementSubclass));
 
 TEST(ListContainerTest, ConstructorCalledInAllocateAndConstruct) {
   ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign,
@@ -277,7 +277,7 @@
 }
 
 TEST(ListContainerTest, SimpleIndexAccessNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
 
   size_t size = 3;
@@ -294,7 +294,7 @@
 }
 
 TEST(ListContainerTest, SimpleInsertionNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
 
   size_t size = 3;
@@ -308,7 +308,7 @@
 }
 
 TEST(ListContainerTest, SimpleInsertionAndClearNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
   EXPECT_TRUE(list.empty());
   EXPECT_EQ(0u, list.size());
@@ -329,7 +329,7 @@
 }
 
 TEST(ListContainerTest, SimpleInsertionClearAndInsertAgainNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
   EXPECT_TRUE(list.empty());
   EXPECT_EQ(0u, list.size());
@@ -362,7 +362,7 @@
 // for, ListContainer can still perform like normal vector.
 TEST(ListContainerTest,
      SimpleInsertionTriggerMoreThanOneAllocationNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 2);
   std::vector<NonDerivedElement*> nde_list;
   size_t size = 10;
@@ -385,7 +385,7 @@
   // Constructor sets the allocation size to 2. Every time ListContainer needs
   // to allocate again, it doubles allocation size. In this test, 10 elements is
   // needed, thus ListContainerShould allocate spaces 2, 4 and 8 elements.
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 2);
   std::vector<NonDerivedElement*> nde_list;
   size_t size = 10;
@@ -466,7 +466,7 @@
 }
 
 TEST(ListContainerTest, SimpleIterationNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
   std::vector<NonDerivedElement*> nde_list;
   size_t size = 10;
@@ -502,7 +502,7 @@
 }
 
 TEST(ListContainerTest, SimpleConstIteratorIterationNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
   std::vector<const NonDerivedElement*> nde_list;
   size_t size = 10;
@@ -545,7 +545,7 @@
 }
 
 TEST(ListContainerTest, SimpleReverseInsertionNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
   std::vector<NonDerivedElement*> nde_list;
   size_t size = 10;
@@ -884,7 +884,7 @@
 
 TEST(ListContainerTest,
      SimpleIterationAndReverseIterationWithIndexNonDerivedElement) {
-  ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement),
+  ListContainer<NonDerivedElement> list(alignof(NonDerivedElement),
                                         sizeof(NonDerivedElement), 0);
   std::vector<NonDerivedElement*> nde_list;
   size_t size = 10;
@@ -931,7 +931,7 @@
   // We keep an explicit instance count to make sure that the destructors are
   // indeed getting called.
   int counter = 0;
-  ListContainer<InstanceCounter> list(ALIGNOF(InstanceCounter),
+  ListContainer<InstanceCounter> list(alignof(InstanceCounter),
                                       sizeof(InstanceCounter), 1);
   EXPECT_EQ(0, counter);
   EXPECT_EQ(0u, list.size());
@@ -976,7 +976,7 @@
   struct SmallStruct {
     char dummy[16];
   };
-  ListContainer<SmallStruct> list(ALIGNOF(SmallStruct), sizeof(SmallStruct), 1);
+  ListContainer<SmallStruct> list(alignof(SmallStruct), sizeof(SmallStruct), 1);
   std::vector<SmallStruct*> pointers;
 
   // Utilities which keep these two lists in sync and check that their iteration
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index de23ab3..7ee5098 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <memory>
 
+#include "base/memory/aligned_memory.h"
 #include "base/message_loop/message_loop.h"
 #include "cc/base/math_util.h"
 #include "cc/output/gl_renderer.h"
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 9393f40..0689974 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -190,7 +190,7 @@
 #undef M
 
 #define M(T)                                               \
-  static_assert(ALIGNOF(T) <= PaintOpBuffer::PaintOpAlign, \
+  static_assert(alignof(T) <= PaintOpBuffer::PaintOpAlign, \
                 #T " must have alignment no bigger than PaintOpAlign");
 TYPES(M);
 #undef M
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index a5e04be..ae40cad4 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -211,10 +211,10 @@
     // if T is aligned, and M's alignment needs are a multiple of T's size, then
     // M will also be aligned when placed immediately after T.
     static_assert(
-        sizeof(T) % ALIGNOF(M) == 0,
+        sizeof(T) % alignof(M) == 0,
         "T must be padded such that an array of M is aligned after it");
     static_assert(
-        ALIGNOF(T) >= ALIGNOF(M),
+        alignof(T) >= alignof(M),
         "T must have not have less alignment requirements than the array data");
     return reinterpret_cast<const M*>(op + 1);
   }
@@ -778,7 +778,7 @@
   enum { kInitialBufferSize = 4096 };
   // It's not necessarily the case that the op with the maximum alignment
   // requirements is also the biggest op, but for now that's true.
-  static constexpr size_t PaintOpAlign = ALIGNOF(DrawDRRectOp);
+  static constexpr size_t PaintOpAlign = alignof(DrawDRRectOp);
 
   PaintOpBuffer();
   ~PaintOpBuffer() override;
@@ -935,7 +935,7 @@
 
   template <typename T, typename... Args>
   T* push_internal(size_t bytes, Args&&... args) {
-    static_assert(ALIGNOF(T) <= PaintOpAlign, "");
+    static_assert(alignof(T) <= PaintOpAlign, "");
 
     auto pair = AllocatePaintOp(sizeof(T), bytes);
     T* op = reinterpret_cast<T*>(pair.first);
diff --git a/cc/quads/largest_draw_quad.cc b/cc/quads/largest_draw_quad.cc
index de99f54..c62a5bd8 100644
--- a/cc/quads/largest_draw_quad.cc
+++ b/cc/quads/largest_draw_quad.cc
@@ -47,8 +47,8 @@
 struct MaxAlign {};
 template <class T, class... Args>
 struct MaxAlign<T, Args...> {
-  static constexpr size_t value = ALIGNOF(T) > MaxAlign<Args...>::value
-                                      ? ALIGNOF(T)
+  static constexpr size_t value = alignof(T) > MaxAlign<Args...>::value
+                                      ? alignof(T)
                                       : MaxAlign<Args...>::value;
 };
 template <>
diff --git a/cc/quads/render_pass.cc b/cc/quads/render_pass.cc
index 021c5e5..77faaf0 100644
--- a/cc/quads/render_pass.cc
+++ b/cc/quads/render_pass.cc
@@ -63,7 +63,7 @@
 
 RenderPass::RenderPass()
     : quad_list(kDefaultNumQuadsToReserve),
-      shared_quad_state_list(ALIGNOF(SharedQuadState),
+      shared_quad_state_list(alignof(SharedQuadState),
                              sizeof(SharedQuadState),
                              kDefaultNumSharedQuadStatesToReserve) {}
 
@@ -72,7 +72,7 @@
 RenderPass::RenderPass(size_t num_layers)
     : has_transparent_background(true),
       quad_list(kDefaultNumQuadsToReserve),
-      shared_quad_state_list(ALIGNOF(SharedQuadState),
+      shared_quad_state_list(alignof(SharedQuadState),
                              sizeof(SharedQuadState),
                              num_layers) {}
 
@@ -80,7 +80,7 @@
                        size_t quad_list_size)
     : has_transparent_background(true),
       quad_list(quad_list_size),
-      shared_quad_state_list(ALIGNOF(SharedQuadState),
+      shared_quad_state_list(alignof(SharedQuadState),
                              sizeof(SharedQuadState),
                              shared_quad_state_list_size) {}
 
diff --git a/cc/raster/texture_compressor_etc1.h b/cc/raster/texture_compressor_etc1.h
index 311a4c30..6e85313c 100644
--- a/cc/raster/texture_compressor_etc1.h
+++ b/cc/raster/texture_compressor_etc1.h
@@ -41,15 +41,10 @@
 
 // Codeword tables.
 // See: Table 3.17.2
-ALIGNAS(16) static const int16_t g_codeword_tables[8][4] = {
-    {-8, -2, 2, 8},
-    {-17, -5, 5, 17},
-    {-29, -9, 9, 29},
-    {-42, -13, 13, 42},
-    {-60, -18, 18, 60},
-    {-80, -24, 24, 80},
-    {-106, -33, 33, 106},
-    {-183, -47, 47, 183}};
+alignas(16) static const int16_t g_codeword_tables[8][4] = {
+    {-8, -2, 2, 8},       {-17, -5, 5, 17},    {-29, -9, 9, 29},
+    {-42, -13, 13, 42},   {-60, -18, 18, 60},  {-80, -24, 24, 80},
+    {-106, -33, 33, 106}, {-183, -47, 47, 183}};
 
 // Maps modifier indices to pixel index values.
 // See: Table 3.17.3
diff --git a/cc/raster/texture_compressor_etc1_sse.cc b/cc/raster/texture_compressor_etc1_sse.cc
index cb8579c..f093688 100644
--- a/cc/raster/texture_compressor_etc1_sse.cc
+++ b/cc/raster/texture_compressor_etc1_sse.cc
@@ -790,7 +790,7 @@
   DCHECK_GE(height, 4);
   DCHECK_EQ((height & 3), 0);
 
-  ALIGNAS(16) uint8_t block[64];
+  alignas(16) uint8_t block[64];
   __m128i packed[4];
   __m128i red[4], green[4], blue[4], alpha[4];
   __sse_data data;
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 83ca950..423f812 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -2790,7 +2790,8 @@
   bool sent_gesture_;
 };
 
-MULTI_THREAD_TEST_F(ViewportDeltasAppliedDuringPinch);
+// Disabled for flakiness, http://crbug.com/733001
+// MULTI_THREAD_TEST_F(ViewportDeltasAppliedDuringPinch);
 
 class LayerTreeHostTestSetVisible : public LayerTreeHostTest {
  public:
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 d340d473..90ceaec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -769,10 +769,6 @@
 
         if (ReaderModeManager.isEnabled(this)) {
             mReaderModeManager = new ReaderModeManager(getTabModelSelector(), this);
-            if (mToolbarManager != null) {
-                mToolbarManager.addFindToolbarObserver(
-                        mReaderModeManager.getFindToolbarObserver());
-            }
         }
 
         TraceEvent.end("ChromeActivity:CompositorInitialization");
@@ -1348,11 +1344,8 @@
      * @return Whether the app menu should be shown.
      */
     public boolean shouldShowAppMenu() {
-        // Do not show the menu if Contextual Search or Reader Mode panel is opened.
-        // TODO(mdjones): This could potentially be handled by the OverlayPanelManager or as
-        // an event if the panels were SceneOverlays.
-        if ((mContextualSearchManager != null && mContextualSearchManager.isSearchPanelOpened())
-                || (mReaderModeManager != null && mReaderModeManager.isPanelOpened())) {
+        // Do not show the menu if Contextual Search panel is opened.
+        if (mContextualSearchManager != null && mContextualSearchManager.isSearchPanelOpened()) {
             return false;
         }
 
@@ -1660,8 +1653,7 @@
         mCompositorViewHolder.setFullscreenHandler(getFullscreenManager());
         mCompositorViewHolder.setUrlBar(urlBar);
         mCompositorViewHolder.onFinishNativeInitialization(getTabModelSelector(), this,
-                getTabContentManager(), contentContainer, mContextualSearchManager,
-                mReaderModeManager);
+                getTabContentManager(), contentContainer, mContextualSearchManager);
 
         if (controlContainer != null
                 && DeviceClassManager.enableToolbarSwipe()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 3c6e912..4356348 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -39,7 +39,6 @@
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
 import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -772,16 +771,14 @@
      * @param androidContentContainer The {@link ViewGroup} the {@link LayoutManager} should bind
      *                                Android content to.
      * @param contextualSearchManager A {@link ContextualSearchManagementDelegate} instance.
-     * @param readerModeManager       A {@link ReaderModeManagerDelegate} instance.
      */
     public void onFinishNativeInitialization(TabModelSelector tabModelSelector,
             TabCreatorManager tabCreatorManager, TabContentManager tabContentManager,
             ViewGroup androidContentContainer,
-            ContextualSearchManagementDelegate contextualSearchManager,
-            ReaderModeManagerDelegate readerModeManager) {
+            ContextualSearchManagementDelegate contextualSearchManager) {
         assert mLayoutManager != null;
         mLayoutManager.init(tabModelSelector, tabCreatorManager, tabContentManager,
-                androidContentContainer, contextualSearchManager, readerModeManager,
+                androidContentContainer, contextualSearchManager,
                 mCompositorView.getResourceManager().getDynamicResourceLoader());
 
         attachToTabModelSelector(tabModelSelector);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/readermode/ReaderModePanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/readermode/ReaderModePanel.java
index cb2845f..9959500c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/readermode/ReaderModePanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/readermode/ReaderModePanel.java
@@ -267,10 +267,6 @@
         } else if (mTimerRunning && !animatingToOpenState) {
             onTimerEnded();
         }
-
-        if (getPanelState() == PanelState.PEEKED) {
-            mManagerDelegate.onPeek();
-        }
     }
 
     @Override
@@ -321,10 +317,6 @@
         if (width != getWidth()) destroyReaderModeBarControl();
 
         super.onLayoutChanged(width, height, visibleViewportOffsetY);
-
-        if (mManagerDelegate != null) {
-            mManagerDelegate.onLayoutChanged();
-        }
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
index 3ff08762..b2792b85 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -26,7 +26,6 @@
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
@@ -213,13 +212,11 @@
      * @param content                  A {@link TabContentManager} instance.
      * @param androidContentContainer  A {@link ViewGroup} for Android views to be bound to.
      * @param contextualSearchDelegate A {@link ContextualSearchDelegate} instance.
-     * @param readerModeDelegate       A {@link ReaderModeManagerDelegate} instance.
      * @param dynamicResourceLoader    A {@link DynamicResourceLoader} instance.
      */
     public void init(TabModelSelector selector, TabCreatorManager creator,
             TabContentManager content, ViewGroup androidContentContainer,
             ContextualSearchManagementDelegate contextualSearchDelegate,
-            ReaderModeManagerDelegate readerModeDelegate,
             DynamicResourceLoader dynamicResourceLoader) {
         mTabModelSelector = selector;
         mContentContainer = androidContentContainer;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index 0555a641..b9eac7b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -20,7 +20,6 @@
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperManager;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
 import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
@@ -196,7 +195,6 @@
     public void init(TabModelSelector selector, TabCreatorManager creator,
             TabContentManager content, ViewGroup androidContentContainer,
             ContextualSearchManagementDelegate contextualSearchDelegate,
-            ReaderModeManagerDelegate readerModeDelegate,
             DynamicResourceLoader dynamicResourceLoader) {
         // TODO: TitleCache should be a part of the ResourceManager.
         mTitleCache = mHost.getTitleCache();
@@ -207,7 +205,7 @@
         if (mOverviewLayout != null) mOverviewLayout.setTabModelSelector(selector, content);
 
         super.init(selector, creator, content, androidContentContainer, contextualSearchDelegate,
-                readerModeDelegate, dynamicResourceLoader);
+                dynamicResourceLoader);
 
         mTabModelSelectorObserver = new EmptyTabModelSelectorObserver() {
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
index c756e4c5..a8cbfec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
@@ -13,7 +13,6 @@
 import org.chromium.chrome.browser.compositor.overlays.SceneOverlay;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
 import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
@@ -56,13 +55,12 @@
     public void init(TabModelSelector selector, TabCreatorManager creator,
             TabContentManager content, ViewGroup androidContentContainer,
             ContextualSearchManagementDelegate contextualSearchDelegate,
-            ReaderModeManagerDelegate readerModeDelegate,
             DynamicResourceLoader dynamicResourceLoader) {
         // Initialize Layouts
         mSimpleAnimationLayout.setTabModelSelector(selector, content);
 
         super.init(selector, creator, content, androidContentContainer, contextualSearchDelegate,
-                readerModeDelegate, dynamicResourceLoader);
+                dynamicResourceLoader);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
index 7f41a70c..1f0a07a1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -13,7 +13,6 @@
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.ScrollDirection;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperManager;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -122,14 +121,13 @@
     public void init(TabModelSelector selector, TabCreatorManager creator,
             TabContentManager content, ViewGroup androidContentContainer,
             ContextualSearchManagementDelegate contextualSearchDelegate,
-            ReaderModeManagerDelegate readerModeDelegate,
             DynamicResourceLoader dynamicResourceLoader) {
         if (mTabStripLayoutHelperManager != null) {
             mTabStripLayoutHelperManager.setTabModelSelector(selector, creator);
         }
 
         super.init(selector, creator, content, androidContentContainer, contextualSearchDelegate,
-                readerModeDelegate, dynamicResourceLoader);
+                dynamicResourceLoader);
 
         mTabObserver = new TabModelSelectorTabObserver(selector) {
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java
index e778def..7028fe88 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerDocument.java
@@ -16,7 +16,6 @@
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelContentViewDelegate;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel;
-import org.chromium.chrome.browser.compositor.bottombar.readermode.ReaderModePanel;
 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
 import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
@@ -27,7 +26,6 @@
 import org.chromium.chrome.browser.compositor.scene_layer.ToolbarSceneLayer;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
 import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.tab.Tab;
@@ -60,7 +58,6 @@
     // Internal State
     private final SparseArray<LayoutTab> mTabCache = new SparseArray<LayoutTab>();
     private final ContextualSearchPanel mContextualSearchPanel;
-    private final ReaderModePanel mReaderModePanel;
     private final OverlayPanelManager mOverlayPanelManager;
     private final ToolbarSceneLayer mToolbarOverlay;
     /** A delegate for interacting with the Contextual Search manager. */
@@ -92,9 +89,6 @@
         // Contextual Search scene overlay.
         mContextualSearchPanel = new ContextualSearchPanel(mContext, this, mOverlayPanelManager);
 
-        // Reader Mode scene overlay.
-        mReaderModePanel = new ReaderModePanel(mContext, this, mOverlayPanelManager, this);
-
         // Set up layout parameters
         mStaticLayout.setLayoutHandlesTabLifecycles(true);
 
@@ -105,7 +99,6 @@
     public void init(TabModelSelector selector, TabCreatorManager creator,
             TabContentManager content, ViewGroup androidContentContainer,
             ContextualSearchManagementDelegate contextualSearchDelegate,
-            ReaderModeManagerDelegate readerModeDelegate,
             DynamicResourceLoader dynamicResourceLoader) {
         // Add any SceneOverlays to a layout.
         addAllSceneOverlays();
@@ -124,11 +117,6 @@
             contextualSearchDelegate.setContextualSearchPanel(mContextualSearchPanel);
         }
 
-        mReaderModePanel.setManagerDelegate(readerModeDelegate);
-        if (readerModeDelegate != null) {
-            readerModeDelegate.setReaderModePanel(mReaderModePanel);
-        }
-
         // Set the dynamic resource loader for all overlay panels.
         mOverlayPanelManager.setDynamicResourceLoader(dynamicResourceLoader);
         mOverlayPanelManager.setContainerView(androidContentContainer);
@@ -151,7 +139,7 @@
         };
 
         super.init(selector, creator, content, androidContentContainer, contextualSearchDelegate,
-                readerModeDelegate, dynamicResourceLoader);
+                dynamicResourceLoader);
     }
 
     @Override
@@ -213,7 +201,6 @@
     protected void addAllSceneOverlays() {
         addGlobalSceneOverlay(mToolbarOverlay);
         mStaticLayout.addSceneOverlay(mContextualSearchPanel);
-        mStaticLayout.addSceneOverlay(mReaderModePanel);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabLayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabLayoutManager.java
index fee790ef..85fa999 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabLayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabLayoutManager.java
@@ -10,7 +10,6 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerHost;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
-import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
@@ -54,10 +53,9 @@
     public void init(TabModelSelector selector, TabCreatorManager creator,
             TabContentManager content, ViewGroup androidContentContainer,
             ContextualSearchManagementDelegate contextualSearchDelegate,
-            ReaderModeManagerDelegate readerModeDelegate,
             DynamicResourceLoader dynamicResourceLoader) {
         super.init(selector, creator, content, androidContentContainer, contextualSearchDelegate,
-                readerModeDelegate, dynamicResourceLoader);
+                dynamicResourceLoader);
         for (TabModel model : selector.getModels()) model.addObserver(mTabModelObserver);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 97a091c6..9ac0f677 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -8,18 +8,12 @@
 import android.text.TextUtils;
 
 import org.chromium.base.CommandLine;
-import org.chromium.base.SysUtils;
-import org.chromium.base.VisibleForTesting;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelState;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
-import org.chromium.chrome.browser.compositor.bottombar.readermode.ReaderModePanel;
-import org.chromium.chrome.browser.infobar.InfoBar;
-import org.chromium.chrome.browser.infobar.InfoBarContainer;
-import org.chromium.chrome.browser.infobar.InfoBarContainer.InfoBarContainerObserver;
+import org.chromium.chrome.browser.infobar.ReaderModeInfoBar;
 import org.chromium.chrome.browser.rappor.RapporServiceBridge;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
@@ -27,14 +21,14 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarObserver;
 import org.chromium.components.dom_distiller.content.DistillablePageUtils;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.NavigationController;
+import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.ui.UiUtils;
-import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.PageTransition;
 
 import java.util.HashMap;
@@ -46,59 +40,37 @@
  * reader mode and reader mode preferences toolbar icon and hiding the
  * browser controls when a reader mode page has finished loading.
  */
-public class ReaderModeManager extends TabModelSelectorTabObserver
-        implements InfoBarContainerObserver, ReaderModeManagerDelegate {
-
-    /**
-     * POSSIBLE means reader mode can be entered.
-     */
+public class ReaderModeManager
+        extends TabModelSelectorTabObserver implements ReaderModeManagerDelegate {
+    /** POSSIBLE means reader mode can be entered. */
     public static final int POSSIBLE = 0;
 
-    /**
-     * NOT_POSSIBLE means reader mode cannot be entered.
-     */
+    /** NOT_POSSIBLE means reader mode cannot be entered. */
     public static final int NOT_POSSIBLE = 1;
 
-    /**
-     * STARTED means reader mode is currently in reader mode.
-     */
+    /** STARTED means reader mode is currently in reader mode. */
     public static final int STARTED = 2;
 
-    // The url of the last page visited if the last page was reader mode page.  Otherwise null.
+    /** The url of the last page visited if the last page was reader mode page.  Otherwise null. */
     private String mReaderModePageUrl;
 
-    // Whether the fact that the current web page was distillable or not has been recorded.
+    /** Whether the fact that the current web page was distillable or not has been recorded. */
     private boolean mIsUmaRecorded;
 
-    // The per-tab state of distillation.
+    /** The per-tab state of distillation. */
     protected Map<Integer, ReaderModeTabInfo> mTabStatusMap;
 
-    // The current tab ID. This will change as the user switches between tabs.
-    private int mTabId;
-
-    // The ReaderModePanel that this class is managing.
-    protected ReaderModePanel mReaderModePanel;
-
-    // The ChromeActivity that this panel exists in.
+    /** The ChromeActivity that this infobar exists in. */
     private ChromeActivity mChromeActivity;
 
-    // The primary means of getting the currently active tab.
+    /** The primary means of getting the currently active tab. */
     private TabModelSelector mTabModelSelector;
 
-    private boolean mIsFullscreenModeEntered;
-    private boolean mIsFindToolbarShowing;
-    private boolean mIsKeyboardShowing;
-
-    // InfoBar tracking.
-    private boolean mIsInfoBarContainerShown;
-
-    // If Reader Mode is detecting all pages as distillable.
+    /** If Reader Mode is detecting all pages as distillable. */
     private boolean mIsReaderHeuristicAlwaysTrue;
 
-
     public ReaderModeManager(TabModelSelector selector, ChromeActivity activity) {
         super(selector);
-        mTabId = Tab.INVALID_TAB_ID;
         mTabModelSelector = selector;
         mChromeActivity = activity;
         mTabStatusMap = new HashMap<>();
@@ -129,29 +101,9 @@
         DomDistillerUIUtils.destroy(this);
 
         mChromeActivity = null;
-        mReaderModePanel = null;
         mTabModelSelector = null;
     }
 
-    /**
-     * @return A FindToolbarObserver capable of hiding the Reader Mode panel.
-     */
-    public FindToolbarObserver getFindToolbarObserver() {
-        return new FindToolbarObserver() {
-            @Override
-            public void onFindToolbarShown() {
-                mIsFindToolbarShowing = true;
-                closeReaderPanel(StateChangeReason.UNKNOWN, true);
-            }
-
-            @Override
-            public void onFindToolbarHidden() {
-                mIsFindToolbarShowing = false;
-                requestReaderPanelShow(StateChangeReason.UNKNOWN);
-            }
-        };
-    }
-
     // TabModelSelectorTabObserver:
 
     @Override
@@ -159,10 +111,8 @@
         if (mTabModelSelector == null) return;
 
         int shownTabId = shownTab.getId();
-        Tab previousTab = mTabModelSelector.getTabById(mTabId);
-        mTabId = shownTabId;
 
-        // If the reader panel was dismissed, stop here.
+        // If the reader infobar was dismissed, stop here.
         if (mTabStatusMap.containsKey(shownTabId)
                 && mTabStatusMap.get(shownTabId).isDismissed()) {
             return;
@@ -171,20 +121,6 @@
         // Set this manager as the active one for the UI utils.
         DomDistillerUIUtils.setReaderModeManagerDelegate(this);
 
-        // Update infobar state based on current tab.
-        if (shownTab.getInfoBarContainer() != null) {
-            mIsInfoBarContainerShown = shownTab.getInfoBarContainer().hasInfoBars();
-        }
-
-        // Remove the infobar observer from the previous tab and attach it to the current one.
-        if (previousTab != null && previousTab.getInfoBarContainer() != null) {
-            previousTab.getInfoBarContainer().removeObserver(this);
-        }
-
-        if (shownTab.getInfoBarContainer() != null) {
-            shownTab.getInfoBarContainer().addObserver(this);
-        }
-
         // If there is no state info for this tab, create it.
         ReaderModeTabInfo tabInfo = mTabStatusMap.get(shownTabId);
         if (tabInfo == null) {
@@ -194,6 +130,11 @@
             mTabStatusMap.put(shownTabId, tabInfo);
         }
 
+        if (DomDistillerUrlUtils.isDistilledPage(shownTab.getUrl())
+                && !tabInfo.isViewingReaderModePage()) {
+            tabInfo.onStartedReaderMode();
+        }
+
         // Make sure there is a WebContentsObserver on this tab's WebContents.
         if (tabInfo.getWebContentsObserver() == null) {
             tabInfo.setWebContentsObserver(createWebContentsObserver(shownTab.getWebContents()));
@@ -202,24 +143,33 @@
         // Make sure there is a distillability delegate set on the WebContents.
         setDistillabilityCallback(shownTabId);
 
-        requestReaderPanelShow(StateChangeReason.UNKNOWN);
+        tryShowingInfoBar();
     }
 
     @Override
     public void onHidden(Tab tab) {
         closeReaderPanel(StateChangeReason.UNKNOWN, false);
+        ReaderModeTabInfo info = mTabStatusMap.get(tab.getId());
+        if (info != null && info.isViewingReaderModePage()) {
+            long timeMs = info.onExitReaderMode();
+            recordReaderModeViewDuration(timeMs);
+        }
     }
 
     @Override
     public void onDestroyed(Tab tab) {
         if (tab == null) return;
-        if (tab.getInfoBarContainer() != null) {
-            tab.getInfoBarContainer().removeObserver(this);
-        }
-        // If the panel was not shown for the previous navigation, record it now.
+
+        // If the infobar was not shown for the previous navigation, record it now.
         ReaderModeTabInfo info = mTabStatusMap.get(tab.getId());
-        if (info != null && !info.isPanelShowRecorded()) {
-            recordPanelVisibilityForNavigation(false);
+        if (info != null) {
+            if (!info.isInfoBarShowRecorded()) {
+                recordInfoBarVisibilityForNavigation(false);
+            }
+            if (info.isViewingReaderModePage()) {
+                long timeMs = info.onExitReaderMode();
+                recordReaderModeViewDuration(timeMs);
+            }
         }
         removeTabState(tab.getId());
     }
@@ -239,20 +189,23 @@
 
     @Override
     public void onContentChanged(Tab tab) {
-        // Only listen to events on the currently active tab.
-        if (tab.getId() != mTabId) return;
-        closeReaderPanel(StateChangeReason.UNKNOWN, false);
-
-        if (mTabStatusMap.containsKey(mTabId)) {
-            // If the panel was closed using the "x" icon, don't show it again for this tab.
-            if (mTabStatusMap.get(mTabId).isDismissed()) return;
-            removeTabState(mTabId);
+        // If the content change was because of distiller switching web contents or Reader Mode has
+        // already been dismissed for this tab do nothing.
+        if (mTabStatusMap.containsKey(tab.getId()) && mTabStatusMap.get(tab.getId()).isDismissed()
+                && !DomDistillerUrlUtils.isDistilledPage(tab.getUrl())) {
+            return;
         }
 
-        ReaderModeTabInfo tabInfo = new ReaderModeTabInfo();
+        ReaderModeTabInfo tabInfo = mTabStatusMap.get(tab.getId());
+        if (!mTabStatusMap.containsKey(tab.getId())) {
+            tabInfo = new ReaderModeTabInfo();
+            mTabStatusMap.put(tab.getId(), tabInfo);
+        }
+        // If the tab state already existed, only reset the relevant data. Things like view duration
+        // need to be preserved.
         tabInfo.setStatus(NOT_POSSIBLE);
         tabInfo.setUrl(tab.getUrl());
-        mTabStatusMap.put(tab.getId(), tabInfo);
+        tabInfo.setIsCallbackSet(false);
 
         if (tab.getWebContents() != null) {
             tabInfo.setWebContentsObserver(createWebContentsObserver(tab.getWebContents()));
@@ -264,63 +217,11 @@
             // Make sure there is a distillability delegate set on the WebContents.
             setDistillabilityCallback(tab.getId());
         }
-
-        if (tab.getInfoBarContainer() != null) tab.getInfoBarContainer().addObserver(this);
-    }
-
-    @Override
-    public void onToggleFullscreenMode(Tab tab, boolean enable) {
-        // Temporarily hide the reader mode panel while fullscreen is enabled.
-        if (enable) {
-            mIsFullscreenModeEntered = true;
-            closeReaderPanel(StateChangeReason.FULLSCREEN_ENTERED, false);
-        } else {
-            mIsFullscreenModeEntered = false;
-            requestReaderPanelShow(StateChangeReason.FULLSCREEN_EXITED);
-        }
-    }
-
-    // InfoBarContainerObserver:
-
-    @Override
-    public void onAddInfoBar(InfoBarContainer container, InfoBar infoBar, boolean isFirst) {
-        mIsInfoBarContainerShown = true;
-        // If the panel is opened past the peeking state, obscure the infobar.
-        if (mReaderModePanel != null && mReaderModePanel.isPanelOpened() && container != null) {
-            container.setIsObscuredByOtherView(true);
-        } else if (isFirst) {
-            // Temporarily hides the reader mode button while the infobars are shown.
-            closeReaderPanel(StateChangeReason.INFOBAR_SHOWN, false);
-        }
-    }
-
-    @Override
-    public void onRemoveInfoBar(InfoBarContainer container, InfoBar infoBar, boolean isLast) {
-        // Re-shows the reader mode button if necessary once the infobars are dismissed.
-        if (isLast) {
-            mIsInfoBarContainerShown = false;
-            requestReaderPanelShow(StateChangeReason.INFOBAR_HIDDEN);
-        }
-    }
-
-    @Override
-    public void onInfoBarContainerAttachedToWindow(boolean hasInfoBars) {
-        mIsInfoBarContainerShown = hasInfoBars;
-        if (mIsInfoBarContainerShown) {
-            closeReaderPanel(StateChangeReason.INFOBAR_SHOWN, false);
-        } else {
-            requestReaderPanelShow(StateChangeReason.INFOBAR_HIDDEN);
-        }
     }
 
     // ReaderModeManagerDelegate:
 
     @Override
-    public void setReaderModePanel(ReaderModePanel panel) {
-        mReaderModePanel = panel;
-    }
-
-    @Override
     public ChromeActivity getChromeActivity() {
         return mChromeActivity;
     }
@@ -331,9 +232,9 @@
         int tabId = mTabModelSelector.getCurrentTabId();
 
         ReaderModeTabInfo info = mTabStatusMap.get(tabId);
-        if (info != null && !info.isPanelShowRecorded()) {
-            info.setIsPanelShowRecorded(true);
-            recordPanelVisibilityForNavigation(true);
+        if (info != null && !info.isInfoBarShowRecorded()) {
+            info.setIsInfoBarShowRecorded(true);
+            recordInfoBarVisibilityForNavigation(true);
             if (LibraryLoader.isInitialized()) {
                 RapporServiceBridge.sampleDomainAndRegistryFromURL(
                         "DomDistiller.PromptPanel", info.getUrl());
@@ -342,31 +243,18 @@
     }
 
     /**
-     * Record if the panel became visible on the current page. This can be overridden for testing.
-     * @param visible If the panel was visible at any time.
+     * Record if the infobar became visible on the current page. This can be overridden for testing.
+     * @param visible If the infobar was visible at any time.
      */
-    protected void recordPanelVisibilityForNavigation(boolean visible) {
+    protected void recordInfoBarVisibilityForNavigation(boolean visible) {
         RecordHistogram.recordBooleanHistogram("DomDistiller.ReaderShownForPageLoad", visible);
     }
 
     @Override
     public void onClosed(StateChangeReason reason) {
-        if (mReaderModePanel == null || mTabModelSelector == null) return;
+        if (mTabModelSelector == null) return;
 
-        restoreInfobars();
-
-        // Only dismiss the panel if the close was a result of user interaction.
-        if (reason != StateChangeReason.FLING && reason != StateChangeReason.SWIPE
-                && reason != StateChangeReason.CLOSE_BUTTON) {
-            return;
-        }
-
-        // Record close button usage.
-        if (reason == StateChangeReason.CLOSE_BUTTON) {
-            RecordHistogram.recordBooleanHistogram("DomDistiller.BarCloseButtonUsage",
-                    mReaderModePanel.getPanelState() == PanelState.EXPANDED
-                    || mReaderModePanel.getPanelState() == PanelState.MAXIMIZED);
-        }
+        RecordHistogram.recordBooleanHistogram("DomDistiller.InfoBarUsage", false);
 
         int currentTabId = mTabModelSelector.getCurrentTabId();
         if (!mTabStatusMap.containsKey(currentTabId)) return;
@@ -374,29 +262,6 @@
     }
 
     @Override
-    public void onPeek() {
-        restoreInfobars();
-    }
-
-    /**
-     * Restore any infobars that may have been hidden by Reader Mode.
-     */
-    private void restoreInfobars() {
-        if (!mIsInfoBarContainerShown) return;
-
-        Tab curTab = mTabModelSelector.getCurrentTab();
-        if (curTab == null) return;
-
-        InfoBarContainer container = curTab.getInfoBarContainer();
-        if (container == null) return;
-
-        container.setIsObscuredByOtherView(false);
-
-        // Temporarily hides the reader mode button while the infobars are shown.
-        closeReaderPanel(StateChangeReason.INFOBAR_SHOWN, false);
-    }
-
-    @Override
     public WebContents getBasePageWebContents() {
         Tab tab = mTabModelSelector.getCurrentTab();
         if (tab == null) return null;
@@ -406,8 +271,7 @@
 
     @Override
     public void closeReaderPanel(StateChangeReason reason, boolean animate) {
-        if (mReaderModePanel == null) return;
-        mReaderModePanel.closePanel(reason, animate);
+        // TODO(mdjones): Remove this method and dependencies.
     }
 
     @Override
@@ -416,17 +280,6 @@
                 timeMs, TimeUnit.MILLISECONDS);
     }
 
-    @Override
-    public void onLayoutChanged() {
-        if (isKeyboardShowing()) {
-            mIsKeyboardShowing = true;
-            closeReaderPanel(StateChangeReason.KEYBOARD_SHOWN, false);
-        } else if (mIsKeyboardShowing) {
-            mIsKeyboardShowing = false;
-            requestReaderPanelShow(StateChangeReason.KEYBOARD_HIDDEN);
-        }
-    }
-
     /**
      * @return True if the keyboard might be showing. This is not 100% accurate; see
      *         UiUtils.isKeyboardShowing(...).
@@ -436,15 +289,33 @@
                 mChromeActivity.findViewById(android.R.id.content));
     }
 
-    protected WebContentsObserver createWebContentsObserver(WebContents webContents) {
+    protected WebContentsObserver createWebContentsObserver(final WebContents webContents) {
         final int readerTabId = mTabModelSelector.getCurrentTabId();
         if (readerTabId == Tab.INVALID_TAB_ID) return null;
 
         return new WebContentsObserver(webContents) {
+            /** Whether or not the previous navigation should be removed. */
+            private boolean mShouldRemovePreviousNavigation;
+
+            /** The index of the last committed distiller page in history. */
+            private int mLastDistillerPageIndex;
+
             @Override
             public void didStartNavigation(String url, boolean isInMainFrame,
                     boolean isSameDocument, boolean isErrorPage) {
                 if (!isInMainFrame || isSameDocument) return;
+
+                // Reader Mode should not pollute the navigation stack. To avoid this, watch for
+                // navigations and prepare to remove any that are "chrome-distiller" urls.
+                NavigationController controller = webContents.getNavigationController();
+                int index = controller.getLastCommittedEntryIndex();
+                NavigationEntry entry = controller.getEntryAtIndex(index);
+
+                if (entry != null && DomDistillerUrlUtils.isDistilledPage(entry.getUrl())) {
+                    mShouldRemovePreviousNavigation = true;
+                    mLastDistillerPageIndex = index;
+                }
+
                 // If there is a navigation in the current tab, hide the bar. It will show again
                 // once the distillability test is successful.
                 if (readerTabId == mTabModelSelector.getCurrentTabId()) {
@@ -470,7 +341,12 @@
                 // TODO(cjhopman): This should possibly ignore navigations that replace the entry
                 // (like those from history.replaceState()).
                 if (!hasCommitted || !isInMainFrame || isSameDocument) return;
-                if (DomDistillerUrlUtils.isDistilledPage(url)) return;
+
+                if (mShouldRemovePreviousNavigation) {
+                    mShouldRemovePreviousNavigation = false;
+                    NavigationController controller = webContents.getNavigationController();
+                    controller.removeEntryAtIndex(mLastDistillerPageIndex);
+                }
 
                 // Make sure the tab was not destroyed.
                 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId);
@@ -488,7 +364,7 @@
                 if (tabInfo.getStatus() != POSSIBLE) {
                     closeReaderPanel(StateChangeReason.UNKNOWN, false);
                 } else {
-                    requestReaderPanelShow(StateChangeReason.UNKNOWN);
+                    tryShowingInfoBar();
                 }
             }
 
@@ -501,22 +377,35 @@
                 // happening.
                 tabInfo.setIsDismissed(false);
 
-                // If the panel was not shown for the previous navigation, record it now.
+                // If the infobar was not shown for the previous navigation, record it now.
                 Tab curTab = mTabModelSelector.getTabById(readerTabId);
                 if (curTab != null && !curTab.isNativePage() && !curTab.isBeingRestored()) {
-                    recordPanelVisibilityForNavigation(false);
+                    recordInfoBarVisibilityForNavigation(false);
                 }
-                tabInfo.setIsPanelShowRecorded(false);
+                tabInfo.setIsInfoBarShowRecorded(false);
+
+                if (curTab != null && !DomDistillerUrlUtils.isDistilledPage(curTab.getUrl())
+                        && tabInfo.isViewingReaderModePage()) {
+                    long timeMs = tabInfo.onExitReaderMode();
+                    recordReaderModeViewDuration(timeMs);
+                }
             }
         };
     }
 
     /**
-     * This is a wrapper for "requestPanelShow" that checks if reader mode is possible before
-     * showing.
-     * @param reason The reason the panel is requesting to be shown.
+     * Record the amount of time the user spent in Reader Mode.
+     * @param timeMs The amount of time in ms that the user spent in Reader Mode.
      */
-    protected void requestReaderPanelShow(StateChangeReason reason) {
+    private void recordReaderModeViewDuration(long timeMs) {
+        RecordHistogram.recordLongTimesHistogram(
+                "DomDistiller.Time.ViewingReaderModePage", timeMs, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Try showing the reader mode infobar.
+     */
+    protected void tryShowingInfoBar() {
         if (mTabModelSelector == null) return;
 
         int currentTabId = mTabModelSelector.getCurrentTabId();
@@ -528,23 +417,36 @@
                 && getBasePageWebContents().getNavigationController().getUseDesktopUserAgent()
                 && !mIsReaderHeuristicAlwaysTrue;
 
-        if (mReaderModePanel == null || !mTabStatusMap.containsKey(currentTabId)
-                || usingRequestDesktopSite
+        if (!mTabStatusMap.containsKey(currentTabId) || usingRequestDesktopSite
                 || mTabStatusMap.get(currentTabId).getStatus() != POSSIBLE
                 || mTabStatusMap.get(currentTabId).isDismissed()
-                || mIsInfoBarContainerShown
-                || mIsFindToolbarShowing
-                || mIsFullscreenModeEntered
-                || mIsKeyboardShowing
                 || AccessibilityUtil.isAccessibilityEnabled()) {
             return;
         }
 
-        mReaderModePanel.requestPanelShow(reason);
+        ReaderModeInfoBar.showReaderModeInfoBar(mTabModelSelector.getCurrentTab(), this);
     }
 
     /**
-     * Open a link from the panel in a new tab.
+     * Navigate the current tab to a Reader Mode URL.
+     */
+    public void navigateToReaderMode() {
+        RecordHistogram.recordBooleanHistogram("DomDistiller.InfoBarUsage", true);
+
+        WebContents baseWebContents = getBasePageWebContents();
+        if (baseWebContents == null || mChromeActivity == null || mTabModelSelector == null) return;
+
+        String url = baseWebContents.getUrl();
+        if (url == null) return;
+
+        ReaderModeTabInfo info = mTabStatusMap.get(mTabModelSelector.getCurrentTabId());
+        if (info != null) info.onStartedReaderMode();
+
+        DomDistillerTabUtils.distillCurrentPageAndView(getBasePageWebContents());
+    }
+
+    /**
+     * Open a link from the infobar in a new tab.
      * @param url The URL to load.
      */
     @Override
@@ -563,22 +465,6 @@
     }
 
     /**
-     * @return Whether the Reader Mode panel is opened (state is EXPANDED or MAXIMIZED).
-     */
-    public boolean isPanelOpened() {
-        if (mReaderModePanel == null) return false;
-        return mReaderModePanel.isPanelOpened();
-    }
-
-    /**
-     * @return The ReaderModePanel for testing.
-     */
-    @VisibleForTesting
-    public ReaderModePanel getPanelForTesting() {
-        return mReaderModePanel;
-    }
-
-    /**
      * Set the callback for updating reader mode status based on whether or not the page should
      * be viewed in reader mode.
      * @param tabId The ID of the tab having its callback set.
@@ -617,8 +503,7 @@
                             tabInfo.setStatus(POSSIBLE);
                             // The user may have changed tabs.
                             if (tabId == mTabModelSelector.getCurrentTabId()) {
-                                // TODO(mdjones): Add reason DISTILLER_STATE_CHANGE.
-                                requestReaderPanelShow(StateChangeReason.UNKNOWN);
+                                tryShowingInfoBar();
                             }
                         } else {
                             tabInfo.setStatus(NOT_POSSIBLE);
@@ -644,9 +529,7 @@
         boolean enabled = CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_DOM_DISTILLER)
                 && !CommandLine.getInstance().hasSwitch(
                            ChromeSwitches.DISABLE_READER_MODE_BOTTOM_BAR)
-                && !DeviceFormFactor.isTablet()
-                && DomDistillerTabUtils.isDistillerHeuristicsEnabled()
-                && !SysUtils.isLowEndDevice();
+                && DomDistillerTabUtils.isDistillerHeuristicsEnabled();
         return enabled;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerDelegate.java
index 78869c37..66d5cf426 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerDelegate.java
@@ -6,7 +6,6 @@
 
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
-import org.chromium.chrome.browser.compositor.bottombar.readermode.ReaderModePanel;
 import org.chromium.content_public.browser.WebContents;
 
 /**
@@ -14,11 +13,6 @@
  */
 public interface ReaderModeManagerDelegate {
     /**
-     * @param panel The panel to be managed.
-     */
-    void setReaderModePanel(ReaderModePanel panel);
-
-    /**
      * Load a URL in a new tab.
      * @param url The URL to load in the tab.
      */
@@ -35,11 +29,6 @@
     void onClosed(StateChangeReason reason);
 
     /**
-     * Notify the manager that the panel has entered the peeking state.
-     */
-    void onPeek();
-
-    /**
      * Get the WebContents of the page that is being distilled.
      * @return The WebContents for the currently visible tab.
      */
@@ -63,9 +52,4 @@
      * @param timeInMs The amount of time spent in ms.
      */
     void recordTimeSpentInReader(long timeInMs);
-
-    /**
-     * Notification that the layout has changed.
-     */
-    void onLayoutChanged();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTabInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTabInfo.java
index ef50dc7..0f90ba5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTabInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTabInfo.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.dom_distiller;
 
+import android.os.SystemClock;
+
 import org.chromium.content_public.browser.WebContentsObserver;
 
 /**
@@ -16,7 +18,7 @@
     // The distillation status of the tab.
     private int mStatus;
 
-    // If the panel was closed due to the close button.
+    // If the infobar was closed due to the close button.
     private boolean mIsDismissed;
 
     // The URL that distiller is using for this tab. This is used to check if a result comes
@@ -27,8 +29,14 @@
     // be distilled. This flag is used to detect if the callback is set for this tab.
     private boolean mIsCallbackSet;
 
-    // Used to flag the the panel was shown and recorded by UMA.
-    private boolean mShowPanelRecorded;
+    // Used to flag the the infobar was shown and recorded by UMA.
+    private boolean mShowInfoBarRecorded;
+
+    // The time that the user started viewing Reader Mode content.
+    private long mViewStartTimeMs;
+
+    // Whether or not the current tab is a Reader Mode page.
+    private boolean mIsViewingReaderModePage;
 
     /**
      * @param observer The WebContentsObserver for the tab this object represents.
@@ -45,6 +53,31 @@
     }
 
     /**
+     * A notification that the user started viewing Reader Mode.
+     */
+    public void onStartedReaderMode() {
+        mIsViewingReaderModePage = true;
+        mViewStartTimeMs = SystemClock.elapsedRealtime();
+    }
+
+    /**
+     * A notification that the user is no longer viewing Reader Mode. This could be because of a
+     * navigation away from the page, switching tabs, or closing the browser.
+     * @return The amount of time in ms that the user spent viewing Reader Mode.
+     */
+    public long onExitReaderMode() {
+        mIsViewingReaderModePage = false;
+        return SystemClock.elapsedRealtime() - mViewStartTimeMs;
+    }
+
+    /**
+     * @return Whether or not the user is on a Reader Mode page.
+     */
+    public boolean isViewingReaderModePage() {
+        return mIsViewingReaderModePage;
+    }
+
+    /**
      * @param status The status of reader mode for this object's tab.
      */
     public void setStatus(int status) {
@@ -59,14 +92,14 @@
     }
 
     /**
-     * @return If the panel has been dismissed for this object's tab.
+     * @return If the infobar has been dismissed for this object's tab.
      */
     public boolean isDismissed() {
         return mIsDismissed;
     }
 
     /**
-     * @param dismissed Set the panel as dismissed for this object's tab.
+     * @param dismissed Set the infobar as dismissed for this object's tab.
      */
     public void setIsDismissed(boolean dismissed) {
         mIsDismissed = dismissed;
@@ -101,17 +134,17 @@
     }
 
     /**
-     * @return If the call to show the panel was recorded.
+     * @return If the call to show the infobar was recorded.
      */
-    public boolean isPanelShowRecorded() {
-        return mShowPanelRecorded;
+    public boolean isInfoBarShowRecorded() {
+        return mShowInfoBarRecorded;
     }
 
     /**
      * @param isRecorded True if the action has been recorded.
      */
-    public void setIsPanelShowRecorded(boolean isRecorded) {
-        mShowPanelRecorded = isRecorded;
+    public void setIsInfoBarShowRecorded(boolean isRecorded) {
+        mShowInfoBarRecorded = isRecorded;
     }
 
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
index 23e115f2..8d7749e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
@@ -12,6 +12,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.dom_distiller.ReaderModeManager;
 import org.chromium.chrome.browser.tab.Tab;
 
@@ -49,17 +51,17 @@
         prompt.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                // TODO(mdjones): Trigger navigation from manager.
+                sManager.navigateToReaderMode();
             }
         });
 
         layout.addContent(prompt, 1f);
     }
 
+    @Override
     public void onCloseButtonClicked() {
         super.onCloseButtonClicked();
-
-        // TODO(mdjones): Notifiy the manager that the infobar was closed.
+        sManager.onClosed(StateChangeReason.CLOSE_BUTTON);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index 5163f1bb..1f89665 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -182,7 +182,9 @@
                 SearchActivity.this, onSearchEngineFinalizedCallback);
     }
 
-    void finishDeferredInitialization() {
+    private void finishDeferredInitialization() {
+        assert !mIsActivityUsable
+                : "finishDeferredInitialization() incorrectly called multiple times";
         mIsActivityUsable = true;
         if (mQueuedUrl != null) loadUrl(mQueuedUrl);
 
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index ae1fcb64..074ad45 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1403,7 +1403,6 @@
   "javatests/src/org/chromium/chrome/browser/document/LauncherActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/dom_distiller/DistillabilityServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsTest.java",
-  "javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/ChromeDownloadDelegateTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
index a545b738..328ce20d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
@@ -135,7 +135,7 @@
 
         mManagerPhone = new LayoutManagerChromePhone(layoutManagerHost);
         mManager = mManagerPhone;
-        mManager.init(mTabModelSelector, null, null, container, null, null, null);
+        mManager.init(mTabModelSelector, null, null, container, null, null);
         initializeMotionEvent();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/DistillabilityServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/DistillabilityServiceTest.java
index 245cc44..86b72a1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/DistillabilityServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/DistillabilityServiceTest.java
@@ -13,13 +13,17 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.compositor.bottombar.readermode.ReaderModePanel;
+import org.chromium.chrome.browser.infobar.InfoBar;
+import org.chromium.chrome.browser.infobar.InfoBarContainer;
+import org.chromium.chrome.browser.infobar.InfoBarContainer.InfoBarContainerObserver;
+import org.chromium.chrome.browser.infobar.ReaderModeInfoBar;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeRestriction;
@@ -55,13 +59,26 @@
     @Feature({"Distillability-Service"})
     @MediumTest
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
-    @DisabledTest
     public void testServiceAliveAfterNativePage() throws InterruptedException, TimeoutException {
         EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
                 InstrumentationRegistry.getInstrumentation().getContext());
 
-        final ReaderModePanel panel =
-                mActivityTestRule.getActivity().getReaderModeManager().getPanelForTesting();
+        final CallbackHelper readerShownCallbackHelper = new CallbackHelper();
+
+        Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        tab.getInfoBarContainer().addObserver(new InfoBarContainerObserver() {
+            @Override
+            public void onAddInfoBar(InfoBarContainer container, InfoBar infoBar, boolean isFirst) {
+                if (infoBar instanceof ReaderModeInfoBar) readerShownCallbackHelper.notifyCalled();
+            }
+
+            @Override
+            public void onRemoveInfoBar(
+                    InfoBarContainer container, InfoBar infoBar, boolean isLast) {}
+
+            @Override
+            public void onInfoBarContainerAttachedToWindow(boolean hasInfobars) {}
+        });
 
         TestWebContentsObserver observer = new TestWebContentsObserver(
                 mActivityTestRule.getActivity().getActivityTab().getWebContents());
@@ -71,12 +88,12 @@
         int curCallCount = finishHelper.getCallCount();
         mActivityTestRule.loadUrl("chrome://history");
         finishHelper.waitForCallback(curCallCount, 1);
-        Assert.assertFalse(panel.isShowing());
+        Assert.assertEquals(0, readerShownCallbackHelper.getCallCount());
 
         // Navigate to a normal page.
-        curCallCount = finishHelper.getCallCount();
+        curCallCount = readerShownCallbackHelper.getCallCount();
         mActivityTestRule.loadUrl(testServer.getURL(TEST_PAGE));
-        finishHelper.waitForCallback(curCallCount, 1);
-        Assert.assertTrue(panel.isShowing());
+        readerShownCallbackHelper.waitForCallback(curCallCount, 1);
+        Assert.assertEquals(1, readerShownCallbackHelper.getCallCount());
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerTest.java
deleted file mode 100644
index c70e4ce..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManagerTest.java
+++ /dev/null
@@ -1,288 +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.
-
-package org.chromium.chrome.browser.dom_distiller;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.UiThreadTestRule;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RetryOnFailure;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelContent;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManagerWrapper;
-import org.chromium.chrome.browser.compositor.bottombar.readermode.ReaderModeBarControl;
-import org.chromium.chrome.browser.compositor.bottombar.readermode.ReaderModePanel;
-import org.chromium.chrome.browser.compositor.scene_layer.ReaderModeSceneLayer;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel.MockTabModelDelegate;
-import org.chromium.chrome.test.util.browser.tabmodel.MockTabModelSelector;
-import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.browser.WebContentsObserver;
-
-/**
- * Tests logic in the ReaderModeManager.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-public class ReaderModeManagerTest {
-    OverlayPanelManagerWrapper mPanelManager;
-    ReaderModeManagerWrapper mReaderManager;
-    MockReaderModePanel mPanel;
-    ReaderModeMockTabModelSelector mTabModelSelector;
-
-    @Rule
-    public UiThreadTestRule mRule = new UiThreadTestRule();
-
-    /**
-     * A mock TabModelSelector for creating tabs.
-     */
-    private static class ReaderModeMockTabModelSelector extends MockTabModelSelector {
-        public ReaderModeMockTabModelSelector() {
-            super(2, 0,
-                    new MockTabModelDelegate() {
-                        @Override
-                        public Tab createTab(int id, boolean incognito) {
-                            return new Tab(id, incognito, null);
-                        }
-                    });
-        }
-
-        @Override
-        public int getCurrentTabId() {
-            return 0;
-        }
-    }
-
-    /**
-     * A wrapper for the ReaderModeManager; this is used for recording and triggering events
-     * manually.
-     */
-    private static class ReaderModeManagerWrapper extends ReaderModeManager {
-        private int mRecordedCount;
-        private int mVisibleCount;
-        private boolean mShouldTrigger;
-
-        public ReaderModeManagerWrapper(MockTabModelSelector selector) {
-            super(selector, null);
-            mShouldTrigger = true;
-        }
-
-        @Override
-        protected void requestReaderPanelShow(StateChangeReason reason) {
-            // Skip tab checks and request the panel be shown.
-            if (mShouldTrigger) mReaderModePanel.requestPanelShow(reason);
-        }
-
-        @Override
-        public WebContentsObserver createWebContentsObserver(WebContents webContents) {
-            // Do not attempt to create or attach a WebContentsObserver.
-            return null;
-        }
-
-        @Override
-        protected boolean isDistillerHeuristicAlwaysTrue() {
-            return true;
-        }
-
-        @Override
-        protected void recordPanelVisibilityForNavigation(boolean visible) {
-            mRecordedCount++;
-            mVisibleCount += visible ? 1 : 0;
-        }
-
-        public void setShouldTrigger(boolean shouldTrigger) {
-            mShouldTrigger = shouldTrigger;
-        }
-
-        public int getRecordedCount() {
-            return mRecordedCount;
-        }
-
-        public int getVisibleCount() {
-            return mVisibleCount;
-        }
-
-        public ReaderModeTabInfo getTabInfo(int id) {
-            return mTabStatusMap.get(id);
-        }
-    }
-
-    /**
-     * Mock ReaderModePanel.
-     */
-    private static class MockReaderModePanel extends ReaderModePanel {
-        public MockReaderModePanel(Context context, OverlayPanelManager manager) {
-            super(context, null, manager, null);
-        }
-
-        @Override
-        public ReaderModeSceneLayer createNewReaderModeSceneLayer() {
-            return null;
-        }
-
-        @Override
-        public void peekPanel(StateChangeReason reason) {
-            setHeightForTesting(1);
-            super.peekPanel(reason);
-        }
-
-        @Override
-        protected ReaderModeBarControl getReaderModeBarControl() {
-            return new MockReaderModeBarControl();
-        }
-
-        /**
-         * This class is overridden to be completely inert; it would otherwise call many native
-         * methods.
-         */
-        private static class MockReaderModeBarControl extends ReaderModeBarControl {
-            public MockReaderModeBarControl() {
-                super(null, null, null, null);
-            }
-
-            @Override
-            public void setBarText(int stringId) {}
-
-            @Override
-            public void inflate() {}
-
-            @Override
-            public void invalidate() {}
-        }
-
-        @Override
-        protected void initializeUiState() {}
-
-        /**
-         * Override creation and destruction of the ContentViewCore as they rely on native methods.
-         */
-        private static class MockOverlayPanelContent extends OverlayPanelContent {
-            public MockOverlayPanelContent() {
-                super(null, null, null);
-            }
-
-            @Override
-            public void removeLastHistoryEntry(String url, long timeInMs) {}
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        mPanelManager = new OverlayPanelManagerWrapper();
-        mTabModelSelector = new ReaderModeMockTabModelSelector();
-        mReaderManager = new ReaderModeManagerWrapper(mTabModelSelector);
-        mPanel = new MockReaderModePanel(
-                InstrumentationRegistry.getInstrumentation().getTargetContext(), mPanelManager);
-        mReaderManager.setReaderModePanel(mPanel);
-        mPanel.setManagerDelegate(mReaderManager);
-    }
-
-    // Start ReaderModeManager test suite.
-
-    /**
-     * Tests that the panel behaves appropriately with infobar events.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ReaderModeManager"})
-    @UiThreadTest
-    @RetryOnFailure
-    public void testInfoBarEvents() {
-        mPanel.requestPanelShow(StateChangeReason.UNKNOWN);
-
-        mReaderManager.onAddInfoBar(null, null, true);
-        Assert.assertEquals(1, mPanelManager.getRequestPanelShowCount());
-        Assert.assertEquals(1, mPanelManager.getPanelHideCount());
-
-        mReaderManager.onRemoveInfoBar(null, null, true);
-        Assert.assertEquals(2, mPanelManager.getRequestPanelShowCount());
-        Assert.assertEquals(1, mPanelManager.getPanelHideCount());
-    }
-
-    /**
-     * Tests that the panel behaves appropriately with fullscreen events.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ReaderModeManager"})
-    @UiThreadTest
-    @RetryOnFailure
-    public void testFullscreenEvents() {
-        mPanel.requestPanelShow(StateChangeReason.UNKNOWN);
-
-        mReaderManager.onToggleFullscreenMode(null, true);
-        Assert.assertEquals(1, mPanelManager.getRequestPanelShowCount());
-        Assert.assertEquals(1, mPanelManager.getPanelHideCount());
-
-        mReaderManager.onToggleFullscreenMode(null, false);
-        Assert.assertEquals(2, mPanelManager.getRequestPanelShowCount());
-        Assert.assertEquals(1, mPanelManager.getPanelHideCount());
-    }
-
-    /**
-     * Tests that the metric that tracks when the panel is visible is correctly recorded.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ReaderModeManager"})
-    @UiThreadTest
-    @RetryOnFailure
-    public void testPanelOpenRecorded() {
-        Tab tab = new Tab(0, false, null);
-        mReaderManager.onShown(tab);
-
-        Assert.assertEquals(1, mReaderManager.getRecordedCount());
-        Assert.assertEquals(1, mReaderManager.getVisibleCount());
-        Assert.assertTrue(null != mReaderManager.getTabInfo(0));
-
-        // Make sure recording the panel showing only occurs once per navigation.
-        mReaderManager.onShown(tab);
-
-        Assert.assertEquals(1, mReaderManager.getRecordedCount());
-        Assert.assertEquals(1, mReaderManager.getVisibleCount());
-
-        // Destroy shouldn't record either if the panel showed.
-        mReaderManager.onDestroyed(tab);
-
-        Assert.assertEquals(1, mReaderManager.getRecordedCount());
-        Assert.assertEquals(1, mReaderManager.getVisibleCount());
-        Assert.assertTrue(null == mReaderManager.getTabInfo(0));
-    }
-
-    /**
-     * Tests that a tab closing records the panel was not visible.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ReaderModeManager"})
-    @UiThreadTest
-    @RetryOnFailure
-    public void testPanelCloseRecorded() {
-        Tab tab = new Tab(0, false, null);
-        mReaderManager.setShouldTrigger(false);
-        mReaderManager.onShown(tab);
-        mReaderManager.onDestroyed(tab);
-
-        Assert.assertEquals(1, mReaderManager.getRecordedCount());
-        Assert.assertEquals(0, mReaderManager.getVisibleCount());
-        Assert.assertTrue(null == mReaderManager.getTabInfo(0));
-    }
-
-    // TODO(mdjones): Test add/remove infobar while fullscreen is enabled.
-    // TODO(mdjones): Test onclosebuttonpressed disables Reader Mode for a particular tab.
-    // TODO(mdjones): Test onorientationchanged closes and re-opens panel.
-    // TODO(mdjones): Test tab events.
-    // TODO(mdjones): Test distillability callback.
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java
index d1b719ac..01367fa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java
@@ -107,6 +107,7 @@
     @TargetApi(Build.VERSION_CODES.KITKAT)
     @LargeTest
     @Feature({"Printing"})
+    @DisabledTest(message = "crbug.com/733002")
     public void testNormalPrintingFlow() throws Throwable {
         if (!ApiCompatibilityUtils.isPrintingSupported()) return;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
index 47ef46f..4b15554b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
@@ -27,6 +27,7 @@
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -61,6 +62,8 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class SearchActivityTest {
+    private static final long OMNIBOX_SHOW_TIMEOUT_MS = ScalableTimeout.scaleTimeout(5000);
+
     private static class TestDelegate
             extends SearchActivityDelegate implements DefaultSearchEnginePromoDialogObserver {
         public final CallbackHelper shouldDelayNativeInitializationCallback = new CallbackHelper();
@@ -73,6 +76,7 @@
         public boolean shouldShowRealSearchDialog;
 
         public DefaultSearchEnginePromoDialog shownPromoDialog;
+        public Callback<Boolean> onSearchEngineFinalizedCallback;
 
         @Override
         boolean shouldDelayNativeInitialization() {
@@ -83,6 +87,7 @@
         @Override
         void showSearchEngineDialogIfNeeded(
                 Activity activity, Callback<Boolean> onSearchEngineFinalized) {
+            onSearchEngineFinalizedCallback = onSearchEngineFinalized;
             showSearchEngineDialogIfNeededCallback.notifyCalled();
 
             if (shouldShowRealSearchDialog) {
@@ -96,7 +101,7 @@
                 return;
             }
 
-            onSearchEngineFinalized.onResult(!shouldDelayDeferredInitialization);
+            if (!shouldDelayDeferredInitialization) onSearchEngineFinalized.onResult(true);
         }
 
         @Override
@@ -268,6 +273,7 @@
         final SearchActivity searchActivity = startSearchActivity();
         mTestDelegate.shouldDelayNativeInitializationCallback.waitForCallback(0);
         mTestDelegate.showSearchEngineDialogIfNeededCallback.waitForCallback(0);
+        Assert.assertNotNull(mTestDelegate.onSearchEngineFinalizedCallback);
         Assert.assertEquals(0, mTestDelegate.onFinishDeferredInitializationCallback.getCallCount());
 
         // Set some text in the search box, then continue startup.
@@ -275,7 +281,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                searchActivity.finishDeferredInitialization();
+                mTestDelegate.onSearchEngineFinalizedCallback.onResult(true);
             }
         });
 
@@ -289,7 +295,7 @@
         final SearchActivityLocationBarLayout locationBar =
                 (SearchActivityLocationBarLayout) searchActivity.findViewById(
                         R.id.search_location_bar);
-        OmniboxTestUtils.waitForOmniboxSuggestions(locationBar);
+        OmniboxTestUtils.waitForOmniboxSuggestions(locationBar, OMNIBOX_SHOW_TIMEOUT_MS);
 
         // Hitting enter should submit the URL and kick the user to the browser.
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
@@ -376,7 +382,7 @@
         final SearchActivityLocationBarLayout locationBar =
                 (SearchActivityLocationBarLayout) searchActivity.findViewById(
                         R.id.search_location_bar);
-        OmniboxTestUtils.waitForOmniboxSuggestions(locationBar);
+        OmniboxTestUtils.waitForOmniboxSuggestions(locationBar, OMNIBOX_SHOW_TIMEOUT_MS);
 
         // Start the Activity again by firing another copy of the same Intent.
         SearchActivity restartedActivity = startSearchActivity(1);
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6d0fb260..a7c596d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2992,6 +2992,24 @@
      SINGLE_VALUE_TYPE(switches::kShowCertLink)},
 #endif
 
+    {"omnibox-ui-hide-suggestion-url-path",
+     flag_descriptions::kOmniboxUIHideSuggestionUrlPathName,
+     flag_descriptions::kOmniboxUIHideSuggestionUrlPathDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(omnibox::kUIExperimentHideSuggestionUrlPath)},
+
+    {"omnibox-ui-hide-suggestion-url-scheme",
+     flag_descriptions::kOmniboxUIHideSuggestionUrlSchemeName,
+     flag_descriptions::kOmniboxUIHideSuggestionUrlSchemeDescription,
+     kOsDesktop,
+     FEATURE_VALUE_TYPE(omnibox::kUIExperimentHideSuggestionUrlScheme)},
+
+    {"omnibox-ui-hide-suggestion-url-trivial-subdomains",
+     flag_descriptions::kOmniboxUIHideSuggestionUrlTrivialSubdomainsName,
+     flag_descriptions::kOmniboxUIHideSuggestionUrlTrivialSubdomainsDescription,
+     kOsDesktop,
+     FEATURE_VALUE_TYPE(
+         omnibox::kUIExperimentHideSuggestionUrlTrivialSubdomains)},
+
     {"omnibox-ui-max-autocomplete-matches",
      flag_descriptions::kOmniboxUIMaxAutocompleteMatchesName,
      flag_descriptions::kOmniboxUIMaxAutocompleteMatchesDescription, kOsDesktop,
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index 4653099..31966bd 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -108,10 +108,12 @@
 
 // Populates webapk::WebApk and returns it.
 // Must be called on a worker thread because it encodes an SkBitmap.
-std::unique_ptr<webapk::WebApk> BuildWebApkProtoInBackground(
+std::unique_ptr<webapk::WebApk> BuildProtoInBackground(
     const ShortcutInfo& shortcut_info,
     const SkBitmap& primary_icon,
     const SkBitmap& badge_icon,
+    const std::string& package_name,
+    const std::string& version,
     const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
     bool is_manifest_stale) {
   std::unique_ptr<webapk::WebApk> webapk(new webapk::WebApk);
@@ -120,6 +122,8 @@
       base::android::BuildInfo::GetInstance()->package_name());
   webapk->set_requester_application_version(version_info::GetVersionNumber());
   webapk->set_android_abi(getCurrentAbi());
+  webapk->set_package_name(package_name);
+  webapk->set_version(version);
   webapk->set_stale_manifest(is_manifest_stale);
 
   webapk::WebAppManifest* web_app_manifest = webapk->mutable_manifest();
@@ -170,13 +174,6 @@
   return webapk;
 }
 
-// Calls the callback when the |webapk| request is created.
-void OnWebApkProtoBuilt(
-    const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback,
-    std::unique_ptr<webapk::WebApk> webapk) {
-  callback.Run(std::move(webapk));
-}
-
 // Returns task runner for running background tasks.
 scoped_refptr<base::TaskRunner> GetBackgroundTaskRunner() {
   return base::CreateTaskRunnerWithTraits(
@@ -254,15 +251,22 @@
   OnResult(static_cast<WebApkInstallResult>(result));
 }
 
-void WebApkInstaller::BuildWebApkProtoInBackgroundForTesting(
-    const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback,
+// static
+void WebApkInstaller::BuildProto(
+    const ShortcutInfo& shortcut_info,
+    const SkBitmap& primary_icon,
+    const SkBitmap& badge_icon,
+    const std::string& package_name,
+    const std::string& version,
     const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-    bool is_manifest_stale) {
+    bool is_manifest_stale,
+    const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback) {
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, primary_icon_,
-                 badge_icon_, icon_url_to_murmur2_hash, is_manifest_stale),
-      base::Bind(&OnWebApkProtoBuilt, callback));
+      base::Bind(&BuildProtoInBackground, shortcut_info, primary_icon,
+                 badge_icon, package_name, version, icon_url_to_murmur2_hash,
+                 is_manifest_stale),
+      callback);
 }
 
 // static
@@ -366,12 +370,11 @@
   finish_callback_ = finish_callback;
   task_type_ = UPDATE;
 
-  base::PostTaskAndReplyWithResult(
-      GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, primary_icon_,
-                 badge_icon_, icon_url_to_murmur2_hash, is_manifest_stale),
-      base::Bind(&WebApkInstaller::SendUpdateWebApkRequest,
-                 weak_ptr_factory_.GetWeakPtr(), webapk_version));
+  BuildProto(shortcut_info_, primary_icon_, badge_icon_, webapk_package_,
+             std::to_string(webapk_version), icon_url_to_murmur2_hash,
+             is_manifest_stale,
+             base::Bind(&WebApkInstaller::SendRequest,
+                        weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WebApkInstaller::OnURLFetchComplete(const net::URLFetcher* source) {
@@ -456,38 +459,22 @@
       icon_url_to_murmur2_hash[icon_url] = "";
   }
 
-  base::PostTaskAndReplyWithResult(
-      GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, primary_icon_,
-                 badge_icon_, icon_url_to_murmur2_hash,
-                 false /* is_manifest_stale */),
-      base::Bind(&WebApkInstaller::SendCreateWebApkRequest,
-                 weak_ptr_factory_.GetWeakPtr()));
+  BuildProto(shortcut_info_, primary_icon_, badge_icon_, "" /* package_name */,
+             "" /* version */, icon_url_to_murmur2_hash,
+             false /* is_manifest_stale */,
+             base::Bind(&WebApkInstaller::SendRequest,
+                        weak_ptr_factory_.GetWeakPtr()));
 }
 
-void WebApkInstaller::SendCreateWebApkRequest(
-    std::unique_ptr<webapk::WebApk> webapk) {
-  SendRequest(std::move(webapk), server_url_);
-}
-
-void WebApkInstaller::SendUpdateWebApkRequest(
-    int webapk_version,
-    std::unique_ptr<webapk::WebApk> webapk) {
-  webapk->set_package_name(webapk_package_);
-  webapk->set_version(std::to_string(webapk_version));
-
-  SendRequest(std::move(webapk), server_url_);
-}
-
-void WebApkInstaller::SendRequest(std::unique_ptr<webapk::WebApk> request_proto,
-                                  const GURL& server_url) {
+void WebApkInstaller::SendRequest(
+    std::unique_ptr<webapk::WebApk> request_proto) {
   timer_.Start(
       FROM_HERE, base::TimeDelta::FromMilliseconds(webapk_server_timeout_ms_),
       base::Bind(&WebApkInstaller::OnResult, weak_ptr_factory_.GetWeakPtr(),
                  WebApkInstallResult::FAILURE));
 
   url_fetcher_ =
-      net::URLFetcher::Create(server_url, net::URLFetcher::POST, this);
+      net::URLFetcher::Create(server_url_, net::URLFetcher::POST, this);
   url_fetcher_->SetRequestContext(request_context_getter_);
   std::string serialized_request;
   request_proto->SerializeToString(&serialized_request);
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h
index 52ab9b48..807b24b4 100644
--- a/chrome/browser/android/webapk/webapk_installer.h
+++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -87,12 +87,17 @@
                          const base::android::JavaParamRef<jobject>& obj,
                          jint result);
 
-  // Creates a WebApk install or update request.
-  // Should be used only for testing.
-  void BuildWebApkProtoInBackgroundForTesting(
-      const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback,
+  // Asynchronously builds the WebAPK proto on a background thread for an update
+  // or install request. Runs |callback| on the calling thread when complete.
+  static void BuildProto(
+      const ShortcutInfo& shortcut_info,
+      const SkBitmap& primary_icon,
+      const SkBitmap& badge_icon,
+      const std::string& package_name,
+      const std::string& version,
       const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
-      bool is_manifest_stale);
+      bool is_manifest_stale,
+      const base::Callback<void(std::unique_ptr<webapk::WebApk>)>& callback);
 
   // Registers JNI hooks.
   static bool Register(JNIEnv* env);
@@ -150,22 +155,10 @@
                                  const std::string& primary_icon_hash,
                                  const std::string& badge_icon_hash);
 
-  // Sends request to WebAPK server to create WebAPK. During a successful
-  // request the WebAPK server responds with the URL of the generated WebAPK.
-  // |webapk| is the proto to send to the WebAPK server.
-  void SendCreateWebApkRequest(std::unique_ptr<webapk::WebApk> webapk_proto);
-
-  // Sends request to WebAPK server to update a WebAPK. During a successful
-  // request the WebAPK server responds with the URL of the generated WebAPK.
-  // |webapk| is the proto to send to the WebAPK server.
-  void SendUpdateWebApkRequest(int webapk_version,
-                               std::unique_ptr<webapk::WebApk> webapk_proto);
-
   // Sends a request to WebAPK server to create/update WebAPK. During a
   // successful request the WebAPK server responds with the URL of the generated
   // WebAPK.
-  void SendRequest(std::unique_ptr<webapk::WebApk> request_proto,
-                   const GURL& server_url);
+  void SendRequest(std::unique_ptr<webapk::WebApk> request_proto);
 
   net::URLRequestContextGetter* request_context_getter_;
 
@@ -203,7 +196,6 @@
   // Whether the server wants the WebAPK to request updates less frequently.
   bool relax_updates_;
 
-
   // Indicates whether the installer is for installing or updating a WebAPK.
   TaskType task_type_;
 
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 78d8342..6b209d35 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -187,9 +187,7 @@
 // Builds WebApk proto and blocks till done.
 class BuildProtoRunner {
  public:
-  explicit BuildProtoRunner(content::BrowserContext* browser_context)
-      : browser_context_(browser_context) {}
-
+  BuildProtoRunner() {}
   ~BuildProtoRunner() {}
 
   void BuildSync(
@@ -201,15 +199,13 @@
     info.best_primary_icon_url = best_primary_icon_url;
     info.best_badge_icon_url = best_badge_icon_url;
 
-    // WebApkInstaller owns itself.
     SkBitmap primary_icon(gfx::test::CreateBitmap(144, 144));
     SkBitmap badge_icon(gfx::test::CreateBitmap(72, 72));
-    WebApkInstaller* installer = new TestWebApkInstaller(
-        browser_context_, info, primary_icon, badge_icon);
-    installer->BuildWebApkProtoInBackgroundForTesting(
+    WebApkInstaller::BuildProto(
+        info, primary_icon, badge_icon, "" /* package_name */, "" /* version */,
+        icon_url_to_murmur2_hash, is_manifest_stale,
         base::Bind(&BuildProtoRunner::OnBuiltWebApkProto,
-                   base::Unretained(this)),
-        icon_url_to_murmur2_hash, is_manifest_stale);
+                   base::Unretained(this)));
 
     base::RunLoop run_loop;
     on_completed_callback_ = run_loop.QuitClosure();
@@ -225,8 +221,6 @@
     on_completed_callback_.Run();
   }
 
-  content::BrowserContext* browser_context_;
-
   // The populated webapk::WebApk.
   std::unique_ptr<webapk::WebApk> webapk_request_;
 
@@ -292,8 +286,7 @@
   }
 
   std::unique_ptr<BuildProtoRunner> CreateBuildProtoRunner() {
-    return std::unique_ptr<BuildProtoRunner>(
-        new BuildProtoRunner(profile_.get()));
+    return std::unique_ptr<BuildProtoRunner>(new BuildProtoRunner());
   }
 
   net::test_server::EmbeddedTestServer* test_server() { return &test_server_; }
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index 3601b81..9c61033 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -71,24 +71,16 @@
   // Replace sync with sync-internals (for legacy reasons).
   } else if (host == chrome::kChromeUISyncHost) {
     host = chrome::kChromeUISyncInternalsHost;
-  // Redirect chrome://extensions.
+// Redirect chrome://extensions, chrome://extensions-frame, and
+// chrome://settings/extensions all to chrome://extensions and forward path.
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  } else if (host == chrome::kChromeUIExtensionsHost) {
-    // If the material design extensions page is enabled, it gets its own host.
-    // Otherwise, it's handled by the uber settings page.
-    if (base::FeatureList::IsEnabled(features::kMaterialDesignExtensions)) {
-      host = chrome::kChromeUIExtensionsHost;
-      path = url->path();
-    } else {
-      host = chrome::kChromeUIUberHost;
-      path = chrome::kChromeUIExtensionsHost + url->path();
-    }
-  // Redirect chrome://settings/extensions (legacy URL).
-  } else if (host == chrome::kChromeUISettingsHost &&
-             url->path() ==
-                 std::string("/") + chrome::kDeprecatedExtensionsSubPage) {
-    host = chrome::kChromeUIUberHost;
-    path = chrome::kChromeUIExtensionsHost;
+  } else if (host == chrome::kChromeUIExtensionsHost ||
+             host == chrome::kChromeUIExtensionsFrameHost ||
+             (host == chrome::kChromeUISettingsHost &&
+              url->path() ==
+                  std::string("/") + chrome::kDeprecatedExtensionsSubPage)) {
+    host = chrome::kChromeUIExtensionsHost;
+    path = url->path();
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   } else if (host == chrome::kChromeUIHistoryHost) {
     // Redirect chrome://history.
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 629dc2f..b64fd81ee 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -20,8 +20,6 @@
       <if expr="chromeos">
         <structure name="IDR_FIRST_RUN_HTML" file="resources\chromeos\first_run\first_run.html" flattenhtml="true" type="chrome_html"/>
         <structure name="IDR_FIRST_RUN_JS" file="resources\chromeos\first_run\first_run.js" flattenhtml="true" type="chrome_html" />
-      </if>
-      <if expr="not is_android">
         <structure name="IDR_HELP_CSS" file="resources\help\help.css" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_HELP_HTML" file="resources\help\help.html" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_HELP_CONTENT_CSS" file="resources\help\help_content.css" flattenhtml="true" type="chrome_html" />
@@ -29,6 +27,8 @@
         <structure name="IDR_HELP_PAGE_HTML" file="resources\help\help_page.html" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_HELP_CHANNEL_CHANGE_PAGE_CSS" file="resources\help\channel_change_page.css" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_HELP_CHANNEL_CHANGE_PAGE_HTML" file="resources\help\channel_change_page.html" flattenhtml="true" type="chrome_html" />
+      </if>
+      <if expr="not is_android">
         <structure name="IDR_INCOGNITO_TAB_HTML" file="resources\ntp4\incognito_tab.html" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_MD_INCOGNITO_TAB_HTML" file="resources\ntp4\md_incognito_tab.html" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_GUEST_TAB_HTML" file="resources\ntp4\guest_tab.html" flattenhtml="true" type="chrome_html" />
@@ -248,7 +248,7 @@
         <include name="IDR_HOTWORD_MANIFEST" file="resources\hotword\manifest.json" type="BINDATA" />
         <include name="IDR_HOTWORD_AUDIO_VERIFICATION_MANIFEST" file="resources\hotword_audio_verification\manifest.json" type="BINDATA" />
       </if>
-      <if expr="not is_android">
+      <if expr="chromeos">
         <include name="IDR_HELP_JS" file="resources\help\help.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_HELP_PAGE_JS" file="resources\help\help_page.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_CHANNEL_CHANGE_PAGE_JS" file="resources\help\channel_change_page.js" flattenhtml="true" type="BINDATA" />
@@ -461,11 +461,13 @@
       <include name="IDR_SIGNIN_EMAIL_CONFIRMATION_JS" file="resources\signin\signin_email_confirmation\signin_email_confirmation.js" type="BINDATA" />
       <include name="IDR_SIGNIN_ERROR_HTML" file="resources\signin\signin_error\signin_error.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_SIGNIN_ERROR_JS" file="resources\signin\signin_error\signin_error.js" type="BINDATA" />
-      <include name="IDR_UBER_HTML" file="resources\uber\uber.html" flattenhtml="true" type="BINDATA" />
-      <include name="IDR_UBER_JS" file="resources\uber\uber.js" type="BINDATA" />
-      <include name="IDR_UBER_FRAME_HTML" file="resources\uber\uber_frame.html" flattenhtml="true" type="BINDATA" />
-      <include name="IDR_UBER_FRAME_JS" file="resources\uber\uber_frame.js" flattenhtml="true" type="BINDATA" />
-      <include name="IDR_UBER_UTILS_JS" file="resources\uber\uber_utils.js" type="BINDATA" />
+      <if expr="chromeos">
+        <include name="IDR_UBER_HTML" file="resources\uber\uber.html" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_UBER_JS" file="resources\uber\uber.js" type="BINDATA" />
+        <include name="IDR_UBER_FRAME_HTML" file="resources\uber\uber_frame.html" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_UBER_FRAME_JS" file="resources\uber\uber_frame.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_UBER_UTILS_JS" file="resources\uber\uber_utils.js" type="BINDATA" />
+      </if>
       <include name="IDR_USB_INTERNALS_CSS" file="resources\usb_internals\usb_internals.css" type="BINDATA" compress="gzip" />
       <include name="IDR_USB_INTERNALS_HTML" file="resources\usb_internals\usb_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
       <include name="IDR_USB_INTERNALS_JS" file="resources\usb_internals\usb_internals.js" type="BINDATA" compress="gzip" />
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index a607f6a0..3431ac3a9 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -589,50 +589,6 @@
   return url.ReplaceComponents(replacements);
 }
 
-// Maps "foo://bar/baz/" to "foo://chrome/bar/baz/".
-GURL AddUberHost(const GURL& url) {
-  const std::string uber_host = chrome::kChromeUIUberHost;
-  std::string new_path;
-  url.host_piece().AppendToString(&new_path);
-  url.path_piece().AppendToString(&new_path);
-
-  return ReplaceURLHostAndPath(url, uber_host, new_path);
-}
-
-// If url->host() is "chrome" and url->path() has characters other than the
-// first slash, changes the url from "foo://chrome/bar/" to "foo://bar/" and
-// returns true. Otherwise returns false.
-bool RemoveUberHost(GURL* url) {
-  if (url->host() != chrome::kChromeUIUberHost)
-    return false;
-
-  if (url->path().empty() || url->path() == "/")
-    return false;
-
-  const std::string old_path = url->path();
-
-  const std::string::size_type separator = old_path.find('/', 1);
-  std::string new_host;
-  std::string new_path;
-  if (separator == std::string::npos) {
-    new_host = old_path.substr(1);
-  } else {
-    new_host = old_path.substr(1, separator - 1);
-    new_path = old_path.substr(separator);
-  }
-
-  // Do not allow URLs with paths empty before the first slash since we can't
-  // have an empty host. (e.g "foo://chrome//")
-  if (new_host.empty())
-    return false;
-
-  *url = ReplaceURLHostAndPath(*url, new_host, new_path);
-
-  DCHECK(url->is_valid());
-
-  return true;
-}
-
 // Handles the rewriting of the new tab page URL based on group policy.
 bool HandleNewTabPageLocationOverride(
     GURL* url,
@@ -662,20 +618,10 @@
     return true;  // Return true to update the displayed URL.
   }
 
-  // Do not handle special URLs such as "about:foo"
-  if (!url->host().empty()) {
-    const GURL chrome_url = AddUberHost(*url);
-
-    // Handle valid "chrome://chrome/foo" URLs so the reverse handler will
-    // be called.
-    if (ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
-            browser_context, chrome_url))
-      return true;
-  }
-
   if (!ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
-          browser_context, *url))
+          browser_context, *url)) {
     return false;
+  }
 
 #if defined(OS_CHROMEOS)
   // Special case : in ChromeOS in Guest mode bookmarks and history are
@@ -699,13 +645,7 @@
 bool HandleWebUIReverse(GURL* url, content::BrowserContext* browser_context) {
   // No need to actually reverse-rewrite the URL, but return true to update the
   // displayed URL when rewriting chrome://help to chrome://settings/help.
-  if (url->host() == chrome::kChromeUISettingsHost)
-    return true;
-
-  if (!url->is_valid() || !url->SchemeIs(content::kChromeUIScheme))
-    return false;
-
-  return RemoveUberHost(url);
+  return url->host() == chrome::kChromeUISettingsHost;
 }
 
 bool CertMatchesFilter(const net::X509Certificate& cert,
diff --git a/chrome/browser/chromeos/system_logs/single_log_source.cc b/chrome/browser/chromeos/system_logs/single_log_source.cc
index 1d09e8b..0d5b7ae 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/single_log_source.cc
@@ -13,7 +13,8 @@
 
 namespace {
 
-const char kDefaultSystemLogDirPath[] = "/var/log";
+constexpr char kDefaultSystemLogDirPath[] = "/var/log";
+constexpr int kMaxNumAllowedLogRotationsDuringFileRead = 3;
 
 // Converts a logs source type to the corresponding file path, relative to the
 // base system log directory path. In the future, if non-file source types are
@@ -30,6 +31,15 @@
   return base::FilePath();
 }
 
+// Returns the inode value of file at |path|, or 0 if it doesn't exist or is
+// otherwise unable to be accessed for file system info.
+ino_t GetInodeValue(const base::FilePath& path) {
+  struct stat file_stats;
+  if (stat(path.value().c_str(), &file_stats) != 0)
+    return 0;
+  return file_stats.st_ino;
+}
+
 // Attempts to store a string |value| in |*response| under |key|. If there is
 // already a string in |*response| under |key|, appends |value| to the existing
 // string value.
@@ -49,6 +59,7 @@
     : SystemLogsSource(GetLogFileSourceRelativeFilePath(source).value()),
       log_file_dir_path_(kDefaultSystemLogDirPath),
       num_bytes_read_(0),
+      file_inode_(0),
       weak_ptr_factory_(this) {}
 
 SingleLogSource::~SingleLogSource() {}
@@ -62,17 +73,24 @@
       FROM_HERE,
       base::TaskTraits(base::MayBlock(), base::TaskPriority::BACKGROUND),
       base::Bind(&SingleLogSource::ReadFile, weak_ptr_factory_.GetWeakPtr(),
-                 response),
+                 kMaxNumAllowedLogRotationsDuringFileRead, response),
       base::Bind(callback, base::Owned(response)));
 }
 
-void SingleLogSource::ReadFile(SystemLogsResponse* result) {
+base::FilePath SingleLogSource::GetLogFilePath() const {
+  return base::FilePath(log_file_dir_path_).Append(source_name());
+}
+
+void SingleLogSource::ReadFile(size_t num_rotations_allowed,
+                               SystemLogsResponse* result) {
   // Attempt to open the file if it was not previously opened.
   if (!file_.IsValid()) {
-    file_.Initialize(base::FilePath(log_file_dir_path_).Append(source_name()),
+    file_.Initialize(GetLogFilePath(),
                      base::File::FLAG_OPEN | base::File::FLAG_READ);
     if (!file_.IsValid())
       return;
+
+    file_inode_ = GetInodeValue(GetLogFilePath());
   }
 
   // Check for file size reset.
@@ -89,8 +107,15 @@
   size_t size_read = file_.ReadAtCurrentPos(&result_string[0], size_to_read);
   result_string.resize(size_read);
 
-  // The reader may only read complete lines.
-  if (result_string.empty() || result_string.back() != '\n') {
+  const bool file_was_rotated = file_inode_ != GetInodeValue(GetLogFilePath());
+  const bool should_handle_file_rotation =
+      file_was_rotated && num_rotations_allowed > 0;
+
+  // The reader may only read complete lines. The exception is when there is a
+  // rotation, in which case all the remaining contents of the old log file
+  // should be read before moving on to read the new log file.
+  if ((result_string.empty() || result_string.back() != '\n') &&
+      !should_handle_file_rotation) {
     // If an incomplete line was read, return only the part that includes whole
     // lines.
     size_t last_newline_pos = result_string.find_last_of('\n');
@@ -114,6 +139,15 @@
   // Pass it back to the callback.
   AppendToSystemLogsResponse(result, source_name(),
                              anonymizer_.Anonymize(result_string));
+
+  // If the file was rotated, close the file handle and call this function
+  // again, to read from the new file.
+  if (should_handle_file_rotation) {
+    file_.Close();
+    num_bytes_read_ = 0;
+    file_inode_ = 0;
+    ReadFile(num_rotations_allowed - 1, result);
+  }
 }
 
 }  // namespace system_logs
diff --git a/chrome/browser/chromeos/system_logs/single_log_source.h b/chrome/browser/chromeos/system_logs/single_log_source.h
index 2ffe730..d4ddaa6 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source.h
+++ b/chrome/browser/chromeos/system_logs/single_log_source.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_SINGLE_LOG_SOURCE_H_
 
 #include <stddef.h>
+#include <sys/types.h>
 
 #include "base/files/file.h"
 #include "base/macros.h"
@@ -34,8 +35,23 @@
  private:
   friend class SingleLogSourceTest;
 
+  // Returns the full path of the log file.
+  base::FilePath GetLogFilePath() const;
+
   // Reads all available content from |file_| that has not already been read.
-  void ReadFile(SystemLogsResponse* result);
+  // Stores results as a single entry in |result|, with |source_name()| as key
+  // and the read log contents as value.
+  //
+  // Handles rotation of underlying log file by reading all remaining contents
+  // of old file and then opening and reading from new file.
+  //
+  // |num_rotations_allowed| limits the number of rotations that can take place
+  // before the function returns. This avoids this function never returning due
+  // to indefinitely repeated log file rotation. If this number is exceeded
+  // during a call, ReadFile() stops checking for log file rotation for the
+  // remainder of its execution. Any further rotation could result in missed log
+  // data.
+  void ReadFile(size_t num_rotations_allowed, SystemLogsResponse* result);
 
   // Path to system log file directory.
   base::FilePath log_file_dir_path_;
@@ -46,6 +62,10 @@
   // Handle for reading the log file that is source of logging data.
   base::File file_;
 
+  // File system inode value that was associated with |log_file_path_| when it
+  // was originally opened for reading.
+  ino_t file_inode_;
+
   // For removing PII from log results.
   feedback::AnonymizerTool anonymizer_;
 
diff --git a/chrome/browser/chromeos/system_logs/single_log_source_unittest.cc b/chrome/browser/chromeos/system_logs/single_log_source_unittest.cc
index ac31300..df830259 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source_unittest.cc
+++ b/chrome/browser/chromeos/system_logs/single_log_source_unittest.cc
@@ -72,6 +72,18 @@
                               input.data(), input.size());
   }
 
+  // Moves source file to destination path, then creates an empty file at the
+  // path of the original source file.
+  //
+  // |src_relative_path|: Source file path relative to |log_dir_|.
+  // |dest_relative_path|: Destination path relative to |log_dir_|.
+  bool RotateFile(const base::FilePath& src_relative_path,
+                  const base::FilePath& dest_relative_path) {
+    return base::Move(log_dir_.GetPath().Append(src_relative_path),
+                      log_dir_.GetPath().Append(dest_relative_path)) &&
+           WriteFile(src_relative_path, "");
+  }
+
   // Calls source_.Fetch() to start a logs fetch operation. Passes in
   // OnFileRead() as a callback. Runs until Fetch() has completed.
   void FetchFromSource() {
@@ -279,4 +291,43 @@
   EXPECT_EQ("Your MAC address is: ab:88:cd:00:00:02\n", latest_response());
 }
 
+TEST_F(SingleLogSourceTest, HandleLogFileRotation) {
+  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+
+  EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "1st log file\n"));
+  FetchFromSource();
+  EXPECT_EQ(1, num_callback_calls());
+  EXPECT_EQ("1st log file\n", latest_response());
+
+  // Rotate file. Make sure the rest of the old file and the contents of the new
+  // file are both read.
+  EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "More 1st log file\n"));
+  EXPECT_TRUE(
+      RotateFile(base::FilePath("messages"), base::FilePath("messages.1")));
+  EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "2nd log file\n"));
+
+  FetchFromSource();
+  EXPECT_EQ(2, num_callback_calls());
+  EXPECT_EQ("More 1st log file\n2nd log file\n", latest_response());
+
+  // Rotate again, but this time omit the newline before rotating.
+  EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "No newline here..."));
+  EXPECT_TRUE(
+      RotateFile(base::FilePath("messages"), base::FilePath("messages.1")));
+  EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "3rd log file\n"));
+  EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "Also no newline here"));
+
+  FetchFromSource();
+  EXPECT_EQ(3, num_callback_calls());
+  // Make sure the rotation didn't break anything: the last part of the new file
+  // does not end with a newline; thus the new file should not be read.
+  EXPECT_EQ("No newline here...3rd log file\n", latest_response());
+
+  // Finish the previous read attempt by adding the missing newline.
+  EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "...yet\n"));
+  FetchFromSource();
+  EXPECT_EQ(4, num_callback_calls());
+  EXPECT_EQ("Also no newline here...yet\n", latest_response());
+}
+
 }  // namespace system_logs
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
index 19be8e0..aa31acd 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
@@ -249,7 +249,7 @@
         RenderFrameHostID(render_process_id, render_frame_id));
     DCHECK(frame_iter == main_render_frame_data_use_map_.end());
     DataUseRecorderEntry entry =
-        CreateNewDataUseRecorder(nullptr, DataUse::TrafficType::UNKNOWN);
+        CreateNewDataUseRecorder(nullptr, DataUse::TrafficType::USER_TRAFFIC);
     entry->set_main_frame_id(
         RenderFrameHostID(render_process_id, render_frame_id));
     main_render_frame_data_use_map_.insert(std::make_pair(
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 49d5ba8..4dbbf13 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -947,7 +947,7 @@
 }
 
 // https://crbug.com/678967
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_LINUX)
 #define MAYBE_DownloadExtensionTest_FileIcon_Active DISABLED_DownloadExtensionTest_FileIcon_Active
 #else
 #define MAYBE_DownloadExtensionTest_FileIcon_Active DownloadExtensionTest_FileIcon_Active
diff --git a/chrome/browser/extensions/api/file_system/file_system_api.cc b/chrome/browser/extensions/api/file_system/file_system_api.cc
index cbd34827..8d5c628b 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_api.cc
@@ -79,8 +79,9 @@
 using apps::SavedFilesService;
 using storage::IsolatedContext;
 
-const char kInvalidCallingPage[] = "Invalid calling page. This function can't "
-    "be called from a background page.";
+const char kInvalidCallingPage[] =
+    "Invalid calling page. "
+    "This function can't be called from a background page.";
 const char kUserCancelled[] = "User cancelled";
 const char kWritableFileErrorFormat[] = "Error opening %s";
 const char kRequiresFileSystemWriteError[] =
@@ -183,11 +184,9 @@
 const char kLastChooseEntryDirectory[] = "last_choose_file_directory";
 
 const int kGraylistedPaths[] = {
-  base::DIR_HOME,
+    base::DIR_HOME,
 #if defined(OS_WIN)
-  base::DIR_PROGRAM_FILES,
-  base::DIR_PROGRAM_FILESX86,
-  base::DIR_WINDOWS,
+    base::DIR_PROGRAM_FILES, base::DIR_PROGRAM_FILESX86, base::DIR_WINDOWS,
 #endif
 };
 
@@ -215,8 +214,8 @@
       content::WebContents::FromRenderFrameHost(render_frame_host);
   // Check if there is an app window associated with the web contents; if not,
   // return null.
-  return AppWindowRegistry::Get(profile)
-                 ->GetAppWindowForWebContents(web_contents)
+  return AppWindowRegistry::Get(profile)->GetAppWindowForWebContents(
+             web_contents)
              ? web_contents
              : nullptr;
 }
@@ -248,8 +247,7 @@
                                            const std::string& extension_id) {
   base::FilePath path;
   std::string string_path;
-  if (prefs->ReadPrefAsString(extension_id,
-                              kLastChooseEntryDirectory,
+  if (prefs->ReadPrefAsString(extension_id, kLastChooseEntryDirectory,
                               &string_path)) {
     path = base::FilePath::FromUTF8Unsafe(string_path);
   }
@@ -360,11 +358,8 @@
                                                const std::string& id_override,
                                                base::DictionaryValue* result) {
   GrantedFileEntry file_entry = app_file_handler_util::CreateFileEntry(
-      GetProfile(),
-      extension(),
-      render_frame_host()->GetProcess()->GetID(),
-      path,
-      is_directory_);
+      GetProfile(), extension(), render_frame_host()->GetProcess()->GetID(),
+      path, is_directory_);
   base::ListValue* entries;
   bool success = result->GetList("entries", &entries);
   DCHECK(success);
@@ -416,9 +411,8 @@
 
 void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (is_directory_ &&
-      !extension_->permissions_data()->HasAPIPermission(
-          APIPermission::kFileSystemDirectory)) {
+  if (is_directory_ && !extension_->permissions_data()->HasAPIPermission(
+                           APIPermission::kFileSystemDirectory)) {
     error_ = kRequiresFileSystemDirectoryError;
     SendResponse(false);
   }
@@ -446,8 +440,7 @@
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
   int renderer_id = render_frame_host()->GetProcess()->GetID();
-  bool is_writable = policy->CanReadWriteFileSystem(renderer_id,
-                                                    filesystem_id);
+  bool is_writable = policy->CanReadWriteFileSystem(renderer_id, filesystem_id);
 
   return RespondNow(OneArgument(base::MakeUnique<base::Value>(is_writable)));
 }
@@ -465,9 +458,9 @@
       : function_(function) {
     select_file_dialog_ = ui::SelectFileDialog::Create(
         this, new ChromeSelectFilePolicy(web_contents));
-    gfx::NativeWindow owning_window = web_contents ?
-        platform_util::GetTopLevel(web_contents->GetNativeView()) :
-        NULL;
+    gfx::NativeWindow owning_window =
+        web_contents ? platform_util::GetTopLevel(web_contents->GetNativeView())
+                     : NULL;
 
     if (g_skip_picker_for_test) {
       if (g_use_suggested_path_for_test) {
@@ -501,14 +494,9 @@
       return;
     }
 
-    select_file_dialog_->SelectFile(picker_type,
-                                    base::string16(),
-                                    suggested_name,
-                                    &file_type_info,
-                                    0,
-                                    base::FilePath::StringType(),
-                                    owning_window,
-                                    NULL);
+    select_file_dialog_->SelectFile(
+        picker_type, base::string16(), suggested_name, &file_type_info, 0,
+        base::FilePath::StringType(), owning_window, NULL);
   }
 
   ~FilePicker() override {}
@@ -587,8 +575,8 @@
   // its destruction (and subsequent sending of the function response) until the
   // user has selected a file or cancelled the picker. At that point, the picker
   // will delete itself, which will also free the function instance.
-  new FilePicker(
-      this, web_contents, initial_path_, file_type_info, picker_type);
+  new FilePicker(this, web_contents, initial_path_, file_type_info,
+                 picker_type);
 }
 
 // static
@@ -647,15 +635,14 @@
 
 // static
 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
-    const std::string& name, const base::FilePath& path) {
+    const std::string& name,
+    const base::FilePath& path) {
   // For testing on Chrome OS, where to deal with remote and local paths
   // smoothly, all accessed paths need to be registered in the list of
   // external mount points.
   storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      name,
-      storage::kFileSystemTypeNativeLocal,
-      storage::FileSystemMountOption(),
-      path);
+      name, storage::kFileSystemTypeNativeLocal,
+      storage::FileSystemMountOption(), path);
 }
 
 void FileSystemChooseEntryFunction::FilesSelected(
@@ -668,8 +655,7 @@
     last_choose_directory = paths[0].DirName();
   }
   file_system_api::SetLastChooseEntryDirectory(
-      ExtensionPrefs::Get(GetProfile()),
-      extension()->id(),
+      ExtensionPrefs::Get(GetProfile()), extension()->id(),
       last_choose_directory);
   if (is_directory_) {
     // Get the WebContents for the app window to be the parent window of the
@@ -778,8 +764,8 @@
   if (acceptsAllTypes)
     file_type_info->include_all_files = *acceptsAllTypes;
 
-  bool need_suggestion = !file_type_info->include_all_files &&
-                         !suggested_extension.empty();
+  bool need_suggestion =
+      !file_type_info->include_all_files && !suggested_extension.empty();
 
   if (accepts) {
     for (const file_system::AcceptOption& option : *accepts) {
@@ -794,8 +780,9 @@
 
       // If we still need to find suggested_extension, hunt for it inside the
       // extensions returned from GetFileTypesFromAcceptOption.
-      if (need_suggestion && std::find(extensions.begin(),
-              extensions.end(), suggested_extension) != extensions.end()) {
+      if (need_suggestion &&
+          std::find(extensions.begin(), extensions.end(),
+                    suggested_extension) != extensions.end()) {
         need_suggestion = false;
       }
     }
@@ -808,7 +795,7 @@
 }
 
 void FileSystemChooseEntryFunction::BuildSuggestion(
-    const std::string *opt_name,
+    const std::string* opt_name,
     base::FilePath* suggested_name,
     base::FilePath::StringType* suggested_extension) {
   if (opt_name) {
@@ -895,10 +882,10 @@
 
     base::FilePath::StringType suggested_extension;
     BuildSuggestion(options->suggested_name.get(), &suggested_name,
-        &suggested_extension);
+                    &suggested_extension);
 
     BuildFileTypeInfo(&file_type_info, suggested_extension,
-        options->accepts.get(), options->accepts_all_types.get());
+                      options->accepts.get(), options->accepts_all_types.get());
   }
 
   file_type_info.allowed_paths = ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
@@ -959,8 +946,7 @@
         content::BrowserContext::GetStoragePartitionForSite(GetProfile(), site)
             ->GetFileSystemContext();
     const storage::FileSystemURL url = context->CreateCrackedFileSystemURL(
-        site,
-        storage::kFileSystemTypeIsolated,
+        site, storage::kFileSystemTypeIsolated,
         IsolatedContext::GetInstance()
             ->CreateVirtualRootPath(filesystem_id)
             .Append(base::FilePath::FromUTF8Unsafe(filesystem_path)));
@@ -994,8 +980,8 @@
   }
 
   SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile());
-  saved_files_service->RegisterFileEntry(
-      extension_->id(), entry_id, path, file_info->is_directory);
+  saved_files_service->RegisterFileEntry(extension_->id(), entry_id, path,
+                                         file_info->is_directory);
   saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
   SendResponse(true);
 }
@@ -1013,8 +999,9 @@
   bool needs_new_entry;
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &needs_new_entry));
-  const SavedFileEntry* file_entry = SavedFilesService::Get(
-      GetProfile())->GetFileEntry(extension_->id(), entry_id);
+  const SavedFileEntry* file_entry =
+      SavedFilesService::Get(GetProfile())
+          ->GetFileEntry(extension_->id(), entry_id);
   if (!file_entry) {
     error_ = kUnknownIdError;
     return false;
@@ -1068,11 +1055,9 @@
 #else
 
 FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction()
-    : chrome_details_(this) {
-}
+    : chrome_details_(this) {}
 
-FileSystemRequestFileSystemFunction::~FileSystemRequestFileSystemFunction() {
-}
+FileSystemRequestFileSystemFunction::~FileSystemRequestFileSystemFunction() {}
 
 ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() {
   using api::file_system::RequestFileSystem::Params;
@@ -1110,7 +1095,8 @@
       util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile());
   scoped_refptr<storage::FileSystemContext> file_system_context =
       content::BrowserContext::GetStoragePartitionForSite(
-          chrome_details_.GetProfile(), site)->GetFileSystemContext();
+          chrome_details_.GetProfile(), site)
+          ->GetFileSystemContext();
   storage::ExternalFileSystemBackend* const backend =
       file_system_context->external_backend();
   DCHECK(backend);
@@ -1164,7 +1150,8 @@
       util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile());
   scoped_refptr<storage::FileSystemContext> file_system_context =
       content::BrowserContext::GetStoragePartitionForSite(
-          chrome_details_.GetProfile(), site)->GetFileSystemContext();
+          chrome_details_.GetProfile(), site)
+          ->GetFileSystemContext();
   storage::ExternalFileSystemBackend* const backend =
       file_system_context->external_backend();
   DCHECK(backend);
@@ -1233,11 +1220,9 @@
 }
 
 FileSystemGetVolumeListFunction::FileSystemGetVolumeListFunction()
-    : chrome_details_(this) {
-}
+    : chrome_details_(this) {}
 
-FileSystemGetVolumeListFunction::~FileSystemGetVolumeListFunction() {
-}
+FileSystemGetVolumeListFunction::~FileSystemGetVolumeListFunction() {}
 
 ExtensionFunction::ResponseAction FileSystemGetVolumeListFunction::Run() {
   // Only kiosk apps in kiosk sessions can use this API.
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 4a87a2d4..6254ba8 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -622,14 +622,16 @@
     return true;
   }
 
-  // Navigations from chrome:// or chrome-search:// pages need to be allowed,
-  // even if |to_url| is not web-accessible.  See https://crbug.com/662602.
+  // Navigations from chrome://, chrome-search:// and chrome-devtools:// pages
+  // need to be allowed, even if |to_url| is not web-accessible. See
+  // https://crbug.com/662602.
   //
   // Note that this is intentionally done after the check for blob: and
   // filesystem: URLs above, for consistency with the renderer-side checks
   // which already disallow navigations from chrome URLs to blob/filesystem
   // URLs.
   if (site_url.SchemeIs(content::kChromeUIScheme) ||
+      site_url.SchemeIs(content::kChromeDevToolsScheme) ||
       site_url.SchemeIs(chrome::kChromeSearchScheme)) {
     *result = true;
     return true;
diff --git a/chrome/browser/extensions/extension_webui_apitest.cc b/chrome/browser/extensions/extension_webui_apitest.cc
index 1fd6e8c..3c234a71 100644
--- a/chrome/browser/extensions/extension_webui_apitest.cc
+++ b/chrome/browser/extensions/extension_webui_apitest.cc
@@ -34,7 +34,6 @@
  protected:
   testing::AssertionResult RunTest(const char* name,
                                    const GURL& page_url,
-                                   const GURL& frame_url,
                                    bool expected_result) {
     std::string script;
     {
@@ -54,7 +53,9 @@
 
     // Run the test.
     bool actual_result = false;
-    content::RenderFrameHost* webui = NavigateToWebUI(page_url, frame_url);
+    ui_test_utils::NavigateToURL(browser(), page_url);
+    content::RenderFrameHost* webui =
+        browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
     if (!webui)
       return testing::AssertionFailure() << "Failed to navigate to WebUI";
     CHECK(content::ExecuteScriptAndExtractBool(webui, script, &actual_result));
@@ -63,80 +64,25 @@
                : (testing::AssertionFailure() << "Check console output");
   }
 
-  testing::AssertionResult RunTestOnExtensionsFrame(const char* name) {
-    // In the current extension WebUI design, the content is actually hosted in
-    // an iframe at chrome://extensions-frame.
-    return RunTest(name,
-                   GURL("chrome://extensions"),
-                   GURL("chrome://extensions-frame"),
-                   true);  // tests on chrome://extensions-frame should succeed
+  testing::AssertionResult RunTestOnExtensionsPage(const char* name) {
+    return RunTest(name, GURL("chrome://extensions"), true);
   }
 
-  testing::AssertionResult RunTestOnChromeExtensionsFrame(const char* name) {
-    // Like RunTestOnExtensionsFrame, but chrome://extensions is an alias for
-    // chrome://chrome/extensions so test it explicitly.
-    return RunTest(name,
-                   GURL("chrome://chrome/extensions"),
-                   GURL("chrome://extensions-frame"),
-                   true);  // tests on chrome://extensions-frame should succeed
-  }
-
-  testing::AssertionResult RunTestOnChromeExtensions(const char* name) {
-    // Despite the extensions page being hosted in an iframe, also test the
-    // top-level chrome://extensions page (which actually loads
-    // chrome://chrome/extensions). In the past there was a bug where top-level
-    // extension WebUI bindings weren't correctly set up.
-    return RunTest(name,
-                   GURL("chrome://chrome/extensions"),
-                   GURL("chrome://chrome/extensions"),
-                   true);  // tests on chrome://chrome/extensions should succeed
-  }
-
-  testing::AssertionResult RunTestOnAbout(const char* name) {
+  testing::AssertionResult RunTestOnAboutPage(const char* name) {
     // chrome://about is an innocuous page that doesn't have any bindings.
     // Tests should fail.
-    return RunTest(name,
-                   GURL("chrome://about"),
-                   GURL("chrome://about"),
-                   false);  // tests on chrome://about should fail
-  }
-
- private:
-  // Navigates the browser to a WebUI page and returns the RenderFrameHost for
-  // that page.
-  content::RenderFrameHost* NavigateToWebUI(const GURL& page_url,
-                                            const GURL& frame_url) {
-    ui_test_utils::NavigateToURL(browser(), page_url);
-
-    content::WebContents* active_web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-
-    if (active_web_contents->GetLastCommittedURL() == frame_url)
-      return active_web_contents->GetMainFrame();
-
-    return FrameMatchingPredicate(
-        active_web_contents,
-        base::Bind(&content::FrameHasSourceUrl, frame_url));
+    return RunTest(name, GURL("chrome://about"), false);
   }
 };
 
 #if !defined(OS_WIN)  // flaky http://crbug.com/530722
 
-IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, SanityCheckAvailableAPIsInFrame) {
-  ASSERT_TRUE(RunTestOnExtensionsFrame("sanity_check_available_apis.js"));
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionWebUITest,
-                       SanityCheckAvailableAPIsInChromeFrame) {
-  ASSERT_TRUE(RunTestOnChromeExtensionsFrame("sanity_check_available_apis.js"));
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, SanityCheckAvailableAPIsInToplevel) {
-  ASSERT_TRUE(RunTestOnChromeExtensions("sanity_check_available_apis.js"));
+IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, SanityCheckAvailableAPIs) {
+  ASSERT_TRUE(RunTestOnExtensionsPage("sanity_check_available_apis.js"));
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, SanityCheckUnavailableAPIs) {
-  ASSERT_TRUE(RunTestOnAbout("sanity_check_available_apis.js"));
+  ASSERT_TRUE(RunTestOnAboutPage("sanity_check_available_apis.js"));
 }
 
 // Tests chrome.test.sendMessage, which exercises WebUI making a
@@ -145,7 +91,7 @@
   std::unique_ptr<ExtensionTestMessageListener> listener(
       new ExtensionTestMessageListener("ping", true));
 
-  ASSERT_TRUE(RunTestOnExtensionsFrame("send_message.js"));
+  ASSERT_TRUE(RunTestOnExtensionsPage("send_message.js"));
 
   ASSERT_TRUE(listener->WaitUntilSatisfied());
   listener->Reply("pong");
@@ -158,7 +104,7 @@
 // Tests chrome.runtime.onMessage, which exercises WebUI registering and
 // receiving an event.
 IN_PROC_BROWSER_TEST_F(ExtensionWebUITest, OnMessage) {
-  ASSERT_TRUE(RunTestOnExtensionsFrame("on_message.js"));
+  ASSERT_TRUE(RunTestOnExtensionsPage("on_message.js"));
 
   OnMessage::Info info;
   info.data = "hi";
@@ -179,7 +125,7 @@
   std::unique_ptr<ExtensionTestMessageListener> listener(
       new ExtensionTestMessageListener("ping", true));
 
-  ASSERT_TRUE(RunTestOnExtensionsFrame("runtime_last_error.js"));
+  ASSERT_TRUE(RunTestOnExtensionsPage("runtime_last_error.js"));
 
   ASSERT_TRUE(listener->WaitUntilSatisfied());
   listener->ReplyWithError("unknown host");
@@ -198,7 +144,7 @@
                         .AppendASCII("embed_self"));
   ASSERT_TRUE(extension);
 
-  ASSERT_TRUE(RunTestOnExtensionsFrame("can_embed_extension_options.js"));
+  ASSERT_TRUE(RunTestOnExtensionsPage("can_embed_extension_options.js"));
 
   ASSERT_TRUE(listener->WaitUntilSatisfied());
   listener->Reply(extension->id());
@@ -216,7 +162,7 @@
   ASSERT_TRUE(extension);
 
   ASSERT_TRUE(
-      RunTestOnExtensionsFrame("receives_extension_options_on_close.js"));
+      RunTestOnExtensionsPage("receives_extension_options_on_close.js"));
 
   ASSERT_TRUE(listener->WaitUntilSatisfied());
   listener->Reply(extension->id());
@@ -242,7 +188,7 @@
     DisableExtension(extension_id);
   }
 
-  ASSERT_TRUE(RunTestOnExtensionsFrame("can_embed_extension_options.js"));
+  ASSERT_TRUE(RunTestOnExtensionsPage("can_embed_extension_options.js"));
 
   ASSERT_TRUE(listener->WaitUntilSatisfied());
   listener->Reply(extension_id);
diff --git a/chrome/browser/extensions/lazy_background_page_apitest.cc b/chrome/browser/extensions/lazy_background_page_apitest.cc
index f6599d61..32e9181e 100644
--- a/chrome/browser/extensions/lazy_background_page_apitest.cc
+++ b/chrome/browser/extensions/lazy_background_page_apitest.cc
@@ -571,6 +571,7 @@
 // when it is destroyed.
 IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, UpdateExtensionsPage) {
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIExtensionsURL));
+  auto* extensions_page = browser()->tab_strip_model()->GetActiveWebContents();
 
   ResultCatcher catcher;
   base::FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page").
@@ -597,13 +598,6 @@
   // Lazy Background Page has been shut down.
   EXPECT_FALSE(IsBackgroundPageAlive(last_loaded_extension_id()));
 
-  // Verify that extensions page shows that the lazy background page is
-  // inactive.
-  content::RenderFrameHost* frame = content::FrameMatchingPredicate(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      base::Bind(&content::FrameHasSourceUrl,
-                 GURL(chrome::kChromeUIExtensionsFrameURL)));
-
   // Updating the extensions page is a process that has back-and-forth
   // communication (i.e., backend tells extensions page something changed,
   // extensions page requests updated data, backend responds with updated data,
@@ -616,7 +610,7 @@
   int num_tries = 0;
   while (!is_inactive && num_tries++ < kMaxTries) {
     EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-        frame,
+        extensions_page,
         "var ele = document.querySelectorAll('div.active-views');"
         "window.domAutomationController.send("
         "    ele[0].innerHTML.search('(Inactive)') > 0);",
diff --git a/chrome/browser/extensions/ntp_overridden_bubble_delegate.cc b/chrome/browser/extensions/ntp_overridden_bubble_delegate.cc
index e4985e4..22fb670 100644
--- a/chrome/browser/extensions/ntp_overridden_bubble_delegate.cc
+++ b/chrome/browser/extensions/ntp_overridden_bubble_delegate.cc
@@ -30,8 +30,14 @@
     "ack_existing_ntp_extensions";
 
 // Whether to acknowledge existing extensions overriding the NTP for the active
-// profile. Currently disabled for all platforms and only available for testing.
-bool g_acknowledge_existing_extensions = false;
+// profile. Active on ChromeOS to rollout the NTP bubble without prompting for
+// previously-installed extensions.
+bool g_acknowledge_existing_extensions =
+#if defined(OS_CHROMEOS)
+    true;
+#else
+    false;
+#endif
 
 }  // namespace
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c349655..af517fd 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3092,6 +3092,22 @@
     "the current page is provided as the first suggestion without a title. "
     "Enabling this flag causes the title to be displayed.";
 
+const char kOmniboxUIHideSuggestionUrlPathName[] =
+    "Omnibox UI Hide Suggestion URL Path";
+const char kOmniboxUIHideSuggestionUrlPathDescription[] =
+    "Elides the paths of suggested URLs in the Omnibox dropdown.";
+
+const char kOmniboxUIHideSuggestionUrlSchemeName[] =
+    "Omnibox UI Hide Suggestion URL Scheme";
+const char kOmniboxUIHideSuggestionUrlSchemeDescription[] =
+    "Elides the schemes of suggested URLs in the Omnibox dropdown.";
+
+const char kOmniboxUIHideSuggestionUrlTrivialSubdomainsName[] =
+    "Omnibox UI Hide Suggestion URL Trivial Subdomains";
+const char kOmniboxUIHideSuggestionUrlTrivialSubdomainsDescription[] =
+    "Elides trivially informative subdomains from suggested URLs in the "
+    "Omnibox dropdown (e.g. www. and m.).";
+
 const char kOmniboxUIMaxAutocompleteMatchesName[] =
     "Omnibox UI Max Autocomplete Matches";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 0faad7e..0a8f6de4 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -501,6 +501,15 @@
 extern const char kOmniboxDisplayTitleForCurrentUrlName[];
 extern const char kOmniboxDisplayTitleForCurrentUrlDescription[];
 
+extern const char kOmniboxUIHideSuggestionUrlPathName[];
+extern const char kOmniboxUIHideSuggestionUrlPathDescription[];
+
+extern const char kOmniboxUIHideSuggestionUrlSchemeName[];
+extern const char kOmniboxUIHideSuggestionUrlSchemeDescription[];
+
+extern const char kOmniboxUIHideSuggestionUrlTrivialSubdomainsName[];
+extern const char kOmniboxUIHideSuggestionUrlTrivialSubdomainsDescription[];
+
 extern const char kOmniboxUIMaxAutocompleteMatchesName[];
 extern const char kOmniboxUIMaxAutocompleteMatchesDescription[];
 
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
index 0233d9c..6b0b535 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -150,13 +150,9 @@
   CheckAllMemoryMetrics(histogram_tester, 1);
 }
 
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
-#define MAYBE_FetchDuringTrace DISABLED_FetchDuringTrace
-#else
-#define MAYBE_FetchDuringTrace FetchDuringTrace
-#endif
+// Flaky or failing everywhere, http://crbug.com/732927.
 IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
-                       MAYBE_FetchDuringTrace) {
+                       DISABLED_FetchDuringTrace) {
   GURL url1("about:blank");
   ui_test_utils::NavigateToURLWithDisposition(
       browser(), url1, WindowOpenDisposition::NEW_FOREGROUND_TAB,
@@ -204,12 +200,9 @@
   CheckAllMemoryMetrics(histogram_tester, 1);
 }
 
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
-#define MAYBE_FetchThreeTimes DISABLED_FetchThreeTimes
-#else
-#define MAYBE_FetchThreeTimes FetchThreeTimes
-#endif
-IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest, MAYBE_FetchThreeTimes) {
+// Flaky or failing everywhere, http://crbug.com/732927.
+IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
+                       DISABLED_FetchThreeTimes) {
   GURL url1("about:blank");
   ui_test_utils::NavigateToURLWithDisposition(
       browser(), url1, WindowOpenDisposition::NEW_FOREGROUND_TAB,
diff --git a/chrome/browser/referrer_policy_browsertest.cc b/chrome/browser/referrer_policy_browsertest.cc
index aaa13bfe..5c97e7aa 100644
--- a/chrome/browser/referrer_policy_browsertest.cc
+++ b/chrome/browser/referrer_policy_browsertest.cc
@@ -90,6 +90,10 @@
         return "origin";
       case blink::kWebReferrerPolicyOriginWhenCrossOrigin:
         return "origin-when-crossorigin";
+      case blink::kWebReferrerPolicySameOrigin:
+        return "same-origin";
+      case blink::kWebReferrerPolicyStrictOrigin:
+        return "strict-origin";
       case blink::kWebReferrerPolicyAlways:
         return "always";
       case blink::kWebReferrerPolicyNever:
@@ -599,6 +603,42 @@
                   blink::WebMouseEvent::Button::kLeft, EXPECT_FULL_REFERRER);
 }
 
+// Same origin
+
+IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest,
+                       HttpLeftClickHTTPRedirectToHTTPSameOrigin) {
+  RunReferrerTest(blink::kWebReferrerPolicySameOrigin, START_ON_HTTP,
+                  REGULAR_LINK, SERVER_REDIRECT_FROM_HTTP_TO_HTTP,
+                  WindowOpenDisposition::CURRENT_TAB,
+                  blink::WebMouseEvent::Button::kLeft, EXPECT_FULL_REFERRER);
+}
+
+IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest,
+                       HttpLeftClickHTTPRedirectToHTTPSSameOrigin) {
+  RunReferrerTest(blink::kWebReferrerPolicySameOrigin, START_ON_HTTPS,
+                  REGULAR_LINK, SERVER_REDIRECT_FROM_HTTPS_TO_HTTP,
+                  WindowOpenDisposition::CURRENT_TAB,
+                  blink::WebMouseEvent::Button::kLeft, EXPECT_EMPTY_REFERRER);
+}
+
+// Strict origin
+
+IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest,
+                       HttpLeftClickHTTPRedirectToHTTPStrictOrigin) {
+  RunReferrerTest(
+      blink::kWebReferrerPolicyStrictOrigin, START_ON_HTTP, REGULAR_LINK,
+      SERVER_REDIRECT_FROM_HTTP_TO_HTTP, WindowOpenDisposition::CURRENT_TAB,
+      blink::WebMouseEvent::Button::kLeft, EXPECT_ORIGIN_AS_REFERRER);
+}
+
+IN_PROC_BROWSER_TEST_F(ReferrerPolicyTest,
+                       HttpLeftClickHTTPSRedirectToHTTPStrictOrigin) {
+  RunReferrerTest(blink::kWebReferrerPolicyStrictOrigin, START_ON_HTTPS,
+                  REGULAR_LINK, SERVER_REDIRECT_FROM_HTTPS_TO_HTTP,
+                  WindowOpenDisposition::CURRENT_TAB,
+                  blink::WebMouseEvent::Button::kLeft, EXPECT_EMPTY_REFERRER);
+}
+
 // Reduced 'referer' granularity flag tests.
 
 // User initiated navigation, from HTTP to HTTPS via server redirect.
diff --git a/chrome/browser/resources/chromeos/bluetooth_pair_device.html b/chrome/browser/resources/chromeos/bluetooth_pair_device.html
index 7199ea13..52bfc17d 100644
--- a/chrome/browser/resources/chromeos/bluetooth_pair_device.html
+++ b/chrome/browser/resources/chromeos/bluetooth_pair_device.html
@@ -25,7 +25,6 @@
 
 <script src="../options/options_page.js"></script>
 <script src="../options/chromeos/bluetooth_pair_device_overlay.js"></script>
-<script src="../uber/uber_utils.js"></script>
 
 <script src="bluetooth_options.js"></script>
 <script src="browser_options.js"></script>
diff --git a/chrome/browser/resources/chromeos/certificate_manager_dialog.html b/chrome/browser/resources/chromeos/certificate_manager_dialog.html
index 5617945..b9650234 100644
--- a/chrome/browser/resources/chromeos/certificate_manager_dialog.html
+++ b/chrome/browser/resources/chromeos/certificate_manager_dialog.html
@@ -48,7 +48,6 @@
 <script src="../options/certificate_edit_ca_trust_overlay.js"></script>
 <script src="../options/certificate_import_error_overlay.js"></script>
 <script src="keyboard/keyboard_utils.js"></script>
-<script src="../uber/uber_utils.js"></script>
 <script src="certificate_manager_dialog.js"></script>
 <script src="chrome://certificate-manager/strings.js"></script>
 </head>
diff --git a/chrome/browser/resources/extensions/compiled_resources2.gyp b/chrome/browser/resources/extensions/compiled_resources2.gyp
index 173955e4..86bdc2d 100644
--- a/chrome/browser/resources/extensions/compiled_resources2.gyp
+++ b/chrome/browser/resources/extensions/compiled_resources2.gyp
@@ -63,7 +63,6 @@
           '<(DEPTH)/third_party/jstemplate/jsevalcontext.js',
           'extension_command_list.js',
           '<(DEPTH)/ui/webui/resources/js/cr/ui/controlled_indicator.js',
-          '<(DEPTH)/chrome/browser/resources/uber/uber_utils.js',
           'extension_error_overlay.js',
           'drag_and_drop_handler.js',
           '<(DEPTH)/ui/webui/resources/js/cr/ui/list_selection_model.js',
diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js
index 235c758..5582d587 100644
--- a/chrome/browser/resources/extensions/extension_list.js
+++ b/chrome/browser/resources/extensions/extension_list.js
@@ -984,7 +984,7 @@
       // Add the options query string. Corner case: the 'options' query string
       // will clobber the 'id' query string if the options link is clicked when
       // 'id' is in the URL, or if both query strings are in the URL.
-      uber.replaceState({}, '?options=' + extensionId);
+      window.history.replaceState({}, '', '/?options=' + extensionId);
 
       var overlay = extensions.ExtensionOptionsOverlay.getInstance();
       var shownCallback = function() {
@@ -1003,7 +1003,7 @@
         $('overlay').removeEventListener('cancelOverlay', f);
 
         // Remove the options query string.
-        uber.replaceState({}, '');
+        window.history.replaceState({}, '', '/');
       });
 
       // TODO(dbeam): why do we need to focus <extensionoptions> before and
diff --git a/chrome/browser/resources/extensions/extensions.html b/chrome/browser/resources/extensions/extensions.html
index 72a4c4c..7ed15c8 100644
--- a/chrome/browser/resources/extensions/extensions.html
+++ b/chrome/browser/resources/extensions/extensions.html
@@ -1,7 +1,8 @@
 <!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
 <head>
 <meta charset="utf-8">
+<title>$i18n{extensionSettings}</title>
 
 <link rel="stylesheet" href="extensions.css">
 <link rel="stylesheet" href="extension_commands_overlay.css">
@@ -49,10 +50,10 @@
 <script src="chrome://resources/js/cr/ui/list.js"></script>
 </if>
 
-<script src="chrome://extensions-frame/extensions.js"></script>
+<script src="chrome://extensions/extensions.js"></script>
 </head>
 
-<body class="uber-frame">
+<body>
 
 <div id="overlay" class="overlay" hidden>
   <include src="extension_commands_overlay.html">
@@ -273,7 +274,7 @@
 
 </div>
 
-<script src="chrome://extensions-frame/strings.js"></script>
+<script src="chrome://extensions/strings.js"></script>
 <script src="chrome://resources/js/i18n_template.js"></script>
 
 </body>
diff --git a/chrome/browser/resources/extensions/extensions.js b/chrome/browser/resources/extensions/extensions.js
index d0dfdd49..e6b511b2 100644
--- a/chrome/browser/resources/extensions/extensions.js
+++ b/chrome/browser/resources/extensions/extensions.js
@@ -4,7 +4,6 @@
 
 // <include src="../../../../ui/webui/resources/js/cr/ui/focus_row.js">
 // <include src="../../../../ui/webui/resources/js/cr/ui/focus_grid.js">
-// <include src="../uber/uber_utils.js">
 // <include src="drag_and_drop_handler.js">
 // <include src="extension_code.js">
 // <include src="extension_commands_overlay.js">
@@ -65,13 +64,9 @@
      */
     initialize: function() {
       this.setLoading_(true);
-      uber.onContentFrameLoaded();
       cr.ui.FocusOutlineManager.forDocument(document);
       measureCheckboxStrings();
 
-      // Set the title.
-      uber.setTitle(loadTimeData.getString('extensionSettings'));
-
       var extensionList = new ExtensionList(this);
       extensionList.id = 'extension-settings-list';
       var wrapper = $('extension-list-wrapper');
@@ -385,7 +380,7 @@
           lastFocused.focus();
 
         $('overlay').removeEventListener('cancelOverlay', f);
-        uber.replaceState({}, '');
+        window.history.replaceState({}, '', '/');
       });
       node.classList.add('showing');
     }
@@ -409,9 +404,6 @@
       assert(settings.dragWrapperHandler_).dragEnabled =
           !node || node == $('drop-target-overlay');
     }
-
-    uber.invokeMethodOnParent(node ? 'beginInterceptingEvents' :
-                                     'stopInterceptingEvents');
   };
 
   ExtensionSettings.focusOverlay = function() {
diff --git a/chrome/browser/resources/local_discovery/local_discovery.html b/chrome/browser/resources/local_discovery/local_discovery.html
index efa8485..9ef41d0 100644
--- a/chrome/browser/resources/local_discovery/local_discovery.html
+++ b/chrome/browser/resources/local_discovery/local_discovery.html
@@ -17,7 +17,7 @@
   <script src="local_discovery.js"></script>
   <script src="strings.js"></script>
 </head>
-<body class="uber-frame">
+<body>
   <div class="page" id="main-page">
     <div class="overlay" id="overlay" hidden>
       <div id="register-overlay" class="page">
diff --git a/chrome/browser/resources/md_bookmarks/actions.js b/chrome/browser/resources/md_bookmarks/actions.js
index 744b365f..7980752 100644
--- a/chrome/browser/resources/md_bookmarks/actions.js
+++ b/chrome/browser/resources/md_bookmarks/actions.js
@@ -253,6 +253,17 @@
     };
   }
 
+  /**
+   * @param {boolean} canEdit
+   * @return {!Action}
+   */
+  function setCanEditBookmarks(canEdit) {
+    return {
+      name: 'set-can-edit',
+      value: canEdit,
+    };
+  }
+
   return {
     changeFolderOpen: changeFolderOpen,
     clearSearch: clearSearch,
@@ -266,6 +277,7 @@
     selectAll: selectAll,
     selectFolder: selectFolder,
     selectItem: selectItem,
+    setCanEditBookmarks: setCanEditBookmarks,
     setIncognitoAvailability: setIncognitoAvailability,
     setSearchResults: setSearchResults,
     setSearchTerm: setSearchTerm,
diff --git a/chrome/browser/resources/md_bookmarks/api_listener.js b/chrome/browser/resources/md_bookmarks/api_listener.js
index 0e54e07..d7907a4 100644
--- a/chrome/browser/resources/md_bookmarks/api_listener.js
+++ b/chrome/browser/resources/md_bookmarks/api_listener.js
@@ -85,6 +85,13 @@
     dispatch(bookmarks.actions.setIncognitoAvailability(availability));
   }
 
+  /**
+   * @param {boolean} canEdit
+   */
+  function onCanEditBookmarksChanged(canEdit) {
+    dispatch(bookmarks.actions.setCanEditBookmarks(canEdit));
+  }
+
   function init() {
     chrome.bookmarks.onChanged.addListener(onBookmarkChanged);
     chrome.bookmarks.onChildrenReordered.addListener(onChildrenReordered);
@@ -98,6 +105,10 @@
         .then(onIncognitoAvailabilityChanged);
     cr.addWebUIListener(
         'incognito-availability-changed', onIncognitoAvailabilityChanged);
+
+    cr.sendWithPromise('getCanEditBookmarks').then(onCanEditBookmarksChanged);
+    cr.addWebUIListener(
+        'can-edit-bookmarks-changed', onCanEditBookmarksChanged);
   }
 
   return {
diff --git a/chrome/browser/resources/md_bookmarks/command_manager.html b/chrome/browser/resources/md_bookmarks/command_manager.html
index dff7845..a57c510 100644
--- a/chrome/browser/resources/md_bookmarks/command_manager.html
+++ b/chrome/browser/resources/md_bookmarks/command_manager.html
@@ -19,7 +19,7 @@
             on-tap="onCommandClick_">
           [[getCommandLabel_(command, menuIds_)]]
         </button>
-        <hr hidden$="[[!showDividerAfter_(command)]]"></hr>
+        <hr hidden$="[[!showDividerAfter_(command, menuIds_)]]"></hr>
       </template>
     </dialog>
     <template is="cr-lazy-render" id="editDialog">
diff --git a/chrome/browser/resources/md_bookmarks/command_manager.js b/chrome/browser/resources/md_bookmarks/command_manager.js
index f21578a..24c26d5 100644
--- a/chrome/browser/resources/md_bookmarks/command_manager.js
+++ b/chrome/browser/resources/md_bookmarks/command_manager.js
@@ -32,14 +32,22 @@
         },
       },
 
-      /** @type {Set<string>} */
+      /** @private {Set<string>} */
       menuIds_: Object,
+
+      /** @private */
+      globalCanEdit_: Boolean,
     },
 
     attached: function() {
       assert(CommandManager.instance_ == null);
       CommandManager.instance_ = this;
 
+      this.watch('globalCanEdit_', function(state) {
+        return state.prefs.canEdit;
+      });
+      this.updateFromStore();
+
       /** @private {function(!Event)} */
       this.boundOnOpenItemMenu_ = this.onOpenItemMenu_.bind(this);
       document.addEventListener('open-item-menu', this.boundOnOpenItemMenu_);
@@ -120,7 +128,7 @@
           return itemIds.size > 0;
         case Command.UNDO:
         case Command.REDO:
-          return true;
+          return this.globalCanEdit_;
         default:
           return this.isCommandVisible_(command, itemIds) &&
               this.isCommandEnabled_(command, itemIds);
@@ -136,13 +144,11 @@
     isCommandVisible_: function(command, itemIds) {
       switch (command) {
         case Command.EDIT:
-          return itemIds.size == 1;
+          return itemIds.size == 1 && this.globalCanEdit_;
         case Command.COPY:
-          return itemIds.size == 1 &&
-              this.containsMatchingNode_(itemIds, function(node) {
-                return !!node.url;
-              });
+          return this.isSingleBookmark_(itemIds);
         case Command.DELETE:
+          return itemIds.size > 0 && this.globalCanEdit_;
         case Command.OPEN_NEW_TAB:
         case Command.OPEN_NEW_WINDOW:
         case Command.OPEN_INCOGNITO:
@@ -160,6 +166,12 @@
      */
     isCommandEnabled_: function(command, itemIds) {
       switch (command) {
+        case Command.EDIT:
+        case Command.DELETE:
+          var state = this.getState();
+          return !this.containsMatchingNode_(itemIds, function(node) {
+            return !bookmarks.util.canEditNode(state, node.id);
+          });
         case Command.OPEN_NEW_TAB:
         case Command.OPEN_NEW_WINDOW:
           return this.expandUrls_(itemIds).length > 0;
@@ -358,6 +370,18 @@
     },
 
     /**
+     * @param {!Set<string>} itemIds
+     * @return {boolean} True if |itemIds| is a single bookmark (non-folder)
+     *     node.
+     */
+    isSingleBookmark_: function(itemIds) {
+      return itemIds.size == 1 &&
+          this.containsMatchingNode_(itemIds, function(node) {
+            return !!node.url;
+          });
+    },
+
+    /**
      * @param {Event} e
      * @private
      */
@@ -447,8 +471,9 @@
      * @return {boolean}
      * @private
      */
-    showDividerAfter_: function(command) {
-      return command == Command.DELETE;
+    showDividerAfter_: function(command, itemIds) {
+      return command == Command.DELETE &&
+          (this.globalCanEdit_ || this.isSingleBookmark_(itemIds));
     },
   });
 
diff --git a/chrome/browser/resources/md_bookmarks/dnd_manager.js b/chrome/browser/resources/md_bookmarks/dnd_manager.js
index a4423b3..71b1bcd 100644
--- a/chrome/browser/resources/md_bookmarks/dnd_manager.js
+++ b/chrome/browser/resources/md_bookmarks/dnd_manager.js
@@ -426,6 +426,13 @@
 
       e.preventDefault();
 
+      // If any node can't be dragged, early return (after preventDefault).
+      var anyUnmodifiable = draggedNodes.some(function(itemId) {
+        return !bookmarks.util.canEditNode(state, itemId);
+      });
+      if (anyUnmodifiable)
+        return;
+
       // If we are dragging a single link, we can do the *Link* effect.
       // Otherwise, we only allow copy and move.
       if (e.dataTransfer) {
@@ -475,7 +482,6 @@
       if (!overElement)
         return;
 
-
       // Now we know that we can drop. Determine if we will drop above, on or
       // below based on mouse position etc.
       this.dropDestination_ =
@@ -547,6 +553,9 @@
       if (isBookmarkList(overElement))
         itemId = state.selectedFolder;
 
+      if (!bookmarks.util.canReorderChildren(state, itemId))
+        return DropPosition.NONE;
+
       // Drags of a bookmark onto itself or of a folder into its children aren't
       // allowed.
       if (dragInfo.isDraggingBookmark(itemId) ||
diff --git a/chrome/browser/resources/md_bookmarks/folder_node.html b/chrome/browser/resources/md_bookmarks/folder_node.html
index dde45d19..e0039ace 100644
--- a/chrome/browser/resources/md_bookmarks/folder_node.html
+++ b/chrome/browser/resources/md_bookmarks/folder_node.html
@@ -80,8 +80,10 @@
       }
     </style>
 
-    <div id="container" class="v-centered" on-tap="selectFolder_"
-        draggable="[[!isTopLevelFolder_(depth)]]"
+    <div id="container"
+        class="v-centered"
+        draggable="true"
+        on-tap="selectFolder_"
         tabindex$="[[getTabIndex_(isSelectedFolder_)]]"
         hidden="[[isRootFolder_(depth)]]">
       <template is="dom-if" if="[[hasChildFolder_(item_.children)]]">
diff --git a/chrome/browser/resources/md_bookmarks/folder_node.js b/chrome/browser/resources/md_bookmarks/folder_node.js
index d9ed141..77aa4375 100644
--- a/chrome/browser/resources/md_bookmarks/folder_node.js
+++ b/chrome/browser/resources/md_bookmarks/folder_node.js
@@ -83,11 +83,6 @@
     return this.$.container;
   },
 
-  /** @return {boolean} */
-  isTopLevelFolder_: function() {
-    return this.depth == 0;
-  },
-
   /**
    * @private
    * @param {!Event} e
diff --git a/chrome/browser/resources/md_bookmarks/reducers.js b/chrome/browser/resources/md_bookmarks/reducers.js
index 7665d35..c4d142b 100644
--- a/chrome/browser/resources/md_bookmarks/reducers.js
+++ b/chrome/browser/resources/md_bookmarks/reducers.js
@@ -408,6 +408,10 @@
         return /** @type {PreferencesState} */ (Object.assign({}, prefs, {
           incognitoAvailability: action.value,
         }));
+      case 'set-can-edit':
+        return /** @type {PreferencesState} */ (Object.assign({}, prefs, {
+          canEdit: action.value,
+        }));
       default:
         return prefs;
     }
diff --git a/chrome/browser/resources/md_bookmarks/toolbar.html b/chrome/browser/resources/md_bookmarks/toolbar.html
index ee3e8d67..6668404 100644
--- a/chrome/browser/resources/md_bookmarks/toolbar.html
+++ b/chrome/browser/resources/md_bookmarks/toolbar.html
@@ -48,20 +48,21 @@
     </style>
     <dialog is="cr-action-menu" id="dropdown">
       <button class="dropdown-item" on-tap="onSortTap_"
-          disabled="[[hasSearchTerm_(searchTerm_)]]">
+          disabled="[[!canChangeList_]]">
         $i18n{menuSort}
       </button>
       <hr>
-      <button class="dropdown-item" on-tap="onAddBookmarkTap_"
-          disabled="[[hasSearchTerm_(searchTerm_)]]">
+      <button id="addBookmarkButton" class="dropdown-item"
+          on-tap="onAddBookmarkTap_" disabled="[[!canChangeList_]]">
         $i18n{menuAddBookmark}
       </button>
       <button class="dropdown-item" on-tap="onAddFolderTap_"
-          disabled="[[hasSearchTerm_(searchTerm_)]]">
+          disabled="[[!canChangeList_]]">
         $i18n{menuAddFolder}
       </button>
       <hr>
-      <button class="dropdown-item" on-tap="onImportTap_">
+      <button id="importBookmarkButton" class="dropdown-item"
+          on-tap="onImportTap_" disabled="[[!globalCanEdit_]]">
         $i18n{menuImport}
       </button>
       <button class="dropdown-item" on-tap="onExportTap_">
@@ -87,7 +88,8 @@
     <template is="dom-if" if="[[showSelectionOverlay]]">
       <cr-toolbar-selection-overlay delete-label="$i18n{delete}"
           cancel-label="$i18n{cancel}"
-          selection-label="[[getItemsSelectedString_(selectedCount_)]]"
+          selection-label="[[getItemsSelectedString_(selectedItems_.size)]]"
+          delete-disabled="[[!canDeleteSelection_(selectedItems_)]]"
           on-delete-selected-items="onDeleteSelectionTap_"
           on-clear-selected-items="onClearSelectionTap_">
       </cr-toolbar-selection-overlay>
diff --git a/chrome/browser/resources/md_bookmarks/toolbar.js b/chrome/browser/resources/md_bookmarks/toolbar.js
index 2ec0469..152eadf 100644
--- a/chrome/browser/resources/md_bookmarks/toolbar.js
+++ b/chrome/browser/resources/md_bookmarks/toolbar.js
@@ -23,7 +23,7 @@
 
     showSelectionOverlay: {
       type: Boolean,
-      computed: 'shouldShowSelectionOverlay_(selectedCount_)',
+      computed: 'shouldShowSelectionOverlay_(selectedItems_, globalCanEdit_)',
       readOnly: true,
       reflectToAttribute: true,
     },
@@ -34,16 +34,35 @@
       reflectToAttribute: true,
     },
 
+    /** @private {!Set<string>} */
+    selectedItems_: Object,
+
     /** @private */
-    selectedCount_: Number,
+    globalCanEdit_: Boolean,
+
+    /** @private */
+    selectedFolder_: String,
+
+    /** @private */
+    canChangeList_: {
+      type: Boolean,
+      computed:
+          'computeCanChangeList_(selectedFolder_, searchTerm_, globalCanEdit_)',
+    }
   },
 
   attached: function() {
     this.watch('searchTerm_', function(state) {
       return state.search.term;
     });
-    this.watch('selectedCount_', function(state) {
-      return state.selection.items.size;
+    this.watch('selectedItems_', function(state) {
+      return state.selection.items;
+    });
+    this.watch('globalCanEdit_', function(state) {
+      return state.prefs.canEdit;
+    });
+    this.watch('selectedFolder_', function(state) {
+      return state.selectedFolder;
     });
     this.updateFromStore();
   },
@@ -143,8 +162,10 @@
    * @return {boolean}
    * @private
    */
-  hasSearchTerm_: function() {
-    return !!this.searchTerm_;
+  computeCanChangeList_: function() {
+    return !this.searchTerm_ &&
+        bookmarks.util.canReorderChildren(
+            this.getState(), this.selectedFolder_);
   },
 
   /**
@@ -152,7 +173,12 @@
    * @private
    */
   shouldShowSelectionOverlay_: function() {
-    return this.selectedCount_ > 1;
+    return this.selectedItems_.size > 1 && this.globalCanEdit_;
+  },
+
+  canDeleteSelection_: function() {
+    return bookmarks.CommandManager.getInstance().canExecute(
+        Command.DELETE, this.selectedItems_);
   },
 
   /**
@@ -160,6 +186,6 @@
    * @private
    */
   getItemsSelectedString_: function() {
-    return loadTimeData.getStringF('itemsSelected', this.selectedCount_);
+    return loadTimeData.getStringF('itemsSelected', this.selectedItems_.size);
   },
 });
diff --git a/chrome/browser/resources/md_bookmarks/types.js b/chrome/browser/resources/md_bookmarks/types.js
index 9b08e8c..cf857680 100644
--- a/chrome/browser/resources/md_bookmarks/types.js
+++ b/chrome/browser/resources/md_bookmarks/types.js
@@ -50,6 +50,7 @@
 
 /**
  * @typedef {{
+ *   canEdit: boolean,
  *   incognitoAvailability: IncognitoAvailability,
  * }}
  */
diff --git a/chrome/browser/resources/md_bookmarks/util.js b/chrome/browser/resources/md_bookmarks/util.js
index 2a9de6a..4b581e4 100644
--- a/chrome/browser/resources/md_bookmarks/util.js
+++ b/chrome/browser/resources/md_bookmarks/util.js
@@ -71,6 +71,7 @@
       selectedFolder: '0',
       closedFolders: new Set(),
       prefs: {
+        canEdit: true,
         incognitoAvailability: IncognitoAvailability.ENABLED,
       },
       search: {
@@ -94,6 +95,33 @@
   }
 
   /**
+   * Returns true if the node with ID |itemId| is modifiable, allowing
+   * the node to be renamed, moved or deleted. Note that if a node is
+   * uneditable, it may still have editable children (for example, the top-level
+   * folders).
+   * @param {BookmarksPageState} state
+   * @param {string} itemId
+   * @return {boolean}
+   */
+  function canEditNode(state, itemId) {
+    return itemId != ROOT_NODE_ID &&
+        state.nodes[itemId].parentId != ROOT_NODE_ID &&
+        !state.nodes[itemId].unmodifiable && state.prefs.canEdit;
+  }
+
+  /**
+   * Returns true if it is possible to modify the children list of the node with
+   * ID |itemId|. This includes rearranging the children or adding new ones.
+   * @param {BookmarksPageState} state
+   * @param {string} itemId
+   * @return {boolean}
+   */
+  function canReorderChildren(state, itemId) {
+    return itemId != ROOT_NODE_ID && !state.nodes[itemId].unmodifiable &&
+        state.prefs.canEdit;
+  }
+
+  /**
    * @param {string} id
    * @param {NodeMap} nodes
    * @return {boolean}
@@ -166,6 +194,8 @@
   }
 
   return {
+    canEditNode: canEditNode,
+    canReorderChildren: canReorderChildren,
     createEmptyState: createEmptyState,
     getDescendants: getDescendants,
     getDisplayedList: getDisplayedList,
diff --git a/chrome/browser/resources/md_user_manager/create_profile.js b/chrome/browser/resources/md_user_manager/create_profile.js
index 3e92efa..a9a1557 100644
--- a/chrome/browser/resources/md_user_manager/create_profile.js
+++ b/chrome/browser/resources/md_user_manager/create_profile.js
@@ -264,7 +264,8 @@
   onImportUserTap_: function(event) {
     if (this.signedInUserIndex_ == NO_USER_SELECTED) {
       // A custodian must be selected.
-      this.handleMessage_(this.i18n('custodianAccountNotSelectedError'));
+      this.handleMessage_(
+          this.i18nAdvanced('custodianAccountNotSelectedError'));
     } else {
       var signedInUser = this.signedInUser_(this.signedInUserIndex_);
       this.hideMessage_();
@@ -286,7 +287,8 @@
       this.createProfile_();
     } else if (this.signedInUserIndex_ == NO_USER_SELECTED) {
       // If the new profile is supervised, a custodian must be selected.
-      this.handleMessage_(this.i18n('custodianAccountNotSelectedError'));
+      this.handleMessage_(
+          this.i18nAdvanced('custodianAccountNotSelectedError'));
     } else {
       var signedInUser = this.signedInUser_(this.signedInUserIndex_);
       this.hideMessage_();
@@ -310,7 +312,7 @@
       this.$.importUserPopup.show(this.signedInUser_(this.signedInUserIndex_),
                                   supervisedUsers);
     } else {
-      this.handleMessage_(this.i18n('noSupervisedUserImportText'));
+      this.handleMessage_(this.i18nAdvanced('noSupervisedUserImportText'));
     }
   },
 
@@ -361,9 +363,10 @@
         }
       };
 
-      this.handleMessage_(allOnCurrentDevice ?
-          this.i18n('managedProfilesExistingLocalSupervisedUser') :
-          this.i18nAdvanced('manageProfilesExistingSupervisedUser', opts));
+      this.handleMessage_(
+          allOnCurrentDevice ?
+              this.i18nAdvanced('managedProfilesExistingLocalSupervisedUser') :
+              this.i18nAdvanced('manageProfilesExistingSupervisedUser', opts));
       return;
     }
     // No existing supervised user's name matches the entered profile name.
diff --git a/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.js b/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.js
index f89d0d5..69a041f6 100644
--- a/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.js
+++ b/chrome/browser/resources/md_user_manager/supervised_user_create_confirm.js
@@ -18,9 +18,7 @@
 Polymer({
   is: 'supervised-user-create-confirm',
 
-  behaviors: [
-    I18nBehavior
-  ],
+  behaviors: [I18nBehavior],
 
   properties: {
     /**
@@ -29,16 +27,16 @@
      */
     profileInfo: {
       type: Object,
-      value: function() { return null; }
+      value: function() {
+        return null;
+      }
     },
 
     /** @private {!signin.ProfileBrowserProxy} */
     browserProxy_: Object
   },
 
-  listeners: {
-    'tap': 'onTap_'
-  },
+  listeners: {'tap': 'onTap_'},
 
   /** @override */
   created: function() {
@@ -104,9 +102,12 @@
    * @private
    */
   confirmationMessage_: function(profileInfo) {
-    return this.i18n('supervisedUserCreatedText',
-                     this.elideProfileName_(profileInfo),
-                     this.elideCustodianUsername_(profileInfo));
+    return this.i18nAdvanced('supervisedUserCreatedText', {
+      substitutions: [
+        this.elideProfileName_(profileInfo),
+        this.elideCustodianUsername_(profileInfo)
+      ],
+    });
   },
 
   /**
diff --git a/chrome/browser/resources/policy.css b/chrome/browser/resources/policy.css
index 399c636..eb4f543 100644
--- a/chrome/browser/resources/policy.css
+++ b/chrome/browser/resources/policy.css
@@ -2,11 +2,11 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-body.uber-frame {
+body {
   -webkit-margin-start: 23px;
 }
 
-body.uber-frame > .page {
+body > .page {
   -webkit-margin-end: 0;
   -webkit-padding-end: 24px;
 }
@@ -17,16 +17,16 @@
   z-index: 4;
 }
 
-body.uber-frame header {
+body header {
   left: 23px;
   max-width: none;
 }
 
-html[dir='rtl'] body.uber-frame header {
+html[dir='rtl'] body header {
   right: 23px;
 }
 
-body.uber-frame section {
+body section {
   max-width: none;
 }
 
diff --git a/chrome/browser/resources/policy.html b/chrome/browser/resources/policy.html
index 61b62a3..1c14ab17 100644
--- a/chrome/browser/resources/policy.html
+++ b/chrome/browser/resources/policy.html
@@ -15,10 +15,9 @@
 <script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script>
 <script src="chrome://resources/js/load_time_data.js"></script>
 <script src="chrome://resources/js/util.js"></script>
-<script src="chrome://policy/uber_utils.js"></script>
 </head>
 
-<body class="uber-frame">
+<body>
   <div id="filter-overlay" class="page">
     <header>
       <input id="filter" class="search-field-container" type="search"
diff --git a/chrome/browser/resources/policy.js b/chrome/browser/resources/policy.js
index aaba4ff..afdfb2f9 100644
--- a/chrome/browser/resources/policy.js
+++ b/chrome/browser/resources/policy.js
@@ -428,7 +428,6 @@
      * Main initialization function. Called by the browser on page load.
      */
     initialize: function() {
-      uber.onContentFrameLoaded();
       cr.ui.FocusOutlineManager.forDocument(document);
 
       this.mainSection = $('main-section');
diff --git a/chrome/browser/resources/policy_android.css b/chrome/browser/resources/policy_android.css
index 6bf8a0d..7bb0e2a 100644
--- a/chrome/browser/resources/policy_android.css
+++ b/chrome/browser/resources/policy_android.css
@@ -2,11 +2,11 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-body.uber-frame {
+body {
   -webkit-margin-start: 0;
 }
 
-body.uber-frame > .page {
+body > .page {
   -webkit-margin-end: 0;
   -webkit-padding-end: 10px;
   -webkit-padding-start: 10px;
@@ -19,22 +19,22 @@
   z-index: 4;
 }
 
-body.uber-frame header {
+body header {
   left: 0;
   max-width: none;
   min-width: 0;
 }
 
-body.uber-frame header > h1 {
+body header > h1 {
   margin-left: 10px;
 }
 
-body.uber-frame section {
+body section {
   -webkit-padding-start: 0;
   max-width: none;
 }
 
-body.uber-frame section > h3 {
+body section > h3 {
   -webkit-margin-start: 0;
 }
 
@@ -132,7 +132,7 @@
   display: none;
 }
 
-body.uber-frame * section.policy-table-section {
+body * section.policy-table-section {
   padding-bottom: 10px;
 }
 
diff --git a/chrome/browser/resources/settings/about_page/about_page.js b/chrome/browser/resources/settings/about_page/about_page.js
index 9d990aa..639345d1 100644
--- a/chrome/browser/resources/settings/about_page/about_page.js
+++ b/chrome/browser/resources/settings/about_page/about_page.js
@@ -277,25 +277,28 @@
     switch (this.currentUpdateStatusEvent_.status) {
       case UpdateStatus.CHECKING:
       case UpdateStatus.NEED_PERMISSION_TO_UPDATE:
-        return this.i18n('aboutUpgradeCheckStarted');
+        return this.i18nAdvanced('aboutUpgradeCheckStarted');
       case UpdateStatus.NEARLY_UPDATED:
 // <if expr="chromeos">
         if (this.currentChannel_ != this.targetChannel_)
-          return this.i18n('aboutUpgradeSuccessChannelSwitch');
+          return this.i18nAdvanced('aboutUpgradeSuccessChannelSwitch');
 // </if>
-        return this.i18n('aboutUpgradeRelaunch');
+        return this.i18nAdvanced('aboutUpgradeRelaunch');
       case UpdateStatus.UPDATED:
-        return this.i18n('aboutUpgradeUpToDate');
+        return this.i18nAdvanced('aboutUpgradeUpToDate');
       case UpdateStatus.UPDATING:
         assert(typeof this.currentUpdateStatusEvent_.progress == 'number');
         var progressPercent = this.currentUpdateStatusEvent_.progress + '%';
 
 // <if expr="chromeos">
         if (this.currentChannel_ != this.targetChannel_) {
-          return this.i18n(
-              'aboutUpgradeUpdatingChannelSwitch',
-              this.i18n(settings.browserChannelToI18nId(this.targetChannel_)),
-              progressPercent);
+          return this.i18nAdvanced('aboutUpgradeUpdatingChannelSwitch', {
+            substitutions: [
+              this.i18nAdvanced(
+                  settings.browserChannelToI18nId(this.targetChannel_)),
+              progressPercent
+            ]
+          });
         }
 // </if>
         if (this.currentUpdateStatusEvent_.progress > 0) {
@@ -304,9 +307,11 @@
           // it's certainly quite possible to validly end up here with 0% on
           // platforms that support incremental progress, nobody really likes
           // seeing that they're 0% done with something.
-          return this.i18n('aboutUpgradeUpdatingPercent', progressPercent);
+          return this.i18nAdvanced('aboutUpgradeUpdatingPercent', {
+            substitutions: [progressPercent],
+          });
         }
-        return this.i18n('aboutUpgradeUpdating');
+        return this.i18nAdvanced('aboutUpgradeUpdating');
       default:
         function formatMessage(msg) {
           return parseHtmlSubset(
diff --git a/chrome/browser/resources/settings/android_apps_page/android_apps_page.html b/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
index 1f3c4fc..e7933dc 100644
--- a/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
+++ b/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
@@ -25,7 +25,7 @@
           <div class="start">
             $i18n{androidAppsPageLabel}
             <div class="secondary" id="secondaryText"
-                inner-h-t-m-l="[[i18n('androidAppsSubtext')]]">
+                inner-h-t-m-l="[[i18nAdvanced('androidAppsSubtext')]]">
             </div>
           </div>
           <cr-policy-pref-indicator pref="[[prefs.arc.enabled]]"
diff --git a/chrome/browser/resources/settings/controls/extension_controlled_indicator.js b/chrome/browser/resources/settings/controls/extension_controlled_indicator.js
index a5737c90..71feaafe 100644
--- a/chrome/browser/resources/settings/controls/extension_controlled_indicator.js
+++ b/chrome/browser/resources/settings/controls/extension_controlled_indicator.js
@@ -21,9 +21,11 @@
    */
   getLabel_: function(extensionId, extensionName) {
     var manageUrl = 'chrome://extensions/?id=' + assert(this.extensionId);
-    return this.i18n('controlledByExtension',
-        '<a href="' + manageUrl + '" target="_blank">' +
-        assert(this.extensionName) + '</a>');
+    return this.i18nAdvanced('controlledByExtension', {
+      substitutions:
+          ['<a href="' + manageUrl + '" target="_blank">' +
+           assert(this.extensionName) + '</a>'],
+    });
   },
 
   /** @private */
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index fdb621c..866d8fd 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -55,8 +55,15 @@
   if (content_settings()) {
     CleanUpExpiredVerdicts();
     UMA_HISTOGRAM_COUNTS_1000(
-        "PasswordProtection.NumberOfCachedVerdictBeforeShutdown",
-        GetStoredVerdictCount());
+        "PasswordProtection.NumberOfCachedVerdictBeforeShutdown."
+        "PasswordOnFocus",
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+    UMA_HISTOGRAM_COUNTS_1000(
+        "PasswordProtection.NumberOfCachedVerdictBeforeShutdown."
+        "ProtectedPasswordEntry",
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
   }
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index a30d126..f496f390 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -75,6 +75,7 @@
   }
 
   void CacheVerdict(const GURL& url,
+                    LoginReputationClientRequest::TriggerType trigger_type,
                     LoginReputationClientResponse* verdict,
                     const base::Time& receive_time) override {}
 
diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc
index a9a1e98..a3cc1fa1 100644
--- a/chrome/browser/shell_integration_linux.cc
+++ b/chrome/browser/shell_integration_linux.cc
@@ -562,8 +562,6 @@
 }  // namespace
 
 base::FilePath GetDataWriteLocation(base::Environment* env) {
-  base::ThreadRestrictions::AssertIOAllowed();
-
   return base::nix::GetXDGDirectory(env, "XDG_DATA_HOME", ".local/share");
 }
 
diff --git a/chrome/browser/shell_integration_linux.h b/chrome/browser/shell_integration_linux.h
index 0f44c01..bdfbd51 100644
--- a/chrome/browser/shell_integration_linux.h
+++ b/chrome/browser/shell_integration_linux.h
@@ -22,7 +22,6 @@
 // Get the path to write user-specific application data files to, as specified
 // in the XDG Base Directory Specification:
 // http://standards.freedesktop.org/basedir-spec/latest/
-// Called on the FILE thread.
 base::FilePath GetDataWriteLocation(base::Environment* env);
 
 // Get the list of paths to search for application data files, in order of
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index c29d82f..7aadf49d 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -43,6 +43,7 @@
 #include "components/translate/core/browser/translate_prefs.h"
 #include "components/translate/core/common/language_detection_details.h"
 #include "components/translate/core/common/language_detection_logging_helper.h"
+#include "components/translate/core/common/translation_logging_helper.h"
 #include "components/variations/service/variations_service.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
@@ -51,26 +52,29 @@
 #include "url/gurl.h"
 
 namespace {
+using metrics::TranslateEventProto;
 
-metrics::TranslateEventProto::EventType BubbleResultToTranslateEvent(
+TranslateEventProto::EventType BubbleResultToTranslateEvent(
     ShowTranslateBubbleResult result) {
   switch (result) {
     case ShowTranslateBubbleResult::BROWSER_WINDOW_NOT_VALID:
-      return metrics::TranslateEventProto::BROWSER_WINDOW_IS_INVALID;
+      return TranslateEventProto::BROWSER_WINDOW_IS_INVALID;
     case ShowTranslateBubbleResult::BROWSER_WINDOW_MINIMIZED:
-      return metrics::TranslateEventProto::BROWSER_WINDOW_IS_MINIMIZED;
+      return TranslateEventProto::BROWSER_WINDOW_IS_MINIMIZED;
     case ShowTranslateBubbleResult::BROWSER_WINDOW_NOT_ACTIVE:
-      return metrics::TranslateEventProto::BROWSER_WINDOW_NOT_ACTIVE;
+      return TranslateEventProto::BROWSER_WINDOW_NOT_ACTIVE;
     case ShowTranslateBubbleResult::WEB_CONTENTS_NOT_ACTIVE:
-      return metrics::TranslateEventProto::WEB_CONTENTS_NOT_ACTIVE;
+      return TranslateEventProto::WEB_CONTENTS_NOT_ACTIVE;
     case ShowTranslateBubbleResult::EDITABLE_FIELD_IS_ACTIVE:
-      return metrics::TranslateEventProto::EDITABLE_FIELD_IS_ACTIVE;
+      return TranslateEventProto::EDITABLE_FIELD_IS_ACTIVE;
     default:
       NOTREACHED();
       return metrics::TranslateEventProto::UNKNOWN;
   }
 }
 
+// ========== LOG LANGUAGE DETECTION EVENT ==============
+
 void LogLanguageDetectionEvent(
     const content::WebContents* const web_contents,
     const translate::LanguageDetectionDetails& details) {
@@ -92,6 +96,36 @@
   }
 }
 
+// ========== LOG TRANSLATE EVENT ==============
+
+void LogTranslateEvent(const content::WebContents* const web_contents,
+                       const metrics::TranslateEventProto& translate_event) {
+  DCHECK(web_contents);
+  auto* const profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+
+  syncer::UserEventService* const user_event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(profile);
+
+  const auto* const entry =
+      web_contents->GetController().GetLastCommittedEntry();
+
+  // If entry is null, we don't record the page.
+  // The navigation entry can be null in situations like download or initial
+  // blank page.
+  if (entry == nullptr)
+    return;
+
+  auto specifics = base::MakeUnique<sync_pb::UserEventSpecifics>();
+  // We only log the event we care about.
+  const bool needs_logging = translate::ConstructTranslateEvent(
+      entry->GetTimestamp().ToInternalValue(), translate_event,
+      specifics.get());
+  if (needs_logging) {
+    user_event_service->RecordUserEvent(std::move(specifics));
+  }
+}
+
 }  // namespace
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromeTranslateClient);
@@ -196,6 +230,11 @@
       translate::TranslateManager::GetTargetLanguage(translate_prefs.get());
 }
 
+void ChromeTranslateClient::RecordTranslateEvent(
+    const TranslateEventProto& translate_event) {
+  LogTranslateEvent(web_contents(), translate_event);
+}
+
 // static
 void ChromeTranslateClient::BindContentTranslateDriver(
     content::RenderFrameHost* render_frame_host,
diff --git a/chrome/browser/translate/chrome_translate_client.h b/chrome/browser/translate/chrome_translate_client.h
index b597b61..da171a3 100644
--- a/chrome/browser/translate/chrome_translate_client.h
+++ b/chrome/browser/translate/chrome_translate_client.h
@@ -89,6 +89,7 @@
   std::unique_ptr<translate::TranslatePrefs> GetTranslatePrefs() override;
   translate::TranslateAcceptLanguages* GetTranslateAcceptLanguages() override;
   int GetInfobarIconID() const override;
+  void RecordTranslateEvent(const metrics::TranslateEventProto&) override;
 #if !defined(USE_AURA)
   std::unique_ptr<infobars::InfoBar> CreateInfoBar(
       std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 81b753c..0266b0d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -925,12 +925,6 @@
       "webui/extensions/install_extension_handler.h",
       "webui/foreign_session_handler.cc",
       "webui/foreign_session_handler.h",
-      "webui/help/help_handler.cc",
-      "webui/help/help_handler.h",
-      "webui/help/help_ui.cc",
-      "webui/help/help_ui.h",
-      "webui/help/help_utils_chromeos.cc",
-      "webui/help/help_utils_chromeos.h",
       "webui/help/version_updater.h",
       "webui/help/version_updater_chromeos.cc",
       "webui/help/version_updater_chromeos.h",
@@ -1084,8 +1078,6 @@
       "webui/system_info_ui.h",
       "webui/theme_handler.cc",
       "webui/theme_handler.h",
-      "webui/uber/uber_ui.cc",
-      "webui/uber/uber_ui.h",
       "window_sizer/window_sizer.cc",
       "window_sizer/window_sizer.h",
       "zoom/chrome_zoom_level_otr_delegate.cc",
@@ -1188,6 +1180,10 @@
       "webui/chromeos/certificate_manager_dialog_ui.h",
       "webui/chromeos/proxy_settings_ui.cc",
       "webui/chromeos/proxy_settings_ui.h",
+      "webui/help/help_handler.cc",
+      "webui/help/help_handler.h",
+      "webui/help/help_utils_chromeos.cc",
+      "webui/help/help_utils_chromeos.h",
       "webui/options/autofill_options_handler.cc",
       "webui/options/autofill_options_handler.h",
       "webui/options/automatic_settings_reset_handler.cc",
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
index c7470dd7..05ca5b6 100644
--- a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
+++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
@@ -71,6 +71,13 @@
       JNIEnv* env = base::android::AttachCurrentThread();
       Java_TranslateCompactInfoBar_setAutoAlwaysTranslate(env,
                                                           GetJavaInfoBar());
+
+      // Auto-always is triggered by the line above.  Need to increment the
+      // auto-always counter.
+      delegate->IncrementTranslationAutoAlwaysCount();
+      // Reset translateAcceptedCount so that auto-always could be triggered
+      // again.
+      delegate->ResetTranslationAcceptedCount();
     }
   } else if (action == InfoBarAndroid::ACTION_TRANSLATE_SHOW_ORIGINAL) {
     action_flags_ |= FLAG_REVERT;
@@ -137,7 +144,8 @@
 
 bool TranslateCompactInfoBar::ShouldAutoAlwaysTranslate() {
   translate::TranslateInfoBarDelegate* delegate = GetDelegate();
-  return (delegate->GetTranslationAcceptedCount() == kAcceptCountThreshold);
+  return (delegate->GetTranslationAcceptedCount() == kAcceptCountThreshold &&
+          delegate->GetTranslationAutoAlwaysCount() < kMaxNumberOfAutoAlways);
 }
 
 jboolean TranslateCompactInfoBar::ShouldAutoNeverTranslate(
@@ -157,7 +165,24 @@
       !delegate->IsTranslatableLanguageByPrefs())
     return false;
 
-  return (delegate->GetTranslationDeniedCount() == kDeniedCountThreshold);
+  int auto_never_count = delegate->GetTranslationAutoNeverCount();
+
+  // At the beginning (auto_never_count == 0), deniedCount starts at 0 and is
+  // off-by-one (because this checking is done before increment). However, after
+  // auto-never is triggered once (auto_never_count > 0), deniedCount starts at
+  // 1.  So there is no off-by-one by then.
+  int off_by_one = auto_never_count == 0 ? 1 : 0;
+
+  bool never_translate = (delegate->GetTranslationDeniedCount() + off_by_one ==
+                              kDeniedCountThreshold &&
+                          auto_never_count < kMaxNumberOfAutoNever);
+  if (never_translate) {
+    // Auto-never will be triggered.  Need to increment the auto-never counter.
+    delegate->IncrementTranslationAutoNeverCount();
+    // Reset translateDeniedCount so that auto-never could be triggered again.
+    delegate->ResetTranslationDeniedCount();
+  }
+  return never_translate;
 }
 
 translate::TranslateInfoBarDelegate* TranslateCompactInfoBar::GetDelegate() {
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.h b/chrome/browser/ui/android/infobars/translate_compact_infobar.h
index a2c65e1..25d78ab 100644
--- a/chrome/browser/ui/android/infobars/translate_compact_infobar.h
+++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.h
@@ -85,6 +85,10 @@
   // If number of consecutive denied is equal to this number, infobar will
   // automatically trigger "Never Translate Language".
   const int kDeniedCountThreshold = 7;
+  // Maximum number of times to trigger "Always Translate" automatically.
+  const int kMaxNumberOfAutoAlways = 2;
+  // Maximum number of times to trigger "Never Translate" automatically.
+  const int kMaxNumberOfAutoNever = 2;
 
   DISALLOW_COPY_AND_ASSIGN(TranslateCompactInfoBar);
 };
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index 7e207451..093f814 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -45,6 +45,7 @@
 #include "chrome/common/features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/login/login_state.h"
 #include "chromeos/network/portal_detector/network_portal_detector.h"
 #include "components/google/core/browser/google_util.h"
@@ -103,6 +104,8 @@
   ui::ime::InputMethodMenuManager::GetInstance()->AddObserver(this);
 
   BrowserList::AddObserver(this);
+
+  DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this);
 }
 
 SystemTrayDelegateChromeOS::~SystemTrayDelegateChromeOS() {
@@ -119,6 +122,9 @@
 
   BrowserList::RemoveObserver(this);
   StopObservingAppWindowRegistry();
+
+  if (DBusThreadManager::IsInitialized())
+    DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
 }
 
 void SystemTrayDelegateChromeOS::ShowUserLogin() {
@@ -376,6 +382,10 @@
     const std::string& engine_id,
     const std::vector<input_method::InputMethodManager::MenuItem>& items) {}
 
+void SystemTrayDelegateChromeOS::OnUpdateOverCellularTargetSet(bool success) {
+  GetSystemTrayNotifier()->NotifyUpdateOverCellularTargetSet(success);
+}
+
 ash::SystemTrayDelegate* CreateSystemTrayDelegate() {
   return new SystemTrayDelegateChromeOS();
 }
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index 2e267bd..fa734088 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -18,6 +18,7 @@
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list_observer.h"
+#include "chromeos/dbus/update_engine_client.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -39,7 +40,8 @@
       public input_method::InputMethodManager::Observer,
       public chrome::BrowserListObserver,
       public extensions::AppWindowRegistry::Observer,
-      public input_method::InputMethodManager::ImeMenuObserver {
+      public input_method::InputMethodManager::ImeMenuObserver,
+      public UpdateEngineClient::Observer {
  public:
   SystemTrayDelegateChromeOS();
   ~SystemTrayDelegateChromeOS() override;
@@ -110,6 +112,9 @@
       const std::vector<input_method::InputMethodManager::MenuItem>& items)
       override;
 
+  // Overridden from UpdateEngineClient::Observer.
+  void OnUpdateOverCellularTargetSet(bool success) override;
+
   std::unique_ptr<content::NotificationRegistrar> registrar_;
   std::unique_ptr<PrefChangeRegistrar> user_pref_registrar_;
   Profile* user_profile_ = nullptr;
diff --git a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
index 53644928..3e37718 100644
--- a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
+++ b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
@@ -25,9 +25,9 @@
 
 namespace {
 
-// Whether the NTP bubble is enabled. By default, this is Windows-only, but can
-// be overridden for testing.
-#if defined(OS_WIN)
+// Whether the NTP bubble is enabled. By default, this is limited to Windows and
+// ChromeOS, but can be overridden for testing.
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
 bool g_ntp_bubble_enabled = true;
 #else
 bool g_ntp_bubble_enabled = false;
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 2d56788..8b8d0b3 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/ui/webui/flags_ui.h"
 #include "chrome/browser/ui/webui/flash_ui.h"
 #include "chrome/browser/ui/webui/gcm_internals_ui.h"
-#include "chrome/browser/ui/webui/help/help_ui.h"
 #include "chrome/browser/ui/webui/identity_internals_ui.h"
 #include "chrome/browser/ui/webui/instant_ui.h"
 #include "chrome/browser/ui/webui/interstitials/interstitial_ui.h"
@@ -125,7 +124,6 @@
 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
 #include "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h"
 #include "chrome/browser/ui/webui/system_info_ui.h"
-#include "chrome/browser/ui/webui/uber/uber_ui.h"
 #endif
 
 #if defined(OS_CHROMEOS)
@@ -414,9 +412,6 @@
       ::switches::MdFeedbackEnabled()) {
     return &NewWebUI<MdFeedbackUI>;
   }
-  // Help is implemented with native UI elements on Android.
-  if (url.host_piece() == chrome::kChromeUIHelpFrameHost)
-    return &NewWebUI<HelpUI>;
   // Identity API is not available on Android.
   if (url.host_piece() == chrome::kChromeUIIdentityInternalsHost)
     return &NewWebUI<IdentityInternalsUI>;
@@ -427,24 +422,14 @@
       url.host_piece() == chrome::kChromeUIMdSettingsHost) {
     return &NewWebUI<settings::MdSettingsUI>;
   }
-  // If the material design extensions page is enabled, it gets its own host.
-  // Otherwise, it's handled by the uber settings page.
-  if (url.host_piece() == chrome::kChromeUIExtensionsHost &&
-      base::FeatureList::IsEnabled(features::kMaterialDesignExtensions)) {
+  if (url.host_piece() == chrome::kChromeUIExtensionsHost)
     return &NewWebUI<extensions::ExtensionsUI>;
-  }
   if (url.host_piece() == chrome::kChromeUIHistoryHost)
     return &NewWebUI<MdHistoryUI>;
   if (url.host_piece() == chrome::kChromeUISyncFileSystemInternalsHost)
     return &NewWebUI<SyncFileSystemInternalsUI>;
   if (url.host_piece() == chrome::kChromeUISystemInfoHost)
     return &NewWebUI<SystemInfoUI>;
-  // Uber frame is not used on Android.
-  if (url.host_piece() == chrome::kChromeUIUberFrameHost)
-    return &NewWebUI<UberFrameUI>;
-  // Uber page is not used on Android.
-  if (url.host_piece() == chrome::kChromeUIUberHost)
-    return &NewWebUI<UberUI>;
 #endif  // !defined(OS_ANDROID)
 #if defined(OS_WIN)
   if (url.host_piece() == chrome::kChromeUIConflictsHost)
diff --git a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
index 5a7b9f2..a0314cf5 100644
--- a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
+++ b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
@@ -17,7 +17,7 @@
   /**
    * Browse to the kiosk app settings page.
    */
-  browsePreload: 'chrome://extensions-frame/',
+  browsePreload: 'chrome://extensions/',
 
   /** @override */
   commandLineSwitches: [{
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
index 3bf00fe..be7b755 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
+++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
@@ -44,7 +44,7 @@
    * @type {string}
    * @const
    */
-  browsePreload: 'chrome://extensions-frame/',
+  browsePreload: 'chrome://extensions/',
 
   /** @override */
   typedefCppFixture: 'ExtensionSettingsUIBrowserTest',
@@ -299,7 +299,7 @@
   __proto__: BasicExtensionSettingsWebUITest.prototype,
 
   /** @override */
-  browsePreload: 'chrome://extensions-frame/?id=' + GOOD_EXTENSION_ID,
+  browsePreload: 'chrome://extensions/?id=' + GOOD_EXTENSION_ID,
 
   /** @override */
   testGenPreamble: function() {
@@ -398,7 +398,7 @@
    * @type {string}
    * @const
    */
-  browsePreload: 'chrome://extensions-frame/configureCommands',
+  browsePreload: 'chrome://extensions/configureCommands',
 };
 
 TEST_F('SettingsCommandsExtensionSettingsWebUITest', 'testChromeSendHandler',
@@ -421,8 +421,7 @@
     assertTrue($('extension-commands-overlay').classList.contains('showing'));
     assertEquals($('extension-commands-overlay').getAttribute('aria-hidden'),
                  'false');
-    assertEquals(window.location.href,
-                 'chrome://extensions-frame/configureCommands');
+    assertEquals(window.location.href, 'chrome://extensions/configureCommands');
 
     // Close command overlay.
     $('extension-commands-dismiss').click();
@@ -436,7 +435,7 @@
   var checkExtensionsUrl = function() {
     // After closing the overlay, the URL shouldn't include commands overlay
     // reference.
-    assertEquals(window.location.href, 'chrome://extensions-frame/');
+    assertEquals(window.location.href, 'chrome://extensions/');
     this.nextStep();
   };
 
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 7ae6d56..b41e3e0 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -310,7 +310,7 @@
 
 content::WebUIDataSource* CreateExtensionsHTMLSource() {
   content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIExtensionsFrameHost);
+      content::WebUIDataSource::Create(chrome::kChromeUIExtensionsHost);
 
   source->SetJsonPath("strings.js");
   source->AddResourcePath("extensions.js", IDR_EXTENSIONS_JS);
diff --git a/chrome/browser/ui/webui/help/help_browsertest.js b/chrome/browser/ui/webui/help/help_browsertest.js
deleted file mode 100644
index 45bd272..0000000
--- a/chrome/browser/ui/webui/help/help_browsertest.js
+++ /dev/null
@@ -1,147 +0,0 @@
-// 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.
-
-/**
- * TestFixture for extension settings WebUI testing.
- * @extends {testing.Test}
- * @constructor
- */
-function HelpPageWebUITest() {}
-
-HelpPageWebUITest.prototype = {
-  __proto__: testing.Test.prototype,
-
-  /** @override */
-  runAccessibilityChecks: true,
-
-  /** @override */
-  accessibilityIssuesAreErrors: true,
-
-  browsePreload: 'chrome://help-frame/',
-};
-
-// Test opening extension settings has correct location.
-TEST_F('HelpPageWebUITest', 'testOpenHelpPage', function() {
-  assertEquals(this.browsePreload, document.location.href);
-});
-
-GEN('#if defined(OS_LINUX) || defined(GOOGLE_CHROME_BUILD)');
-
-TEST_F('HelpPageWebUITest', 'testUpdateStateIcon', function() {
-  var icon = $('update-status-icon');
-  help.HelpPage.setUpdateStatus('checking', '');
-  assertEquals(icon.getAttribute('class'), 'help-page-icon working');
-  help.HelpPage.setUpdateStatus('updating', '');
-  assertEquals(icon.getAttribute('class'), 'help-page-icon working');
-  help.HelpPage.setUpdateStatus('nearly_updated', '');
-  assertEquals(icon.getAttribute('class'), 'help-page-icon up-to-date');
-  help.HelpPage.setUpdateStatus('updated', '');
-  assertEquals(icon.getAttribute('class'), 'help-page-icon up-to-date');
-  help.HelpPage.setUpdateStatus('failed', '');
-  assertEquals(icon.getAttribute('class'), 'help-page-icon failed');
-  help.HelpPage.setUpdateStatus('disabled_by_admin', '');
-  assertEquals(icon.getAttribute('class'), 'help-page-icon disabled-by-admin');
-});
-
-// Test that repeated calls to setUpdateStatus work.
-TEST_F('HelpPageWebUITest', 'testUpdateState', function() {
-  var relaunch = $('relaunch');
-  var container = $('update-status-container');
-  var update = $('request-update');
-
-  help.HelpPage.setUpdateStatus('updated', '');
-  expectTrue(relaunch.hidden);
-  expectTrue(cr.isChromeOS == container.hidden);
-  expectTrue(!cr.isChromeOS || !update.hidden && !update.disabled);
-
-  help.HelpPage.setUpdateStatus('disabled', '');
-  expectTrue(relaunch.hidden);
-  expectTrue(container.hidden);
-  expectTrue(!cr.isChromeOS || update.hidden);
-
-  help.HelpPage.setUpdateStatus('nearly_updated', '');
-  expectTrue(!relaunch.hidden);
-  expectTrue(!container.hidden);
-  expectTrue(!cr.isChromeOS || update.hidden);
-
-  help.HelpPage.setUpdateStatus('disabled', '');
-  expectTrue($('relaunch').hidden);
-  expectTrue($('update-status-container').hidden);
-  expectTrue(!cr.isChromeOS || update.hidden);
-});
-
-GEN('#endif');
-
-GEN('#if defined(OS_CHROMEOS)');
-
-// Test that the request update button is shown and hidden properly.
-TEST_F('HelpPageWebUITest', 'testRequestUpdate', function() {
-  var container = $('update-status-container');
-  var update = $('request-update');
-  var policyIcon = $('controlled-feature-icon');
-
-  help.HelpPage.setUpdateStatus('updated', '');
-  expectTrue(container.hidden);
-  expectTrue(!update.hidden && !update.disabled);
-  expectTrue(policyIcon.hidden);
-
-  update.click();
-  expectTrue(!update.hidden && update.disabled);
-  expectFalse(container.hidden);
-  expectTrue(policyIcon.hidden);
-
-  help.HelpPage.setUpdateStatus('checking', '');
-  expectFalse(container.hidden);
-  expectTrue(!update.hidden && update.disabled);
-  expectTrue(policyIcon.hidden);
-
-  help.HelpPage.setUpdateStatus('failed', 'Error');
-  expectFalse(container.hidden);
-  expectTrue(!update.hidden && !update.disabled);
-  expectTrue(policyIcon.hidden);
-
-  update.click();
-  help.HelpPage.setUpdateStatus('checking', '');
-  expectFalse(container.hidden);
-  expectTrue(!update.hidden && update.disabled);
-  expectTrue(policyIcon.hidden);
-
-  help.HelpPage.setUpdateStatus('nearly_updated', '');
-  expectFalse(container.hidden);
-  expectTrue(update.hidden);
-  expectTrue(policyIcon.hidden);
-
-  help.HelpPage.setUpdateStatus('updated', '');
-  expectFalse(container.hidden);
-  expectTrue(!update.hidden && update.disabled);
-  expectTrue(policyIcon.hidden);
-
-  help.HelpPage.setUpdateStatus('disabled_by_admin', '');
-  expectTrue(container.hidden);
-  expectTrue(!update.hidden && update.disabled);
-  expectFalse(policyIcon.hidden);
-});
-
-// Test that the EndofLife String is shown and hidden properly.
-TEST_F('HelpPageWebUITest', 'testUpdateEolMessage', function() {
-   // Enable when failure is resolved.
-   // AX_TEXT_04: http://crbug.com/570563
-  this.accessibilityAuditConfig.ignoreSelectors(
-      'linkWithUnclearPurpose',
-      '#eol-learnMore > A');
-
-  var updateStatusContainer = $('update-status-container');
-  var update = $('request-update');
-  var eolStatusContainer = $('eol-status-container');
-
-  help.HelpPage.updateEolMessage('device_supported', '');
-  expectTrue(eolStatusContainer.hidden);
-
-  help.HelpPage.updateEolMessage('device_endoflife', '');
-  expectFalse(eolStatusContainer.hidden);
-  expectTrue(update.disabled);
-  expectTrue(updateStatusContainer.hidden);
-});
-
-GEN('#endif');
diff --git a/chrome/browser/ui/webui/help/help_ui.cc b/chrome/browser/ui/webui/help/help_ui.cc
deleted file mode 100644
index 94301b65..0000000
--- a/chrome/browser/ui/webui/help/help_ui.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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 "chrome/browser/ui/webui/help/help_ui.h"
-
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/help/help_handler.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-
-namespace {
-
-content::WebUIDataSource* CreateAboutPageHTMLSource() {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIHelpFrameHost);
-  source->SetJsonPath("strings.js");
-  source->AddResourcePath("help.js", IDR_HELP_JS);
-  source->AddResourcePath("help_page.js", IDR_HELP_PAGE_JS);
-  source->AddResourcePath("channel_change_page.js", IDR_CHANNEL_CHANGE_PAGE_JS);
-  source->SetDefaultResource(IDR_HELP_HTML);
-  source->DisableDenyXFrameOptions();
-  return source;
-}
-
-}  // namespace
-
-HelpUI::HelpUI(content::WebUI* web_ui)
-    : WebUIController(web_ui) {
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::WebUIDataSource* source = CreateAboutPageHTMLSource();
-
-  base::DictionaryValue localized_strings;
-  HelpHandler::GetLocalizedValues(&localized_strings);
-  source->AddLocalizedStrings(localized_strings);
-  content::WebUIDataSource::Add(profile, source);
-  web_ui->AddMessageHandler(base::MakeUnique<HelpHandler>());
-}
-
-HelpUI::~HelpUI() {
-}
diff --git a/chrome/browser/ui/webui/help/help_ui.h b/chrome/browser/ui/webui/help/help_ui.h
deleted file mode 100644
index c2b8aca..0000000
--- a/chrome/browser/ui/webui/help/help_ui.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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 CHROME_BROWSER_UI_WEBUI_HELP_HELP_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_HELP_HELP_UI_H_
-
-#include "base/macros.h"
-#include "content/public/browser/web_ui_controller.h"
-
-class HelpUI : public content::WebUIController {
- public:
-  explicit HelpUI(content::WebUI* web_ui);
-  ~HelpUI() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(HelpUI);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_HELP_HELP_UI_H_
diff --git a/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc b/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
index 4bbc452..386eaa6 100644
--- a/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
+++ b/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
@@ -18,9 +18,9 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
-#include "extensions/features/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
@@ -35,8 +35,15 @@
   LogWebUIUrlTest() {}
   ~LogWebUIUrlTest() override {}
 
-  std::vector<Bucket> GetSamples() {
-    return histogram_tester_.GetAllSamples(webui::kWebUICreatedForUrl);
+  void RunTest(int title_ids, const GURL& url) {
+    auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
+    base::string16 title = l10n_util::GetStringUTF16(title_ids);
+    content::TitleWatcher title_watcher(tab, title);
+    ui_test_utils::NavigateToURL(browser(), url);
+    ASSERT_EQ(title, title_watcher.WaitAndGetTitle());
+    uint32_t origin_hash = base::Hash(url.GetOrigin().spec());
+    EXPECT_THAT(histogram_tester_.GetAllSamples(webui::kWebUICreatedForUrl),
+                ElementsAre(Bucket(origin_hash, 1)));
   }
 
  private:
@@ -45,35 +52,17 @@
   DISALLOW_COPY_AND_ASSIGN(LogWebUIUrlTest);
 };
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 IN_PROC_BROWSER_TEST_F(LogWebUIUrlTest, TestExtensionsPage) {
-  content::WebContents* tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  base::string16 extension_title =
-      l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE);
-
-  {
-    content::TitleWatcher title_watcher(tab, extension_title);
-    ui_test_utils::NavigateToURL(browser(),
-                                 GURL(chrome::kChromeUIExtensionsURL));
-    ASSERT_EQ(extension_title, title_watcher.WaitAndGetTitle());
-  }
-
-  std::string scheme(content::kChromeUIScheme);
-  GURL uber_url(scheme + "://" + chrome::kChromeUIUberHost);
-  uint32_t uber_url_hash = base::Hash(uber_url.spec());
-
-  GURL uber_frame_url(chrome::kChromeUIUberFrameURL);
-  uint32_t uber_frame_url_hash = base::Hash(uber_frame_url.spec());
-
-  GURL extensions_frame_url(chrome::kChromeUIExtensionsFrameURL);
-  uint32_t extensions_frame_url_hash = base::Hash(extensions_frame_url.spec());
-
-  EXPECT_THAT(GetSamples(), ElementsAre(Bucket(extensions_frame_url_hash, 1),
-                                        Bucket(uber_frame_url_hash, 1),
-                                        Bucket(uber_url_hash, 1)));
+  RunTest(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE,
+          GURL(chrome::kChromeUIExtensionsURL));
 }
-#endif
+
+IN_PROC_BROWSER_TEST_F(LogWebUIUrlTest, TestHistoryPage) {
+  RunTest(IDS_HISTORY_TITLE, GURL(chrome::kChromeUIHistoryURL));
+}
+
+IN_PROC_BROWSER_TEST_F(LogWebUIUrlTest, TestSettingsPage) {
+  RunTest(IDS_SETTINGS_SETTINGS, GURL(chrome::kChromeUISettingsURL));
+}
 
 }  // namespace webui
diff --git a/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.cc b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.cc
index 47dea5bc..249049a 100644
--- a/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.cc
+++ b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.cc
@@ -8,6 +8,7 @@
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
+#include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 
@@ -20,6 +21,10 @@
       "getIncognitoAvailability",
       base::Bind(&BookmarksMessageHandler::HandleGetIncognitoAvailability,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getCanEditBookmarks",
+      base::Bind(&BookmarksMessageHandler::HandleGetCanEditBookmarks,
+                 base::Unretained(this)));
 }
 
 void BookmarksMessageHandler::OnJavascriptAllowed() {
@@ -29,17 +34,16 @@
       prefs::kIncognitoModeAvailability,
       base::Bind(&BookmarksMessageHandler::UpdateIncognitoAvailability,
                  base::Unretained(this)));
+  pref_change_registrar_.Add(
+      bookmarks::prefs::kEditBookmarksEnabled,
+      base::Bind(&BookmarksMessageHandler::UpdateCanEditBookmarks,
+                 base::Unretained(this)));
 }
 
 void BookmarksMessageHandler::OnJavascriptDisallowed() {
   pref_change_registrar_.RemoveAll();
 }
 
-void BookmarksMessageHandler::UpdateIncognitoAvailability() {
-  FireWebUIListener("incognito-availability-changed",
-                    base::Value(GetIncognitoAvailability()));
-}
-
 int BookmarksMessageHandler::GetIncognitoAvailability() {
   PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
   return prefs->GetInteger(prefs::kIncognitoModeAvailability);
@@ -56,3 +60,29 @@
   ResolveJavascriptCallback(*callback_id,
                             base::Value(GetIncognitoAvailability()));
 }
+
+void BookmarksMessageHandler::UpdateIncognitoAvailability() {
+  FireWebUIListener("incognito-availability-changed",
+                    base::Value(GetIncognitoAvailability()));
+}
+
+bool BookmarksMessageHandler::CanEditBookmarks() {
+  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+  return prefs->GetBoolean(bookmarks::prefs::kEditBookmarksEnabled);
+}
+
+void BookmarksMessageHandler::HandleGetCanEditBookmarks(
+    const base::ListValue* args) {
+  CHECK_EQ(1U, args->GetSize());
+  const base::Value* callback_id;
+  CHECK(args->Get(0, &callback_id));
+
+  AllowJavascript();
+
+  ResolveJavascriptCallback(*callback_id, base::Value(CanEditBookmarks()));
+}
+
+void BookmarksMessageHandler::UpdateCanEditBookmarks() {
+  FireWebUIListener("can-edit-bookmarks-changed",
+                    base::Value(CanEditBookmarks()));
+}
diff --git a/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h
index e76640c..c7e39217 100644
--- a/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h
+++ b/chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h
@@ -22,6 +22,10 @@
   void HandleGetIncognitoAvailability(const base::ListValue* args);
   void UpdateIncognitoAvailability();
 
+  bool CanEditBookmarks();
+  void HandleGetCanEditBookmarks(const base::ListValue* args);
+  void UpdateCanEditBookmarks();
+
   // content::WebUIMessageHandler:
   void RegisterMessages() override;
   void OnJavascriptAllowed() override;
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc
index fa9ce17..7dea1b0 100644
--- a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc
+++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
+#include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/prefs/pref_service.h"
 
 MdBookmarksBrowserTest::MdBookmarksBrowserTest() {}
@@ -19,6 +20,10 @@
       "testSetIncognito",
       base::Bind(&MdBookmarksBrowserTest::HandleSetIncognitoAvailability,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "testSetCanEdit",
+      base::Bind(&MdBookmarksBrowserTest::HandleSetCanEditBookmarks,
+                 base::Unretained(this)));
 }
 
 void MdBookmarksBrowserTest::SetIncognitoAvailability(int availability) {
@@ -28,6 +33,11 @@
       prefs::kIncognitoModeAvailability, availability);
 }
 
+void MdBookmarksBrowserTest::SetCanEditBookmarks(bool canEdit) {
+  browser()->profile()->GetPrefs()->SetBoolean(
+      bookmarks::prefs::kEditBookmarksEnabled, canEdit);
+}
+
 void MdBookmarksBrowserTest::HandleSetIncognitoAvailability(
     const base::ListValue* args) {
   AllowJavascript();
@@ -43,6 +53,21 @@
   ResolveJavascriptCallback(*callback_id, base::Value());
 }
 
+void MdBookmarksBrowserTest::HandleSetCanEditBookmarks(
+    const base::ListValue* args) {
+  AllowJavascript();
+
+  ASSERT_EQ(2U, args->GetSize());
+  const base::Value* callback_id;
+  ASSERT_TRUE(args->Get(0, &callback_id));
+  bool pref_value;
+  ASSERT_TRUE(args->GetBoolean(1, &pref_value));
+
+  SetCanEditBookmarks(pref_value);
+
+  ResolveJavascriptCallback(*callback_id, base::Value());
+}
+
 content::WebUIMessageHandler* MdBookmarksBrowserTest::GetMockMessageHandler() {
   return this;
 }
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h
index 4fd3d57..2026989a 100644
--- a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h
+++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_browsertest.h
@@ -15,9 +15,11 @@
   ~MdBookmarksBrowserTest() override;
 
   void SetIncognitoAvailability(int availability);
+  void SetCanEditBookmarks(bool canEdit);
 
  private:
   void HandleSetIncognitoAvailability(const base::ListValue* args);
+  void HandleSetCanEditBookmarks(const base::ListValue* args);
 
   // content::WebUIMessageHandler:
   void RegisterMessages() override;
diff --git a/chrome/browser/ui/webui/policy_ui.cc b/chrome/browser/ui/webui/policy_ui.cc
index 802be6a4..22f60e2 100644
--- a/chrome/browser/ui/webui/policy_ui.cc
+++ b/chrome/browser/ui/webui/policy_ui.cc
@@ -46,7 +46,6 @@
   // Add required resources.
   source->AddResourcePath("policy.css", IDR_POLICY_CSS);
   source->AddResourcePath("policy.js", IDR_POLICY_JS);
-  source->AddResourcePath("uber_utils.js", IDR_UBER_UTILS_JS);
   source->SetDefaultResource(IDR_POLICY_HTML);
   return source;
 }
diff --git a/chrome/browser/ui/webui/uber/uber_ui.cc b/chrome/browser/ui/webui/uber/uber_ui.cc
deleted file mode 100644
index 966231d3..0000000
--- a/chrome/browser/ui/webui/uber/uber_ui.cc
+++ /dev/null
@@ -1,191 +0,0 @@
-// 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 "chrome/browser/ui/webui/uber/uber_ui.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
-#include "build/build_config.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
-#include "chrome/browser/ui/webui/extensions/extensions_ui.h"
-#include "chrome/browser/ui/webui/log_web_ui_url.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/strings/grit/components_strings.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/common/browser_side_navigation_policy.h"
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/ui/webui/options/options_ui.h"
-#endif
-
-using content::NavigationController;
-using content::NavigationEntry;
-using content::RenderFrameHost;
-using content::WebContents;
-
-namespace {
-
-content::WebUIDataSource* CreateUberHTMLSource() {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIUberHost);
-
-  source->SetJsonPath("strings.js");
-  source->AddResourcePath("uber.js", IDR_UBER_JS);
-  source->AddResourcePath("uber_utils.js", IDR_UBER_UTILS_JS);
-  source->SetDefaultResource(IDR_UBER_HTML);
-  source->OverrideContentSecurityPolicyChildSrc("child-src chrome:;");
-
-  // Hack alert: continue showing "Loading..." until a real title is set.
-  source->AddLocalizedString("pageTitle", IDS_TAB_LOADING_TITLE);
-
-  source->AddString("extensionsFrameURL", chrome::kChromeUIExtensionsFrameURL);
-  source->AddString("extensionsHost", chrome::kChromeUIExtensionsHost);
-  source->AddString("helpFrameURL", chrome::kChromeUIHelpFrameURL);
-  source->AddString("helpHost", chrome::kChromeUIHelpHost);
-  source->AddString("settingsFrameURL", chrome::kChromeUISettingsFrameURL);
-  source->AddString("settingsHost", chrome::kChromeUISettingsHost);
-
-  return source;
-}
-
-content::WebUIDataSource* CreateUberFrameHTMLSource(
-    content::BrowserContext* browser_context) {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIUberFrameHost);
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-
-  source->SetJsonPath("strings.js");
-  source->AddResourcePath("uber_frame.js", IDR_UBER_FRAME_JS);
-  source->SetDefaultResource(IDR_UBER_FRAME_HTML);
-
-  // TODO(jhawkins): Attempt to get rid of IDS_SHORT_PRODUCT_OS_NAME.
-#if defined(OS_CHROMEOS)
-  source->AddLocalizedString("shortProductName", IDS_SHORT_PRODUCT_OS_NAME);
-#else
-  source->AddLocalizedString("shortProductName", IDS_SHORT_PRODUCT_NAME);
-#endif  // defined(OS_CHROMEOS)
-
-  source->AddBoolean("hideExtensions",
-      base::FeatureList::IsEnabled(features::kMaterialDesignExtensions));
-  // TODO(dbeam): remove hideSettingsAndHelp soon.
-  source->AddBoolean("hideSettingsAndHelp", true);
-  source->AddString("extensionsHost", chrome::kChromeUIExtensionsHost);
-  source->AddLocalizedString("extensionsDisplayName",
-                             IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE);
-  source->AddString("helpHost", chrome::kChromeUIHelpHost);
-  source->AddLocalizedString("helpDisplayName", IDS_ABOUT_TITLE);
-  source->AddString("settingsHost", chrome::kChromeUISettingsHost);
-  source->AddLocalizedString("settingsDisplayName", IDS_SETTINGS_TITLE);
-
-  source->DisableDenyXFrameOptions();
-  source->OverrideContentSecurityPolicyChildSrc("child-src chrome:;");
-
-  source->AddBoolean("profileIsGuest", profile->IsGuestSession());
-
-  return source;
-}
-
-}  // namespace
-
-SubframeLogger::SubframeLogger(content::WebContents* contents)
-    : WebContentsObserver(contents) {}
-
-SubframeLogger::~SubframeLogger() {}
-
-void SubframeLogger::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->HasCommitted())
-    return;
-
-  const GURL& url = navigation_handle->GetURL();
-  if (url == chrome::kChromeUIExtensionsFrameURL ||
-      url == chrome::kChromeUIHelpFrameURL ||
-      url == chrome::kChromeUISettingsFrameURL ||
-      url == chrome::kChromeUIUberFrameURL) {
-    webui::LogWebUIUrl(url);
-  }
-}
-
-UberUI::UberUI(content::WebUI* web_ui) : WebUIController(web_ui) {
-  subframe_logger_ = base::MakeUnique<SubframeLogger>(web_ui->GetWebContents());
-  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                CreateUberHTMLSource());
-
-  RegisterSubpage(chrome::kChromeUIExtensionsFrameURL,
-                  chrome::kChromeUIExtensionsHost);
-  RegisterSubpage(chrome::kChromeUIHelpFrameURL,
-                  chrome::kChromeUIHelpHost);
-#if defined(OS_CHROMEOS)
-  RegisterSubpage(chrome::kChromeUISettingsFrameURL,
-                  chrome::kChromeUISettingsHost);
-#endif
-  RegisterSubpage(chrome::kChromeUIUberFrameURL,
-                  chrome::kChromeUIUberHost);
-}
-
-UberUI::~UberUI() {
-}
-
-void UberUI::RegisterSubpage(const std::string& page_url,
-                             const std::string& page_host) {
-  sub_uis_[page_url] = web_ui()->GetWebContents()->CreateSubframeWebUI(
-      GURL(page_url), page_host);
-}
-
-content::WebUI* UberUI::GetSubpage(const std::string& page_url) {
-  if (!base::ContainsKey(sub_uis_, page_url))
-    return nullptr;
-  return sub_uis_[page_url].get();
-}
-
-void UberUI::RenderFrameCreated(RenderFrameHost* render_frame_host) {
-  for (auto iter = sub_uis_.begin(); iter != sub_uis_.end(); ++iter) {
-    iter->second->GetController()->RenderFrameCreated(render_frame_host);
-  }
-}
-
-bool UberUI::OverrideHandleWebUIMessage(const GURL& source_url,
-                                        const std::string& message,
-                                        const base::ListValue& args) {
-  // Find the appropriate subpage and forward the message.
-  auto subpage = sub_uis_.find(source_url.GetOrigin().spec());
-  if (subpage == sub_uis_.end()) {
-    // The message was sent from the uber page itself.
-    DCHECK_EQ(std::string(chrome::kChromeUIUberHost), source_url.host());
-    return false;
-  }
-
-  // The message was sent from a subpage.
-  // TODO(jam) fix this to use interface
-  // return subpage->second->GetController()->OverrideHandleWebUIMessage(
-  //     source_url, message, args);
-  subpage->second->ProcessWebUIMessage(source_url, message, args);
-  return true;
-}
-
-// UberFrameUI
-
-UberFrameUI::UberFrameUI(content::WebUI* web_ui) : WebUIController(web_ui) {
-  content::BrowserContext* browser_context =
-      web_ui->GetWebContents()->GetBrowserContext();
-  content::WebUIDataSource::Add(browser_context,
-                                CreateUberFrameHTMLSource(browser_context));
-}
-
-UberFrameUI::~UberFrameUI() {
-}
diff --git a/chrome/browser/ui/webui/uber/uber_ui.h b/chrome/browser/ui/webui/uber/uber_ui.h
deleted file mode 100644
index 8be7708..0000000
--- a/chrome/browser/ui/webui/uber/uber_ui.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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 CHROME_BROWSER_UI_WEBUI_UBER_UBER_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_UBER_UBER_UI_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/values.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_ui_controller.h"
-
-// Logs visits to subframe URLs (e.g. chrome://settings-frame).
-class SubframeLogger : public content::WebContentsObserver {
- public:
-  explicit SubframeLogger(content::WebContents* contents);
-  ~SubframeLogger() override;
-
-  // content::WebContentsObserver implementation.
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SubframeLogger);
-};
-
-// The WebUI class for the uber page (chrome://chrome). It manages the UI for
-// the uber page (navigation bar and so forth) as well as WebUI objects for
-// pages that appear in the uber page.
-class UberUI : public content::WebUIController {
- public:
-  explicit UberUI(content::WebUI* web_ui);
-  ~UberUI() override;
-
-  content::WebUI* GetSubpage(const std::string& page_url);
-
-  // WebUIController implementation.
-  bool OverrideHandleWebUIMessage(const GURL& source_url,
-                                  const std::string& message,
-                                  const base::ListValue& args) override;
-
-  // We forward these to |sub_uis_|.
-  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
-
- private:
-  // Creates and stores a WebUI for the given URL.
-  void RegisterSubpage(const std::string& page_url,
-                       const std::string& page_host);
-
-  std::unique_ptr<SubframeLogger> subframe_logger_;
-
-  // Map from URL origin to WebUI instance.
-  std::map<std::string, std::unique_ptr<content::WebUI>> sub_uis_;
-
-  DISALLOW_COPY_AND_ASSIGN(UberUI);
-};
-
-class UberFrameUI : public content::WebUIController {
- public:
-  explicit UberFrameUI(content::WebUI* web_ui);
-  ~UberFrameUI() override;
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_UBER_UBER_UI_H_
diff --git a/chrome/renderer/extensions/app_bindings_core.cc b/chrome/renderer/extensions/app_bindings_core.cc
index 45f99e1..be5d7d2 100644
--- a/chrome/renderer/extensions/app_bindings_core.cc
+++ b/chrome/renderer/extensions/app_bindings_core.cc
@@ -54,10 +54,8 @@
   std::unique_ptr<base::DictionaryValue> manifest_copy =
       extension->manifest()->value()->CreateDeepCopy();
   manifest_copy->SetString("id", extension->id());
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  return converter->ToV8Value(manifest_copy.get(),
-                              script_context->v8_context());
+  return content::V8ValueConverter::Create()->ToV8Value(
+      manifest_copy.get(), script_context->v8_context());
 }
 
 void AppBindingsCore::GetInstallState(ScriptContext* script_context,
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.cc b/chrome/renderer/extensions/cast_streaming_native_handler.cc
index 11bc9b7..353b557 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.cc
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc
@@ -496,7 +496,7 @@
   if (!transport)
     return;
 
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+  std::unique_ptr<V8ValueConverter> converter = V8ValueConverter::Create();
   std::vector<FrameSenderConfig> configs = transport->GetSupportedConfigs();
   v8::Local<v8::Array> result =
       v8::Array::New(args.GetIsolate(), static_cast<int>(configs.size()));
@@ -522,9 +522,8 @@
   if (!transport)
     return;
 
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-  std::unique_ptr<base::Value> params_value(
-      converter->FromV8Value(args[1], context()->v8_context()));
+  std::unique_ptr<base::Value> params_value =
+      V8ValueConverter::Create()->FromV8Value(args[1], context()->v8_context());
   if (!params_value) {
     args.GetIsolate()->ThrowException(v8::Exception::TypeError(
         v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertParams)));
@@ -618,9 +617,9 @@
   if (!transport)
     return;
 
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-  std::unique_ptr<base::DictionaryValue> options = base::DictionaryValue::From(
-      converter->FromV8Value(args[1], context()->v8_context()));
+  std::unique_ptr<base::DictionaryValue> options =
+      base::DictionaryValue::From(V8ValueConverter::Create()->FromV8Value(
+          args[1], context()->v8_context()));
   if (!options) {
     args.GetIsolate()->ThrowException(v8::Exception::TypeError(
         v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs)));
@@ -705,9 +704,8 @@
       get_raw_events_callbacks_.find(transport_id);
   if (it == get_raw_events_callbacks_.end())
     return;
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-  v8::Local<v8::Value> callback_args[] = {
-      converter->ToV8Value(raw_events.get(), context()->v8_context())};
+  v8::Local<v8::Value> callback_args[] = {V8ValueConverter::Create()->ToV8Value(
+      raw_events.get(), context()->v8_context())};
   context()->SafeCallFunction(v8::Local<v8::Function>::New(isolate, it->second),
                               arraysize(callback_args), callback_args);
   get_raw_events_callbacks_.erase(it);
@@ -724,9 +722,8 @@
   if (it == get_stats_callbacks_.end())
     return;
 
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-  v8::Local<v8::Value> callback_args[] = {
-      converter->ToV8Value(stats.get(), context()->v8_context())};
+  v8::Local<v8::Value> callback_args[] = {V8ValueConverter::Create()->ToV8Value(
+      stats.get(), context()->v8_context())};
   context()->SafeCallFunction(v8::Local<v8::Function>::New(isolate, it->second),
                               arraysize(callback_args), callback_args);
   get_stats_callbacks_.erase(it);
@@ -760,9 +757,8 @@
     v8::Isolate* isolate,
     const v8::Local<v8::Value>& arg,
     media::cast::FrameReceiverConfig* config) const {
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-  std::unique_ptr<base::Value> params_value(
-      converter->FromV8Value(arg, context()->v8_context()));
+  std::unique_ptr<base::Value> params_value =
+      V8ValueConverter::Create()->FromV8Value(arg, context()->v8_context());
   if (!params_value) {
     isolate->ThrowException(v8::Exception::TypeError(
         v8::String::NewFromUtf8(isolate, kUnableToConvertParams)));
@@ -833,9 +829,8 @@
     v8::Isolate* isolate,
     const v8::Local<v8::Value>& arg,
     net::IPEndPoint* ip_endpoint) const {
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-  std::unique_ptr<base::Value> destination_value(
-      converter->FromV8Value(arg, context()->v8_context()));
+  std::unique_ptr<base::Value> destination_value =
+      V8ValueConverter::Create()->FromV8Value(arg, context()->v8_context());
   if (!destination_value) {
     isolate->ThrowException(v8::Exception::TypeError(
         v8::String::NewFromUtf8(isolate, kInvalidAesIvMask)));
@@ -929,9 +924,9 @@
 
   std::unique_ptr<base::DictionaryValue> options;
   if (args.Length() >= 9) {
-    std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-    std::unique_ptr<base::Value> options_value(
-        converter->FromV8Value(args[8], context()->v8_context()));
+    std::unique_ptr<base::Value> options_value =
+        V8ValueConverter::Create()->FromV8Value(args[8],
+                                                context()->v8_context());
     if (!options_value->IsType(base::Value::Type::NONE)) {
       options = base::DictionaryValue::From(std::move(options_value));
       if (!options) {
diff --git a/chrome/renderer/extensions/platform_keys_natives.cc b/chrome/renderer/extensions/platform_keys_natives.cc
index c0da667..97ff814c 100644
--- a/chrome/renderer/extensions/platform_keys_natives.cc
+++ b/chrome/renderer/extensions/platform_keys_natives.cc
@@ -125,10 +125,8 @@
   if (!algorithm_dict)
     return;
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  call_info.GetReturnValue().Set(
-      converter->ToV8Value(algorithm_dict.get(), context()->v8_context()));
+  call_info.GetReturnValue().Set(content::V8ValueConverter::Create()->ToV8Value(
+      algorithm_dict.get(), context()->v8_context()));
 }
 
 }  // namespace extensions
diff --git a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
index f025f3e..506acffb 100644
--- a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
+++ b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
@@ -11,7 +11,6 @@
 var ChromeWebViewSchema =
     requireNative('schema_registry').GetSchema('chromeWebViewInternal');
 var CreateEvent = require('guestViewEvents').CreateEvent;
-var EventBindings = require('event_bindings');
 var GuestViewInternalNatives = requireNative('guest_view_internal');
 var idGeneratorNatives = requireNative('id_generator');
 var utils = require('utils');
@@ -30,76 +29,75 @@
 var ContextMenusHandlerEvent =
     CreateEvent('chromeWebViewInternal.onContextMenuShow');
 
-// -----------------------------------------------------------------------------
-// ContextMenusOnClickedEvent object.
+var jsEvent;
+function createCustomEvent(name, schema, options, webviewId) {
+  if (bindingUtil)
+    return bindingUtil.createCustomEvent(name, undefined, false);
+  if (!jsEvent)
+    jsEvent = require('event_bindings').Event;
+  return new jsEvent(name, schema, options, webviewId);
+}
 
 // This event is exposed as <webview>.contextMenus.onClicked.
-function ContextMenusOnClickedEvent(webViewInstanceId,
-                                    opt_eventName,
-                                    opt_argSchemas,
-                                    opt_eventOptions) {
+function createContextMenusOnClickedEvent(webViewInstanceId,
+                                          opt_eventName,
+                                          opt_argSchemas,
+                                          opt_eventOptions) {
   var subEventName = GetUniqueSubEventName(opt_eventName);
-  $Function.call(EventBindings.Event,
-                 this,
-                 subEventName,
-                 opt_argSchemas,
-                 opt_eventOptions,
-                 webViewInstanceId);
+  var newEvent = createCustomEvent(subEventName, opt_argSchemas,
+                                   opt_eventOptions, webViewInstanceId);
 
   var view = GuestViewInternalNatives.GetViewFromID(webViewInstanceId);
-  if (!view) {
-    return;
+  if (view) {
+    view.events.addScopedListener(
+        ContextMenusEvent,
+        $Function.bind(function() {
+          // Re-dispatch to subEvent's listeners.
+          $Function.apply(newEvent.dispatch, newEvent, $Array.slice(arguments));
+        }, newEvent),
+        {instanceId: webViewInstanceId});
   }
-  view.events.addScopedListener(ContextMenusEvent, $Function.bind(function() {
-    // Re-dispatch to subEvent's listeners.
-    $Function.apply(this.dispatch, this, $Array.slice(arguments));
-  }, this), {instanceId: webViewInstanceId});
+  return newEvent;
 }
-$Object.setPrototypeOf(ContextMenusOnClickedEvent.prototype,
-                       EventBindings.Event.prototype);
 
 // This event is exposed as <webview>.contextMenus.onShow.
-function ContextMenusOnContextMenuEvent(webViewInstanceId,
-                                        opt_eventName,
-                                        opt_argSchemas,
-                                        opt_eventOptions) {
+function createContextMenusOnContextMenuEvent(webViewInstanceId,
+                                              opt_eventName,
+                                              opt_argSchemas,
+                                              opt_eventOptions) {
   var subEventName = GetUniqueSubEventName(opt_eventName);
-  $Function.call(EventBindings.Event,
-                 this,
-                 subEventName,
-                 opt_argSchemas,
-                 opt_eventOptions,
-                 webViewInstanceId);
+  var newEvent = createCustomEvent(subEventName, opt_argSchemas,
+                                   opt_eventOptions, webViewInstanceId);
 
   var view = GuestViewInternalNatives.GetViewFromID(webViewInstanceId);
-  if (!view) {
-    return;
+  if (view) {
+    view.events.addScopedListener(
+        ContextMenusHandlerEvent,
+        $Function.bind(function(e) {
+          var defaultPrevented = false;
+          var event = {
+            preventDefault: function() { defaultPrevented = true; }
+          };
+
+          // Re-dispatch to subEvent's listeners.
+          $Function.apply(newEvent.dispatch, newEvent, [event]);
+
+          if (!defaultPrevented) {
+          // TODO(lazyboy): Remove |items| parameter completely from
+          // ChromeWebView.showContextMenu as we don't do anything useful with
+          // it currently.
+          var items = [];
+          var guestInstanceId = GuestViewInternalNatives.
+              GetViewFromID(webViewInstanceId).guest.getId();
+          ChromeWebView.showContextMenu(guestInstanceId, e.requestId, items);
+        }
+      }, newEvent),
+      {instanceId: webViewInstanceId});
   }
-  view.events.addScopedListener(
-      ContextMenusHandlerEvent, $Function.bind(function(e) {
-    var defaultPrevented = false;
-    var event = {
-      'preventDefault': function() { defaultPrevented = true; }
-    };
 
-    // Re-dispatch to subEvent's listeners.
-    $Function.apply(this.dispatch, this, [event]);
-
-    if (!defaultPrevented) {
-      // TODO(lazyboy): Remove |items| parameter completely from
-      // ChromeWebView.showContextMenu as we don't do anything useful with it
-      // currently.
-      var items = [];
-      var guestInstanceId = GuestViewInternalNatives.
-          GetViewFromID(webViewInstanceId).guest.getId();
-      ChromeWebView.showContextMenu(guestInstanceId, e.requestId, items);
-    }
-  }, this), {instanceId: webViewInstanceId});
+  return newEvent;
 }
 
-$Object.setPrototypeOf(ContextMenusOnContextMenuEvent.prototype,
-                       EventBindings.Event.prototype);
-
 // -----------------------------------------------------------------------------
 // WebViewContextMenusImpl object.
 
@@ -149,7 +147,7 @@
     var eventSchema =
         utils.lookup(ChromeWebViewSchema.events, 'name', 'onShow');
     var eventOptions = {supportsListeners: true};
-    this.contextMenusOnContextMenuEvent_ = new ContextMenusOnContextMenuEvent(
+    this.contextMenusOnContextMenuEvent_ = createContextMenusOnContextMenuEvent(
         this.viewInstanceId, eventName, eventSchema, eventOptions);
   }
 
@@ -169,7 +167,7 @@
             var eventSchema =
                 utils.lookup(ChromeWebViewSchema.events, 'name', 'onClicked');
             var eventOptions = {supportsListeners: true};
-            var onClickedEvent = new ContextMenusOnClickedEvent(
+            var onClickedEvent = createContextMenusOnClickedEvent(
                 this.viewInstanceId, eventName, eventSchema, eventOptions);
             this.contextMenusOnClickedEvent_ = onClickedEvent;
             return onClickedEvent;
diff --git a/chrome/renderer/sandbox_status_extension_android.cc b/chrome/renderer/sandbox_status_extension_android.cc
index fe3f834..3b14bf38 100644
--- a/chrome/renderer/sandbox_status_extension_android.cc
+++ b/chrome/renderer/sandbox_status_extension_android.cc
@@ -148,10 +148,8 @@
   v8::Local<v8::Function> callback_local =
       v8::Local<v8::Function>::New(isolate, *callback);
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-
-  v8::Local<v8::Value> argv[] = {converter->ToV8Value(status.get(), context)};
+  v8::Local<v8::Value> argv[] = {
+      content::V8ValueConverter::Create()->ToV8Value(status.get(), context)};
   render_frame()->GetWebFrame()->CallFunctionEvenIfScriptDisabled(
       callback_local, v8::Object::New(isolate), 1, argv);
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
index b2e0ab0..3f46101 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
@@ -328,16 +328,37 @@
      * @param locationBar The LocationBar who owns the suggestions.
      */
     public static void waitForOmniboxSuggestions(final LocationBarLayout locationBar) {
+        waitForOmniboxSuggestions(locationBar, CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL);
+    }
+
+    /**
+     * Waits for a non-empty list of omnibox suggestions is shown.
+     *
+     * @param locationBar The LocationBar who owns the suggestions.
+     * @param maxPollTimeMs The maximum time to wait for the suggestions to be visible.
+     */
+    public static void waitForOmniboxSuggestions(
+            final LocationBarLayout locationBar, long maxPollTimeMs) {
         CriteriaHelper.pollUiThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
                 LocationBarLayout.OmniboxSuggestionsList suggestionsList =
                         locationBar.getSuggestionList();
-                return suggestionsList != null
-                        && suggestionsList.isShown()
-                        && suggestionsList.getCount() >= 1;
+                if (suggestionsList == null) {
+                    updateFailureReason("suggestionList is null");
+                    return false;
+                }
+                if (!suggestionsList.isShown()) {
+                    updateFailureReason("suggestionList is not shown");
+                    return false;
+                }
+                if (suggestionsList.getCount() == 0) {
+                    updateFailureReason("suggestionList has no entries");
+                    return false;
+                }
+                return true;
             }
-        });
+        }, maxPollTimeMs, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
     }
 
     /**
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index fa3f1d2..cea4894 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -40,7 +40,6 @@
     "../../../browser/ui/webui/chromeos/set_time_ui_browsertest.js",
     "../../../browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js",
     "../../../browser/ui/webui/extensions/extension_settings_browsertest.js",
-    "../../../browser/ui/webui/help/help_browsertest.js",
     "../../../browser/ui/webui/identity_internals_ui_browsertest.js",
     "../../../browser/ui/webui/sync_internals_browsertest.js",
     "../../../browser/ui/webui/sync_setup_browsertest.js",
diff --git a/chrome/test/data/webui/i18n_behavior_test.html b/chrome/test/data/webui/i18n_behavior_test.html
index e79ef72..fca9944 100644
--- a/chrome/test/data/webui/i18n_behavior_test.html
+++ b/chrome/test/data/webui/i18n_behavior_test.html
@@ -3,8 +3,6 @@
 <body>
 <script>
 
-/* Execute these tests with the browser_tests executable. */
-
 var allowedByDefault = '<a href="https://google.com">Google!</a>';
 var text = 'I\'m just text, nobody should have a problem with me!';
 var nonBreakingSpace = 'A\u00a0B\u00a0C';  // \u00a0 is a unicode nbsp.
@@ -22,7 +20,6 @@
 }
 
 function testI18n() {
-  assertEquals(allowedByDefault, I18nBehavior.i18n('allowedByDefault'));
   assertEquals(text, I18nBehavior.i18n('text'));
   assertEquals(nonBreakingSpace, I18nBehavior.i18n('nonBreakingSpace'));
 
@@ -33,6 +30,9 @@
 }
 
 function testI18nAdvanced() {
+  assertEquals(
+      allowedByDefault,
+      I18nBehavior.i18nAdvanced('allowedByDefault'));
   I18nBehavior.i18nAdvanced('customAttr', {
     attrs: {
       is: function(el, val) {
diff --git a/chrome/test/data/webui/md_bookmarks/command_manager_test.js b/chrome/test/data/webui/md_bookmarks/command_manager_test.js
index a616a69..641c11d4 100644
--- a/chrome/test/data/webui/md_bookmarks/command_manager_test.js
+++ b/chrome/test/data/webui/md_bookmarks/command_manager_test.js
@@ -30,6 +30,11 @@
                     '12',
                     [
                       createItem('121', {url: 'http://121/'}),
+                      createFolder(
+                          '122',
+                          [
+                            createItem('1221'),
+                          ]),
                     ]),
                 createItem('13', {url: 'http://13/'}),
               ]),
@@ -136,11 +141,11 @@
       lastDelete = idArray.sort();
     };
 
-    var parentAndChildren = new Set(['1', '2', '12', '111']);
+    var parentAndChildren = new Set(['11', '12', '111', '1221']);
     assertTrue(commandManager.canExecute(Command.DELETE, parentAndChildren));
     commandManager.handle(Command.DELETE, parentAndChildren);
 
-    assertDeepEquals(['1', '2'], lastDelete);
+    assertDeepEquals(['11', '12'], lastDelete);
   });
 
   test('expandUrls_ expands one level of URLs', function() {
@@ -187,6 +192,39 @@
     assertTrue(commandItem[Command.OPEN_INCOGNITO].disabled);
     assertFalse(commandItem[Command.OPEN_INCOGNITO].hidden);
   });
+
+  test('cannot execute editing commands when editing is disabled', function() {
+    var items = new Set(['12']);
+
+    store.data.prefs.canEdit = false;
+    store.data.selection.items = items;
+    store.notifyObservers();
+
+    assertFalse(commandManager.canExecute(Command.EDIT, items));
+    assertFalse(commandManager.canExecute(Command.DELETE, items));
+    assertFalse(commandManager.canExecute(Command.UNDO, items));
+    assertFalse(commandManager.canExecute(Command.REDO, items));
+
+    // No divider line should be visible when only 'Open' commands are enabled.
+    commandManager.openCommandMenuAtPosition(0, 0);
+    commandManager.root.querySelectorAll('hr').forEach(element => {
+      assertTrue(element.hidden);
+    });
+  });
+
+  test('cannot edit unmodifiable nodes', function() {
+    // Cannot edit root folders.
+    var items = new Set(['1']);
+    assertFalse(commandManager.canExecute(Command.EDIT, items));
+    assertFalse(commandManager.canExecute(Command.DELETE, items));
+
+    store.data.nodes['12'].unmodifiable = 'managed';
+    store.notifyObservers();
+
+    items = new Set(['12']);
+    assertFalse(commandManager.canExecute(Command.EDIT, items));
+    assertFalse(commandManager.canExecute(Command.DELETE, items));
+  });
 });
 
 suite('<bookmarks-item> CommandManager integration', function() {
diff --git a/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js b/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js
index 1cc323b4..02cfeac 100644
--- a/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js
+++ b/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js
@@ -91,6 +91,7 @@
     });
     store.replaceSingleton();
 
+    draggedIds = null;
     chrome.bookmarkManagerPrivate.startDrag = function(nodes, isTouch) {
       draggedIds = nodes;
     };
@@ -490,4 +491,40 @@
     assertDeepEquals([], normalizeSet(store.data.selection.items));
     dispatchDragEvent('dragend', dragElement);
   });
+
+  test('cannot drag items when editing is disabled', function() {
+    store.data.prefs.canEdit = false;
+    store.notifyObservers();
+
+    var dragElement = getFolderNode('11');
+    dispatchDragEvent('dragstart', dragElement);
+    assertEquals(null, draggedIds);
+  });
+
+  test('cannot start dragging unmodifiable items', function() {
+    store.data.nodes['2'].unmodifiable = 'managed';
+    store.notifyObservers();
+
+    var dragElement = getFolderNode('1');
+    dispatchDragEvent('dragstart', dragElement);
+    assertEquals(null, draggedIds);
+
+    dragElement = getFolderNode('2');
+    dispatchDragEvent('dragstart', dragElement);
+    assertEquals(null, draggedIds);
+  });
+
+  test('cannot drag onto folders with unmodifiable children', function() {
+    store.data.nodes['2'].unmodifiable = 'managed';
+    store.notifyObservers();
+
+    var dragElement = getListItem('12');
+    dispatchDragEvent('dragstart', dragElement);
+
+    // Can't drag onto the unmodifiable node.
+    var dragTarget = getFolderNode('2');
+    dispatchDragEvent('dragover', dragTarget);
+    assertEquals(
+        DropPosition.NONE, dndManager.calculateValidDropPositions_(dragTarget));
+  });
 });
diff --git a/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js b/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js
index 524b1be..c8d7eab 100644
--- a/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js
+++ b/chrome/test/data/webui/md_bookmarks/md_bookmarks_browsertest.js
@@ -193,6 +193,7 @@
 
   testGenPreamble: function() {
     GEN('SetIncognitoAvailability(IncognitoModePrefs::DISABLED);');
+    GEN('SetCanEditBookmarks(false);');
   },
 
   extraLibraries: MaterialBookmarksBrowserTest.prototype.extraLibraries.concat([
diff --git a/chrome/test/data/webui/md_bookmarks/md_bookmarks_focus_test.js b/chrome/test/data/webui/md_bookmarks/md_bookmarks_focus_test.js
index a0d9130..628007ed 100644
--- a/chrome/test/data/webui/md_bookmarks/md_bookmarks_focus_test.js
+++ b/chrome/test/data/webui/md_bookmarks/md_bookmarks_focus_test.js
@@ -188,12 +188,13 @@
       chrome.bookmarkManagerPrivate.removeTrees = function() {}
 
       store.data.selection.items = new Set(['3', '4']);
+      store.data.selectedFolder = '2';
       store.notifyObservers();
 
-      getFolderNode('1').$.container.focus();
-      keydown('1', 'delete');
+      getFolderNode('2').$.container.focus();
+      keydown('2', 'delete');
 
-      commandManager.assertLastCommand(Command.DELETE, ['1']);
+      commandManager.assertLastCommand(Command.DELETE, ['2']);
     });
   });
 
diff --git a/chrome/test/data/webui/md_bookmarks/policy_test.js b/chrome/test/data/webui/md_bookmarks/policy_test.js
index 3654007..6e5cb07 100644
--- a/chrome/test/data/webui/md_bookmarks/policy_test.js
+++ b/chrome/test/data/webui/md_bookmarks/policy_test.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.
 
-suite('Incognito policy', function() {
+suite('Bookmarks policies', function() {
   var store;
   var app;
 
@@ -17,13 +17,14 @@
     });
     store.setReducersEnabled(true);
     store.expectAction('set-incognito-availability');
+    store.expectAction('set-can-edit');
     store.replaceSingleton();
 
     app = document.createElement('bookmarks-app');
     replaceBody(app);
   });
 
-  test('updates when changed by the browser', function() {
+  test('incognito availability updates when changed', function() {
     var commandManager = bookmarks.CommandManager.getInstance();
     // Incognito is disabled during testGenPreamble(). Wait for the front-end to
     // load the config.
@@ -42,4 +43,17 @@
           commandManager.canExecute(Command.OPEN_INCOGNITO, new Set(['11'])));
     });
   });
+
+  test('canEdit updates when changed', function() {
+    var commandManager = bookmarks.CommandManager.getInstance();
+    return store.waitForAction('set-can-edit').then(action => {
+      assertFalse(store.data.prefs.canEdit);
+      assertFalse(commandManager.canExecute(Command.DELETE, new Set(['11'])));
+
+      return cr.sendWithPromise('testSetCanEdit', true);
+    }).then(() => {
+      assertTrue(store.data.prefs.canEdit);
+      assertTrue(commandManager.canExecute(Command.DELETE, new Set(['11'])));
+    });
+  });
 });
diff --git a/chrome/test/data/webui/md_bookmarks/toolbar_test.js b/chrome/test/data/webui/md_bookmarks/toolbar_test.js
index b5c697a..8fc0282c 100644
--- a/chrome/test/data/webui/md_bookmarks/toolbar_test.js
+++ b/chrome/test/data/webui/md_bookmarks/toolbar_test.js
@@ -13,10 +13,13 @@
 
   setup(function() {
     store = new bookmarks.TestStore({
-      nodes: testTree(createFolder('1', [
-        createItem('2'),
-        createItem('3'),
-      ])),
+      nodes: testTree(createFolder(
+          '1',
+          [
+            createItem('2'),
+            createItem('3'),
+            createFolder('4', [], {unmodifiable: 'managed'}),
+          ])),
       selection: {
         items: new Set(),
         anchor: null,
@@ -43,13 +46,21 @@
     assertTrue(toolbar.showSelectionOverlay);
   });
 
+  test('overlay does not show when editing is disabled', function() {
+    store.data.prefs.canEdit = false
+    store.data.selection.items = new Set(['2', '3']);
+    store.notifyObservers();
+    assertFalse(toolbar.showSelectionOverlay);
+  });
+
   test('clicking overlay delete button triggers a delete command', function() {
     store.data.selection.items = new Set(['2', '3']);
     store.notifyObservers();
 
     Polymer.dom.flush();
-    MockInteractions.tap(
-        toolbar.$$('cr-toolbar-selection-overlay').deleteButton);
+    var button = toolbar.$$('cr-toolbar-selection-overlay').deleteButton;
+    assertFalse(button.disabled);
+    MockInteractions.tap(button);
 
     commandManager.assertLastCommand(Command.DELETE, ['2', '3']);
   });
@@ -64,4 +75,34 @@
 
     commandManager.assertLastCommand(null);
   });
+
+  test('delete button is disabled when items are unmodifiable', function() {
+    store.data.nodes['3'].unmodifiable = 'managed';
+    store.data.selection.items = new Set(['2', '3']);
+    store.notifyObservers();
+    Polymer.dom.flush();
+
+    assertTrue(toolbar.showSelectionOverlay);
+    assertTrue(
+        toolbar.$$('cr-toolbar-selection-overlay').deleteButton.disabled);
+  });
+
+  test('overflow menu options are disabled when appropriate', function() {
+    store.data.selectedFolder = '1';
+    store.notifyObservers();
+
+    assertFalse(toolbar.$.addBookmarkButton.disabled);
+
+    store.data.selectedFolder = '4';
+    store.notifyObservers();
+
+    assertTrue(toolbar.$.addBookmarkButton.disabled);
+    assertFalse(toolbar.$.importBookmarkButton.disabled);
+
+    store.data.prefs.canEdit = false;
+    store.notifyObservers();
+
+    assertTrue(toolbar.$.addBookmarkButton.disabled);
+    assertTrue(toolbar.$.importBookmarkButton.disabled);
+  });
 });
diff --git a/chrome/test/data/webui/md_bookmarks/util_test.js b/chrome/test/data/webui/md_bookmarks/util_test.js
index 0a05763..081923c 100644
--- a/chrome/test/data/webui/md_bookmarks/util_test.js
+++ b/chrome/test/data/webui/md_bookmarks/util_test.js
@@ -59,4 +59,47 @@
     var newSet = bookmarks.util.removeIdsFromSet(set, toRemove);
     assertDeepEquals(['5'], normalizeSet(newSet));
   });
+
+  test('canEditNode and canReorderChildren', function() {
+    var store = new bookmarks.TestStore({
+      nodes: testTree(
+          createFolder(
+              '1',
+              [
+                createItem('11'),
+              ]),
+          createFolder(
+              '4',
+              [
+                createItem('41', {unmodifiable: 'managed'}),
+              ],
+              {unmodifiable: 'managed'})),
+    });
+
+    // Top-level folders are unmodifiable, but their children can be changed.
+    assertFalse(bookmarks.util.canEditNode(store.data, '1'));
+    assertTrue(bookmarks.util.canReorderChildren(store.data, '1'));
+
+    // Managed folders are entirely unmodifiable.
+    assertFalse(bookmarks.util.canEditNode(store.data, '4'));
+    assertFalse(bookmarks.util.canReorderChildren(store.data, '4'));
+    assertFalse(bookmarks.util.canEditNode(store.data, '41'));
+    assertFalse(bookmarks.util.canReorderChildren(store.data, '41'));
+
+    // Regular nodes are modifiable.
+    assertTrue(bookmarks.util.canEditNode(store.data, '11'));
+    assertTrue(bookmarks.util.canReorderChildren(store.data, '11'));
+
+    // When editing is disabled globally, everything is unmodifiable.
+    store.data.prefs.canEdit = false;
+
+    assertFalse(bookmarks.util.canEditNode(store.data, '1'));
+    assertFalse(bookmarks.util.canReorderChildren(store.data, '1'));
+
+    assertFalse(bookmarks.util.canEditNode(store.data, '41'));
+    assertFalse(bookmarks.util.canReorderChildren(store.data, '41'));
+
+    assertFalse(bookmarks.util.canEditNode(store.data, '11'));
+    assertFalse(bookmarks.util.canReorderChildren(store.data, '11'));
+  });
 });
diff --git a/chromecast/base/BUILD.gn b/chromecast/base/BUILD.gn
index 17b4f9a7..08902b1 100644
--- a/chromecast/base/BUILD.gn
+++ b/chromecast/base/BUILD.gn
@@ -12,19 +12,23 @@
 
 declare_args() {
   # Denotes the type of Cast product. This is #defined as CAST_PRODUCT_TYPE in
-  # version.h. This is an integer in the range [0-5].
+  # version.h. See //components/metrics/proto/cast_logs.proto for valid values.
   if (is_android) {
-    cast_product_type = 4
+    if (is_android_things) {
+      cast_product_type = 6  # CAST_PRODUCT_TYPE_ANDROID_THINGS
+    } else {
+      cast_product_type = 4  # CAST_PRODUCT_TYPE_ANDROID_TV
+    }
   } else {
-    cast_product_type = 0
+    cast_product_type = 0  # CAST_PRODUCT_TYPE_UNKNOWN
   }
 }
 
 if (is_android) {
-  assert(cast_product_type == 4)
+  assert(cast_product_type == 4 || cast_product_type == 6)
 } else {
-  assert(cast_product_type >= 0 && cast_product_type <= 5 &&
-         cast_product_type != 4)
+  assert(cast_product_type >= 0 && cast_product_type <= 6 &&
+         cast_product_type != 4 && cast_product_type != 6)
 }
 
 source_set("base") {
diff --git a/chromecast/chromecast.gni b/chromecast/chromecast.gni
index 67f30c20..6cc70ad 100644
--- a/chromecast/chromecast.gni
+++ b/chromecast/chromecast.gni
@@ -41,6 +41,9 @@
   # device supports multizone. The command line flag --enable-multizone must
   # still be used to enable multizone.
   supports_multizone = is_cast_audio_only && !is_cast_desktop_build
+
+  # Set true for builds targeting Android Things.
+  is_android_things = false
 }
 
 declare_args() {
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc
index 218cd4af..ac34a99 100644
--- a/chromeos/dbus/update_engine_client.cc
+++ b/chromeos/dbus/update_engine_client.cc
@@ -468,13 +468,18 @@
   void OnSetUpdateOverCellularTarget(
       const SetUpdateOverCellularTargetCallback& callback,
       dbus::Response* response) {
+    bool success = true;
     if (!response) {
+      success = false;
       LOG(ERROR) << update_engine::kSetUpdateOverCellularTarget
                  << " call failed";
-      callback.Run(false);
-      return;
     }
-    callback.Run(true);
+
+    for (auto& observer : observers_) {
+      observer.OnUpdateOverCellularTargetSet(success);
+    }
+
+    callback.Run(success);
   }
 
   // Called when a status update signal is received.
diff --git a/chromeos/dbus/update_engine_client.h b/chromeos/dbus/update_engine_client.h
index 5206aa6..113bfde2 100644
--- a/chromeos/dbus/update_engine_client.h
+++ b/chromeos/dbus/update_engine_client.h
@@ -73,6 +73,9 @@
 
     // Called when the status is updated.
     virtual void UpdateStatusChanged(const Status& status) {}
+
+    // Called when the update over cellular target is set.
+    virtual void OnUpdateOverCellularTargetSet(bool success) {}
   };
 
   ~UpdateEngineClient() override;
diff --git a/components/arc/audio/arc_audio_bridge.cc b/components/arc/audio/arc_audio_bridge.cc
index 3f5a60d..1512775 100644
--- a/components/arc/audio/arc_audio_bridge.cc
+++ b/components/arc/audio/arc_audio_bridge.cc
@@ -11,6 +11,9 @@
 
 namespace arc {
 
+// static
+const char ArcAudioBridge::kArcServiceName[] = "arc::ArcAudioBridge";
+
 ArcAudioBridge::ArcAudioBridge(ArcBridgeService* bridge_service)
     : ArcService(bridge_service), binding_(this) {
   arc_bridge_service()->audio()->AddObserver(this);
diff --git a/components/arc/audio/arc_audio_bridge.h b/components/arc/audio/arc_audio_bridge.h
index b29def6..20efba93 100644
--- a/components/arc/audio/arc_audio_bridge.h
+++ b/components/arc/audio/arc_audio_bridge.h
@@ -33,6 +33,9 @@
   void ShowVolumeControls() override;
   void OnSystemVolumeUpdateRequest(int32_t percent) override;
 
+  // For supporting ArcServiceManager::GetService<T>().
+  static const char kArcServiceName[];
+
  private:
   mojo::Binding<mojom::AudioHost> binding_;
 
diff --git a/components/arc/common/intent_helper.mojom b/components/arc/common/intent_helper.mojom
index 179d5ea2..1f68d518 100644
--- a/components/arc/common/intent_helper.mojom
+++ b/components/arc/common/intent_helper.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 16
+// Next MinVersion: 17
 
 module arc.mojom;
 
@@ -84,7 +84,7 @@
 
 // Handles intents from ARC in Chrome.
 // Deprecated method ID: 4
-// Next method ID: 6
+// Next method ID: 7
 interface IntentHelperHost {
   // Called when icons associated with the package are no longer up to date.
   [MinVersion=3] OnIconInvalidated@1(string package_name);
@@ -105,6 +105,9 @@
   // Sets an image as the wallpaper.
   // |jpeg_data| is a JPEG encoded wallpaper image.
   [MinVersion=8] SetWallpaperDeprecated@4(array<uint8> jpeg_data);
+
+  // Opens the volume control.
+  [MinVersion=16] OpenVolumeControl@6();
 };
 
 // Sends intents to ARC on behalf of Chrome.
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc
index 053d526..d4435fa8 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/audio/arc_audio_bridge.h"
 #include "components/arc/intent_helper/link_handler_model_impl.h"
 #include "components/arc/intent_helper/local_activity_resolver.h"
 #include "ui/base/layout.h"
@@ -89,6 +90,10 @@
   LOG(ERROR) << "IntentHelper.SetWallpaper is deprecated";
 }
 
+void ArcIntentHelperBridge::OpenVolumeControl() {
+  ArcServiceManager::Get()->GetService<ArcAudioBridge>()->ShowVolumeControls();
+}
+
 ArcIntentHelperBridge::GetResult ArcIntentHelperBridge::GetActivityIcons(
     const std::vector<ActivityName>& activities,
     const OnIconsReadyCallback& callback) {
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.h b/components/arc/intent_helper/arc_intent_helper_bridge.h
index 06e58aa..eaebfa3 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.h
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -60,6 +60,7 @@
   void OnOpenUrl(const std::string& url) override;
   void OpenWallpaperPicker() override;
   void SetWallpaperDeprecated(const std::vector<uint8_t>& jpeg_data) override;
+  void OpenVolumeControl() override;
 
   // Retrieves icons for the |activities| and calls |callback|.
   // See ActivityIconLoader::GetActivityIcons() for more details.
diff --git a/components/content_settings/core/common/content_settings.cc b/components/content_settings/core/common/content_settings.cc
index 2333fe25..4bee639 100644
--- a/components/content_settings/core/common/content_settings.cc
+++ b/components/content_settings/core/common/content_settings.cc
@@ -57,6 +57,7 @@
     {CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA, 31},
     {CONTENT_SETTINGS_TYPE_ADS, 32},
     {CONTENT_SETTINGS_TYPE_ADS_DATA, 33},
+    {CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 34},
 };
 
 int ContentSettingTypeToHistogramValue(ContentSettingsType content_setting,
diff --git a/components/leveldb/leveldb_database_impl.cc b/components/leveldb/leveldb_database_impl.cc
index 59ab078..c9e352e 100644
--- a/components/leveldb/leveldb_database_impl.cc
+++ b/components/leveldb/leveldb_database_impl.cc
@@ -4,12 +4,16 @@
 
 #include "components/leveldb/leveldb_database_impl.h"
 
+#include <inttypes.h>
 #include <map>
 #include <string>
 #include <utility>
 
 #include "base/optional.h"
 #include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_dump_manager.h"
 #include "components/leveldb/env_mojo.h"
 #include "components/leveldb/public/cpp/util.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
@@ -37,10 +41,18 @@
 
 LevelDBDatabaseImpl::LevelDBDatabaseImpl(
     std::unique_ptr<leveldb::Env> environment,
-    std::unique_ptr<leveldb::DB> db)
-    : environment_(std::move(environment)), db_(std::move(db)) {}
+    std::unique_ptr<leveldb::DB> db,
+    base::Optional<base::trace_event::MemoryAllocatorDumpGuid> memory_dump_id)
+    : environment_(std::move(environment)),
+      db_(std::move(db)),
+      memory_dump_id_(memory_dump_id) {
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "MojoLevelDB", base::ThreadTaskRunnerHandle::Get());
+}
 
 LevelDBDatabaseImpl::~LevelDBDatabaseImpl() {
+  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+      this);
   for (auto& p : iterator_map_)
     delete p.second;
   for (auto& p : snapshot_map_)
@@ -270,6 +282,42 @@
   ReplyToIteratorMessage(it->second, std::move(callback));
 }
 
+bool LevelDBDatabaseImpl::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  auto* mad = pmd->CreateAllocatorDump(base::StringPrintf(
+      "leveldb/mojo/0x%" PRIXPTR, reinterpret_cast<uintptr_t>(db_.get())));
+
+  uint64_t memory_usage = 0;
+  std::string memory_usage_string;
+  bool got_memory_usage =
+      db_->GetProperty("leveldb.approximate-memory-usage",
+                       &memory_usage_string) &&
+      base::StringToUint64(memory_usage_string, &memory_usage);
+  DCHECK(got_memory_usage);
+  mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                 memory_usage);
+
+  const char* system_allocator_name =
+      base::trace_event::MemoryDumpManager::GetInstance()
+          ->system_allocator_pool_name();
+  if (system_allocator_name)
+    pmd->AddSuballocation(mad->guid(), system_allocator_name);
+
+  if (memory_dump_id_) {
+    auto* global_dump = pmd->CreateSharedGlobalAllocatorDump(*memory_dump_id_);
+    pmd->AddOwnershipEdge(global_dump->guid(), mad->guid());
+    // Add size to global dump to propagate the size of the database to the
+    // client's dump.
+    global_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                           memory_usage);
+  }
+
+  return true;
+}
+
 void LevelDBDatabaseImpl::ReplyToIteratorMessage(
     leveldb::Iterator* it,
     IteratorSeekToFirstCallback callback) {
diff --git a/components/leveldb/leveldb_database_impl.h b/components/leveldb/leveldb_database_impl.h
index 277748d4..862fdc4 100644
--- a/components/leveldb/leveldb_database_impl.h
+++ b/components/leveldb/leveldb_database_impl.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/trace_event/memory_dump_provider.h"
 #include "base/unguessable_token.h"
 #include "components/leveldb/public/interfaces/leveldb.mojom.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
@@ -15,10 +16,13 @@
 namespace leveldb {
 
 // The backing to a database object that we pass to our called.
-class LevelDBDatabaseImpl : public mojom::LevelDBDatabase {
+class LevelDBDatabaseImpl : public mojom::LevelDBDatabase,
+                            public base::trace_event::MemoryDumpProvider {
  public:
   LevelDBDatabaseImpl(std::unique_ptr<leveldb::Env> environment,
-                      std::unique_ptr<leveldb::DB> db);
+                      std::unique_ptr<leveldb::DB> db,
+                      base::Optional<base::trace_event::MemoryAllocatorDumpGuid>
+                          memory_dump_id);
   ~LevelDBDatabaseImpl() override;
 
   // Overridden from LevelDBDatabase:
@@ -56,6 +60,10 @@
   void IteratorPrev(const base::UnguessableToken& iterator,
                     IteratorPrevCallback callback) override;
 
+  // base::trace_event::MemoryDumpProvider implementation.
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override;
+
  private:
   // Returns the state of |it| to a caller. Note: This assumes that all the
   // iterator movement methods have the same callback signature. We don't
@@ -68,6 +76,7 @@
 
   std::unique_ptr<leveldb::Env> environment_;
   std::unique_ptr<leveldb::DB> db_;
+  base::Optional<base::trace_event::MemoryAllocatorDumpGuid> memory_dump_id_;
 
   std::map<base::UnguessableToken, const Snapshot*> snapshot_map_;
 
diff --git a/components/leveldb/leveldb_service_impl.cc b/components/leveldb/leveldb_service_impl.cc
index f2e4eb85..98b657b 100644
--- a/components/leveldb/leveldb_service_impl.cc
+++ b/components/leveldb/leveldb_service_impl.cc
@@ -30,16 +30,21 @@
 void LevelDBServiceImpl::Open(
     filesystem::mojom::DirectoryPtr directory,
     const std::string& dbname,
+    const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+        memory_dump_id,
     leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
     OpenCallback callback) {
   OpenWithOptions(leveldb::mojom::OpenOptions::New(), std::move(directory),
-                  dbname, std::move(database), std::move(callback));
+                  dbname, memory_dump_id, std::move(database),
+                  std::move(callback));
 }
 
 void LevelDBServiceImpl::OpenWithOptions(
     leveldb::mojom::OpenOptionsPtr open_options,
     filesystem::mojom::DirectoryPtr directory,
     const std::string& dbname,
+    const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+        memory_dump_id,
     leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
     OpenCallback callback) {
   leveldb::Options options;
@@ -64,8 +69,8 @@
 
   if (s.ok()) {
     mojo::MakeStrongAssociatedBinding(
-        base::MakeUnique<LevelDBDatabaseImpl>(std::move(env_mojo),
-                                              base::WrapUnique(db)),
+        base::MakeUnique<LevelDBDatabaseImpl>(
+            std::move(env_mojo), base::WrapUnique(db), memory_dump_id),
         std::move(database));
   }
 
@@ -73,6 +78,8 @@
 }
 
 void LevelDBServiceImpl::OpenInMemory(
+    const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+        memory_dump_id,
     leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
     OpenCallback callback) {
   leveldb::Options options;
@@ -87,9 +94,10 @@
   leveldb::Status s = leveldb::DB::Open(options, "", &db);
 
   if (s.ok()) {
-    mojo::MakeStrongAssociatedBinding(base::MakeUnique<LevelDBDatabaseImpl>(
-                                          std::move(env), base::WrapUnique(db)),
-                                      std::move(database));
+    mojo::MakeStrongAssociatedBinding(
+        base::MakeUnique<LevelDBDatabaseImpl>(
+            std::move(env), base::WrapUnique(db), memory_dump_id),
+        std::move(database));
   }
 
   std::move(callback).Run(LeveldbStatusToError(s));
diff --git a/components/leveldb/leveldb_service_impl.h b/components/leveldb/leveldb_service_impl.h
index 9a5beed6..364b6190 100644
--- a/components/leveldb/leveldb_service_impl.h
+++ b/components/leveldb/leveldb_service_impl.h
@@ -26,16 +26,23 @@
   // Overridden from LevelDBService:
   void Open(filesystem::mojom::DirectoryPtr directory,
             const std::string& dbname,
+            const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+                memory_dump_id,
             leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
             OpenCallback callback) override;
   void OpenWithOptions(
       leveldb::mojom::OpenOptionsPtr open_options,
       filesystem::mojom::DirectoryPtr directory,
       const std::string& dbname,
+      const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+          memory_dump_id,
       leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
       OpenCallback callback) override;
-  void OpenInMemory(leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
-                    OpenInMemoryCallback callback) override;
+  void OpenInMemory(
+      const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
+          memory_dump_id,
+      leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
+      OpenInMemoryCallback callback) override;
   void Destroy(filesystem::mojom::DirectoryPtr directory,
                const std::string& dbname,
                DestroyCallback callback) override;
diff --git a/components/leveldb/leveldb_service_unittest.cc b/components/leveldb/leveldb_service_unittest.cc
index 0651b07..0ffefeb 100644
--- a/components/leveldb/leveldb_service_unittest.cc
+++ b/components/leveldb/leveldb_service_unittest.cc
@@ -98,7 +98,7 @@
                              mojom::LevelDBDatabaseAssociatedRequest database,
                              mojom::DatabaseError* out_error) {
   base::RunLoop run_loop;
-  leveldb->OpenInMemory(std::move(database),
+  leveldb->OpenInMemory(base::nullopt, std::move(database),
                         Capture(out_error, run_loop.QuitClosure()));
   run_loop.Run();
 }
@@ -265,7 +265,7 @@
     options->create_if_missing = true;
     base::RunLoop run_loop;
     leveldb()->OpenWithOptions(std::move(options), std::move(directory), "test",
-                               MakeRequest(&database),
+                               base::nullopt, MakeRequest(&database),
                                Capture(&error, run_loop.QuitClosure()));
     run_loop.Run();
     EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -285,7 +285,8 @@
     // Reconnect to the database.
     mojom::LevelDBDatabaseAssociatedPtr database;
     base::RunLoop run_loop;
-    leveldb()->Open(std::move(directory), "test", MakeRequest(&database),
+    leveldb()->Open(std::move(directory), "test", base::nullopt,
+                    MakeRequest(&database),
                     Capture(&error, run_loop.QuitClosure()));
     run_loop.Run();
     EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -315,7 +316,7 @@
     options->create_if_missing = true;
     base::RunLoop run_loop;
     leveldb()->OpenWithOptions(std::move(options), std::move(directory), "test",
-                               MakeRequest(&database),
+                               base::nullopt, MakeRequest(&database),
                                Capture(&error, run_loop.QuitClosure()));
     run_loop.Run();
     EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -347,7 +348,8 @@
     // Reconnect to the database should fail.
     mojom::LevelDBDatabaseAssociatedPtr database;
     base::RunLoop run_loop;
-    leveldb()->Open(std::move(directory), "test", MakeRequest(&database),
+    leveldb()->Open(std::move(directory), "test", base::nullopt,
+                    MakeRequest(&database),
                     Capture(&error, run_loop.QuitClosure()));
     run_loop.Run();
     EXPECT_EQ(mojom::DatabaseError::INVALID_ARGUMENT, error);
diff --git a/components/leveldb/public/interfaces/leveldb.mojom b/components/leveldb/public/interfaces/leveldb.mojom
index 11e5670..a410cd5 100644
--- a/components/leveldb/public/interfaces/leveldb.mojom
+++ b/components/leveldb/public/interfaces/leveldb.mojom
@@ -6,6 +6,7 @@
 
 import "components/filesystem/public/interfaces/directory.mojom";
 import "mojo/common/unguessable_token.mojom";
+import "mojo/common/memory_allocator_dump_cross_process_uid.mojom";
 
 enum DatabaseError {
   OK,
@@ -66,16 +67,19 @@
   // Fails if the database doesn't already exist.
   Open(filesystem.mojom.Directory directory,
        string dbname,
+       mojo.common.mojom.MemoryAllocatorDumpCrossProcessUid? memory_dump_id,
        associated LevelDBDatabase& database) => (DatabaseError status);
 
   // Open the database with the specified "name" in the specified "directory".
   OpenWithOptions(OpenOptions options,
                   filesystem.mojom.Directory directory,
                   string dbname,
+                  mojo.common.mojom.MemoryAllocatorDumpCrossProcessUid? memory_dump_id,
                   associated LevelDBDatabase& database) => (DatabaseError status);
 
   // Opens a database stored purely in memory.
-  OpenInMemory(associated LevelDBDatabase& database) => (DatabaseError status);
+  OpenInMemory(mojo.common.mojom.MemoryAllocatorDumpCrossProcessUid? memory_dump_id,
+               associated LevelDBDatabase& database) => (DatabaseError status);
 
   // Destroys the contents of the specified database. Returns OK if the database
   // already didn't exist.
diff --git a/components/leveldb/remote_iterator_unittest.cc b/components/leveldb/remote_iterator_unittest.cc
index 8665711a..f37bd48 100644
--- a/components/leveldb/remote_iterator_unittest.cc
+++ b/components/leveldb/remote_iterator_unittest.cc
@@ -52,7 +52,7 @@
 
     mojom::DatabaseError error;
     base::RunLoop run_loop;
-    leveldb()->OpenInMemory(MakeRequest(&database_),
+    leveldb()->OpenInMemory(base::nullopt, MakeRequest(&database_),
                             Capture(&error, run_loop.QuitClosure()));
     run_loop.Run();
     EXPECT_EQ(mojom::DatabaseError::OK, error);
diff --git a/components/metrics/proto/cast_logs.proto b/components/metrics/proto/cast_logs.proto
index 4bea0fdf7..1c40c3a 100644
--- a/components/metrics/proto/cast_logs.proto
+++ b/components/metrics/proto/cast_logs.proto
@@ -18,7 +18,7 @@
   // Next tag: 6
   message CastDeviceInfo {
     // The product type of Cast device sent from Cast-enabled devices.
-    // Next tag: 5
+    // Next tag: 7
     enum CastProductType {
       CAST_PRODUCT_TYPE_UNKNOWN = 0;
       CAST_PRODUCT_TYPE_CHROMECAST = 1;
@@ -26,6 +26,7 @@
       CAST_PRODUCT_TYPE_AUDIO = 3;
       CAST_PRODUCT_TYPE_ANDROID_TV = 4;
       CAST_PRODUCT_TYPE_ASSISTANT = 5;
+      CAST_PRODUCT_TYPE_ANDROID_THINGS = 6;
     }
     optional CastProductType type = 1;
 
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index a814bb1d..8fd9240 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -91,6 +91,22 @@
     "OmniboxUIExperimentMaxAutocompleteMatches",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Feature used for hiding the suggestion URL path as a UI experiment.
+const base::Feature kUIExperimentHideSuggestionUrlPath{
+    "OmniboxUIExperimentHideSuggestionUrlPath",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Feature used for hiding the suggestion URL scheme as a UI experiment.
+const base::Feature kUIExperimentHideSuggestionUrlScheme{
+    "OmniboxUIExperimentHideSuggestionUrlScheme",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Feature used for hiding the suggestion URL subdomain as a UI experiment.
+// This only hides some trivially informative subdomains such as "www" or "m".
+const base::Feature kUIExperimentHideSuggestionUrlTrivialSubdomains{
+    "OmniboxUIExperimentHideSuggestionUrlTrivialSubdomains",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Feature used for the omnibox narrow suggestions dropdown UI experiment.
 const base::Feature kUIExperimentNarrowDropdown{
     "OmniboxUIExperimentNarrowDropdown", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 8aa261a5..b96588c 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -33,6 +33,9 @@
 extern const base::Feature kZeroSuggestRedirectToChrome;
 extern const base::Feature kZeroSuggestSwapTitleAndUrl;
 extern const base::Feature kDisplayTitleForCurrentUrl;
+extern const base::Feature kUIExperimentHideSuggestionUrlPath;
+extern const base::Feature kUIExperimentHideSuggestionUrlScheme;
+extern const base::Feature kUIExperimentHideSuggestionUrlTrivialSubdomains;
 extern const base::Feature kUIExperimentMaxAutocompleteMatches;
 extern const base::Feature kUIExperimentNarrowDropdown;
 extern const base::Feature kUIExperimentVerticalLayout;
diff --git a/components/plugins/renderer/loadable_plugin_placeholder.cc b/components/plugins/renderer/loadable_plugin_placeholder.cc
index 41ff1b0..c5b3b058 100644
--- a/components/plugins/renderer/loadable_plugin_placeholder.cc
+++ b/components/plugins/renderer/loadable_plugin_placeholder.cc
@@ -343,13 +343,11 @@
   blink::WebElement element = plugin()->Container()->GetElement();
   element.SetAttribute("placeholderReady", "true");
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
   base::Value value("placeholderReady");
   blink::WebSerializedScriptValue message_data =
       blink::WebSerializedScriptValue::Serialize(
           blink::MainThreadIsolate(),
-          converter->ToV8Value(
+          content::V8ValueConverter::Create()->ToV8Value(
               &value,
               element.GetDocument().GetFrame()->MainWorldScriptContext()));
   blink::WebDOMMessageEvent msg_event(message_data);
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index 909c848..f23e47e 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -36,12 +36,14 @@
       password_form_action_(password_form_action),
       password_form_frame_url_(password_form_frame_url),
       saved_domain_(saved_domain),
-      request_type_(type),
+      trigger_type_(type),
       password_field_exists_(password_field_exists),
       password_protection_service_(pps),
       request_timeout_in_ms_(request_timeout_in_ms),
       weakptr_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type_ == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
 }
 
 PasswordProtectionRequest::~PasswordProtectionRequest() {
@@ -85,7 +87,7 @@
   std::unique_ptr<LoginReputationClientResponse> cached_response =
       base::MakeUnique<LoginReputationClientResponse>();
   auto verdict = password_protection_service_->GetCachedVerdict(
-      main_frame_url_, cached_response.get());
+      main_frame_url_, trigger_type_, cached_response.get());
   if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED)
     Finish(PasswordProtectionService::RESPONSE_ALREADY_CACHED,
            std::move(cached_response));
@@ -96,11 +98,11 @@
 void PasswordProtectionRequest::FillRequestProto() {
   request_proto_ = base::MakeUnique<LoginReputationClientRequest>();
   request_proto_->set_page_url(main_frame_url_.spec());
-  request_proto_->set_trigger_type(request_type_);
-  password_protection_service_->FillUserPopulation(request_type_,
+  request_proto_->set_trigger_type(trigger_type_);
+  password_protection_service_->FillUserPopulation(trigger_type_,
                                                    request_proto_.get());
   request_proto_->set_stored_verdict_cnt(
-      password_protection_service_->GetStoredVerdictCount());
+      password_protection_service_->GetStoredVerdictCount(trigger_type_));
   LoginReputationClientRequest::Frame* main_frame =
       request_proto_->add_frames();
   main_frame->set_url(main_frame_url_.spec());
@@ -108,7 +110,7 @@
   password_protection_service_->FillReferrerChain(
       main_frame_url_, -1 /* tab id not available */, main_frame);
 
-  switch (request_type_) {
+  switch (trigger_type_) {
     case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: {
       LoginReputationClientRequest::Frame::Form* password_form;
       if (password_form_frame_url_ == main_frame_url_) {
@@ -247,7 +249,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   tracker_.TryCancelAll();
 
-  if (request_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+  if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
     UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName,
                               outcome, PasswordProtectionService::MAX_OUTCOME);
   } else {
@@ -256,7 +258,7 @@
   }
 
   if (outcome == PasswordProtectionService::SUCCEEDED && response) {
-    switch (request_type_) {
+    switch (trigger_type_) {
       case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
         UMA_HISTOGRAM_ENUMERATION(
             "PasswordProtection.Verdict.PasswordFieldOnFocus",
diff --git a/components/safe_browsing/password_protection/password_protection_request.h b/components/safe_browsing/password_protection/password_protection_request.h
index 721739b..756a65d 100644
--- a/components/safe_browsing/password_protection/password_protection_request.h
+++ b/components/safe_browsing/password_protection/password_protection_request.h
@@ -74,8 +74,8 @@
 
   content::WebContents* web_contents() const { return web_contents_; }
 
-  LoginReputationClientRequest::TriggerType request_type() const {
-    return request_type_;
+  LoginReputationClientRequest::TriggerType trigger_type() const {
+    return trigger_type_;
   }
 
  private:
@@ -129,7 +129,7 @@
   const std::string saved_domain_;
 
   // If this request is for unfamiliar login page or for a password reuse event.
-  const LoginReputationClientRequest::TriggerType request_type_;
+  const LoginReputationClientRequest::TriggerType trigger_type_;
 
   // If there is a password field on the page.
   const bool password_field_exists_;
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index c7ec2a9d..b24e456 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -4,6 +4,9 @@
 
 #include "components/safe_browsing/password_protection/password_protection_service.h"
 
+#include <stddef.h>
+#include <string>
+
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -38,6 +41,7 @@
 const int kRequestTimeoutMs = 10000;
 const char kPasswordProtectionRequestUrl[] =
     "https://sb-ssl.google.com/safebrowsing/clientreport/login";
+const char kPasswordOnFocusCacheKey[] = "password_on_focus_cache_key";
 
 // Helper function to determine if the given origin matches content settings
 // map's patterns.
@@ -87,7 +91,8 @@
     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     HistoryService* history_service,
     HostContentSettingsMap* host_content_settings_map)
-    : stored_verdict_count_(-1),
+    : stored_verdict_count_password_on_focus_(-1),
+      stored_verdict_count_password_entry_(-1),
       database_manager_(database_manager),
       request_context_getter_(request_context_getter),
       history_service_observer_(this),
@@ -122,24 +127,50 @@
          hostname.find('.') != std::string::npos;
 }
 
+// We cache both types of pings under the same content settings type (
+// CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION). Since UNFAMILIAR_LOGING_PAGE
+// verdicts are only enabled on extended reporting users, we cache them one
+// layer lower in the content setting DictionaryValue than PASSWORD_REUSE_EVENT
+// verdicts.
+// In other words, to cache a UNFAMILIAR_LOGIN_PAGE verdict we needs two levels
+// of keys: (1) origin, (2) cache expression returned in verdict.
+// To cache a PASSWORD_REUSE_EVENT, three levels of keys are used:
+// (1) origin, (2) 2nd level key is always |kPasswordOnFocusCacheKey|,
+// (3) cache expression.
 LoginReputationClientResponse::VerdictType
 PasswordProtectionService::GetCachedVerdict(
     const GURL& url,
+    TriggerType trigger_type,
     LoginReputationClientResponse* out_response) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+
   if (!url.is_valid())
     return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
 
-  DCHECK(content_settings_);
-
   GURL hostname = GetHostNameWithHTTPScheme(url);
-  std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
       base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
           hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
           std::string(), nullptr));
-  // Return early if there is no verdict cached for this origin.
-  if (!verdict_dictionary.get() || verdict_dictionary->empty())
+
+  if (!cache_dictionary.get() || cache_dictionary->empty())
     return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
 
+  base::DictionaryValue* verdict_dictionary = nullptr;
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
+    // are cached under |kPasswordOnFocusCacheKey|.
+    if (!cache_dictionary->HasKey(
+            base::StringPiece(kPasswordOnFocusCacheKey))) {
+      return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+    }
+    DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
+        base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary));
+  } else {
+    verdict_dictionary = cache_dictionary.get();
+  }
+
   std::vector<std::string> paths;
   GeneratePathVariantsWithoutQuery(url, &paths);
   int max_path_depth = -1;
@@ -148,8 +179,12 @@
   // For all the verdicts of the same origin, we key them by |cache_expression|.
   // Its corresponding value is a DictionaryValue contains its creation time and
   // the serialized verdict proto.
-  for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
-       !it.IsAtEnd(); it.Advance()) {
+  for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
+       it.Advance()) {
+    if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
+        it.key() == base::StringPiece(kPasswordOnFocusCacheKey)) {
+      continue;
+    }
     base::DictionaryValue* verdict_entry = nullptr;
     CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
         it.key() /* cache_expression */, &verdict_entry));
@@ -180,39 +215,66 @@
 
 void PasswordProtectionService::CacheVerdict(
     const GURL& url,
+    TriggerType trigger_type,
     LoginReputationClientResponse* verdict,
     const base::Time& receive_time) {
   DCHECK(verdict);
   DCHECK(content_settings_);
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
 
   GURL hostname = GetHostNameWithHTTPScheme(url);
-  std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+  int* stored_verdict_count =
+      trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+          ? &stored_verdict_count_password_on_focus_
+          : &stored_verdict_count_password_entry_;
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
       base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
           hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
           std::string(), nullptr));
 
-  if (!verdict_dictionary.get())
-    verdict_dictionary = base::MakeUnique<base::DictionaryValue>();
+  if (!cache_dictionary.get())
+    cache_dictionary = base::MakeUnique<base::DictionaryValue>();
 
   std::unique_ptr<base::DictionaryValue> verdict_entry =
       CreateDictionaryFromVerdict(verdict, receive_time);
 
+  base::DictionaryValue* verdict_dictionary = nullptr;
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
+    // are cached under |kPasswordOnFocusCacheKey|.
+    if (!cache_dictionary->HasKey(
+            base::StringPiece(kPasswordOnFocusCacheKey))) {
+      cache_dictionary->SetDictionaryWithoutPathExpansion(
+          base::StringPiece(kPasswordOnFocusCacheKey),
+          base::MakeUnique<base::DictionaryValue>());
+    }
+    DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
+        base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary));
+  } else {
+    verdict_dictionary = cache_dictionary.get();
+  }
+
   // Increases stored verdict count if we haven't seen this cache expression
   // before.
   if (!verdict_dictionary->HasKey(verdict->cache_expression()))
-    stored_verdict_count_ = GetStoredVerdictCount() + 1;
+    *stored_verdict_count = GetStoredVerdictCount(trigger_type) + 1;
   // If same cache_expression is already in this verdict_dictionary, we simply
   // override it.
   verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
                                               std::move(verdict_entry));
   content_settings_->SetWebsiteSettingDefaultScope(
       hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
-      std::string(), std::move(verdict_dictionary));
+      std::string(), std::move(cache_dictionary));
 }
 
 void PasswordProtectionService::CleanUpExpiredVerdicts() {
   DCHECK(content_settings_);
-  if (GetStoredVerdictCount() <= 0)
+
+  if (GetStoredVerdictCount(
+          LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
+      GetStoredVerdictCount(
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
     return;
 
   ContentSettingsForOneType password_protection_settings;
@@ -224,44 +286,29 @@
        password_protection_settings) {
     GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
     // Find all verdicts associated with this origin.
-    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+    std::unique_ptr<base::DictionaryValue> cache_dictionary =
         base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
             primary_pattern_url, GURL(),
             CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
-    std::vector<std::string> expired_keys;
-    for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
-         !it.IsAtEnd(); it.Advance()) {
-      base::DictionaryValue* verdict_entry = nullptr;
-      CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
-          it.key(), &verdict_entry));
-      int verdict_received_time;
-      LoginReputationClientResponse verdict;
-      CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
+    bool has_expired_password_on_focus_entry = RemoveExpiredVerdicts(
+        LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+        cache_dictionary.get());
+    bool has_expired_password_reuse_entry = RemoveExpiredVerdicts(
+        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        cache_dictionary.get());
 
-      if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
-        // Since DictionaryValue::Iterator cannot be used to modify the
-        // dictionary, we record the keys of expired verdicts in |expired_keys|
-        // and remove them in the next for-loop.
-        expired_keys.push_back(it.key());
-      }
-    }
-
-    for (const std::string& key : expired_keys) {
-      verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
-      stored_verdict_count_--;
-    }
-
-    if (verdict_dictionary->size() == 0u) {
+    if (cache_dictionary->size() == 0u) {
       content_settings_->ClearSettingsForOneTypeWithPredicate(
           CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
           base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url));
-    } else if (expired_keys.size() > 0u) {
+    } else if (has_expired_password_on_focus_entry ||
+               has_expired_password_reuse_entry) {
       // Set the website setting of this origin with the updated
       // |verdict_diectionary|.
       content_settings_->SetWebsiteSettingDefaultScope(
           primary_pattern_url, GURL(),
           CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
-          std::move(verdict_dictionary));
+          std::move(cache_dictionary));
     }
   }
 }
@@ -272,14 +319,15 @@
     const GURL& password_form_action,
     const GURL& password_form_frame_url,
     const std::string& saved_domain,
-    LoginReputationClientRequest::TriggerType type,
+    TriggerType trigger_type,
     bool password_field_exists) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<PasswordProtectionRequest> request(
       new PasswordProtectionRequest(
           web_contents, main_frame_url, password_form_action,
-          password_form_frame_url, saved_domain, type, password_field_exists,
-          this, GetRequestTimeoutInMS()));
+          password_form_frame_url, saved_domain, trigger_type,
+          password_field_exists, this, GetRequestTimeoutInMS()));
+
   DCHECK(request);
   request->Start();
   requests_.insert(std::move(request));
@@ -332,11 +380,11 @@
 
   if (response) {
     if (!already_cached) {
-      CacheVerdict(request->main_frame_url(), response.get(),
-                   base::Time::Now());
+      CacheVerdict(request->main_frame_url(), request->trigger_type(),
+                   response.get(), base::Time::Now());
     }
 
-    if (request->request_type() ==
+    if (request->trigger_type() ==
             LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
         response->verdict_type() == LoginReputationClientResponse::PHISHING &&
         base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) {
@@ -379,30 +427,51 @@
   return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
 }
 
-int PasswordProtectionService::GetStoredVerdictCount() {
+int PasswordProtectionService::GetStoredVerdictCount(TriggerType trigger_type) {
   DCHECK(content_settings_);
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  int* stored_verdict_count =
+      trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+          ? &stored_verdict_count_password_on_focus_
+          : &stored_verdict_count_password_entry_;
   // If we have already computed this, return its value.
-  if (stored_verdict_count_ >= 0)
-    return stored_verdict_count_;
+  if (*stored_verdict_count >= 0)
+    return *stored_verdict_count;
 
   ContentSettingsForOneType password_protection_settings;
   content_settings_->GetSettingsForOneType(
       CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
       &password_protection_settings);
-  stored_verdict_count_ = 0;
+  stored_verdict_count_password_on_focus_ = 0;
+  stored_verdict_count_password_entry_ = 0;
   if (password_protection_settings.empty())
     return 0;
 
   for (const ContentSettingPatternSource& source :
        password_protection_settings) {
-    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+    std::unique_ptr<base::DictionaryValue> cache_dictionary =
         base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
             GURL(source.primary_pattern.ToString()), GURL(),
             CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
-    if (verdict_dictionary.get() && !verdict_dictionary->empty())
-      stored_verdict_count_ += static_cast<int>(verdict_dictionary->size());
+    if (cache_dictionary.get() && !cache_dictionary->empty()) {
+      stored_verdict_count_password_entry_ +=
+          static_cast<int>(cache_dictionary->size());
+      if (cache_dictionary->HasKey(
+              base::StringPiece(kPasswordOnFocusCacheKey))) {
+        // Substracts 1 from password_entry count if |kPasswordOnFocusCacheKey|
+        // presents.
+        stored_verdict_count_password_entry_ -= 1;
+        base::DictionaryValue* password_on_focus_dict = nullptr;
+        cache_dictionary->GetDictionaryWithoutPathExpansion(
+            base::StringPiece(kPasswordOnFocusCacheKey),
+            &password_on_focus_dict);
+        stored_verdict_count_password_on_focus_ +=
+            static_cast<int>(password_on_focus_dict->size());
+      }
+    }
   }
-  return stored_verdict_count_;
+  return *stored_verdict_count;
 }
 
 int PasswordProtectionService::GetRequestTimeoutInMS() {
@@ -410,7 +479,7 @@
 }
 
 void PasswordProtectionService::FillUserPopulation(
-    const LoginReputationClientRequest::TriggerType& request_type,
+    TriggerType trigger_type,
     LoginReputationClientRequest* request_proto) {
   ChromeUserPopulation* user_population = request_proto->mutable_population();
   user_population->set_user_population(
@@ -440,8 +509,10 @@
     bool expired,
     const history::URLRows& deleted_rows,
     const std::set<GURL>& favicon_urls) {
-  if (stored_verdict_count_ <= 0)
+  if (stored_verdict_count_password_on_focus_ <= 0 &&
+      stored_verdict_count_password_entry_ <= 0) {
     return;
+  }
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
@@ -463,7 +534,8 @@
   if (all_history) {
     content_settings_->ClearSettingsForOneType(
         CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
-    stored_verdict_count_ = 0;
+    stored_verdict_count_password_on_focus_ = 0;
+    stored_verdict_count_password_entry_ = 0;
     return;
   }
 
@@ -474,24 +546,104 @@
   for (const history::URLRow& row : deleted_rows) {
     if (!row.url().SchemeIsHTTPOrHTTPS())
       continue;
+
     GURL url_key = GetHostNameWithHTTPScheme(row.url());
-    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
-        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-            url_key, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
-            std::string(), nullptr));
-
-    // Move on if we have no cached verdict for this deleted history row.
-    if (!verdict_dictionary.get() || verdict_dictionary->empty())
-      continue;
-
-    int verdict_count = static_cast<int>(verdict_dictionary->size());
-    stored_verdict_count_ = GetStoredVerdictCount() - verdict_count;
+    stored_verdict_count_password_on_focus_ =
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
+        GetVerdictCountForURL(
+            url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
+    stored_verdict_count_password_entry_ =
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
+        GetVerdictCountForURL(
+            url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
     content_settings_->ClearSettingsForOneTypeWithPredicate(
         CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
         base::Bind(&OriginMatchPrimaryPattern, url_key));
   }
 }
 
+int PasswordProtectionService::GetVerdictCountForURL(const GURL& url,
+                                                     TriggerType trigger_type) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
+      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+          url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
+          nullptr));
+  if (!cache_dictionary.get() || cache_dictionary->empty())
+    return 0;
+
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    base::DictionaryValue* password_on_focus_dict = nullptr;
+    cache_dictionary->GetDictionaryWithoutPathExpansion(
+        base::StringPiece(kPasswordOnFocusCacheKey), &password_on_focus_dict);
+    return password_on_focus_dict ? password_on_focus_dict->size() : 0;
+  } else {
+    return cache_dictionary->HasKey(base::StringPiece(kPasswordOnFocusCacheKey))
+               ? cache_dictionary->size() - 1
+               : cache_dictionary->size();
+  }
+}
+
+bool PasswordProtectionService::RemoveExpiredVerdicts(
+    TriggerType trigger_type,
+    base::DictionaryValue* cache_dictionary) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  base::DictionaryValue* verdict_dictionary = nullptr;
+  if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT) {
+    verdict_dictionary = cache_dictionary;
+  } else {
+    if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
+            base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary)) {
+      return false;
+    }
+  }
+
+  if (!verdict_dictionary || verdict_dictionary->empty())
+    return false;
+
+  std::vector<std::string> expired_keys;
+  for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
+       it.Advance()) {
+    if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
+        it.key() == std::string(kPasswordOnFocusCacheKey))
+      continue;
+
+    base::DictionaryValue* verdict_entry = nullptr;
+    CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
+        it.key(), &verdict_entry));
+    int verdict_received_time;
+    LoginReputationClientResponse verdict;
+    CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
+
+    if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
+      // Since DictionaryValue::Iterator cannot be used to modify the
+      // dictionary, we record the keys of expired verdicts in |expired_keys|
+      // and remove them in the next for-loop.
+      expired_keys.push_back(it.key());
+    }
+  }
+
+  for (const std::string& key : expired_keys) {
+    verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
+    if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT)
+      stored_verdict_count_password_entry_--;
+    else
+      stored_verdict_count_password_on_focus_--;
+  }
+
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
+      verdict_dictionary->size() == 0U) {
+    cache_dictionary->RemoveWithoutPathExpansion(
+        base::StringPiece(kPasswordOnFocusCacheKey), nullptr);
+  }
+
+  return expired_keys.size() > 0U;
+}
+
 // static
 bool PasswordProtectionService::ParseVerdictEntry(
     base::DictionaryValue* verdict_entry,
diff --git a/components/safe_browsing/password_protection/password_protection_service.h b/components/safe_browsing/password_protection/password_protection_service.h
index 89870aa..10036f4 100644
--- a/components/safe_browsing/password_protection/password_protection_service.h
+++ b/components/safe_browsing/password_protection/password_protection_service.h
@@ -49,6 +49,7 @@
 // HostContentSettingsMap instance.
 class PasswordProtectionService : public history::HistoryServiceObserver {
  public:
+  using TriggerType = LoginReputationClientRequest::TriggerType;
   // The outcome of the request. These values are used for UMA.
   // DO NOT CHANGE THE ORDERING OF THESE VALUES.
   enum RequestOutcome {
@@ -87,11 +88,13 @@
   // any thread.
   LoginReputationClientResponse::VerdictType GetCachedVerdict(
       const GURL& url,
+      TriggerType trigger_type,
       LoginReputationClientResponse* out_response);
 
-  // Stores |verdict| in |settings| based on |url|, |verdict| and
-  // |receive_time|.
+  // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
+  // |verdict| and |receive_time|.
   virtual void CacheVerdict(const GURL& url,
+                            TriggerType trigger_type,
                             LoginReputationClientResponse* verdict,
                             const base::Time& receive_time);
 
@@ -106,7 +109,7 @@
                     const GURL& password_form_action,
                     const GURL& password_form_frame_url,
                     const std::string& saved_domain,
-                    LoginReputationClientRequest::TriggerType type,
+                    TriggerType trigger_type,
                     bool password_field_exists);
 
   virtual void MaybeStartPasswordFieldOnFocusRequest(
@@ -152,9 +155,9 @@
   // the requests.
   void CancelPendingRequests();
 
-  // Gets the total number of verdict (no matter expired or not) we cached for
-  // current active profile.
-  virtual int GetStoredVerdictCount();
+  // Gets the total number of verdicts of the specified |trigger_type| we cached
+  // for this profile. This counts both expired and active verdicts.
+  virtual int GetStoredVerdictCount(TriggerType trigger_type);
 
   scoped_refptr<net::URLRequestContextGetter> request_context_getter() {
     return request_context_getter_;
@@ -173,9 +176,8 @@
       int event_tab_id,  // -1 if tab id is not available.
       LoginReputationClientRequest::Frame* frame) = 0;
 
-  void FillUserPopulation(
-      const LoginReputationClientRequest::TriggerType& request_type,
-      LoginReputationClientRequest* request_proto);
+  void FillUserPopulation(TriggerType trigger_type,
+                          LoginReputationClientRequest* request_proto);
 
   virtual bool IsExtendedReporting() = 0;
 
@@ -223,6 +225,15 @@
   void RemoveContentSettingsOnURLsDeleted(bool all_history,
                                           const history::URLRows& deleted_rows);
 
+  // Helper function called by RemoveContentSettingsOnURLsDeleted(..). It
+  // calculate the number of verdicts of |type| that associate with |url|.
+  int GetVerdictCountForURL(const GURL& url, TriggerType type);
+
+  // Remove verdict of |type| from |cache_dictionary|. Return false if no
+  // verdict removed, true otherwise.
+  bool RemoveExpiredVerdicts(TriggerType type,
+                             base::DictionaryValue* cache_dictionary);
+
   static bool ParseVerdictEntry(base::DictionaryValue* verdict_entry,
                                 int* out_verdict_received_time,
                                 LoginReputationClientResponse* out_verdict);
@@ -245,8 +256,12 @@
 
   static void RecordNoPingingReason(const base::Feature& feature,
                                     RequestOutcome reason);
-  // Number of verdict stored for this profile.
-  int stored_verdict_count_;
+  // Number of verdict stored for this profile for password on focus pings.
+  int stored_verdict_count_password_on_focus_;
+
+  // Number of verdict stored for this profile for protected password entry
+  // pings.
+  int stored_verdict_count_password_entry_;
 
   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
 
diff --git a/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
index d297ef1..1baee7c 100644
--- a/components/safe_browsing/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
@@ -201,18 +201,19 @@
   }
 
   void CacheVerdict(const GURL& url,
+                    LoginReputationClientRequest::TriggerType trigger,
                     LoginReputationClientResponse::VerdictType verdict,
                     int cache_duration_sec,
                     const std::string& cache_expression,
                     const base::Time& verdict_received_time) {
     LoginReputationClientResponse response(
         CreateVerdictProto(verdict, cache_duration_sec, cache_expression));
-    password_protection_service_->CacheVerdict(url, &response,
+    password_protection_service_->CacheVerdict(url, trigger, &response,
                                                verdict_received_time);
   }
 
-  size_t GetStoredVerdictCount() {
-    return password_protection_service_->GetStoredVerdictCount();
+  size_t GetStoredVerdictCount(LoginReputationClientRequest::TriggerType type) {
+    return password_protection_service_->GetStoredVerdictCount(type);
   }
 
  protected:
@@ -304,80 +305,173 @@
       GURL("http://evil.com/worse/index.html"), cache_expression_with_slash));
 }
 
-TEST_F(PasswordProtectionServiceTest, TestCachedVerdicts) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
+TEST_F(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
   // Assume each verdict has a TTL of 10 minutes.
   // Cache a verdict for http://www.test.com/foo/index.html
   CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foo",
                base::Time::Now());
 
-  EXPECT_EQ(1U, GetStoredVerdictCount());
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
 
   // Cache another verdict with the some origin and cache_expression should
   // override the cache.
   CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/foo",
                base::Time::Now());
-  EXPECT_EQ(1U, GetStoredVerdictCount());
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
   LoginReputationClientResponse out_verdict;
-  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.test.com/foo/index2.html"), &out_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::PHISHING,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://www.test.com/foo/index2.html"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &out_verdict));
 
   // Cache another verdict with the same origin but different cache_expression
   // will not increase setting count, but will increase the number of verdicts
   // in the given origin.
   CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::SAFE, 10 * 60, "test.com/bar",
                base::Time::Now());
-  EXPECT_EQ(2U, GetStoredVerdictCount());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Now cache a UNFAMILIAR_LOGIN_PAGE verdict, stored verdict count for
+  // PASSWORD_REUSE_EVENT should be the same.
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foobar",
+               base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+}
+
+TEST_F(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Assume each verdict has a TTL of 10 minutes.
+  // Cache a verdict for http://www.test.com/foo/index.html
+  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foo",
+               base::Time::Now());
+
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Cache another verdict with the same origin but different cache_expression
+  // will not increase setting count, but will increase the number of verdicts
+  // in the given origin.
+  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/bar",
+               base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Now cache a PASSWORD_REUSE_EVENT verdict, stored verdict count for
+  // UNFAMILIAR_LOGIN_PAGE should be the same.
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foobar",
+               base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestGetCachedVerdicts) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
-  // Prepare 2 verdicts of the same origin with different cache expressions,
-  // one is expired, the other is not.
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  // Prepare 3 verdicts of the same origin with different cache expressions,
+  // one is expired, one is not, the other is of a different type.
   base::Time now = base::Time::Now();
   CacheVerdict(GURL("http://test.com/login.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::SAFE, 10 * 60, "test.com", now);
   CacheVerdict(
       GURL("http://test.com/def/index.jsp"),
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
       LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/def",
       base::Time::FromDoubleT(now.ToDoubleT() -
                               24.0 * 60.0 * 60.0));  // Yesterday, expired.
-  ASSERT_EQ(2U, GetStoredVerdictCount());
+  CacheVerdict(GURL("http://test.com/bar/login.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/bar",
+               now);
+
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 
   // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL with unknown origin.
   LoginReputationClientResponse actual_verdict;
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.unknown.com/"), &actual_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://www.unknown.com/"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
 
   // Return SAFE if look up for a URL that matches "test.com" cache expression.
-  EXPECT_EQ(LoginReputationClientResponse::SAFE,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/xyz/foo.jsp"), &actual_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::SAFE,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://test.com/xyz/foo.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
 
   // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL whose variants match
   // test.com/def, but the corresponding verdict is expired.
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/def/ghi/index.html"), &actual_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://test.com/def/ghi/index.html"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestRemoveCachedVerdictOnURLsDeleted) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
   // Prepare 2 verdicts. One is for origin "http://foo.com", and the other is
   // for "http://bar.com".
   base::Time now = base::Time::Now();
   CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
                "foo.com/abc", now);
   CacheVerdict(GURL("http://bar.com/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 10 * 60, "bar.com",
                now);
-  ASSERT_EQ(2U, GetStoredVerdictCount());
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
+               "foo.com/abc", now);
+  CacheVerdict(GURL("http://bar.com/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::PHISHING, 10 * 60, "bar.com",
+               now);
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 
   // Delete a bar.com URL. Corresponding content setting keyed on
   // origin "http://bar.com" should be removed,
@@ -390,17 +484,31 @@
 
   password_protection_service_->RemoveContentSettingsOnURLsDeleted(
       false /* all_history */, deleted_urls);
-  EXPECT_EQ(1U, GetStoredVerdictCount());
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
   LoginReputationClientResponse actual_verdict;
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://bar.com"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
   EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
             password_protection_service_->GetCachedVerdict(
-                GURL("http://bar.com"), &actual_verdict));
+                GURL("http://bar.com"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                &actual_verdict));
 
   // If delete all history. All password protection content settings should be
   // gone.
   password_protection_service_->RemoveContentSettingsOnURLsDeleted(
       true /* all_history */, history::URLRows());
-  EXPECT_EQ(0U, GetStoredVerdictCount());
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 }
 
 TEST_F(PasswordProtectionServiceTest, VerifyCanGetReputationOfURL) {
@@ -454,8 +562,10 @@
 
 TEST_F(PasswordProtectionServiceTest, TestNoRequestSentIfVerdictAlreadyCached) {
   histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogramName, 0);
-  CacheVerdict(GURL(kTargetUrl), LoginReputationClientResponse::LOW_REPUTATION,
-               600, GURL(kTargetUrl).host(), base::Time::Now());
+  CacheVerdict(GURL(kTargetUrl),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::LOW_REPUTATION, 600,
+               GURL(kTargetUrl).host(), base::Time::Now());
   InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
                                            10000 /* timeout in ms*/);
   base::RunLoop().RunUntilIdle();
@@ -562,42 +672,87 @@
 }
 
 TEST_F(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
-  // Prepare 4 verdicts:
+  // Prepare 4 verdicts for PASSWORD_REUSE_EVENT:
   // (1) "foo.com/abc" valid
   // (2) "foo.com/def" expired
   // (3) "bar.com/abc" expired
   // (4) "bar.com/def" expired
   base::Time now = base::Time::Now();
   CacheVerdict(GURL("https://foo.com/abc/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
                "foo.com/abc", now);
   CacheVerdict(GURL("https://foo.com/def/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::LOW_REPUTATION, 0, "foo.com/def",
                now);
   CacheVerdict(GURL("https://bar.com/abc/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 0, "bar.com/abc", now);
   CacheVerdict(GURL("https://bar.com/def/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 0, "bar.com/def", now);
-  ASSERT_EQ(4U, GetStoredVerdictCount());
+  ASSERT_EQ(4U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Prepare 2 verdicts for UNFAMILIAR_LOGIN_PAGE:
+  // (1) "bar.com/def" valid
+  // (2) "bar.com/xyz" expired
+  CacheVerdict(GURL("https://bar.com/def/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "bar.com/def",
+               now);
+  CacheVerdict(GURL("https://bar.com/xyz/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::PHISHING, 0, "bar.com/xyz", now);
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 
   password_protection_service_->CleanUpExpiredVerdicts();
 
-  ASSERT_EQ(1U, GetStoredVerdictCount());
+  ASSERT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
   LoginReputationClientResponse actual_verdict;
-  // Has cached verdict for foo.com/abc.
-  EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
+  // Has cached PASSWORD_REUSE_EVENT verdict for foo.com/abc.
+  EXPECT_EQ(
+      LoginReputationClientResponse::LOW_REPUTATION,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://foo.com/abc/test.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for foo.com/def.
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://foo.com/def/index.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/abc.
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://bar.com/abc/index.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/def.
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://bar.com/def/index.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+
+  // Has cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/def.
+  EXPECT_EQ(LoginReputationClientResponse::SAFE,
             password_protection_service_->GetCachedVerdict(
-                GURL("https://foo.com/abc/test.jsp"), &actual_verdict));
-  // No cached verdict for foo.com/def.
+                GURL("https://bar.com/def/index.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                &actual_verdict));
+
+  // No cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/xyz.
   EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
             password_protection_service_->GetCachedVerdict(
-                GURL("https://foo.com/def/index.jsp"), &actual_verdict));
-  // Nothing in content setting for bar.com.
-  EXPECT_EQ(nullptr, content_setting_map_->GetWebsiteSetting(
-                         GURL("https://bar.com"), GURL(),
-                         CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
-                         std::string(), nullptr));
+                GURL("https://bar.com/xyz/index.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                &actual_verdict));
 }
 
 TEST_F(PasswordProtectionServiceTest, VerifyPasswordOnFocusRequestProto) {
diff --git a/components/sessions/ios/ios_serialized_navigation_driver.cc b/components/sessions/ios/ios_serialized_navigation_driver.cc
index 24f7987..5c53c21a 100644
--- a/components/sessions/ios/ios_serialized_navigation_driver.cc
+++ b/components/sessions/ios/ios_serialized_navigation_driver.cc
@@ -7,6 +7,7 @@
 #include "base/memory/singleton.h"
 #include "components/sessions/core/serialized_navigation_entry.h"
 #include "ios/web/public/referrer.h"
+#include "ios/web/public/referrer_util.h"
 
 namespace sessions {
 
@@ -53,29 +54,8 @@
       NOTREACHED();
       referrer.policy = web::ReferrerPolicyNever;
     }
-    bool is_downgrade = referrer.url.SchemeIsCryptographic() &&
-                        !navigation->virtual_url_.SchemeIsCryptographic();
-    switch (referrer.policy) {
-      case web::ReferrerPolicyDefault:
-        if (is_downgrade)
-          referrer.url = GURL();
-        break;
-      case web::ReferrerPolicyNoReferrerWhenDowngrade:
-        if (is_downgrade)
-          referrer.url = GURL();
-      case web::ReferrerPolicyAlways:
-        break;
-      case web::ReferrerPolicyNever:
-        referrer.url = GURL();
-        break;
-      case web::ReferrerPolicyOrigin:
-        referrer.url = referrer.url.GetOrigin();
-        break;
-      case web::ReferrerPolicyOriginWhenCrossOrigin:
-        if (navigation->virtual_url_.GetOrigin() != referrer.url.GetOrigin())
-          referrer.url = referrer.url.GetOrigin();
-        break;
-    }
+    referrer.url = GURL(
+        ReferrerHeaderValueForNavigation(navigation->virtual_url_, referrer));
   }
 
   // Reset the referrer if it has changed.
diff --git a/components/tracing/core/proto_zero_message.h b/components/tracing/core/proto_zero_message.h
index 2e6bf4a..ac005ec 100644
--- a/components/tracing/core/proto_zero_message.h
+++ b/components/tracing/core/proto_zero_message.h
@@ -199,7 +199,7 @@
   // the arena are meaningful only for the root message. The static_assert in
   // the .cc file guarantees that the sizeof(nested_messages_arena_) is enough
   // to contain up to kMaxNestingDepth messages.
-  ALIGNAS(sizeof(void*)) uint8_t nested_messages_arena_[512];
+  alignas(sizeof(void*)) uint8_t nested_messages_arena_[512];
 
   // DO NOT add any fields below |nested_messages_arena_|. The memory layout of
   // nested messages would overflow the storage allocated by the root message.
diff --git a/components/translate/DEPS b/components/translate/DEPS
index 6d31570..bae7be4 100644
--- a/components/translate/DEPS
+++ b/components/translate/DEPS
@@ -3,6 +3,7 @@
   "+components/grit/components_resources.h",
   "+components/infobars",
   "+components/language_usage_metrics",
+  "+components/metrics/proto",
   "+components/pref_registry",
   "+components/prefs",
   "+components/strings/grit/components_strings.h",
diff --git a/components/translate/core/browser/mock_translate_client.h b/components/translate/core/browser/mock_translate_client.h
index bf972ebb..0f364e62 100644
--- a/components/translate/core/browser/mock_translate_client.h
+++ b/components/translate/core/browser/mock_translate_client.h
@@ -10,6 +10,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "components/infobars/core/infobar.h"
+#include "components/metrics/proto/translate_event.pb.h"
 #include "components/translate/core/browser/translate_client.h"
 #include "components/translate/core/browser/translate_driver.h"
 #include "components/translate/core/browser/translate_prefs.h"
@@ -36,6 +37,7 @@
 
   MOCK_METHOD0(GetTranslateAcceptLanguages, TranslateAcceptLanguages*());
   MOCK_CONST_METHOD0(GetInfobarIconID, int());
+  MOCK_METHOD1(RecordTranslateEvent, void(const metrics::TranslateEventProto&));
 
 #if !defined(USE_AURA)
   MOCK_CONST_METHOD1(CreateInfoBarMock,
diff --git a/components/translate/core/browser/translate_client.h b/components/translate/core/browser/translate_client.h
index 1225a27..eda29a19 100644
--- a/components/translate/core/browser/translate_client.h
+++ b/components/translate/core/browser/translate_client.h
@@ -18,7 +18,11 @@
 
 namespace infobars {
 class InfoBar;
-}
+}  // namespace infobars
+
+namespace metrics {
+class TranslateEventProto;
+}  // namespace metrics
 
 namespace translate {
 
@@ -50,6 +54,12 @@
   // Returns the resource ID of the icon to be shown for the Translate infobars.
   virtual int GetInfobarIconID() const = 0;
 
+  // Record translate event.
+  // This is for user ID keyed event logging.
+  // This function will take metrics::TranslateEventProto and log the evnet that
+  // we care about.
+  virtual void RecordTranslateEvent(const metrics::TranslateEventProto&) = 0;
+
 #if !defined(USE_AURA)
   // Returns a translate infobar that owns |delegate|.
   virtual std::unique_ptr<infobars::InfoBar> CreateInfoBar(
diff --git a/components/translate/core/browser/translate_infobar_delegate.cc b/components/translate/core/browser/translate_infobar_delegate.cc
index b00e724..cb32c684 100644
--- a/components/translate/core/browser/translate_infobar_delegate.cc
+++ b/components/translate/core/browser/translate_infobar_delegate.cc
@@ -310,6 +310,32 @@
   return prefs_->GetTranslationDeniedCount(original_language_code());
 }
 
+void TranslateInfoBarDelegate::ResetTranslationAcceptedCount() {
+  prefs_->ResetTranslationAcceptedCount(original_language_code());
+}
+
+void TranslateInfoBarDelegate::ResetTranslationDeniedCount() {
+  prefs_->ResetTranslationDeniedCount(original_language_code());
+}
+
+#if defined(OS_ANDROID)
+int TranslateInfoBarDelegate::GetTranslationAutoAlwaysCount() {
+  return prefs_->GetTranslationAutoAlwaysCount(original_language_code());
+}
+
+int TranslateInfoBarDelegate::GetTranslationAutoNeverCount() {
+  return prefs_->GetTranslationAutoNeverCount(original_language_code());
+}
+
+void TranslateInfoBarDelegate::IncrementTranslationAutoAlwaysCount() {
+  prefs_->IncrementTranslationAutoAlwaysCount(original_language_code());
+}
+
+void TranslateInfoBarDelegate::IncrementTranslationAutoNeverCount() {
+  prefs_->IncrementTranslationAutoNeverCount(original_language_code());
+}
+#endif
+
 // static
 void TranslateInfoBarDelegate::GetAfterTranslateStrings(
     std::vector<base::string16>* strings,
diff --git a/components/translate/core/browser/translate_infobar_delegate.h b/components/translate/core/browser/translate_infobar_delegate.h
index 2ded242..7c344de4 100644
--- a/components/translate/core/browser/translate_infobar_delegate.h
+++ b/components/translate/core/browser/translate_infobar_delegate.h
@@ -153,6 +153,17 @@
   int GetTranslationAcceptedCount();
   int GetTranslationDeniedCount();
 
+  void ResetTranslationAcceptedCount();
+  void ResetTranslationDeniedCount();
+
+#if defined(OS_ANDROID)
+  int GetTranslationAutoAlwaysCount();
+  int GetTranslationAutoNeverCount();
+
+  void IncrementTranslationAutoAlwaysCount();
+  void IncrementTranslationAutoNeverCount();
+#endif
+
   // The following methods are called by the infobar that displays the status
   // while translating and also the one displaying the error message.
   base::string16 GetMessageInfoBarText();
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index f38b7a7..fdfc7f75 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -575,6 +575,7 @@
 void TranslateManager::RecordTranslateEvent(int event_type) {
   translate_ranker_->RecordTranslateEvent(
       event_type, translate_driver_->GetVisibleURL(), translate_event_.get());
+  translate_client_->RecordTranslateEvent(*translate_event_.get());
 }
 
 bool TranslateManager::ShouldOverrideDecision(int event_type) {
diff --git a/components/translate/core/browser/translate_prefs.cc b/components/translate/core/browser/translate_prefs.cc
index 0d13a312..ab49ae55 100644
--- a/components/translate/core/browser/translate_prefs.cc
+++ b/components/translate/core/browser/translate_prefs.cc
@@ -45,6 +45,13 @@
 const char kAlwaysTranslateOfferThreshold[] =
     "always_translate_offer_threshold";
 
+#if defined(OS_ANDROID)
+const char TranslatePrefs::kPrefTranslateAutoAlwaysCount[] =
+    "translate_auto_always_count";
+const char TranslatePrefs::kPrefTranslateAutoNeverCount[] =
+    "translate_auto_never_count";
+#endif
+
 // For reading ULP prefs.
 const char kConfidence[] = "confidence";
 const char kLanguage[] = "language";
@@ -189,6 +196,10 @@
     ResetTranslationDeniedCount(language);
     ResetTranslationIgnoredCount(language);
   }
+#if defined(OS_ANDROID)
+  prefs_->ClearPref(kPrefTranslateAutoAlwaysCount);
+  prefs_->ClearPref(kPrefTranslateAutoNeverCount);
+#endif
 
   prefs_->ClearPref(kPrefTranslateLastDeniedTimeForLanguage);
   prefs_->ClearPref(kPrefTranslateTooOftenDeniedForLanguage);
@@ -362,6 +373,54 @@
   update.Get()->SetInteger(language, 0);
 }
 
+#if defined(OS_ANDROID)
+int TranslatePrefs::GetTranslationAutoAlwaysCount(
+    const std::string& language) const {
+  const base::DictionaryValue* dict =
+      prefs_->GetDictionary(kPrefTranslateAutoAlwaysCount);
+  int count = 0;
+  return dict->GetInteger(language, &count) ? count : 0;
+}
+
+void TranslatePrefs::IncrementTranslationAutoAlwaysCount(
+    const std::string& language) {
+  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoAlwaysCount);
+  base::DictionaryValue* dict = update.Get();
+  int count = 0;
+  dict->GetInteger(language, &count);
+  dict->SetInteger(language, count + 1);
+}
+
+void TranslatePrefs::ResetTranslationAutoAlwaysCount(
+    const std::string& language) {
+  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoAlwaysCount);
+  update.Get()->SetInteger(language, 0);
+}
+
+int TranslatePrefs::GetTranslationAutoNeverCount(
+    const std::string& language) const {
+  const base::DictionaryValue* dict =
+      prefs_->GetDictionary(kPrefTranslateAutoNeverCount);
+  int count = 0;
+  return dict->GetInteger(language, &count) ? count : 0;
+}
+
+void TranslatePrefs::IncrementTranslationAutoNeverCount(
+    const std::string& language) {
+  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoNeverCount);
+  base::DictionaryValue* dict = update.Get();
+  int count = 0;
+  dict->GetInteger(language, &count);
+  dict->SetInteger(language, count + 1);
+}
+
+void TranslatePrefs::ResetTranslationAutoNeverCount(
+    const std::string& language) {
+  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoNeverCount);
+  update.Get()->SetInteger(language, 0);
+}
+#endif  // defined(OS_ANDROID)
+
 void TranslatePrefs::UpdateLastDeniedTime(const std::string& language) {
   if (IsTooOftenDenied(language))
     return;
@@ -496,6 +555,14 @@
   registry->RegisterDictionaryPref(
       kPrefLanguageProfile,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
+#if defined(OS_ANDROID)
+  registry->RegisterDictionaryPref(
+      kPrefTranslateAutoAlwaysCount,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterDictionaryPref(
+      kPrefTranslateAutoNeverCount,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+#endif
 }
 
 // static
diff --git a/components/translate/core/browser/translate_prefs.h b/components/translate/core/browser/translate_prefs.h
index 61a35d8..9582425 100644
--- a/components/translate/core/browser/translate_prefs.h
+++ b/components/translate/core/browser/translate_prefs.h
@@ -87,6 +87,10 @@
   static const char kPrefTranslateBlockedLanguages[];
   static const char kPrefTranslateLastDeniedTimeForLanguage[];
   static const char kPrefTranslateTooOftenDeniedForLanguage[];
+#if defined(OS_ANDROID)
+  static const char kPrefTranslateAutoAlwaysCount[];
+  static const char kPrefTranslateAutoNeverCount[];
+#endif
 
   // |preferred_languages_pref| is only used on Chrome OS, other platforms must
   // pass NULL.
@@ -149,6 +153,20 @@
   void IncrementTranslationAcceptedCount(const std::string& language);
   void ResetTranslationAcceptedCount(const std::string& language);
 
+#if defined(OS_ANDROID)
+  // These methods are used to track how many times the auto-always translation
+  // has been triggered for a specific language.
+  int GetTranslationAutoAlwaysCount(const std::string& language) const;
+  void IncrementTranslationAutoAlwaysCount(const std::string& language);
+  void ResetTranslationAutoAlwaysCount(const std::string& language);
+
+  // These methods are used to track how many times the auto-never translation
+  // has been triggered for a specific language.
+  int GetTranslationAutoNeverCount(const std::string& language) const;
+  void IncrementTranslationAutoNeverCount(const std::string& language);
+  void ResetTranslationAutoNeverCount(const std::string& language);
+#endif
+
   // Update the last time on closing the Translate UI without translation.
   void UpdateLastDeniedTime(const std::string& language);
 
diff --git a/components/translate/core/browser/translate_ui_delegate_unittest.cc b/components/translate/core/browser/translate_ui_delegate_unittest.cc
index 2f33464..679ee011 100644
--- a/components/translate/core/browser/translate_ui_delegate_unittest.cc
+++ b/components/translate/core/browser/translate_ui_delegate_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "components/infobars/core/infobar.h"
+#include "components/metrics/proto/translate_event.pb.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/translate/core/browser/mock_translate_client.h"
diff --git a/components/translate/core/common/BUILD.gn b/components/translate/core/common/BUILD.gn
index 5aca8ab..11cffc67 100644
--- a/components/translate/core/common/BUILD.gn
+++ b/components/translate/core/common/BUILD.gn
@@ -17,10 +17,13 @@
     "translate_switches.h",
     "translate_util.cc",
     "translate_util.h",
+    "translation_logging_helper.cc",
+    "translation_logging_helper.h",
   ]
 
   deps = [
     "//base",
+    "//components/metrics/proto",
     "//components/sync/protocol",
     "//url",
   ]
@@ -32,10 +35,12 @@
     "language_detection_logging_helper_unittest.cc",
     "translate_metrics_unittest.cc",
     "translate_util_unittest.cc",
+    "translation_logging_helper_unittest.cc",
   ]
   deps = [
     ":common",
     "//base",
+    "//components/metrics/proto",
     "//components/sync/protocol",
     "//testing/gtest",
     "//url",
diff --git a/components/translate/core/common/translation_logging_helper.cc b/components/translate/core/common/translation_logging_helper.cc
new file mode 100644
index 0000000..ac6c0d2
--- /dev/null
+++ b/components/translate/core/common/translation_logging_helper.cc
@@ -0,0 +1,71 @@
+// 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/translate/core/common/translation_logging_helper.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "components/metrics/proto/translate_event.pb.h"
+#include "components/sync/protocol/user_event_specifics.pb.h"
+
+namespace translate {
+namespace {
+using metrics::TranslateEventProto;
+}  // namespace
+
+bool ConstructTranslateEvent(const int64_t navigation_id,
+                             const TranslateEventProto& translate_event,
+                             sync_pb::UserEventSpecifics* const specifics) {
+  specifics->set_event_time_usec(base::Time::Now().ToInternalValue());
+
+  // TODO(renjieliu): Revisit this field when the best way to identify
+  // navigations is determined.
+  specifics->set_navigation_id(navigation_id);
+  auto* const translation = specifics->mutable_translation();
+  translation->set_from_language_code(translate_event.source_language());
+  translation->set_to_language_code(translate_event.target_language());
+  switch (translate_event.event_type()) {
+    case TranslateEventProto::UNKNOWN:
+      translation->set_interaction(sync_pb::Translation::UNKNOWN);
+      break;
+    case TranslateEventProto::USER_ACCEPT:
+      if (translate_event.has_modified_source_language() ||
+          translate_event.has_modified_target_language()) {
+        // Special case, since we don't have event enum telling us it's actually
+        // modified by user, we do this by check whether this event has modified
+        // source or target language.
+        if (translate_event.has_modified_source_language()) {
+          translation->set_from_language_code(
+              translate_event.modified_source_language());
+        }
+        if (translate_event.has_modified_target_language()) {
+          translation->set_to_language_code(
+              translate_event.modified_target_language());
+        }
+        translation->set_interaction(sync_pb::Translation::MANUAL);
+      } else {
+        translation->set_interaction(sync_pb::Translation::ACCEPT);
+      }
+      break;
+    case TranslateEventProto::USER_DECLINE:
+      translation->set_interaction(sync_pb::Translation::DECLINE);
+      break;
+    case TranslateEventProto::USER_IGNORE:
+      translation->set_interaction(sync_pb::Translation::IGNORED);
+      break;
+    case TranslateEventProto::USER_DISMISS:
+      translation->set_interaction(sync_pb::Translation::DISMISSED);
+      break;
+    case TranslateEventProto::USER_REVERT:
+      translation->set_interaction(sync_pb::Translation::TRANSLATION_REVERTED);
+      break;
+    case TranslateEventProto::AUTOMATICALLY_TRANSLATED:
+      translation->set_interaction(sync_pb::Translation::AUTOMATIC_TRANSLATION);
+      break;
+    default:  // We don't care about other events.
+      return false;
+  }
+  return true;
+}
+}  // namespace translate
\ No newline at end of file
diff --git a/components/translate/core/common/translation_logging_helper.h b/components/translate/core/common/translation_logging_helper.h
new file mode 100644
index 0000000..1e6e16a
--- /dev/null
+++ b/components/translate/core/common/translation_logging_helper.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_TRANSLATE_CORE_COMMON_TRANSLATION_LOGGING_HELPER_H_
+#define COMPONENTS_TRANSLATE_CORE_COMMON_TRANSLATION_LOGGING_HELPER_H_
+
+#include <stdint.h>
+
+namespace metrics {
+class TranslateEventProto;
+}  // namespace metrics
+
+namespace sync_pb {
+class UserEventSpecifics;
+}  // namespace sync_pb
+
+namespace translate {
+
+// Construct the sync_pb::Translation proto.
+// If the event is the event that we care about, will return true, otherwise,
+// will return false.
+bool ConstructTranslateEvent(
+    int64_t navigation_id,
+    const metrics::TranslateEventProto& translate_event,
+    sync_pb::UserEventSpecifics* const specifics);
+}  // namespace translate
+
+#endif  // COMPONENTS_TRANSLATE_CORE_COMMON_TRANSLATION_LOGGING_HELPER_H_
\ No newline at end of file
diff --git a/components/translate/core/common/translation_logging_helper_unittest.cc b/components/translate/core/common/translation_logging_helper_unittest.cc
new file mode 100644
index 0000000..d25247b
--- /dev/null
+++ b/components/translate/core/common/translation_logging_helper_unittest.cc
@@ -0,0 +1,87 @@
+// 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/translate/core/common/translation_logging_helper.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "components/metrics/proto/translate_event.pb.h"
+#include "components/sync/protocol/user_event_specifics.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using metrics::TranslateEventProto;
+using sync_pb::Translation;
+
+void EqualTranslationProto(const Translation& first,
+                           const Translation& second) {
+  EXPECT_EQ(first.from_language_code(), second.from_language_code());
+  EXPECT_EQ(first.to_language_code(), second.to_language_code());
+  EXPECT_EQ(first.interaction(), second.interaction());
+}
+
+namespace translate {
+
+// Tests that UserEventSpecifics is correctly built.
+TEST(TranslationLoggingHelperTest, ConstructUserEventSpecifics) {
+  // The event we have.
+  TranslateEventProto translation_event;
+  translation_event.set_source_language("ja");
+  translation_event.set_target_language("en");
+  translation_event.set_event_type(TranslateEventProto::USER_DECLINE);
+  // Expected user_event.
+  Translation user_translation_event;
+  user_translation_event.set_from_language_code("ja");
+  user_translation_event.set_to_language_code("en");
+  user_translation_event.set_interaction(Translation::DECLINE);
+  // The user event.
+  sync_pb::UserEventSpecifics user_specifics;
+  const int64_t navigation_id = 1000000000000000LL;
+  const bool needs_logging = ConstructTranslateEvent(
+      navigation_id, translation_event, &user_specifics);
+  EXPECT_TRUE(needs_logging);
+  EXPECT_EQ(user_specifics.navigation_id(), navigation_id);
+  EqualTranslationProto(user_translation_event, user_specifics.translation());
+}
+
+// Tests that if user change the target language, the event is MANUAL.
+TEST(TranslationLoggingHelperTest, UserManualEvent) {
+  // The event we have.
+  TranslateEventProto translation_event;
+  translation_event.set_source_language("ja");
+  translation_event.set_target_language("en");
+  translation_event.set_modified_target_language("fr");
+  translation_event.set_event_type(TranslateEventProto::USER_ACCEPT);
+  // Expected user_event.
+  Translation user_translation_event;
+  user_translation_event.set_from_language_code("ja");
+  user_translation_event.set_to_language_code("fr");
+  user_translation_event.set_interaction(Translation::MANUAL);
+  // The user event.
+  sync_pb::UserEventSpecifics user_specifics;
+  const int64_t navigation_id = 100;
+  const bool needs_logging = ConstructTranslateEvent(
+      navigation_id, translation_event, &user_specifics);
+  EXPECT_TRUE(needs_logging);
+  EXPECT_EQ(user_specifics.navigation_id(), navigation_id);
+  EqualTranslationProto(user_translation_event, user_specifics.translation());
+}
+
+// Tests that we don't build unnecessary events.
+TEST(TranslationLoggingHelperTest, DontBuildUnnecessaryEvent) {
+  // The event we have.
+  TranslateEventProto translation_event;
+  translation_event.set_source_language("ja");
+  translation_event.set_target_language("en");
+  // The event we don't care.
+  translation_event.set_event_type(TranslateEventProto::DISABLED_BY_RANKER);
+  // The user event.
+  sync_pb::UserEventSpecifics user_specifics;
+  const bool needs_logging =
+      ConstructTranslateEvent(100, translation_event, &user_specifics);
+  // We don't expect the event to be logged.
+  EXPECT_FALSE(needs_logging);
+}
+
+}  // namespace translate
\ No newline at end of file
diff --git a/components/variations/android/variations_associated_data_android.cc b/components/variations/android/variations_associated_data_android.cc
index 1dd8096..ebbb0a7 100644
--- a/components/variations/android/variations_associated_data_android.cc
+++ b/components/variations/android/variations_associated_data_android.cc
@@ -17,7 +17,6 @@
 using base::android::ScopedJavaLocalRef;
 
 namespace variations {
-
 namespace android {
 
 ScopedJavaLocalRef<jstring> GetVariationParamValue(
@@ -45,5 +44,4 @@
 }
 
 }  // namespace android
-
 }  // namespace variations
diff --git a/components/variations/experiment_labels.cc b/components/variations/experiment_labels.cc
index c7cce95..1b0d85a 100644
--- a/components/variations/experiment_labels.cc
+++ b/components/variations/experiment_labels.cc
@@ -15,7 +15,6 @@
 #include "components/variations/variations_experiment_util.h"
 
 namespace variations {
-
 namespace {
 
 const char kVariationPrefix[] = "CrVar";
@@ -25,7 +24,7 @@
 base::string16 ExtractNonVariationLabels(const base::string16& labels) {
   // First, split everything by the label separator.
   std::vector<base::StringPiece16> entries = base::SplitStringPiece(
-      labels, base::StringPiece16(&variations::kExperimentLabelSeparator, 1),
+      labels, base::StringPiece16(&kExperimentLabelSeparator, 1),
       base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
 
   // For each label, keep the ones that do not look like a Variations label.
@@ -40,7 +39,7 @@
 
     // Dump the whole thing, including the timestamp.
     if (!non_variation_labels.empty())
-      non_variation_labels += variations::kExperimentLabelSeparator;
+      non_variation_labels += kExperimentLabelSeparator;
     entry.AppendToString(&non_variation_labels);
   }
 
diff --git a/components/variations/field_trial_config/field_trial_util.cc b/components/variations/field_trial_config/field_trial_util.cc
index 76299151..b77180dab 100644
--- a/components/variations/field_trial_config/field_trial_util.cc
+++ b/components/variations/field_trial_config/field_trial_util.cc
@@ -19,7 +19,6 @@
 #include "net/base/escape.h"
 
 namespace variations {
-
 namespace {
 
 std::string EscapeValue(const std::string& value) {
@@ -38,7 +37,7 @@
       const FieldTrialTestingExperimentParams& param = experiment.params[i];
       params[param.key] = param.value;
     }
-    variations::AssociateVariationParams(study_name, experiment.name, params);
+    AssociateVariationParams(study_name, experiment.name, params);
   }
   base::FieldTrial* trial =
       base::FieldTrialList::CreateFieldTrial(study_name, experiment.name);
@@ -126,7 +125,7 @@
       std::string value = EscapeValue(key_values[i + 1]);
       params[key] = value;
     }
-    variations::AssociateVariationParams(trial, group, params);
+    AssociateVariationParams(trial, group, params);
   }
   return true;
 }
diff --git a/components/variations/field_trial_config/field_trial_util_unittest.cc b/components/variations/field_trial_config/field_trial_util_unittest.cc
index 2101280ef..00fc288 100644
--- a/components/variations/field_trial_config/field_trial_util_unittest.cc
+++ b/components/variations/field_trial_config/field_trial_util_unittest.cc
@@ -16,7 +16,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace variations {
-
 namespace {
 
 class FieldTrialUtilTest : public ::testing::Test {
@@ -26,8 +25,8 @@
   ~FieldTrialUtilTest() override {
     // Ensure that the maps are cleared between tests, since they are stored as
     // process singletons.
-    variations::testing::ClearAllVariationIDs();
-    variations::testing::ClearAllVariationParams();
+    testing::ClearAllVariationIDs();
+    testing::ClearAllVariationParams();
   }
 
  private:
@@ -45,12 +44,12 @@
   ASSERT_TRUE(AssociateParamsFromString(kVariationsString));
 
   base::FieldTrialList::CreateFieldTrial(kTrialName, "B");
-  EXPECT_EQ("/", variations::GetVariationParamValue(kTrialName, "a"));
-  EXPECT_EQ(std::string(), variations::GetVariationParamValue(kTrialName, "b"));
-  EXPECT_EQ(std::string(), variations::GetVariationParamValue(kTrialName, "x"));
+  EXPECT_EQ("/", GetVariationParamValue(kTrialName, "a"));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
 
   std::map<std::string, std::string> params;
-  EXPECT_TRUE(variations::GetVariationParams(kTrialName, &params));
+  EXPECT_TRUE(GetVariationParams(kTrialName, &params));
   EXPECT_EQ(1U, params.size());
   EXPECT_EQ("/", params["a"]);
 }
@@ -88,11 +87,11 @@
   base::FeatureList feature_list;
   AssociateParamsFromFieldTrialConfig(kConfig, &feature_list);
 
-  EXPECT_EQ("1", variations::GetVariationParamValue("TestTrial1", "x"));
-  EXPECT_EQ("2", variations::GetVariationParamValue("TestTrial1", "y"));
+  EXPECT_EQ("1", GetVariationParamValue("TestTrial1", "x"));
+  EXPECT_EQ("2", GetVariationParamValue("TestTrial1", "y"));
 
   std::map<std::string, std::string> params;
-  EXPECT_TRUE(variations::GetVariationParams("TestTrial1", &params));
+  EXPECT_TRUE(GetVariationParams("TestTrial1", &params));
   EXPECT_EQ(2U, params.size());
   EXPECT_EQ("1", params["x"]);
   EXPECT_EQ("2", params["y"]);
diff --git a/components/variations/service/ui_string_overrider_unittest.cc b/components/variations/service/ui_string_overrider_unittest.cc
index 0f4ab52..3ceef785 100644
--- a/components/variations/service/ui_string_overrider_unittest.cc
+++ b/components/variations/service/ui_string_overrider_unittest.cc
@@ -10,8 +10,7 @@
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chrome_variations {
-
+namespace variations {
 namespace {
 
 const size_t kNumResources = 4;
@@ -42,7 +41,7 @@
   }
 
  private:
-  variations::UIStringOverrider provider_;
+  UIStringOverrider provider_;
 
   DISALLOW_COPY_AND_ASSIGN(UIStringOverriderTest);
 };
@@ -61,4 +60,4 @@
     EXPECT_EQ(kResourceIndices[i], GetResourceIndex(kResourceHashes[i]));
 }
 
-}  // namespace chrome_variations
+}  // namespace variations
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index fb594ac..ab3b89e 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -50,7 +50,6 @@
 #include "url/gurl.h"
 
 namespace variations {
-
 namespace {
 
 // TODO(mad): To be removed when we stop updating the NetworkTimeTracker.
@@ -65,17 +64,16 @@
 // that channel value. Otherwise, if the fake channel flag is provided, this
 // will return the fake channel. Failing that, this will return the UNKNOWN
 // channel.
-variations::Study_Channel GetChannelForVariations(
-    version_info::Channel product_channel) {
+Study::Channel GetChannelForVariations(version_info::Channel product_channel) {
   switch (product_channel) {
     case version_info::Channel::CANARY:
-      return variations::Study_Channel_CANARY;
+      return Study::CANARY;
     case version_info::Channel::DEV:
-      return variations::Study_Channel_DEV;
+      return Study::DEV;
     case version_info::Channel::BETA:
-      return variations::Study_Channel_BETA;
+      return Study::BETA;
     case version_info::Channel::STABLE:
-      return variations::Study_Channel_STABLE;
+      return Study::STABLE;
     case version_info::Channel::UNKNOWN:
       break;
   }
@@ -83,15 +81,15 @@
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kFakeVariationsChannel);
   if (forced_channel == "stable")
-    return variations::Study_Channel_STABLE;
+    return Study::STABLE;
   if (forced_channel == "beta")
-    return variations::Study_Channel_BETA;
+    return Study::BETA;
   if (forced_channel == "dev")
-    return variations::Study_Channel_DEV;
+    return Study::DEV;
   if (forced_channel == "canary")
-    return variations::Study_Channel_CANARY;
+    return Study::CANARY;
   DVLOG(1) << "Invalid channel provided: " << forced_channel;
-  return variations::Study_Channel_UNKNOWN;
+  return Study::UNKNOWN;
 }
 
 // Returns a string that will be used for the value of the 'osname' URL param
@@ -178,17 +176,17 @@
 
 // Gets current form factor and converts it from enum DeviceFormFactor to enum
 // Study_FormFactor.
-variations::Study_FormFactor GetCurrentFormFactor() {
+Study::FormFactor GetCurrentFormFactor() {
   switch (ui::GetDeviceFormFactor()) {
     case ui::DEVICE_FORM_FACTOR_PHONE:
-      return variations::Study_FormFactor_PHONE;
+      return Study::PHONE;
     case ui::DEVICE_FORM_FACTOR_TABLET:
-      return variations::Study_FormFactor_TABLET;
+      return Study::TABLET;
     case ui::DEVICE_FORM_FACTOR_DESKTOP:
-      return variations::Study_FormFactor_DESKTOP;
+      return Study::DESKTOP;
   }
   NOTREACHED();
-  return variations::Study_FormFactor_DESKTOP;
+  return Study::DESKTOP;
 }
 
 // Gets the hardware class and returns it as a string. This returns an empty
@@ -307,7 +305,7 @@
 
   create_trials_from_seed_called_ = true;
 
-  variations::VariationsSeed seed;
+  VariationsSeed seed;
   if (!LoadSeed(&seed))
     return false;
 
@@ -344,7 +342,7 @@
   // safe because the callback is executed synchronously. It is not possible
   // to pass UIStringOverrider itself to VariationSeedProcessor as variations
   // components should not depends on //ui/base.
-  variations::VariationsSeedProcessor().CreateTrialsFromSeed(
+  VariationsSeedProcessor().CreateTrialsFromSeed(
       seed, *client_state,
       base::Bind(&UIStringOverrider::OverrideUIString,
                  base::Unretained(&ui_string_overrider_)),
@@ -592,8 +590,7 @@
                                   bool is_gzip_compressed) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  std::unique_ptr<variations::VariationsSeed> seed(
-      new variations::VariationsSeed);
+  std::unique_ptr<VariationsSeed> seed(new VariationsSeed);
   if (!seed_store_.StoreSeedData(seed_data, seed_signature, country_code,
                                  date_fetched, is_delta_compressed,
                                  is_gzip_compressed, seed.get())) {
@@ -656,7 +653,7 @@
 }
 
 void VariationsService::NotifyObservers(
-    const variations::VariationsSeedSimulator::Result& result) {
+    const VariationsSeedSimulator::Result& result) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (result.kill_critical_group_change_count > 0) {
@@ -769,7 +766,7 @@
 }
 
 void VariationsService::PerformSimulationWithVersion(
-    std::unique_ptr<variations::VariationsSeed> seed,
+    std::unique_ptr<VariationsSeed> seed,
     const base::Version& version) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -782,8 +779,7 @@
       state_manager_->CreateDefaultEntropyProvider();
   std::unique_ptr<const base::FieldTrial::EntropyProvider> low_provider =
       state_manager_->CreateLowEntropyProvider();
-  variations::VariationsSeedSimulator seed_simulator(*default_provider,
-                                                     *low_provider);
+  VariationsSeedSimulator seed_simulator(*default_provider, *low_provider);
 
   std::unique_ptr<ClientFilterableState> client_state =
       GetClientFilterableStateForVersion(version);
diff --git a/components/variations/service/variations_service_unittest.cc b/components/variations/service/variations_service_unittest.cc
index 9f3217f..f9d523b 100644
--- a/components/variations/service/variations_service_unittest.cc
+++ b/components/variations/service/variations_service_unittest.cc
@@ -34,7 +34,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace variations {
-
 namespace {
 
 class TestVariationsServiceClient : public VariationsServiceClient {
@@ -42,7 +41,7 @@
   TestVariationsServiceClient() {}
   ~TestVariationsServiceClient() override {}
 
-  // variations::VariationsServiceClient:
+  // VariationsServiceClient:
   std::string GetApplicationLocale() override { return std::string(); }
   base::Callback<base::Version(void)> GetVersionForSimulationCallback()
       override {
@@ -201,12 +200,12 @@
 // study called "test", which contains one experiment called "abc" with
 // probability weight 100. |seed|'s study field will be cleared before adding
 // the new study.
-variations::VariationsSeed CreateTestSeed() {
-  variations::VariationsSeed seed;
-  variations::Study* study = seed.add_study();
+VariationsSeed CreateTestSeed() {
+  VariationsSeed seed;
+  Study* study = seed.add_study();
   study->set_name(kTestSeedStudyName);
   study->set_default_experiment_name(kTestSeedExperimentName);
-  variations::Study_Experiment* experiment = study->add_experiment();
+  Study_Experiment* experiment = study->add_experiment();
   experiment->set_name(kTestSeedExperimentName);
   experiment->set_probability_weight(kTestSeedExperimentProbability);
   seed.set_serial_number(kTestSeedSerialNumber);
@@ -214,7 +213,7 @@
 }
 
 // Serializes |seed| to protobuf binary format.
-std::string SerializeSeed(const variations::VariationsSeed& seed) {
+std::string SerializeSeed(const VariationsSeed& seed) {
   std::string serialized_seed;
   seed.SerializeToString(&serialized_seed);
   return serialized_seed;
@@ -607,7 +606,7 @@
     TestVariationsServiceObserver observer;
     service.AddObserver(&observer);
 
-    variations::VariationsSeedSimulator::Result result;
+    VariationsSeedSimulator::Result result;
     result.normal_group_change_count = cases[i].normal_count;
     result.kill_best_effort_group_change_count = cases[i].best_effort_count;
     result.kill_critical_group_change_count = cases[i].critical_count;
@@ -693,7 +692,7 @@
       prefs.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
     }
 
-    variations::VariationsSeed seed(CreateTestSeed());
+    VariationsSeed seed(CreateTestSeed());
     std::string latest_country;
     if (test.latest_country_code)
       latest_country = test.latest_country_code;
@@ -764,7 +763,7 @@
       prefs.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
     }
 
-    variations::VariationsSeed seed(CreateTestSeed());
+    VariationsSeed seed(CreateTestSeed());
 
     EXPECT_EQ(test.has_updated, service.OverrideStoredPermanentCountry(
                                     test.country_code_override))
diff --git a/components/variations/synthetic_trials_active_group_id_provider.cc b/components/variations/synthetic_trials_active_group_id_provider.cc
index 2d99089..527425f7 100644
--- a/components/variations/synthetic_trials_active_group_id_provider.cc
+++ b/components/variations/synthetic_trials_active_group_id_provider.cc
@@ -29,7 +29,7 @@
   }
   // Update the experiments lists for crash reports to include the newly added
   // group.
-  variations::SetVariationListCrashKeys();
+  SetVariationListCrashKeys();
 }
 
 void SyntheticTrialsActiveGroupIdProvider::GetActiveGroupIds(
diff --git a/components/variations/variations_params_manager.cc b/components/variations/variations_params_manager.cc
index 18ebdee..27204b0c 100644
--- a/components/variations/variations_params_manager.cc
+++ b/components/variations/variations_params_manager.cc
@@ -14,7 +14,6 @@
 
 namespace variations {
 namespace testing {
-
 namespace {
 
 // The fixed testing group created in the provided trail when setting up params.
@@ -23,7 +22,7 @@
 base::FieldTrial* CreateFieldTrialWithParams(
     const std::string& trial_name,
     const std::map<std::string, std::string>& param_values) {
-  variations::AssociateVariationParams(trial_name, kGroupTesting, param_values);
+  AssociateVariationParams(trial_name, kGroupTesting, param_values);
   return base::FieldTrialList::CreateFieldTrial(trial_name, kGroupTesting);
 }
 
@@ -79,11 +78,11 @@
 }
 
 void VariationParamsManager::ClearAllVariationIDs() {
-  variations::testing::ClearAllVariationIDs();
+  testing::ClearAllVariationIDs();
 }
 
 void VariationParamsManager::ClearAllVariationParams() {
-  variations::testing::ClearAllVariationParams();
+  testing::ClearAllVariationParams();
   // When the scoped feature list is destroyed, it puts back the original
   // feature list that was there when InitWithFeatureList() was called.
   scoped_feature_list_.reset(new base::test::ScopedFeatureList());
diff --git a/components/variations/variations_request_scheduler.cc b/components/variations/variations_request_scheduler.cc
index dfc39d9c..42c47b6 100644
--- a/components/variations/variations_request_scheduler.cc
+++ b/components/variations/variations_request_scheduler.cc
@@ -47,8 +47,7 @@
 base::TimeDelta VariationsRequestScheduler::GetFetchPeriod() const {
   // The fetch interval can be overridden by a variation param.
   std::string period_min_str =
-      variations::GetVariationParamValue("VariationsServiceControl",
-                                         "fetch_period_min");
+      GetVariationParamValue("VariationsServiceControl", "fetch_period_min");
   size_t period_min;
   if (base::StringToSizeT(period_min_str, &period_min))
     return base::TimeDelta::FromMinutes(period_min);
diff --git a/components/variations/variations_seed_processor_unittest.cc b/components/variations/variations_seed_processor_unittest.cc
index 620c9da5..da4cb4c5 100644
--- a/components/variations/variations_seed_processor_unittest.cc
+++ b/components/variations/variations_seed_processor_unittest.cc
@@ -30,7 +30,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace variations {
-
 namespace {
 
 // Converts |time| to Study proto format.
@@ -938,13 +937,13 @@
   VariationsSeed seed;
   Study* study1 = seed.add_study();
   study1->set_name(kTrial1Name);
-  study1->set_consistency(variations::Study_Consistency_PERMANENT);
+  study1->set_consistency(Study::PERMANENT);
   study1->set_default_experiment_name(kDefaultName);
   AddExperiment(kGroup1Name, 50, study1);
   AddExperiment(kDefaultName, 50, study1);
   Study* study2 = seed.add_study();
   study2->set_name(kTrial2Name);
-  study2->set_consistency(variations::Study_Consistency_PERMANENT);
+  study2->set_consistency(Study::PERMANENT);
   study2->set_default_experiment_name(kDefaultName);
   AddExperiment(kGroup1Name, 50, study2);
   AddExperiment(kDefaultName, 50, study2);
diff --git a/components/variations/variations_seed_store.cc b/components/variations/variations_seed_store.cc
index 9db29888..27382ace 100644
--- a/components/variations/variations_seed_store.cc
+++ b/components/variations/variations_seed_store.cc
@@ -157,7 +157,7 @@
 VariationsSeedStore::~VariationsSeedStore() {
 }
 
-bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
+bool VariationsSeedStore::LoadSeed(VariationsSeed* seed) {
   invalid_base64_signature_.clear();
 
 #if defined(OS_ANDROID)
@@ -203,7 +203,7 @@
     const base::Time& date_fetched,
     bool is_delta_compressed,
     bool is_gzip_compressed,
-    variations::VariationsSeed* parsed_seed) {
+    VariationsSeed* parsed_seed) {
   // If the data is gzip compressed, first uncompress it.
   std::string ungzipped_data;
   if (is_gzip_compressed) {
@@ -403,14 +403,14 @@
     const std::string& base64_seed_signature,
     const std::string& country_code,
     const base::Time& date_fetched,
-    variations::VariationsSeed* parsed_seed) {
+    VariationsSeed* parsed_seed) {
   if (seed_data.empty()) {
     RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS);
     return false;
   }
 
   // Only store the seed data if it parses correctly.
-  variations::VariationsSeed seed;
+  VariationsSeed seed;
   if (!seed.ParseFromString(seed_data)) {
     RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE);
     return false;
diff --git a/components/variations/variations_seed_store.h b/components/variations/variations_seed_store.h
index 53a3654..8b24d7b9 100644
--- a/components/variations/variations_seed_store.h
+++ b/components/variations/variations_seed_store.h
@@ -17,10 +17,8 @@
 class PrefRegistrySimple;
 
 namespace variations {
-class VariationsSeed;
-}
 
-namespace variations {
+class VariationsSeed;
 
 // VariationsSeedStore is a helper class for reading and writing the variations
 // seed from Local State.
@@ -32,7 +30,7 @@
   // Loads the variations seed data from local state into |seed|. If there is a
   // problem with loading, the pref value is cleared and false is returned. If
   // successful, |seed| will contain the loaded data and true is returned.
-  bool LoadSeed(variations::VariationsSeed* seed);
+  bool LoadSeed(VariationsSeed* seed);
 
   // Stores the given seed |data| (serialized protobuf) to local state, along
   // with a base64-encoded digital signature for seed and the date when it was
@@ -51,7 +49,7 @@
                      const base::Time& date_fetched,
                      bool is_delta_compressed,
                      bool is_gzip_compressed,
-                     variations::VariationsSeed* parsed_seed);
+                     VariationsSeed* parsed_seed);
 
   // Updates |kVariationsSeedDate| and logs when previous date was from a
   // different day.
@@ -111,12 +109,11 @@
 
   // Internal version of |StoreSeedData()| that assumes |seed_data| is not delta
   // compressed.
-  bool StoreSeedDataNoDelta(
-      const std::string& seed_data,
-      const std::string& base64_seed_signature,
-      const std::string& country_code,
-      const base::Time& date_fetched,
-      variations::VariationsSeed* parsed_seed);
+  bool StoreSeedDataNoDelta(const std::string& seed_data,
+                            const std::string& base64_seed_signature,
+                            const std::string& country_code,
+                            const base::Time& date_fetched,
+                            VariationsSeed* parsed_seed);
 
   // Applies a delta-compressed |patch| to |existing_data|, producing the result
   // in |output|. Returns whether the operation was successful.
diff --git a/components/variations/variations_seed_store_unittest.cc b/components/variations/variations_seed_store_unittest.cc
index 767afa2..a40ecdbe 100644
--- a/components/variations/variations_seed_store_unittest.cc
+++ b/components/variations/variations_seed_store_unittest.cc
@@ -48,12 +48,12 @@
 // study called "test", which contains one experiment called "abc" with
 // probability weight 100. |seed|'s study field will be cleared before adding
 // the new study.
-variations::VariationsSeed CreateTestSeed() {
-  variations::VariationsSeed seed;
-  variations::Study* study = seed.add_study();
+VariationsSeed CreateTestSeed() {
+  VariationsSeed seed;
+  Study* study = seed.add_study();
   study->set_name("test");
   study->set_default_experiment_name("abc");
-  variations::Study_Experiment* experiment = study->add_experiment();
+  Study_Experiment* experiment = study->add_experiment();
   experiment->set_name("abc");
   experiment->set_probability_weight(100);
   seed.set_serial_number("123");
@@ -61,7 +61,7 @@
 }
 
 // Serializes |seed| to protobuf binary format.
-std::string SerializeSeed(const variations::VariationsSeed& seed) {
+std::string SerializeSeed(const VariationsSeed& seed) {
   std::string serialized_seed;
   seed.SerializeToString(&serialized_seed);
   return serialized_seed;
@@ -76,7 +76,7 @@
 }
 
 // Serializes |seed| to compressed base64-encoded protobuf binary format.
-std::string SerializeSeedBase64(const variations::VariationsSeed& seed) {
+std::string SerializeSeedBase64(const VariationsSeed& seed) {
   std::string serialized_seed = SerializeSeed(seed);
   std::string base64_serialized_seed;
   base::Base64Encode(Compress(serialized_seed), &base64_serialized_seed);
@@ -94,7 +94,7 @@
 
 TEST(VariationsSeedStoreTest, LoadSeed) {
   // Store good seed data to test if loading from prefs works.
-  const variations::VariationsSeed seed = CreateTestSeed();
+  const VariationsSeed seed = CreateTestSeed();
   const std::string base64_seed = SerializeSeedBase64(seed);
 
   TestingPrefServiceSimple prefs;
@@ -103,7 +103,7 @@
 
   TestVariationsSeedStore seed_store(&prefs);
 
-  variations::VariationsSeed loaded_seed;
+  VariationsSeed loaded_seed;
   // Check that loading a seed works correctly.
   EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed));
 
@@ -158,7 +158,7 @@
   prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
 
   VariationsSeedStore seed_store(&prefs);
-  variations::VariationsSeed loaded_seed;
+  VariationsSeed loaded_seed;
   seed_store.LoadSeed(&loaded_seed);
   std::string invalid_signature = seed_store.GetInvalidSignature();
   // Valid signature so we get an empty string.
@@ -184,7 +184,7 @@
 }
 
 TEST(VariationsSeedStoreTest, StoreSeedData) {
-  const variations::VariationsSeed seed = CreateTestSeed();
+  const VariationsSeed seed = CreateTestSeed();
   const std::string serialized_seed = SerializeSeed(seed);
 
   TestingPrefServiceSimple prefs;
@@ -211,14 +211,14 @@
 }
 
 TEST(VariationsSeedStoreTest, StoreSeedData_ParsedSeed) {
-  const variations::VariationsSeed seed = CreateTestSeed();
+  const VariationsSeed seed = CreateTestSeed();
   const std::string serialized_seed = SerializeSeed(seed);
 
   TestingPrefServiceSimple prefs;
   VariationsSeedStore::RegisterPrefs(prefs.registry());
   TestVariationsSeedStore seed_store(&prefs);
 
-  variations::VariationsSeed parsed_seed;
+  VariationsSeed parsed_seed;
   EXPECT_TRUE(seed_store.StoreSeedData(serialized_seed, std::string(),
                                        std::string(), base::Time::Now(), false,
                                        false, &parsed_seed));
@@ -245,7 +245,7 @@
 }
 
 TEST(VariationsSeedStoreTest, StoreSeedData_GzippedSeed) {
-  const variations::VariationsSeed seed = CreateTestSeed();
+  const VariationsSeed seed = CreateTestSeed();
   const std::string serialized_seed = SerializeSeed(seed);
   std::string compressed_seed;
   ASSERT_TRUE(compression::GzipCompress(serialized_seed, &compressed_seed));
@@ -254,7 +254,7 @@
   VariationsSeedStore::RegisterPrefs(prefs.registry());
   TestVariationsSeedStore seed_store(&prefs);
 
-  variations::VariationsSeed parsed_seed;
+  VariationsSeed parsed_seed;
   EXPECT_TRUE(seed_store.StoreSeedData(compressed_seed, std::string(),
                                        std::string(), base::Time::Now(), false,
                                        true, &parsed_seed));
@@ -270,7 +270,7 @@
   VariationsSeedStore::RegisterPrefs(prefs.registry());
   TestVariationsSeedStore seed_store(&prefs);
 
-  variations::VariationsSeed parsed_seed;
+  VariationsSeed parsed_seed;
   EXPECT_FALSE(seed_store.StoreSeedData(compressed_seed, std::string(),
                                        std::string(), base::Time::Now(), false,
                                        true, &parsed_seed));
diff --git a/content/browser/blob_storage/blob_url_unittest.cc b/content/browser/blob_storage/blob_url_unittest.cc
index b30a536..93d7cc9 100644
--- a/content/browser/blob_storage/blob_url_unittest.cc
+++ b/content/browser/blob_storage/blob_url_unittest.cc
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -23,7 +22,6 @@
 #include "content/browser/blob_storage/blob_url_loader_factory.h"
 #include "content/browser/loader/test_url_loader_client.h"
 #include "content/browser/url_loader_factory_getter.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/common/data_pipe_utils.h"
 #include "net/base/net_errors.h"
@@ -132,7 +130,7 @@
 
 }  // namespace
 
-class BlobURLRequestJobTest : public testing::Test {
+class BlobURLRequestJobTest : public testing::TestWithParam<bool> {
  public:
   // A simple ProtocolHandler implementation to create BlobURLRequestJob.
   class MockProtocolHandler
@@ -273,8 +271,7 @@
                    const net::HttpRequestHeaders& extra_headers) {
     GURL url("blob:blah");
 
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kEnableNetworkService)) {
+    if (GetParam()) {
       GetHandleFromBuilder();  // To add to StorageContext.
       const_cast<storage::BlobStorageRegistry&>(blob_context_.registry())
           .CreateUrlMapping(url, blob_data_->uuid());
@@ -420,18 +417,18 @@
   std::string expected_response_;
 };
 
-TEST_F(BlobURLRequestJobTest, TestGetSimpleDataRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetSimpleDataRequest) {
   blob_data_->AppendData(kTestData1);
   TestSuccessNonrangeRequest(kTestData1, arraysize(kTestData1) - 1);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetSimpleFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetSimpleFileRequest) {
   blob_data_->AppendFile(temp_file1_, 0, std::numeric_limits<uint64_t>::max(),
                          base::Time());
   TestSuccessNonrangeRequest(kTestFileData1, arraysize(kTestFileData1) - 1);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetLargeFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetLargeFileRequest) {
   base::FilePath large_temp_file =
       temp_dir_.GetPath().AppendASCII("LargeBlob.dat");
   std::string large_data;
@@ -446,7 +443,7 @@
   TestSuccessNonrangeRequest(large_data, large_data.size());
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileRequest) {
   base::FilePath non_existent_file =
       temp_file1_.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
   blob_data_->AppendFile(non_existent_file, 0,
@@ -454,20 +451,20 @@
   TestErrorRequest(404);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetChangedFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetChangedFileRequest) {
   base::Time old_time =
       temp_file_modification_time1_ - base::TimeDelta::FromSeconds(10);
   blob_data_->AppendFile(temp_file1_, 0, 3, old_time);
   TestErrorRequest(404);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetSlicedFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetSlicedFileRequest) {
   blob_data_->AppendFile(temp_file1_, 2, 4, temp_file_modification_time1_);
   std::string result(kTestFileData1 + 2, 4);
   TestSuccessNonrangeRequest(result, 4);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetSimpleFileSystemFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetSimpleFileSystemFileRequest) {
   SetUpFileSystem();
   blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0,
                                    std::numeric_limits<uint64_t>::max(),
@@ -476,7 +473,7 @@
                              arraysize(kTestFileSystemFileData1) - 1);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetLargeFileSystemFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetLargeFileSystemFileRequest) {
   SetUpFileSystem();
   std::string large_data;
   large_data.reserve(kBufferSize * 5);
@@ -492,7 +489,7 @@
   TestSuccessNonrangeRequest(large_data, large_data.size());
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) {
   SetUpFileSystem();
   GURL non_existent_file = GetFileSystemURL("non-existent.dat");
   blob_data_->AppendFileSystemFile(
@@ -500,7 +497,7 @@
   TestErrorRequest(404);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetInvalidFileSystemFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetInvalidFileSystemFileRequest) {
   SetUpFileSystem();
   GURL invalid_file;
   blob_data_->AppendFileSystemFile(
@@ -508,7 +505,7 @@
   TestErrorRequest(500);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) {
   SetUpFileSystem();
   base::Time old_time = temp_file_system_file_modification_time1_ -
                         base::TimeDelta::FromSeconds(10);
@@ -516,7 +513,7 @@
   TestErrorRequest(404);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) {
   SetUpFileSystem();
   blob_data_->AppendFileSystemFile(temp_file_system_file1_, 2, 4,
                                    temp_file_system_file_modification_time1_);
@@ -524,7 +521,7 @@
   TestSuccessNonrangeRequest(result, 4);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetSimpleDiskCacheRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetSimpleDiskCacheRequest) {
   blob_data_->AppendDiskCacheEntry(new EmptyDataHandle(),
                                    disk_cache_entry_.get(),
                                    kTestDiskCacheStreamIndex);
@@ -532,14 +529,14 @@
                              arraysize(kTestDiskCacheData1) - 1);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetComplicatedDataFileAndDiskCacheRequest) {
+TEST_P(BlobURLRequestJobTest, TestGetComplicatedDataFileAndDiskCacheRequest) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
   TestSuccessNonrangeRequest(result, GetTotalBlobLength());
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetRangeRequest1) {
+TEST_P(BlobURLRequestJobTest, TestGetRangeRequest1) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
@@ -560,7 +557,7 @@
   EXPECT_EQ(GetTotalBlobLength(), length);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetRangeRequest2) {
+TEST_P(BlobURLRequestJobTest, TestGetRangeRequest2) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
@@ -582,7 +579,7 @@
   EXPECT_EQ(total, length);
 }
 
-TEST_F(BlobURLRequestJobTest, TestGetRangeRequest3) {
+TEST_P(BlobURLRequestJobTest, TestGetRangeRequest3) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
@@ -603,7 +600,7 @@
   EXPECT_EQ(GetTotalBlobLength(), length);
 }
 
-TEST_F(BlobURLRequestJobTest, TestExtraHeaders) {
+TEST_P(BlobURLRequestJobTest, TestExtraHeaders) {
   blob_data_->set_content_type(kTestContentType);
   blob_data_->set_content_disposition(kTestContentDisposition);
   blob_data_->AppendData(kTestData1);
@@ -622,7 +619,7 @@
   EXPECT_EQ(kTestContentDisposition, content_disposition);
 }
 
-TEST_F(BlobURLRequestJobTest, TestSideData) {
+TEST_P(BlobURLRequestJobTest, TestSideData) {
   disk_cache::ScopedEntryPtr disk_cache_entry_with_side_data =
       CreateDiskCacheEntryWithSideData(disk_cache_backend_.get(),
                                        kTestDiskCacheKey2, kTestDiskCacheData2,
@@ -639,7 +636,7 @@
   EXPECT_EQ(std::string(kTestDiskCacheSideData), response_metadata_);
 }
 
-TEST_F(BlobURLRequestJobTest, TestZeroSizeSideData) {
+TEST_P(BlobURLRequestJobTest, TestZeroSizeSideData) {
   disk_cache::ScopedEntryPtr disk_cache_entry_with_side_data =
       CreateDiskCacheEntryWithSideData(disk_cache_backend_.get(),
                                        kTestDiskCacheKey2, kTestDiskCacheData2,
@@ -656,10 +653,15 @@
   EXPECT_TRUE(response_metadata_.empty());
 }
 
-TEST_F(BlobURLRequestJobTest, BrokenBlob) {
+TEST_P(BlobURLRequestJobTest, BrokenBlob) {
   blob_handle_ = blob_context_.AddBrokenBlob(
       "uuid", "", "", storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
   TestErrorRequest(500);
 }
 
+// The parameter's value determines whether BlobURLLoaderFactory is used.
+INSTANTIATE_TEST_CASE_P(BlobURLRequestJobTest,
+                        BlobURLRequestJobTest,
+                        ::testing::Bool());
+
 }  // namespace content
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 3e44d09..4538cdc7 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -320,7 +320,7 @@
       if (base::CommandLine::ForCurrentProcess()->HasSwitch(
               switches::kReducedReferrerGranularity)) {
         return Network::Request::ReferrerPolicyEnum::
-            NoReferrerWhenDowngradeOriginWhenCrossOrigin;
+            StrictOriginWhenCrossOrigin;
       } else {
         return Network::Request::ReferrerPolicyEnum::NoReferrerWhenDowngrade;
       }
@@ -332,9 +332,12 @@
       return Network::Request::ReferrerPolicyEnum::Origin;
     case blink::kWebReferrerPolicyOriginWhenCrossOrigin:
       return Network::Request::ReferrerPolicyEnum::OriginWhenCrossOrigin;
+    case blink::kWebReferrerPolicySameOrigin:
+      return Network::Request::ReferrerPolicyEnum::SameOrigin;
+    case blink::kWebReferrerPolicyStrictOrigin:
+      return Network::Request::ReferrerPolicyEnum::StrictOrigin;
     case blink::kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin:
-      return Network::Request::ReferrerPolicyEnum::
-          NoReferrerWhenDowngradeOriginWhenCrossOrigin;
+      return Network::Request::ReferrerPolicyEnum::StrictOriginWhenCrossOrigin;
   }
   NOTREACHED();
   return Network::Request::ReferrerPolicyEnum::NoReferrerWhenDowngrade;
@@ -346,11 +349,9 @@
       return Network::Request::ReferrerPolicyEnum::NoReferrerWhenDowngrade;
     case net::URLRequest::
         REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
-      return Network::Request::ReferrerPolicyEnum::
-          NoReferrerWhenDowngradeOriginWhenCrossOrigin;
+      return Network::Request::ReferrerPolicyEnum::StrictOriginWhenCrossOrigin;
     case net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
-      return Network::Request::ReferrerPolicyEnum::
-          NoReferrerWhenDowngradeOriginWhenCrossOrigin;
+      return Network::Request::ReferrerPolicyEnum::OriginWhenCrossOrigin;
     case net::URLRequest::NEVER_CLEAR_REFERRER:
       return Network::Request::ReferrerPolicyEnum::Origin;
     case net::URLRequest::ORIGIN:
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
index bbdaa6c..e4251a11 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -248,6 +248,8 @@
     : connector_(connector ? connector->Clone() : nullptr),
       subdirectory_(subdirectory),
       special_storage_policy_(std::move(special_storage_policy)),
+      memory_dump_id_(base::StringPrintf("LocalStorage/0x%" PRIXPTR,
+                                         reinterpret_cast<uintptr_t>(this))),
       task_runner_(std::move(legacy_task_runner)),
       old_localstorage_path_(old_localstorage_path),
       weak_ptr_factory_(this) {
@@ -362,15 +364,25 @@
     base::trace_event::ProcessMemoryDump* pmd) {
   if (connection_state_ != CONNECTION_FINISHED)
     return true;
-  // TODO(mek): Somehow account for leveldb memory usage.
+
+  std::string context_name =
+      base::StringPrintf("dom_storage/localstorage_0x%" PRIXPTR,
+                         reinterpret_cast<uintptr_t>(this));
+
+  // Account for leveldb memory usage, which actually lives in the file service.
+  auto* global_dump = pmd->CreateSharedGlobalAllocatorDump(memory_dump_id_);
+  // The size of the leveldb dump will be added by the leveldb service.
+  auto* leveldb_mad = pmd->CreateAllocatorDump(context_name + "/leveldb");
+  // Specifies that the current context is responsible for keeping memory alive.
+  int kImportance = 2;
+  pmd->AddOwnershipEdge(leveldb_mad->guid(), global_dump->guid(), kImportance);
+
   if (args.level_of_detail ==
       base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
     size_t total_cache_size = 0;
     for (const auto& it : level_db_wrappers_)
       total_cache_size += it.second->level_db_wrapper()->bytes_used();
-    auto* mad = pmd->CreateAllocatorDump(
-        base::StringPrintf("dom_storage/0x%" PRIXPTR "/cache_size",
-                           reinterpret_cast<uintptr_t>(this)));
+    auto* mad = pmd->CreateAllocatorDump(context_name + "/cache_size");
     mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
                    total_cache_size);
@@ -386,10 +398,10 @@
       if (!std::isalnum(url[index]))
         url[index] = '_';
     }
-    std::string name = base::StringPrintf(
-        "dom_storage/%s/0x%" PRIXPTR, url.c_str(),
+    std::string wrapper_dump_name = base::StringPrintf(
+        "%s/%s/0x%" PRIXPTR, context_name.c_str(), url.c_str(),
         reinterpret_cast<uintptr_t>(it.second->level_db_wrapper()));
-    it.second->level_db_wrapper()->OnMemoryDump(name, pmd);
+    it.second->level_db_wrapper()->OnMemoryDump(wrapper_dump_name, pmd);
   }
   return true;
 }
@@ -453,7 +465,7 @@
     // We were not given a subdirectory. Use a memory backed database.
     connector_->BindInterface(file::mojom::kServiceName, &leveldb_service_);
     leveldb_service_->OpenInMemory(
-        MakeRequest(&database_),
+        memory_dump_id_, MakeRequest(&database_),
         base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
                    weak_ptr_factory_.GetWeakPtr(), true));
   }
@@ -491,7 +503,7 @@
   options->write_buffer_size = 64 * 1024;
   leveldb_service_->OpenWithOptions(
       std::move(options), std::move(directory_clone), "leveldb",
-      MakeRequest(&database_),
+      memory_dump_id_, MakeRequest(&database_),
       base::Bind(&LocalStorageContextMojo::OnDatabaseOpened,
                  weak_ptr_factory_.GetWeakPtr(), false));
 }
diff --git a/content/browser/dom_storage/local_storage_context_mojo.h b/content/browser/dom_storage/local_storage_context_mojo.h
index d91b5c4..7fb9aa44 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.h
+++ b/content/browser/dom_storage/local_storage_context_mojo.h
@@ -143,6 +143,8 @@
   file::mojom::FileSystemPtr file_system_;
   filesystem::mojom::DirectoryPtr directory_;
 
+  base::trace_event::MemoryAllocatorDumpGuid memory_dump_id_;
+
   leveldb::mojom::LevelDBServicePtr leveldb_service_;
   leveldb::mojom::LevelDBDatabaseAssociatedPtr database_;
   bool tried_to_recreate_ = false;
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 73c92cc3..09bcda3 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -55,6 +55,10 @@
 #include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "url/origin.h"
 
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#include "base/nix/xdg_util.h"
+#endif
+
 namespace content {
 namespace {
 
@@ -174,6 +178,13 @@
   }
 };
 
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+base::FilePath GetTemporaryDownloadDirectory() {
+  std::unique_ptr<base::Environment> env(base::Environment::Create());
+  return base::nix::GetXDGDirectory(env.get(), "XDG_DATA_HOME", ".local/share");
+}
+#endif
+
 }  // namespace
 
 DownloadManagerImpl::DownloadManagerImpl(net::NetLog* net_log,
@@ -359,12 +370,20 @@
   }
 
   base::FilePath default_download_directory;
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  // TODO(thomasanderson): Remove this when all Linux distros with
+  // versions of GTK lower than 3.14.7 are no longer supported.  This
+  // should happen when support for Ubuntu Trusty and Debian Jessie
+  // are removed.
+  default_download_directory = GetTemporaryDownloadDirectory();
+#else
   if (delegate_) {
     base::FilePath website_save_directory;  // Unused
     bool skip_dir_check = false;            // Unused
     delegate_->GetSaveDir(GetBrowserContext(), &website_save_directory,
                           &default_download_directory, &skip_dir_check);
   }
+#endif
 
   std::unique_ptr<DownloadFile> download_file;
 
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc
index b7eb3d24..df34c01 100644
--- a/content/browser/download/download_manager_impl_unittest.cc
+++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -547,8 +547,10 @@
   EXPECT_CALL(GetMockDownloadManagerDelegate(), GetNextId(_))
       .WillOnce(RunCallback<0>(local_id));
 
+#if !defined(OS_LINUX) || defined(OS_CHROMEOS)
   // Doing nothing will set the default download directory to null.
   EXPECT_CALL(GetMockDownloadManagerDelegate(), GetSaveDir(_, _, _, _));
+#endif
   EXPECT_CALL(GetMockDownloadManagerDelegate(),
               ApplicationClientIdForFileScanning())
       .WillRepeatedly(Return("client-id"));
diff --git a/content/browser/download/mhtml_generation_manager.cc b/content/browser/download/mhtml_generation_manager.cc
index 001d30d..df825ba 100644
--- a/content/browser/download/mhtml_generation_manager.cc
+++ b/content/browser/download/mhtml_generation_manager.cc
@@ -253,6 +253,7 @@
   ipc_params.mhtml_binary_encoding = params_.use_binary_encoding;
   ipc_params.mhtml_cache_control_policy = params_.cache_control_policy;
   ipc_params.mhtml_popup_overlay_removal = params_.remove_popup_overlay;
+  ipc_params.mhtml_problem_detection = params_.use_page_problem_detectors;
 
   int frame_tree_node_id = pending_frame_tree_node_ids_.front();
   pending_frame_tree_node_ids_.pop();
@@ -608,8 +609,8 @@
 
   base::File browser_file(file_path, file_flags);
   if (!browser_file.IsValid()) {
-    LOG(ERROR) << "Failed to create file to save MHTML at: " <<
-        file_path.value();
+    LOG(ERROR) << "Failed to create file to save MHTML at: "
+               << file_path.value();
   }
   return browser_file;
 }
diff --git a/content/browser/media/media_devices_permission_checker.cc b/content/browser/media/media_devices_permission_checker.cc
index 3501765..03730e8f 100644
--- a/content/browser/media/media_devices_permission_checker.cc
+++ b/content/browser/media/media_devices_permission_checker.cc
@@ -9,12 +9,14 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/common/media/media_devices.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -38,21 +40,36 @@
   RenderFrameHostDelegate* delegate = frame_host->delegate();
   GURL origin = frame_host->GetLastCommittedOrigin().GetURL();
 
-  // Currently, the MEDIA_DEVICE_AUDIO_CAPTURE permission is used for
-  // both audio input and output.
+  MediaDevicesManager::BoolDeviceTypes result;
+  bool audio_permission =
+      delegate->CheckMediaAccessPermission(origin, MEDIA_DEVICE_AUDIO_CAPTURE);
+  bool mic_feature_policy = true;
+  bool camera_feature_policy = true;
+  if (base::FeatureList::IsEnabled(features::kUseFeaturePolicyForPermissions)) {
+    mic_feature_policy = frame_host->IsFeatureEnabled(
+        blink::WebFeaturePolicyFeature::kMicrophone);
+    camera_feature_policy =
+        frame_host->IsFeatureEnabled(blink::WebFeaturePolicyFeature::kCamera);
+  }
+
+  // Speakers.
   // TODO(guidou): use specific permission for audio output when it becomes
   // available. See http://crbug.com/556542.
-  bool has_audio_permission =
-      (requested_device_types[MEDIA_DEVICE_TYPE_AUDIO_INPUT] ||
-       requested_device_types[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) &&
-      delegate->CheckMediaAccessPermission(origin, MEDIA_DEVICE_AUDIO_CAPTURE);
+  result[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
+      requested_device_types[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] &&
+      audio_permission;
 
-  MediaDevicesManager::BoolDeviceTypes result;
-  result[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = has_audio_permission;
-  result[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = has_audio_permission;
+  // Mic.
+  result[MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
+      requested_device_types[MEDIA_DEVICE_TYPE_AUDIO_INPUT] &&
+      audio_permission && mic_feature_policy;
+
+  // Camera.
   result[MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
       requested_device_types[MEDIA_DEVICE_TYPE_VIDEO_INPUT] &&
-      delegate->CheckMediaAccessPermission(origin, MEDIA_DEVICE_VIDEO_CAPTURE);
+      delegate->CheckMediaAccessPermission(origin,
+                                           MEDIA_DEVICE_VIDEO_CAPTURE) &&
+      camera_feature_policy;
 
   return result;
 }
diff --git a/content/browser/media/media_devices_permission_checker_unittest.cc b/content/browser/media/media_devices_permission_checker_unittest.cc
new file mode 100644
index 0000000..08bfde3
--- /dev/null
+++ b/content/browser/media/media_devices_permission_checker_unittest.cc
@@ -0,0 +1,128 @@
+// 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 "content/browser/media/media_devices_permission_checker.h"
+
+#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/media_stream_request.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/test/test_render_view_host.h"
+#include "content/test/test_web_contents.h"
+#include "third_party/WebKit/public/platform/WebFeaturePolicy.h"
+#include "url/origin.h"
+
+namespace content {
+
+namespace {
+
+class TestWebContentsDelegate : public content::WebContentsDelegate {
+ public:
+  ~TestWebContentsDelegate() override {}
+
+  bool CheckMediaAccessPermission(WebContents* web_contents,
+                                  const GURL& security_origin,
+                                  MediaStreamType type) override {
+    return true;
+  }
+};
+
+}  // namespace
+
+class MediaDevicesPermissionCheckerTest : public RenderViewHostImplTestHarness {
+ public:
+  MediaDevicesPermissionCheckerTest()
+      : origin_(GURL("https://www.google.com")),
+        callback_run_(false),
+        callback_result_(false) {}
+
+  void SetUp() override {
+    RenderViewHostImplTestHarness::SetUp();
+    NavigateAndCommit(origin_.GetURL());
+    contents()->SetDelegate(&delegate_);
+  }
+
+ protected:
+  // The header policy should only be set once on page load, so we refresh the
+  // page to simulate that.
+  void RefreshPageAndSetHeaderPolicy(blink::WebFeaturePolicyFeature feature,
+                                     bool enabled) {
+    NavigateAndCommit(origin_.GetURL());
+    std::vector<url::Origin> whitelist;
+    if (enabled)
+      whitelist.push_back(origin_);
+    RenderFrameHostTester::For(main_rfh())
+        ->SimulateFeaturePolicyHeader(feature, whitelist);
+  }
+
+  bool CheckPermission(MediaDeviceType device_type) {
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    checker_.CheckPermission(
+        device_type, main_rfh()->GetProcess()->GetID(),
+        main_rfh()->GetRoutingID(),
+        base::Bind(&MediaDevicesPermissionCheckerTest::CheckPermissionCallback,
+                   base::Unretained(this)));
+    run_loop.Run();
+
+    EXPECT_TRUE(callback_run_);
+    callback_run_ = false;
+    return callback_result_;
+  }
+
+ private:
+  void CheckPermissionCallback(bool result) {
+    callback_run_ = true;
+    callback_result_ = result;
+    quit_closure_.Run();
+  }
+
+  url::Origin origin_;
+
+  base::Closure quit_closure_;
+
+  bool callback_run_;
+  bool callback_result_;
+
+  MediaDevicesPermissionChecker checker_;
+  TestWebContentsDelegate delegate_;
+};
+
+// Basic tests for feature policy checks through the
+// MediaDevicesPermissionChecker.  These tests are not meant to cover every edge
+// case as the FeaturePolicy class itself is tested thoroughly in
+// feature_policy_unittest.cc and in
+// render_frame_host_feature_policy_unittest.cc.
+TEST_F(MediaDevicesPermissionCheckerTest, CheckPermissionWithFeaturePolicy) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kUseFeaturePolicyForPermissions);
+  // Mic and Camera should be enabled by default for a frame (if permission is
+  // granted).
+  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_AUDIO_INPUT));
+  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_VIDEO_INPUT));
+
+  RefreshPageAndSetHeaderPolicy(blink::WebFeaturePolicyFeature::kMicrophone,
+                                /*enabled=*/false);
+  EXPECT_FALSE(CheckPermission(MEDIA_DEVICE_TYPE_AUDIO_INPUT));
+  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_VIDEO_INPUT));
+
+  RefreshPageAndSetHeaderPolicy(blink::WebFeaturePolicyFeature::kCamera,
+                                /*enabled=*/false);
+  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_AUDIO_INPUT));
+  EXPECT_FALSE(CheckPermission(MEDIA_DEVICE_TYPE_VIDEO_INPUT));
+
+  // Ensure that the policy is ignored if kUseFeaturePolicyForPermissions is
+  // disabled.
+  base::test::ScopedFeatureList empty_feature_list;
+  empty_feature_list.Init();
+  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_AUDIO_INPUT));
+  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_VIDEO_INPUT));
+}
+
+}  // namespace
diff --git a/content/browser/renderer_host/media/audio_input_sync_writer_unittest.cc b/content/browser/renderer_host/media/audio_input_sync_writer_unittest.cc
index ba89624..e68c294c 100644
--- a/content/browser/renderer_host/media/audio_input_sync_writer_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_sync_writer_unittest.cc
@@ -32,8 +32,10 @@
 namespace content {
 
 namespace {
+
 // Number of audio buffers in the faked ring buffer.
 const int kSegments = 10;
+
 }  // namespace
 
 // Mocked out sockets used for Send/ReceiveWithTimeout. Counts the number of
diff --git a/content/browser/renderer_host/media/get_user_media_video_capture_browsertest.cc b/content/browser/renderer_host/media/get_user_media_video_capture_browsertest.cc
deleted file mode 100644
index d8c4479..0000000
--- a/content/browser/renderer_host/media/get_user_media_video_capture_browsertest.cc
+++ /dev/null
@@ -1,99 +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 "base/command_line.h"
-#include "build/build_config.h"
-#include "content/browser/webrtc/webrtc_webcam_browsertest.h"
-#include "content/public/browser/browser_child_process_host.h"
-#include "content/public/common/child_process_host.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/service_manager_connection.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "content/shell/browser/shell.h"
-#include "media/base/media_switches.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/video_capture/public/cpp/constants.h"
-#include "services/video_capture/public/interfaces/constants.mojom.h"
-#include "services/video_capture/public/interfaces/testing_controls.mojom.h"
-
-namespace content {
-
-#if defined(OS_ANDROID)
-// Mojo video capture is currently not supported on Android
-// TODO(chfremer): Enable as soon as https://crbug.com/720500 is resolved.
-#define MAYBE_RecoverFromCrashInVideoCaptureProcess \
-  DISABLED_RecoverFromCrashInVideoCaptureProcess
-#else
-#define MAYBE_RecoverFromCrashInVideoCaptureProcess \
-  RecoverFromCrashInVideoCaptureProcess
-#endif  // defined(OS_ANDROID)
-
-namespace {
-
-static const char kVideoCaptureHtmlFile[] = "/media/video_capture_test.html";
-static const char kStartVideoCaptureAndVerifySize[] =
-    "startVideoCaptureAndVerifySize()";
-static const char kWaitForVideoToTurnBlack[] = "waitForVideoToTurnBlack()";
-static const char kVerifyHasReceivedTrackEndedEvent[] =
-    "verifyHasReceivedTrackEndedEvent()";
-
-}  // anonymous namespace
-
-// Integration test that exercises video capture functionality from the
-// JavaScript level.
-class GetUserMediaVideoCaptureBrowserTest : public ContentBrowserTest {
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
-    command_line->AppendSwitchASCII(switches::kEnableFeatures,
-                                    video_capture::kMojoVideoCapture.name);
-    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
-    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        switches::kEnableBlinkFeatures, "GetUserMedia");
-  }
-
-  void SetUp() override {
-    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
-    EnablePixelOutput();
-    ContentBrowserTest::SetUp();
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(GetUserMediaVideoCaptureBrowserTest,
-                       MAYBE_RecoverFromCrashInVideoCaptureProcess) {
-  embedded_test_server()->StartAcceptingConnections();
-  GURL url(embedded_test_server()->GetURL(kVideoCaptureHtmlFile));
-  NavigateToURL(shell(), url);
-
-  std::string result;
-  // Start video capture and wait until it started rendering
-  ASSERT_TRUE(ExecuteScriptAndExtractString(
-      shell(), kStartVideoCaptureAndVerifySize, &result));
-  ASSERT_EQ("OK", result);
-
-  // Simulate crash in video capture process
-  service_manager::Connector* connector =
-      ServiceManagerConnection::GetForProcess()->GetConnector();
-  video_capture::mojom::TestingControlsPtr service_controls;
-  connector->BindInterface(video_capture::mojom::kServiceName,
-                           mojo::MakeRequest(&service_controls));
-  service_controls->Crash();
-
-  // Wait for video element to turn black
-  ASSERT_TRUE(ExecuteScriptAndExtractString(shell(), kWaitForVideoToTurnBlack,
-                                            &result));
-  ASSERT_EQ("OK", result);
-  ASSERT_TRUE(ExecuteScriptAndExtractString(
-      shell(), kVerifyHasReceivedTrackEndedEvent, &result));
-  ASSERT_EQ("OK", result);
-
-  // Start capturing again and expect it to work.
-  ASSERT_TRUE(ExecuteScriptAndExtractString(
-      shell(), kStartVideoCaptureAndVerifySize, &result));
-  ASSERT_EQ("OK", result);
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.cc b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
index 07194411..dc58efa 100644
--- a/content/browser/renderer_host/media/service_launched_video_capture_device.cc
+++ b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
@@ -24,8 +24,7 @@
 void ServiceLaunchedVideoCaptureDevice::GetPhotoState(
     media::VideoCaptureDevice::GetPhotoStateCallback callback) const {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  // Not yet implemented on service side, but is called during
-  // JsLevelVideoCaptureBrowserTest. Do nothing here.
+  NOTIMPLEMENTED();
 }
 
 void ServiceLaunchedVideoCaptureDevice::SetPhotoOptions(
diff --git a/content/child/v8_value_converter_impl.cc b/content/child/v8_value_converter_impl.cc
index 89b3fc7..a786dc2 100644
--- a/content/child/v8_value_converter_impl.cc
+++ b/content/child/v8_value_converter_impl.cc
@@ -175,8 +175,8 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedUniquenessGuard);
 };
 
-V8ValueConverter* V8ValueConverter::create() {
-  return new V8ValueConverterImpl();
+std::unique_ptr<V8ValueConverter> V8ValueConverter::Create() {
+  return base::MakeUnique<V8ValueConverterImpl>();
 }
 
 V8ValueConverterImpl::V8ValueConverterImpl()
diff --git a/content/child/web_url_loader_impl.cc b/content/child/web_url_loader_impl.cc
index 5a38041..ea9668e 100644
--- a/content/child/web_url_loader_impl.cc
+++ b/content/child/web_url_loader_impl.cc
@@ -186,6 +186,10 @@
       return blink::kWebReferrerPolicyAlways;
     case net::URLRequest::ORIGIN:
       return blink::kWebReferrerPolicyOrigin;
+    case net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN:
+      return blink::kWebReferrerPolicySameOrigin;
+    case net::URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+      return blink::kWebReferrerPolicyStrictOrigin;
     case net::URLRequest::NO_REFERRER:
       return blink::kWebReferrerPolicyNever;
     case net::URLRequest::MAX_REFERRER_POLICY:
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 5dfd3b34..082b933 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -520,6 +520,9 @@
   // Whether to remove popup overlay while serializing.
   IPC_STRUCT_MEMBER(bool, mhtml_popup_overlay_removal)
 
+  // Whether to detect problems while serializing.
+  IPC_STRUCT_MEMBER(bool, mhtml_problem_detection)
+
   // Frame to content-id map.
   // Keys are routing ids of either RenderFrames or RenderFrameProxies.
   // Values are MHTML content-ids - see WebFrameSerializer::generateMHTMLParts.
diff --git a/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java b/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java
index dabd1cf..9d170093 100644
--- a/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java
+++ b/content/public/android/java/src/org/chromium/content/app/ChildProcessServiceImpl.java
@@ -156,7 +156,7 @@
             pidCallback.call(Process.myPid());
             mGpuCallback =
                     gpuCallback != null ? IGpuProcessCallback.Stub.asInterface(gpuCallback) : null;
-            getServiceInfo(args);
+            processConnectionBundle(args);
         }
 
         @Override
@@ -233,16 +233,17 @@
                             linker.disableSharedRelros();
                         }
                     }
-                    boolean isLoaded = false;
                     if (CommandLine.getInstance().hasSwitch(
                             BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER)) {
                         android.os.Debug.waitForDebugger();
                     }
 
+                    LibraryLoader libraryLoader = null;
                     boolean loadAtFixedAddressFailed = false;
+                    boolean isLoaded = false;
                     try {
-                        LibraryLoader.get(mLibraryProcessType)
-                                .loadNowOverrideApplicationContext(hostContext);
+                        libraryLoader = LibraryLoader.get(mLibraryProcessType);
+                        libraryLoader.loadNowOverrideApplicationContext(hostContext);
                         isLoaded = true;
                     } catch (ProcessInitException e) {
                         if (requestedSharedRelro) {
@@ -253,11 +254,10 @@
                             Log.e(TAG, "Failed to load native library", e);
                         }
                     }
-                    if (!isLoaded && requestedSharedRelro) {
+                    if (!isLoaded && libraryLoader != null && requestedSharedRelro) {
                         linker.disableSharedRelros();
                         try {
-                            LibraryLoader.get(mLibraryProcessType)
-                                    .loadNowOverrideApplicationContext(hostContext);
+                            libraryLoader.loadNowOverrideApplicationContext(hostContext);
                             isLoaded = true;
                         } catch (ProcessInitException e) {
                             Log.e(TAG, "Failed to load native library on retry", e);
@@ -266,10 +266,9 @@
                     if (!isLoaded) {
                         System.exit(-1);
                     }
-                    LibraryLoader.get(mLibraryProcessType)
-                            .registerRendererProcessHistogram(requestedSharedRelro,
-                                    loadAtFixedAddressFailed);
-                    LibraryLoader.get(mLibraryProcessType).initialize();
+                    libraryLoader.registerRendererProcessHistogram(
+                            requestedSharedRelro, loadAtFixedAddressFailed);
+                    libraryLoader.initialize();
                     synchronized (mLibraryInitializedLock) {
                         mLibraryInitialized = true;
                         mLibraryInitializedLock.notifyAll();
@@ -360,7 +359,7 @@
         return mBinder;
     }
 
-    private void getServiceInfo(Bundle bundle) {
+    private void processConnectionBundle(Bundle bundle) {
         // Required to unparcel FileDescriptorInfo.
         bundle.setClassLoader(mHostClassLoader);
         synchronized (mMainThread) {
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 0326e861..cfedb98a 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -86,7 +86,7 @@
 
         ],
         "resource_coordinator": [ "coordination_unit" ],
-        "video_capture": [ "capture", "tests" ]
+        "video_capture": [ "capture" ]
       }
     },
     "navigation:frame": {
diff --git a/content/public/child/v8_value_converter.h b/content/public/child/v8_value_converter.h
index f53c7c6..56305492 100644
--- a/content/public/child/v8_value_converter.h
+++ b/content/public/child/v8_value_converter.h
@@ -72,7 +72,7 @@
     virtual bool FromV8Undefined(std::unique_ptr<base::Value>* out) const;
   };
 
-  static V8ValueConverter* create();
+  static std::unique_ptr<V8ValueConverter> Create();
 
   virtual ~V8ValueConverter() {}
 
diff --git a/content/public/common/referrer.cc b/content/public/common/referrer.cc
index dbb2ae2..3543e43 100644
--- a/content/public/common/referrer.cc
+++ b/content/public/common/referrer.cc
@@ -61,6 +61,17 @@
       if (request.GetOrigin() != sanitized_referrer.url.GetOrigin())
         sanitized_referrer.url = sanitized_referrer.url.GetOrigin();
       break;
+    case blink::kWebReferrerPolicyStrictOrigin:
+      if (is_downgrade) {
+        sanitized_referrer.url = GURL();
+      } else {
+        sanitized_referrer.url = sanitized_referrer.url.GetOrigin();
+      }
+      break;
+    case blink::kWebReferrerPolicySameOrigin:
+      if (request.GetOrigin() != sanitized_referrer.url.GetOrigin())
+        sanitized_referrer.url = GURL();
+      break;
     case blink::kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin:
       if (is_downgrade) {
         sanitized_referrer.url = GURL();
@@ -101,6 +112,11 @@
           CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
     case blink::kWebReferrerPolicyOriginWhenCrossOrigin:
       return net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN;
+    case blink::kWebReferrerPolicySameOrigin:
+      return net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN;
+    case blink::kWebReferrerPolicyStrictOrigin:
+      return net::URLRequest::
+          ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
     case blink::kWebReferrerPolicyDefault:
       if (base::CommandLine::ForCurrentProcess()->HasSwitch(
               switches::kReducedReferrerGranularity)) {
diff --git a/content/public/renderer/render_frame_observer.h b/content/public/renderer/render_frame_observer.h
index ebf4cd4..8bb72a3 100644
--- a/content/public/renderer/render_frame_observer.h
+++ b/content/public/renderer/render_frame_observer.h
@@ -124,6 +124,9 @@
   // Called when script in the page calls window.print().
   virtual void ScriptedPrint(bool user_initiated) {}
 
+  // Called when draggable regions change.
+  virtual void DraggableRegionsChanged() {}
+
   // IPC::Listener implementation.
   bool OnMessageReceived(const IPC::Message& message) override;
 
diff --git a/content/public/renderer/render_view_observer.h b/content/public/renderer/render_view_observer.h
index be14ae7..c879f8b 100644
--- a/content/public/renderer/render_view_observer.h
+++ b/content/public/renderer/render_view_observer.h
@@ -14,7 +14,6 @@
 class GURL;
 
 namespace blink {
-class WebFrame;
 class WebGestureEvent;
 class WebLocalFrame;
 struct WebURLError;
@@ -43,7 +42,6 @@
   virtual void DidCommitProvisionalLoad(blink::WebLocalFrame* frame,
                                         bool is_new_navigation) {}
   virtual void DidClearWindowObject(blink::WebLocalFrame* frame) {}
-  virtual void DraggableRegionsChanged(blink::WebFrame* frame) {}
   virtual void DidCommitCompositorFrame() {}
   virtual void DidUpdateLayout() {}
 
diff --git a/content/renderer/gpu/gpu_benchmarking_extension.cc b/content/renderer/gpu/gpu_benchmarking_extension.cc
index c1b2c31..f15bd60 100644
--- a/content/renderer/gpu/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu/gpu_benchmarking_extension.cc
@@ -311,9 +311,8 @@
   v8::Context::Scope context_scope(context);
   WebLocalFrame* frame = WebLocalFrame::FrameForContext(context);
   if (frame) {
-    std::unique_ptr<V8ValueConverter> converter =
-        base::WrapUnique(V8ValueConverter::create());
-    v8::Local<v8::Value> value = converter->ToV8Value(result.get(), context);
+    v8::Local<v8::Value> value =
+        V8ValueConverter::Create()->ToV8Value(result.get(), context);
     v8::Local<v8::Value> argv[] = { value };
 
     frame->CallFunctionEvenIfScriptDisabled(callback_and_context->GetCallback(),
@@ -1025,11 +1024,10 @@
     return false;
   }
 
-  std::unique_ptr<V8ValueConverter> converter =
-      base::WrapUnique(V8ValueConverter::create());
   v8::Local<v8::Context> v8_context =
       context.web_frame()->MainWorldScriptContext();
-  std::unique_ptr<base::Value> value = converter->FromV8Value(obj, v8_context);
+  std::unique_ptr<base::Value> value =
+      V8ValueConverter::Create()->FromV8Value(obj, v8_context);
 
   // Get all the pointer actions from the user input and wrap them into a
   // SyntheticPointerActionListParams object.
@@ -1081,11 +1079,9 @@
       new CallbackAndContext(args->isolate(), callback,
                              context.web_frame()->MainWorldScriptContext());
 
-  std::unique_ptr<V8ValueConverter> converter =
-      base::WrapUnique(V8ValueConverter::create());
   v8::Local<v8::Context> v8_context = callback_and_context->GetContext();
   std::unique_ptr<base::Value> value =
-      converter->FromV8Value(arguments, v8_context);
+      V8ValueConverter::Create()->FromV8Value(arguments, v8_context);
 
   return context.compositor()->ScheduleMicroBenchmark(
       name, std::move(value),
@@ -1100,12 +1096,10 @@
   if (!context.Init(true))
     return false;
 
-  std::unique_ptr<V8ValueConverter> converter =
-      base::WrapUnique(V8ValueConverter::create());
   v8::Local<v8::Context> v8_context =
       context.web_frame()->MainWorldScriptContext();
   std::unique_ptr<base::Value> value =
-      converter->FromV8Value(message, v8_context);
+      V8ValueConverter::Create()->FromV8Value(message, v8_context);
 
   return context.compositor()->SendMessageToMicroBenchmark(id,
                                                            std::move(value));
diff --git a/content/renderer/java/gin_java_bridge_value_converter.cc b/content/renderer/java/gin_java_bridge_value_converter.cc
index c22cbf3c..8a6230d 100644
--- a/content/renderer/java/gin_java_bridge_value_converter.cc
+++ b/content/renderer/java/gin_java_bridge_value_converter.cc
@@ -19,7 +19,7 @@
 namespace content {
 
 GinJavaBridgeValueConverter::GinJavaBridgeValueConverter()
-    : converter_(V8ValueConverter::create()) {
+    : converter_(V8ValueConverter::Create()) {
   converter_->SetDateAllowed(false);
   converter_->SetRegExpAllowed(false);
   converter_->SetFunctionAllowed(true);
diff --git a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
index 875a1ff..b19007ec 100644
--- a/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
+++ b/content/renderer/media/webrtc/webrtc_video_capturer_adapter.cc
@@ -5,7 +5,6 @@
 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h"
 
 #include "base/bind.h"
-#include "base/memory/aligned_memory.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 09d68da..9837f8b0 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -416,17 +416,23 @@
   if (is_view_source_mode_enabled)
     request.SetCachePolicy(WebCachePolicy::kReturnCacheDataElseLoad);
 
+  WebString web_referrer;
   if (common_params.referrer.url.is_valid()) {
-    WebString web_referrer = WebSecurityPolicy::GenerateReferrerHeader(
+    web_referrer = WebSecurityPolicy::GenerateReferrerHeader(
         common_params.referrer.policy, common_params.url,
         WebString::FromUTF8(common_params.referrer.url.spec()));
+    request.SetHTTPReferrer(web_referrer, common_params.referrer.policy);
     if (!web_referrer.IsEmpty()) {
-      request.SetHTTPReferrer(web_referrer, common_params.referrer.policy);
       request.AddHTTPOriginIfNeeded(
           WebSecurityOrigin(url::Origin(common_params.referrer.url)));
     }
   }
 
+  if (!web_referrer.IsEmpty() ||
+      common_params.referrer.policy != blink::kWebReferrerPolicyDefault) {
+    request.SetHTTPReferrer(web_referrer, common_params.referrer.policy);
+  }
+
   request.SetIsSameDocumentNavigation(is_same_document_navigation);
   request.SetPreviewsState(
       static_cast<WebURLRequest::PreviewsState>(common_params.previews_state));
@@ -644,6 +650,10 @@
     return params_.mhtml_popup_overlay_removal;
   }
 
+  bool UsePageProblemDetectors() override {
+    return params_.mhtml_problem_detection;
+  }
+
  private:
   const FrameMsg_SerializeAsMHTML_Params& params_;
   std::set<std::string>* serialized_resources_uri_digests_;
@@ -6691,6 +6701,11 @@
   return blink::Platform::Current()->CreateURLLoader();
 }
 
+void RenderFrameImpl::DraggableRegionsChanged() {
+  for (auto& observer : observers_)
+    observer.DraggableRegionsChanged();
+}
+
 blink::WebPageVisibilityState RenderFrameImpl::GetVisibilityState() const {
   return VisibilityState();
 }
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 61e8867..f9efacf6 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -668,6 +668,7 @@
       blink::WebSetSinkIdCallbacks* web_callbacks) override;
   blink::WebPageVisibilityState VisibilityState() const override;
   std::unique_ptr<blink::WebURLLoader> CreateURLLoader() override;
+  void DraggableRegionsChanged() override;
 
   // WebFrameSerializerClient implementation:
   void DidSerializeDataForFrame(
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 2cec6d95..4ff3c7ae 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -2312,11 +2312,6 @@
   return ZoomFactorToZoomLevel(factor);
 }
 
-void RenderViewImpl::DraggableRegionsChanged() {
-  for (auto& observer : observers_)
-    observer.DraggableRegionsChanged(webview()->MainFrame());
-}
-
 void RenderViewImpl::PageImportanceSignalsChanged() {
   if (!webview() || !main_render_frame_)
     return;
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 030950e..4266f15 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -327,7 +327,6 @@
   void PageScaleFactorChanged() override;
   virtual double zoomLevelToZoomFactor(double zoom_level) const;
   virtual double zoomFactorToZoomLevel(double factor) const;
-  void DraggableRegionsChanged() override;
   void PageImportanceSignalsChanged() override;
   void DidAutoResize(const blink::WebSize& newSize) override;
   blink::WebRect RootWindowRect() override;
diff --git a/content/renderer/skia_benchmarking_extension.cc b/content/renderer/skia_benchmarking_extension.cc
index b034452..0081051 100644
--- a/content/renderer/skia_benchmarking_extension.cc
+++ b/content/renderer/skia_benchmarking_extension.cc
@@ -76,10 +76,8 @@
 
 std::unique_ptr<base::Value> ParsePictureArg(v8::Isolate* isolate,
                                              v8::Local<v8::Value> arg) {
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  return std::unique_ptr<base::Value>(
-      converter->FromV8Value(arg, isolate->GetCurrentContext()));
+  return content::V8ValueConverter::Create()->FromV8Value(
+      arg, isolate->GetCurrentContext());
 }
 
 std::unique_ptr<Picture> CreatePictureFromEncodedString(
@@ -209,10 +207,8 @@
   if (!args->PeekNext().IsEmpty()) {
     v8::Local<v8::Value> params;
     args->GetNext(&params);
-    std::unique_ptr<content::V8ValueConverter> converter(
-        content::V8ValueConverter::create());
-    std::unique_ptr<base::Value> params_value(
-        converter->FromV8Value(params, context));
+    std::unique_ptr<base::Value> params_value =
+        content::V8ValueConverter::Create()->FromV8Value(params, context);
 
     const base::DictionaryValue* params_dict = NULL;
     if (params_value.get() && params_value->GetAsDictionary(&params_dict)) {
@@ -282,10 +278,9 @@
   picture->picture->playback(&benchmarking_canvas);
 
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
 
-  args->Return(converter->ToV8Value(&benchmarking_canvas.Commands(), context));
+  args->Return(content::V8ValueConverter::Create()->ToV8Value(
+      &benchmarking_canvas.Commands(), context));
 }
 
 void SkiaBenchmarking::GetOpTimings(gin::Arguments* args) {
diff --git a/content/renderer/web_ui_extension.cc b/content/renderer/web_ui_extension.cc
index 19593e17..802bd7a 100644
--- a/content/renderer/web_ui_extension.cc
+++ b/content/renderer/web_ui_extension.cc
@@ -121,9 +121,8 @@
       return;
     }
 
-    std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-    content = base::ListValue::From(
-        converter->FromV8Value(obj, frame->MainWorldScriptContext()));
+    content = base::ListValue::From(V8ValueConverter::Create()->FromV8Value(
+        obj, frame->MainWorldScriptContext()));
     DCHECK(content);
   }
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 0585890..984e402 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -692,7 +692,6 @@
     "../browser/renderer_host/input/touch_action_browsertest.cc",
     "../browser/renderer_host/input/touch_input_browsertest.cc",
     "../browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc",
-    "../browser/renderer_host/media/get_user_media_video_capture_browsertest.cc",
     "../browser/renderer_host/media/video_capture_browsertest.cc",
     "../browser/renderer_host/render_process_host_browsertest.cc",
     "../browser/renderer_host/render_view_host_browsertest.cc",
@@ -803,7 +802,6 @@
     "//services/ui/gpu/interfaces",
     "//services/ui/public/cpp/gpu",
     "//services/video_capture/public/cpp",
-    "//services/video_capture/public/interfaces:constants",
     "//storage/browser",
     "//testing/gmock",
     "//testing/gtest",
@@ -1214,6 +1212,7 @@
     "../browser/media/capture/audio_mirroring_manager_unittest.cc",
     "../browser/media/capture/web_contents_audio_input_stream_unittest.cc",
     "../browser/media/cdm_registry_impl_unittest.cc",
+    "../browser/media/media_devices_permission_checker_unittest.cc",
     "../browser/media/media_internals_unittest.cc",
     "../browser/media/midi_host_unittest.cc",
     "../browser/media/session/audio_focus_manager_unittest.cc",
diff --git a/content/test/data/media/video_capture_test.html b/content/test/data/media/video_capture_test.html
deleted file mode 100644
index faf8a28cf..0000000
--- a/content/test/data/media/video_capture_test.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<html>
-<head>
-  <script type="text/javascript" src="webrtc_test_utilities.js"></script>
-  <script type="text/javascript">
-  $ = function(id) {
-    return document.getElementById(id);
-  };
-
-  const WIDTH = 320;
-  var CONSTRAINTS = { video: { width: { exact : WIDTH } } };
-  var hasReceivedTrackEndedEvent = false;
-
-  function startVideoCaptureAndVerifySize() {
-    console.log('Calling getUserMediaAndWaitForVideoRendering.');
-    navigator.webkitGetUserMedia(
-        CONSTRAINTS,
-        gotStreamCallback,
-        failedCallback);
-  }
-
-  function failedCallback(error) {
-    failTest('GetUserMedia call failed with code ' + error.code);
-  }
-
-  function gotStreamCallback(stream) {
-    var localView = $('local-view');
-    localView.src = URL.createObjectURL(stream);
-
-    var videoTracks = stream.getVideoTracks();
-    if (videoTracks.length == 0) {
-      failTest('Did not receive any video tracks');
-    }
-    var videoTrack = videoTracks[0];
-    videoTrack.onended = function() {
-      hasReceivedTrackEndedEvent = true;
-    };
-
-    detectVideoPlaying('local-view', function() {
-      if (localView.videoWidth == WIDTH) {
-        reportTestSuccess();
-      } else {
-        failTest('Video has unexpected width.');
-      }
-    });
-  }
-
-  function waitForVideoToTurnBlack() {
-    detectBlackVideo('local-view', function() {
-      reportTestSuccess();
-    });
-  }
-
-  function verifyHasReceivedTrackEndedEvent() {
-    if (hasReceivedTrackEndedEvent) {
-      reportTestSuccess();
-    } else {
-      failTest('Did not receive ended event from track.');
-    }
-  }
-
-  </script>
-</head>
-<body>
-  <table border="0">
-    <tr>
-      <td><video id="local-view" width="96" height="96" autoplay
-          style="display:none"></video></td>
-      <!-- The canvas is used to detect when video starts and stops. -->
-      <td><canvas id="local-view-canvas" width="96" height="96"
-          style="display:none"></canvas></td>
-    </tr>
-  </table>
-</body>
-</html>
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 743b2e9..46315eaa 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -167,6 +167,59 @@
     "file_reader.h",
     "granted_file_entry.cc",
     "granted_file_entry.h",
+    "guest_view/app_view/app_view_constants.cc",
+    "guest_view/app_view/app_view_constants.h",
+    "guest_view/app_view/app_view_guest.cc",
+    "guest_view/app_view/app_view_guest.h",
+    "guest_view/app_view/app_view_guest_delegate.cc",
+    "guest_view/app_view/app_view_guest_delegate.h",
+    "guest_view/extension_options/extension_options_constants.cc",
+    "guest_view/extension_options/extension_options_constants.h",
+    "guest_view/extension_options/extension_options_guest.cc",
+    "guest_view/extension_options/extension_options_guest.h",
+    "guest_view/extension_options/extension_options_guest_delegate.cc",
+    "guest_view/extension_options/extension_options_guest_delegate.h",
+    "guest_view/extension_view/extension_view_constants.cc",
+    "guest_view/extension_view/extension_view_constants.h",
+    "guest_view/extension_view/extension_view_guest.cc",
+    "guest_view/extension_view/extension_view_guest.h",
+    "guest_view/extension_view/whitelist/extension_view_whitelist.cc",
+    "guest_view/extension_view/whitelist/extension_view_whitelist.h",
+    "guest_view/extensions_guest_view_manager_delegate.cc",
+    "guest_view/extensions_guest_view_manager_delegate.h",
+    "guest_view/extensions_guest_view_message_filter.cc",
+    "guest_view/extensions_guest_view_message_filter.h",
+    "guest_view/guest_view_events.cc",
+    "guest_view/guest_view_events.h",
+    "guest_view/mime_handler_view/mime_handler_stream_manager.cc",
+    "guest_view/mime_handler_view/mime_handler_stream_manager.h",
+    "guest_view/mime_handler_view/mime_handler_view_constants.cc",
+    "guest_view/mime_handler_view/mime_handler_view_constants.h",
+    "guest_view/mime_handler_view/mime_handler_view_guest.cc",
+    "guest_view/mime_handler_view/mime_handler_view_guest.h",
+    "guest_view/mime_handler_view/mime_handler_view_guest_delegate.cc",
+    "guest_view/mime_handler_view/mime_handler_view_guest_delegate.h",
+
+    # TODO(crbug.com/730220): Ideally web_view should be a separate target on
+    # which the extension system depends.
+    "guest_view/web_view/javascript_dialog_helper.cc",
+    "guest_view/web_view/javascript_dialog_helper.h",
+    "guest_view/web_view/web_view_constants.cc",
+    "guest_view/web_view/web_view_constants.h",
+    "guest_view/web_view/web_view_content_script_manager.cc",
+    "guest_view/web_view/web_view_content_script_manager.h",
+    "guest_view/web_view/web_view_find_helper.cc",
+    "guest_view/web_view/web_view_find_helper.h",
+    "guest_view/web_view/web_view_guest.cc",
+    "guest_view/web_view/web_view_guest.h",
+    "guest_view/web_view/web_view_guest_delegate.h",
+    "guest_view/web_view/web_view_permission_helper.cc",
+    "guest_view/web_view/web_view_permission_helper.h",
+    "guest_view/web_view/web_view_permission_helper_delegate.cc",
+    "guest_view/web_view/web_view_permission_helper_delegate.h",
+    "guest_view/web_view/web_view_permission_types.h",
+    "guest_view/web_view/web_view_renderer_state.cc",
+    "guest_view/web_view/web_view_renderer_state.h",
     "image_loader.cc",
     "image_loader.h",
     "image_loader_factory.cc",
@@ -279,12 +332,13 @@
     "//crypto:platform",
     "//crypto:platform",
     "//extensions:extensions_browser_resources",
-    "//extensions/common",
+    "//extensions/browser/guest_view/web_view/web_ui",
     "//extensions/common",
     "//extensions/common/api",
     "//extensions/features",
     "//extensions/strings",
     "//google_apis",
+    "//ppapi/features",
     "//services/preferences/public/cpp",
     "//services/service_manager/public/cpp",
     "//ui/display",
@@ -292,7 +346,6 @@
 
   public_deps = [
     "//extensions/browser/app_window",
-    "//extensions/browser/guest_view",
     "//extensions/browser/install",
     "//extensions/browser/kiosk",
     "//extensions/browser/updater",
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index 7394afb3..7742a7d 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -88,7 +88,6 @@
     "//base:i18n",
     "//components/keyed_service/content:content",
     "//extensions/browser:browser_sources",
-    "//extensions/browser/guest_view",
     "//extensions/common/api",
     "//extensions/strings",
   ]
diff --git a/extensions/browser/app_window/app_window_contents.cc b/extensions/browser/app_window/app_window_contents.cc
index 2478dde..875d2cce 100644
--- a/extensions/browser/app_window/app_window_contents.cc
+++ b/extensions/browser/app_window/app_window_contents.cc
@@ -97,9 +97,11 @@
   return nullptr;
 }
 
-bool AppWindowContentsImpl::OnMessageReceived(const IPC::Message& message) {
+bool AppWindowContentsImpl::OnMessageReceived(
+    const IPC::Message& message,
+    content::RenderFrameHost* sender) {
   bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(AppWindowContentsImpl, message)
+  IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(AppWindowContentsImpl, message, sender)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_UpdateDraggableRegions,
                         UpdateDraggableRegions)
     IPC_MESSAGE_UNHANDLED(handled = false)
@@ -114,8 +116,10 @@
 }
 
 void AppWindowContentsImpl::UpdateDraggableRegions(
+    content::RenderFrameHost* sender,
     const std::vector<DraggableRegion>& regions) {
-  host_->UpdateDraggableRegions(regions);
+  if (!sender->GetParent())  // Only process events from the main frame.
+    host_->UpdateDraggableRegions(regions);
 }
 
 void AppWindowContentsImpl::SuspendRenderFrameHost(
diff --git a/extensions/browser/app_window/app_window_contents.h b/extensions/browser/app_window/app_window_contents.h
index 4afa13d..6446f45 100644
--- a/extensions/browser/app_window/app_window_contents.h
+++ b/extensions/browser/app_window/app_window_contents.h
@@ -45,10 +45,12 @@
 
  private:
   // content::WebContentsObserver
-  bool OnMessageReceived(const IPC::Message& message) override;
+  bool OnMessageReceived(const IPC::Message& message,
+                         content::RenderFrameHost* sender) override;
   void ReadyToCommitNavigation(content::NavigationHandle* handle) override;
 
-  void UpdateDraggableRegions(const std::vector<DraggableRegion>& regions);
+  void UpdateDraggableRegions(content::RenderFrameHost* sender,
+                              const std::vector<DraggableRegion>& regions);
   void SuspendRenderFrameHost(content::RenderFrameHost* rfh);
 
   AppWindow* host_;  // This class is owned by |host_|
diff --git a/extensions/browser/guest_view/BUILD.gn b/extensions/browser/guest_view/BUILD.gn
deleted file mode 100644
index 2b81c0d1..0000000
--- a/extensions/browser/guest_view/BUILD.gn
+++ /dev/null
@@ -1,30 +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.
-
-source_set("guest_view") {
-  sources = [
-    "extensions_guest_view_manager_delegate.cc",
-    "extensions_guest_view_manager_delegate.h",
-    "extensions_guest_view_message_filter.cc",
-    "extensions_guest_view_message_filter.h",
-    "guest_view_events.cc",
-    "guest_view_events.h",
-  ]
-
-  public_deps = [
-    "//extensions/browser/guest_view/app_view",
-    "//extensions/browser/guest_view/extension_options",
-    "//extensions/browser/guest_view/extension_view",
-    "//extensions/browser/guest_view/extension_view/whitelist",
-    "//extensions/browser/guest_view/mime_handler_view",
-    "//extensions/browser/guest_view/web_view",
-    "//extensions/browser/guest_view/web_view/web_ui",
-  ]
-
-  deps = [
-    "//components/browsing_data/content",
-    "//components/guest_view/browser",
-    "//content/public/common",
-  ]
-}
diff --git a/extensions/browser/guest_view/app_view/BUILD.gn b/extensions/browser/guest_view/app_view/BUILD.gn
deleted file mode 100644
index 97d00d6..0000000
--- a/extensions/browser/guest_view/app_view/BUILD.gn
+++ /dev/null
@@ -1,20 +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.
-
-source_set("app_view") {
-  sources = [
-    "app_view_constants.cc",
-    "app_view_constants.h",
-    "app_view_guest.cc",
-    "app_view_guest.h",
-    "app_view_guest_delegate.cc",
-    "app_view_guest_delegate.h",
-  ]
-
-  deps = [
-    "//content/public/common",
-    "//extensions/common/api",
-    "//extensions/strings",
-  ]
-}
diff --git a/extensions/browser/guest_view/extension_options/BUILD.gn b/extensions/browser/guest_view/extension_options/BUILD.gn
deleted file mode 100644
index aa74bd5c..0000000
--- a/extensions/browser/guest_view/extension_options/BUILD.gn
+++ /dev/null
@@ -1,20 +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.
-
-source_set("extension_options") {
-  sources = [
-    "extension_options_constants.cc",
-    "extension_options_constants.h",
-    "extension_options_guest.cc",
-    "extension_options_guest.h",
-    "extension_options_guest_delegate.cc",
-    "extension_options_guest_delegate.h",
-  ]
-
-  deps = [
-    "//content/public/common",
-    "//extensions/common/api",
-    "//extensions/strings",
-  ]
-}
diff --git a/extensions/browser/guest_view/extension_view/BUILD.gn b/extensions/browser/guest_view/extension_view/BUILD.gn
deleted file mode 100644
index cb2e282..0000000
--- a/extensions/browser/guest_view/extension_view/BUILD.gn
+++ /dev/null
@@ -1,18 +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.
-
-source_set("extension_view") {
-  sources = [
-    "extension_view_constants.cc",
-    "extension_view_constants.h",
-    "extension_view_guest.cc",
-    "extension_view_guest.h",
-  ]
-
-  deps = [
-    "//content/public/common",
-    "//extensions/common/api",
-    "//extensions/strings",
-  ]
-}
diff --git a/extensions/browser/guest_view/extension_view/whitelist/BUILD.gn b/extensions/browser/guest_view/extension_view/whitelist/BUILD.gn
deleted file mode 100644
index 2ebc9d7..0000000
--- a/extensions/browser/guest_view/extension_view/whitelist/BUILD.gn
+++ /dev/null
@@ -1,10 +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.
-
-source_set("whitelist") {
-  sources = [
-    "extension_view_whitelist.cc",
-    "extension_view_whitelist.h",
-  ]
-}
diff --git a/extensions/browser/guest_view/mime_handler_view/BUILD.gn b/extensions/browser/guest_view/mime_handler_view/BUILD.gn
deleted file mode 100644
index abde2786..0000000
--- a/extensions/browser/guest_view/mime_handler_view/BUILD.gn
+++ /dev/null
@@ -1,22 +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.
-
-source_set("mime_handler_view") {
-  sources = [
-    "mime_handler_stream_manager.cc",
-    "mime_handler_stream_manager.h",
-    "mime_handler_view_constants.cc",
-    "mime_handler_view_constants.h",
-    "mime_handler_view_guest.cc",
-    "mime_handler_view_guest.h",
-    "mime_handler_view_guest_delegate.cc",
-    "mime_handler_view_guest_delegate.h",
-  ]
-
-  deps = [
-    "//content/public/common",
-    "//extensions/common/api",
-    "//extensions/strings",
-  ]
-}
diff --git a/extensions/browser/guest_view/web_view/BUILD.gn b/extensions/browser/guest_view/web_view/BUILD.gn
deleted file mode 100644
index c0da6a44..0000000
--- a/extensions/browser/guest_view/web_view/BUILD.gn
+++ /dev/null
@@ -1,34 +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.
-
-source_set("web_view") {
-  sources = [
-    "javascript_dialog_helper.cc",
-    "javascript_dialog_helper.h",
-    "web_view_constants.cc",
-    "web_view_constants.h",
-    "web_view_content_script_manager.cc",
-    "web_view_content_script_manager.h",
-    "web_view_find_helper.cc",
-    "web_view_find_helper.h",
-    "web_view_guest.cc",
-    "web_view_guest.h",
-    "web_view_guest_delegate.h",
-    "web_view_permission_helper.cc",
-    "web_view_permission_helper.h",
-    "web_view_permission_helper_delegate.cc",
-    "web_view_permission_helper_delegate.h",
-    "web_view_permission_types.h",
-    "web_view_renderer_state.cc",
-    "web_view_renderer_state.h",
-  ]
-
-  deps = [
-    "//components/web_cache/browser",
-    "//content/public/common",
-    "//extensions/common/api",
-    "//extensions/strings",
-    "//ppapi/features",
-  ]
-}
diff --git a/extensions/renderer/activity_log_converter_strategy_unittest.cc b/extensions/renderer/activity_log_converter_strategy_unittest.cc
index 5d82de9..79059af 100644
--- a/extensions/renderer/activity_log_converter_strategy_unittest.cc
+++ b/extensions/renderer/activity_log_converter_strategy_unittest.cc
@@ -10,8 +10,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "v8/include/v8.h"
 
-using content::V8ValueConverter;
-
 namespace extensions {
 
 class ActivityLogConverterStrategyTest : public testing::Test {
@@ -24,7 +22,7 @@
 
  protected:
   void SetUp() override {
-    converter_.reset(V8ValueConverter::create());
+    converter_ = content::V8ValueConverter::Create();
     strategy_.reset(new ActivityLogConverterStrategy());
     converter_->SetFunctionAllowed(true);
     converter_->SetStrategy(strategy_.get());
@@ -94,7 +92,7 @@
   v8::HandleScope handle_scope_;
   v8::Global<v8::Context> context_;
   v8::Context::Scope context_scope_;
-  std::unique_ptr<V8ValueConverter> converter_;
+  std::unique_ptr<content::V8ValueConverter> converter_;
   std::unique_ptr<ActivityLogConverterStrategy> strategy_;
 };
 
diff --git a/extensions/renderer/api_activity_logger.cc b/extensions/renderer/api_activity_logger.cc
index 08eb53e..baaca3f 100644
--- a/extensions/renderer/api_activity_logger.cc
+++ b/extensions/renderer/api_activity_logger.cc
@@ -15,8 +15,6 @@
 #include "extensions/renderer/dispatcher.h"
 #include "extensions/renderer/script_context.h"
 
-using content::V8ValueConverter;
-
 namespace extensions {
 
 APIActivityLogger::APIActivityLogger(ScriptContext* context,
@@ -65,7 +63,8 @@
   // Get the array of api call arguments.
   v8::Local<v8::Array> arg_array = v8::Local<v8::Array>::Cast(args[2]);
   if (arg_array->Length() > 0) {
-    std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+    std::unique_ptr<content::V8ValueConverter> converter =
+        content::V8ValueConverter::Create();
     ActivityLogConverterStrategy strategy;
     converter->SetFunctionAllowed(true);
     converter->SetStrategy(&strategy);
diff --git a/extensions/renderer/api_binding.cc b/extensions/renderer/api_binding.cc
index 600522f..7220697 100644
--- a/extensions/renderer/api_binding.cc
+++ b/extensions/renderer/api_binding.cc
@@ -497,7 +497,7 @@
         isolate, new DeclarativeEvent(
                      event_data->full_name, event_data->binding->type_refs_,
                      event_data->binding->request_handler_, event_data->actions,
-                     event_data->conditions));
+                     event_data->conditions, 0));
     retval = event.ToV8();
   } else {
     retval = event_data->binding->event_handler_->CreateEventInstance(
diff --git a/extensions/renderer/api_binding_js_util.cc b/extensions/renderer/api_binding_js_util.cc
index 1f84df2..29c39be 100644
--- a/extensions/renderer/api_binding_js_util.cc
+++ b/extensions/renderer/api_binding_js_util.cc
@@ -9,15 +9,17 @@
 #include "extensions/renderer/api_request_handler.h"
 #include "extensions/renderer/api_signature.h"
 #include "extensions/renderer/api_type_reference_map.h"
+#include "extensions/renderer/declarative_event.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
+#include "gin/handle.h"
 #include "gin/object_template_builder.h"
 
 namespace extensions {
 
 gin::WrapperInfo APIBindingJSUtil::kWrapperInfo = {gin::kEmbedderNativeGin};
 
-APIBindingJSUtil::APIBindingJSUtil(const APITypeReferenceMap* type_refs,
+APIBindingJSUtil::APIBindingJSUtil(APITypeReferenceMap* type_refs,
                                    APIRequestHandler* request_handler,
                                    APIEventHandler* event_handler,
                                    const binding::RunJSFunction& run_js)
@@ -35,6 +37,8 @@
       .SetMethod("registerEventArgumentMassager",
                  &APIBindingJSUtil::RegisterEventArgumentMassager)
       .SetMethod("createCustomEvent", &APIBindingJSUtil::CreateCustomEvent)
+      .SetMethod("createCustomDeclarativeEvent",
+                 &APIBindingJSUtil::CreateCustomDeclarativeEvent)
       .SetMethod("invalidateEvent", &APIBindingJSUtil::InvalidateEvent)
       .SetMethod("setLastError", &APIBindingJSUtil::SetLastError)
       .SetMethod("clearLastError", &APIBindingJSUtil::ClearLastError)
@@ -137,6 +141,23 @@
   arguments->Return(event);
 }
 
+void APIBindingJSUtil::CreateCustomDeclarativeEvent(
+    gin::Arguments* arguments,
+    const std::string& event_name,
+    const std::vector<std::string>& actions_list,
+    const std::vector<std::string>& conditions_list,
+    int webview_instance_id) {
+  v8::Isolate* isolate = arguments->isolate();
+  v8::HandleScope handle_scope(isolate);
+
+  gin::Handle<DeclarativeEvent> event = gin::CreateHandle(
+      isolate,
+      new DeclarativeEvent(event_name, type_refs_, request_handler_,
+                           actions_list, conditions_list, webview_instance_id));
+
+  arguments->Return(event.ToV8());
+}
+
 void APIBindingJSUtil::InvalidateEvent(gin::Arguments* arguments,
                                        v8::Local<v8::Object> event) {
   v8::Isolate* isolate = arguments->isolate();
diff --git a/extensions/renderer/api_binding_js_util.h b/extensions/renderer/api_binding_js_util.h
index e4b4aadf..9963c82 100644
--- a/extensions/renderer/api_binding_js_util.h
+++ b/extensions/renderer/api_binding_js_util.h
@@ -26,7 +26,7 @@
 // some of our JS bindings, we can reduce or remove this class.
 class APIBindingJSUtil final : public gin::Wrappable<APIBindingJSUtil> {
  public:
-  APIBindingJSUtil(const APITypeReferenceMap* type_refs,
+  APIBindingJSUtil(APITypeReferenceMap* type_refs,
                    APIRequestHandler* request_handler,
                    APIEventHandler* event_handler,
                    const binding::RunJSFunction& run_js);
@@ -55,8 +55,6 @@
 
   // A handler to allow custom bindings to create custom extension API event
   // objects (e.g. foo.onBar).
-  // Note: The JS version allows for constructing declarative events; it's
-  // unclear if we'll need to support this.
   // TODO(devlin): Currently, we ignore schema. We may want to take it into
   // account.
   void CreateCustomEvent(gin::Arguments* arguments,
@@ -64,6 +62,14 @@
                          v8::Local<v8::Value> unused_schema,
                          bool supports_filters);
 
+  // Creates a new declarative event.
+  void CreateCustomDeclarativeEvent(
+      gin::Arguments* arguments,
+      const std::string& event_name,
+      const std::vector<std::string>& actions_list,
+      const std::vector<std::string>& conditions_list,
+      int webview_instance_id);
+
   // Invalidates an event, removing its listeners and preventing any more from
   // being added.
   void InvalidateEvent(gin::Arguments* arguments, v8::Local<v8::Object> event);
@@ -84,7 +90,7 @@
                                 v8::Local<v8::Function> callback);
 
   // Type references. Guaranteed to outlive this object.
-  const APITypeReferenceMap* type_refs_;
+  APITypeReferenceMap* type_refs_;
 
   // The request handler. Guaranteed to outlive this object.
   APIRequestHandler* request_handler_;
diff --git a/extensions/renderer/api_binding_test_util.cc b/extensions/renderer/api_binding_test_util.cc
index 262ae6a7c..9977556 100644
--- a/extensions/renderer/api_binding_test_util.cc
+++ b/extensions/renderer/api_binding_test_util.cc
@@ -110,9 +110,7 @@
 
 std::unique_ptr<base::Value> V8ToBaseValue(v8::Local<v8::Value> value,
                                            v8::Local<v8::Context> context) {
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  return converter->FromV8Value(value, context);
+  return content::V8ValueConverter::Create()->FromV8Value(value, context);
 }
 
 v8::Local<v8::Value> RunFunction(v8::Local<v8::Function> function,
diff --git a/extensions/renderer/api_event_handler.cc b/extensions/renderer/api_event_handler.cc
index 14480a1..1057a17b 100644
--- a/extensions/renderer/api_event_handler.cc
+++ b/extensions/renderer/api_event_handler.cc
@@ -221,8 +221,8 @@
   // Note: since we only convert the arguments once, if a listener modifies an
   // object (including an array), other listeners will see that modification.
   // TODO(devlin): This is how it's always been, but should it be?
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
+  std::unique_ptr<content::V8ValueConverter> converter =
+      content::V8ValueConverter::Create();
 
   auto massager_iter = data->massagers.find(event_name);
   if (massager_iter == data->massagers.end()) {
diff --git a/extensions/renderer/api_event_listeners.cc b/extensions/renderer/api_event_listeners.cc
index 96945dd..27f2ab39 100644
--- a/extensions/renderer/api_event_listeners.cc
+++ b/extensions/renderer/api_event_listeners.cc
@@ -64,9 +64,8 @@
     return false;
   }
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  std::unique_ptr<base::Value> value = converter->FromV8Value(filter, context);
+  std::unique_ptr<base::Value> value =
+      content::V8ValueConverter::Create()->FromV8Value(filter, context);
   if (!value || !value->is_dict()) {
     *error = "could not convert filter.";
     return false;
diff --git a/extensions/renderer/api_request_handler.cc b/extensions/renderer/api_request_handler.cc
index 42f78dc..bcc1c0e 100644
--- a/extensions/renderer/api_request_handler.cc
+++ b/extensions/renderer/api_request_handler.cc
@@ -126,8 +126,8 @@
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = pending_request.context.Get(isolate);
   v8::Context::Scope context_scope(context);
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
+  std::unique_ptr<content::V8ValueConverter> converter =
+      content::V8ValueConverter::Create();
   std::vector<v8::Local<v8::Value>> args;
   args.reserve(response_args.GetSize() +
                pending_request.callback_arguments.size());
diff --git a/extensions/renderer/api_signature.cc b/extensions/renderer/api_signature.cc
index b2eb02f..c07705fb 100644
--- a/extensions/renderer/api_signature.cc
+++ b/extensions/renderer/api_signature.cc
@@ -327,8 +327,8 @@
   }
 
   auto json = base::MakeUnique<base::ListValue>();
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
+  std::unique_ptr<content::V8ValueConverter> converter =
+      content::V8ValueConverter::Create();
   converter->SetFunctionAllowed(true);
   for (size_t i = 0; i < size; ++i) {
     std::unique_ptr<base::Value> converted =
diff --git a/extensions/renderer/argument_spec.cc b/extensions/renderer/argument_spec.cc
index 70afeba1..1ecb1dad 100644
--- a/extensions/renderer/argument_spec.cc
+++ b/extensions/renderer/argument_spec.cc
@@ -618,11 +618,11 @@
                                       std::string* error) const {
   DCHECK(type_ == ArgumentType::ANY || type_ == ArgumentType::BINARY);
   if (out_value) {
-    std::unique_ptr<content::V8ValueConverter> converter(
-        content::V8ValueConverter::create());
+    std::unique_ptr<content::V8ValueConverter> converter =
+        content::V8ValueConverter::Create();
     converter->SetStripNullFromObjects(!preserve_null_);
-    std::unique_ptr<base::Value> converted(
-        converter->FromV8Value(value, context));
+    std::unique_ptr<base::Value> converted =
+        converter->FromV8Value(value, context);
     if (!converted) {
       *error = api_errors::UnserializableValue();
       return false;
diff --git a/extensions/renderer/declarative_event.cc b/extensions/renderer/declarative_event.cc
index 9365c8d..531b111 100644
--- a/extensions/renderer/declarative_event.cc
+++ b/extensions/renderer/declarative_event.cc
@@ -114,10 +114,12 @@
     APITypeReferenceMap* type_refs,
     APIRequestHandler* request_handler,
     const std::vector<std::string>& actions_list,
-    const std::vector<std::string>& conditions_list)
+    const std::vector<std::string>& conditions_list,
+    int webview_instance_id)
     : event_name_(name),
       type_refs_(type_refs),
-      request_handler_(request_handler) {
+      request_handler_(request_handler),
+      webview_instance_id_(webview_instance_id) {
   // In declarative events, the specification of the rules can change. This only
   // matters for the events.addRules function. Check whether or not a
   // specialized version for this event exists, and, if not, create it.
@@ -179,12 +181,9 @@
   // The events API has two undocumented parameters for each function: the name
   // of the event, and the "webViewInstanceId". Currently, stub 0 for webview
   // instance id.
-  // TODO(devlin): We'll need to fix that to get it to work with webviews.
-  // Longer term, it would be *great* to just factor that out entirely (can we
-  // not get that information on the browser side? Investigate).
   argument_list.insert(argument_list.begin(),
                        {gin::StringToSymbol(isolate, event_name_),
-                        v8::Integer::New(isolate, 0)});
+                        v8::Integer::New(isolate, webview_instance_id_)});
 
   std::unique_ptr<base::ListValue> converted_arguments;
   v8::Local<v8::Function> callback;
diff --git a/extensions/renderer/declarative_event.h b/extensions/renderer/declarative_event.h
index f51eeba..b0a7bcba 100644
--- a/extensions/renderer/declarative_event.h
+++ b/extensions/renderer/declarative_event.h
@@ -27,7 +27,8 @@
                    APITypeReferenceMap* type_refs,
                    APIRequestHandler* request_handler,
                    const std::vector<std::string>& actions_list,
-                   const std::vector<std::string>& conditions_list);
+                   const std::vector<std::string>& conditions_list,
+                   int webview_instance_id);
   ~DeclarativeEvent() override;
 
   static gin::WrapperInfo kWrapperInfo;
@@ -52,6 +53,8 @@
 
   APIRequestHandler* request_handler_;
 
+  const int webview_instance_id_;
+
   DISALLOW_COPY_AND_ASSIGN(DeclarativeEvent);
 };
 
diff --git a/extensions/renderer/declarative_event_unittest.cc b/extensions/renderer/declarative_event_unittest.cc
index 95ab8033..60be826 100644
--- a/extensions/renderer/declarative_event_unittest.cc
+++ b/extensions/renderer/declarative_event_unittest.cc
@@ -120,7 +120,7 @@
   gin::Handle<DeclarativeEvent> emitter = gin::CreateHandle(
       context->GetIsolate(),
       new DeclarativeEvent("declEvent", type_refs(), request_handler(),
-                           {"action1", "action2"}, {"condition"}));
+                           {"action1", "action2"}, {"condition"}, 0));
 
   v8::Local<v8::Value> emitter_value = emitter.ToV8();
 
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 75c29b7..4930eb6 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -149,8 +149,8 @@
   v8::HandleScope handle_scope(context->isolate());
   v8::Context::Scope context_scope(context->v8_context());
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
+  std::unique_ptr<content::V8ValueConverter> converter =
+      content::V8ValueConverter::Create();
 
   std::vector<v8::Local<v8::Value>> arguments;
   for (const auto& arg : *args) {
@@ -707,7 +707,6 @@
   std::vector<std::pair<const char*, int>> resources = {
       {"appView", IDR_APP_VIEW_JS},
       {"entryIdManager", IDR_ENTRY_ID_MANAGER},
-      {kEventBindings, IDR_EVENT_BINDINGS_JS},
       {"extensionOptions", IDR_EXTENSION_OPTIONS_JS},
       {"extensionOptionsAttributes", IDR_EXTENSION_OPTIONS_ATTRIBUTES_JS},
       {"extensionOptionsConstants", IDR_EXTENSION_OPTIONS_CONSTANTS_JS},
@@ -800,6 +799,7 @@
 
   if (!FeatureSwitch::native_crx_bindings()->IsEnabled()) {
     resources.emplace_back("binding", IDR_BINDING_JS);
+    resources.emplace_back(kEventBindings, IDR_EVENT_BINDINGS_JS);
 
     // Custom types sources.
     resources.emplace_back("StorageArea", IDR_STORAGE_AREA_JS);
diff --git a/extensions/renderer/display_source_custom_bindings.cc b/extensions/renderer/display_source_custom_bindings.cc
index 37c8bcb..0214e53 100644
--- a/extensions/renderer/display_source_custom_bindings.cc
+++ b/extensions/renderer/display_source_custom_bindings.cc
@@ -17,8 +17,6 @@
 
 namespace extensions {
 
-using content::V8ValueConverter;
-
 namespace {
 const char kErrorNotSupported[] = "Not supported";
 const char kInvalidStreamArgs[] = "Invalid stream arguments";
@@ -136,9 +134,9 @@
       GetChildValue(start_info, "authenticationInfo", isolate);
   if (!auth_info_v8_val->IsNull()) {
     CHECK(auth_info_v8_val->IsObject());
-    std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-    std::unique_ptr<base::Value> auth_info_val(
-        converter->FromV8Value(auth_info_v8_val, context()->v8_context()));
+    std::unique_ptr<base::Value> auth_info_val =
+        content::V8ValueConverter::Create()->FromV8Value(
+            auth_info_v8_val, context()->v8_context());
     CHECK(auth_info_val);
     auth_info = DisplaySourceAuthInfo::FromValue(*auth_info_val);
   }
diff --git a/extensions/renderer/dom_activity_logger.cc b/extensions/renderer/dom_activity_logger.cc
index 9e0d2ad6..1e59903 100644
--- a/extensions/renderer/dom_activity_logger.cc
+++ b/extensions/renderer/dom_activity_logger.cc
@@ -14,7 +14,6 @@
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 
-using content::V8ValueConverter;
 using blink::WebString;
 using blink::WebURL;
 
@@ -28,7 +27,8 @@
                    const v8::Local<v8::Value>& v8_value,
                    base::ListValue* list) {
   DCHECK(list);
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+  std::unique_ptr<content::V8ValueConverter> converter =
+      content::V8ValueConverter::Create();
   ActivityLogConverterStrategy strategy;
   converter->SetFunctionAllowed(true);
   converter->SetStrategy(&strategy);
diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc
index 96762d8..63b9c31 100644
--- a/extensions/renderer/event_bindings.cc
+++ b/extensions/renderer/event_bindings.cc
@@ -218,11 +218,11 @@
   else
     listener_ids = v8::Array::New(context->isolate());
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
   v8::Local<v8::Value> v8_args[] = {
       gin::StringToSymbol(context->isolate(), event_name),
-      converter->ToV8Value(event_args, context->v8_context()), listener_ids,
+      content::V8ValueConverter::Create()->ToV8Value(event_args,
+                                                     context->v8_context()),
+      listener_ids,
   };
 
   context->module_system()->CallModuleMethodSafe(
@@ -322,10 +322,9 @@
 
   std::unique_ptr<base::DictionaryValue> filter;
   {
-    std::unique_ptr<content::V8ValueConverter> converter(
-        content::V8ValueConverter::create());
-    std::unique_ptr<base::Value> filter_value(converter->FromV8Value(
-        v8::Local<v8::Object>::Cast(args[1]), context()->v8_context()));
+    std::unique_ptr<base::Value> filter_value =
+        content::V8ValueConverter::Create()->FromV8Value(
+            v8::Local<v8::Object>::Cast(args[1]), context()->v8_context());
     if (!filter_value || !filter_value->IsType(base::Value::Type::DICTIONARY)) {
       args.GetReturnValue().Set(static_cast<int32_t>(-1));
       return;
@@ -334,7 +333,9 @@
   }
 
   int id = g_event_filter.Get().AddEventMatcher(
-      event_name, ParseEventMatcher(std::move(filter)));
+      event_name,
+      base::MakeUnique<EventMatcher>(
+          std::move(filter), context()->GetRenderFrame()->GetRoutingID()));
   if (id == -1) {
     args.GetReturnValue().Set(static_cast<int32_t>(-1));
     return;
@@ -403,12 +404,6 @@
   g_unmanaged_listeners.Get()[context()].erase(event_name);
 }
 
-std::unique_ptr<EventMatcher> EventBindings::ParseEventMatcher(
-    std::unique_ptr<base::DictionaryValue> filter) {
-  return base::MakeUnique<EventMatcher>(
-      std::move(filter), context()->GetRenderFrame()->GetRoutingID());
-}
-
 IPC::Sender* EventBindings::GetIPCSender() {
   const bool is_service_worker_context =
       context()->context_type() == Feature::SERVICE_WORKER_CONTEXT;
diff --git a/extensions/renderer/event_bindings.h b/extensions/renderer/event_bindings.h
index c43dca3..853eebe 100644
--- a/extensions/renderer/event_bindings.h
+++ b/extensions/renderer/event_bindings.h
@@ -23,7 +23,6 @@
 }
 
 namespace extensions {
-class EventMatcher;
 
 // This class deals with the javascript bindings related to Event objects.
 class EventBindings : public ObjectBackedNativeHandler {
@@ -89,9 +88,6 @@
   void AttachUnmanagedEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
   void DetachUnmanagedEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
 
-  std::unique_ptr<EventMatcher> ParseEventMatcher(
-      std::unique_ptr<base::DictionaryValue> filter);
-
   IPC::Sender* GetIPCSender();
 
   // Called when our context, and therefore us, is invalidated. Run any cleanup.
diff --git a/extensions/renderer/extension_frame_helper.cc b/extensions/renderer/extension_frame_helper.cc
index da47e687..a7da93e 100644
--- a/extensions/renderer/extension_frame_helper.cc
+++ b/extensions/renderer/extension_frame_helper.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_util.h"
 #include "base/timer/elapsed_timer.h"
 #include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_view.h"
 #include "extensions/common/api/messaging/message.h"
 #include "extensions/common/api/messaging/port_id.h"
 #include "extensions/common/constants.h"
@@ -335,4 +336,23 @@
   delete this;
 }
 
+void ExtensionFrameHelper::DraggableRegionsChanged() {
+  if (!render_frame()->IsMainFrame())
+    return;
+
+  blink::WebVector<blink::WebDraggableRegion> webregions =
+      render_frame()->GetWebFrame()->GetDocument().DraggableRegions();
+  std::vector<DraggableRegion> regions;
+  for (blink::WebDraggableRegion& webregion : webregions) {
+    render_frame()->GetRenderView()->ConvertViewportToWindowViaWidget(
+        &webregion.bounds);
+
+    regions.push_back(DraggableRegion());
+    DraggableRegion& region = regions.back();
+    region.bounds = webregion.bounds;
+    region.draggable = webregion.draggable;
+  }
+  Send(new ExtensionHostMsg_UpdateDraggableRegions(routing_id(), regions));
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/extension_frame_helper.h b/extensions/renderer/extension_frame_helper.h
index d4fc8d6a..e1a6d66e 100644
--- a/extensions/renderer/extension_frame_helper.h
+++ b/extensions/renderer/extension_frame_helper.h
@@ -103,6 +103,7 @@
   void WillReleaseScriptContext(v8::Local<v8::Context>, int world_id) override;
   bool OnMessageReceived(const IPC::Message& message) override;
   void OnDestruct() override;
+  void DraggableRegionsChanged() override;
 
   // IPC handlers.
   void OnExtensionValidateMessagePort(const PortId& id);
diff --git a/extensions/renderer/extension_helper.cc b/extensions/renderer/extension_helper.cc
index eb64c4b6..7c6ee40 100644
--- a/extensions/renderer/extension_helper.cc
+++ b/extensions/renderer/extension_helper.cc
@@ -46,20 +46,6 @@
   delete this;
 }
 
-void ExtensionHelper::DraggableRegionsChanged(blink::WebFrame* frame) {
-  blink::WebVector<blink::WebDraggableRegion> webregions =
-      frame->GetDocument().DraggableRegions();
-  std::vector<DraggableRegion> regions;
-  for (size_t i = 0; i < webregions.size(); ++i) {
-    DraggableRegion region;
-    render_view()->ConvertViewportToWindowViaWidget(&webregions[i].bounds);
-    region.bounds = webregions[i].bounds;
-    region.draggable = webregions[i].draggable;
-    regions.push_back(region);
-  }
-  Send(new ExtensionHostMsg_UpdateDraggableRegions(routing_id(), regions));
-}
-
 void ExtensionHelper::OnSetFrameName(const std::string& name) {
   blink::WebView* web_view = render_view()->GetWebView();
   if (web_view)
diff --git a/extensions/renderer/extension_helper.h b/extensions/renderer/extension_helper.h
index 7cb3a2d..9bcd10a 100644
--- a/extensions/renderer/extension_helper.h
+++ b/extensions/renderer/extension_helper.h
@@ -22,7 +22,6 @@
  private:
   // RenderViewObserver implementation.
   bool OnMessageReceived(const IPC::Message& message) override;
-  void DraggableRegionsChanged(blink::WebFrame* frame) override;
   void OnDestruct() override;
 
   void OnAppWindowClosed();
diff --git a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
index ddba6ea5..191f117 100644
--- a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
+++ b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
@@ -163,14 +163,11 @@
 
   int guest_instance_id = args[1]->Int32Value();
 
-  std::unique_ptr<base::DictionaryValue> params;
-  {
-    std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-    std::unique_ptr<base::Value> params_as_value(
-        converter->FromV8Value(args[2], context()->v8_context()));
-    params = base::DictionaryValue::From(std::move(params_as_value));
-    CHECK(params);
-  }
+  std::unique_ptr<base::DictionaryValue> params = base::DictionaryValue::From(
+      content::V8ValueConverter::Create()->FromV8Value(
+          args[2], context()->v8_context()));
+  CHECK(params);
+
   // We should be careful that some malicious JS in the GuestView's embedder
   // hasn't destroyed |guest_view_container| during the enumeration of the
   // properties of the guest's object during extraction of |params| above
@@ -249,14 +246,11 @@
   content::RenderFrame* render_frame = GetRenderFrame(args[3]);
   RenderFrameStatus render_frame_status(render_frame);
 
-  std::unique_ptr<base::DictionaryValue> params;
-  {
-    std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
-    std::unique_ptr<base::Value> params_as_value(
-        converter->FromV8Value(args[2], context()->v8_context()));
-    params = base::DictionaryValue::From(std::move(params_as_value));
-    CHECK(params);
-  }
+  std::unique_ptr<base::DictionaryValue> params = base::DictionaryValue::From(
+      content::V8ValueConverter::Create()->FromV8Value(
+          args[2], context()->v8_context()));
+  CHECK(params);
+
   if (!render_frame_status.is_ok())
     return;
 
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
index 14d599a2..22b266b 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
@@ -261,10 +261,8 @@
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::HandleScope handle_scope(isolate);
   v8::Context::Scope context_scope(frame->MainWorldScriptContext());
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  PostMessage(isolate,
-              converter->ToV8Value(&message, frame->MainWorldScriptContext()));
+  PostMessage(isolate, content::V8ValueConverter::Create()->ToV8Value(
+                           &message, frame->MainWorldScriptContext()));
 }
 
 void MimeHandlerViewContainer::OnCreateMimeHandlerViewGuestACK(
diff --git a/extensions/renderer/i18n_custom_bindings.cc b/extensions/renderer/i18n_custom_bindings.cc
index 57e0e96..828a4376 100644
--- a/extensions/renderer/i18n_custom_bindings.cc
+++ b/extensions/renderer/i18n_custom_bindings.cc
@@ -104,9 +104,8 @@
   v8::Isolate* isolate = v8_context->GetIsolate();
   v8::EscapableHandleScope handle_scope(isolate);
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  v8::Local<v8::Value> result = converter->ToV8Value(&dict_value, v8_context);
+  v8::Local<v8::Value> result =
+      content::V8ValueConverter::Create()->ToV8Value(&dict_value, v8_context);
   return handle_scope.Escape(result);
 }
 
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc
index a15870b..5b279b30 100644
--- a/extensions/renderer/messaging_bindings.cc
+++ b/extensions/renderer/messaging_bindings.cc
@@ -128,9 +128,8 @@
 
   if (extension) {
     if (!source->tab.empty() && !extension->is_platform_app()) {
-      std::unique_ptr<content::V8ValueConverter> converter(
-          content::V8ValueConverter::create());
-      tab = converter->ToV8Value(&source->tab, script_context->v8_context());
+      tab = content::V8ValueConverter::Create()->ToV8Value(
+          &source->tab, script_context->v8_context());
     }
 
     ExternallyConnectableInfo* externally_connectable =
diff --git a/extensions/renderer/resources/app_runtime_custom_bindings.js b/extensions/renderer/resources/app_runtime_custom_bindings.js
index f40f44df..d7fb573 100644
--- a/extensions/renderer/resources/app_runtime_custom_bindings.js
+++ b/extensions/renderer/resources/app_runtime_custom_bindings.js
@@ -10,13 +10,15 @@
     getInternalApi ?
         getInternalApi('appViewGuestInternal') :
         require('binding').Binding.create('appViewGuestInternal').generate();
-var eventBindings = require('event_bindings');
+var registerArgumentMassager = bindingUtil ?
+    $Function.bind(bindingUtil.registerEventArgumentMassager, bindingUtil) :
+    require('event_bindings').registerArgumentMassager;
 var fileSystemHelpers = requireNative('file_system_natives');
 var GetIsolatedFileSystem = fileSystemHelpers.GetIsolatedFileSystem;
 var entryIdManager = require('entryIdManager');
 
-eventBindings.registerArgumentMassager('app.runtime.onEmbedRequested',
-    function(args, dispatch) {
+registerArgumentMassager('app.runtime.onEmbedRequested',
+                         function(args, dispatch) {
   var appEmbeddingRequest = args[0];
   var id = appEmbeddingRequest.guestInstanceId;
   delete appEmbeddingRequest.guestInstanceId;
@@ -31,8 +33,7 @@
   dispatch([appEmbeddingRequest]);
 });
 
-eventBindings.registerArgumentMassager('app.runtime.onLaunched',
-    function(args, dispatch) {
+registerArgumentMassager('app.runtime.onLaunched', function(args, dispatch) {
   var launchData = args[0];
   if (launchData.items) {
     // An onLaunched corresponding to file_handlers in the app's manifest.
diff --git a/extensions/renderer/resources/app_window_custom_bindings.js b/extensions/renderer/resources/app_window_custom_bindings.js
index 2917c32..2432366 100644
--- a/extensions/renderer/resources/app_window_custom_bindings.js
+++ b/extensions/renderer/resources/app_window_custom_bindings.js
@@ -6,7 +6,6 @@
 
 var appWindowNatives = requireNative('app_window_natives');
 var runtimeNatives = requireNative('runtime');
-var Event = require('event_bindings').Event;
 var forEach = require('utils').forEach;
 var renderFrameObserverNatives = requireNative('renderFrameObserverNatives');
 
@@ -20,6 +19,17 @@
 if (!apiBridge)
   var binding = require('binding').Binding;
 
+var jsEvent;
+function createAnonymousEvent() {
+  if (bindingUtil) {
+    // Native custom events ignore schema.
+    return bindingUtil.createCustomEvent(undefined, undefined, false);
+  }
+  if (!jsEvent)
+    jsEvent = require('event_bindings').Event;
+  return new jsEvent();
+}
+
 // Bounds class definition.
 var Bounds = function(boundsKey) {
   privates(this).boundsKey_ = boundsKey;
@@ -211,7 +221,7 @@
     currentWindowInternal =
         getInternalApi ?
             getInternalApi('app.currentWindowInternal') :
-        binding.create('app.currentWindowInternal').generate();
+            binding.create('app.currentWindowInternal').generate();
     var AppWindow = function() {
       this.innerBounds = new Bounds('innerBounds');
       this.outerBounds = new Bounds('outerBounds');
@@ -225,7 +235,7 @@
     AppWindow.prototype.moveTo = $Function.bind(window.moveTo, window);
     AppWindow.prototype.resizeTo = $Function.bind(window.resizeTo, window);
     AppWindow.prototype.contentWindow = window;
-    AppWindow.prototype.onClosed = new Event();
+    AppWindow.prototype.onClosed = createAnonymousEvent();
     AppWindow.prototype.close = function() {
       this.contentWindow.close();
     };
diff --git a/extensions/renderer/resources/guest_view/guest_view.js b/extensions/renderer/resources/guest_view/guest_view.js
index 25dc6510..70726b46 100644
--- a/extensions/renderer/resources/guest_view/guest_view.js
+++ b/extensions/renderer/resources/guest_view/guest_view.js
@@ -6,7 +6,6 @@
 // creation, attaching, and destruction.
 
 var CreateEvent = require('guestViewEvents').CreateEvent;
-var EventBindings = require('event_bindings');
 var GuestViewInternal = getInternalApi ?
     getInternalApi('guestViewInternal') :
     require('binding').Binding.create('guestViewInternal').generate();
diff --git a/extensions/renderer/resources/guest_view/guest_view_events.js b/extensions/renderer/resources/guest_view/guest_view_events.js
index 4a0a7bd..870868d 100644
--- a/extensions/renderer/resources/guest_view/guest_view_events.js
+++ b/extensions/renderer/resources/guest_view/guest_view_events.js
@@ -4,7 +4,6 @@
 
 // Event management for GuestViewContainers.
 
-var EventBindings = require('event_bindings');
 var GuestViewInternalNatives = requireNative('guest_view_internal');
 var MessagingNatives = requireNative('messaging_natives');
 
diff --git a/extensions/renderer/resources/guest_view/web_view/web_view_events.js b/extensions/renderer/resources/guest_view/web_view/web_view_events.js
index 5cf50369..b413af3 100644
--- a/extensions/renderer/resources/guest_view/web_view/web_view_events.js
+++ b/extensions/renderer/resources/guest_view/web_view/web_view_events.js
@@ -7,7 +7,6 @@
 var CreateEvent = require('guestViewEvents').CreateEvent;
 var DeclarativeWebRequestSchema =
     requireNative('schema_registry').GetSchema('declarativeWebRequest');
-var EventBindings = require('event_bindings');
 var GuestViewEvents = require('guestViewEvents').GuestViewEvents;
 var GuestViewInternalNatives = requireNative('guest_view_internal');
 var IdGenerator = requireNative('id_generator');
@@ -26,6 +25,38 @@
   this.view.maybeSetupContextMenus();
 }
 
+var jsEvent;
+function createCustomDeclarativeEvent(name, schema, options, webviewId) {
+  if (bindingUtil) {
+    return bindingUtil.createCustomDeclarativeEvent(
+        name, options.actions, options.conditions, webviewId || 0);
+  }
+  if (!jsEvent)
+    jsEvent = require('event_bindings').Event;
+  return new jsEvent(name, schema, options, webviewId);
+}
+
+function createOnMessageEvent(name, schema, options, webviewId) {
+  var subEventName = name + '/' + IdGenerator.GetNextId();
+  var newEvent = createCustomDeclarativeEvent(subEventName,
+                                              schema,
+                                              options,
+                                              webviewId);
+
+  var view = GuestViewInternalNatives.GetViewFromID(webviewId || 0);
+  if (view) {
+    view.events.addScopedListener(
+        WebRequestMessageEvent,
+        $Function.bind(function() {
+          // Re-dispatch to subEvent's listeners.
+          $Function.apply(newEvent.dispatch, newEvent, $Array.slice(arguments));
+        }, newEvent),
+        {instanceId: webviewId || 0});
+  }
+
+  return newEvent;
+}
+
 WebViewEvents.prototype.__proto__ = GuestViewEvents.prototype;
 
 // A dictionary of <webview> extension events to be listened for. This
@@ -169,17 +200,25 @@
       $Function.bind(function(webRequestEvent) {
     return this.weakWrapper(function() {
       if (!this[webRequestEvent.name]) {
-        // The onMessage event gets a special event type because we want the
-        // listener to fire only for messages targeted for this particular
-        // <webview>.
-        var EventClass = webRequestEvent.name === 'onMessage' ?
-            DeclarativeWebRequestEvent : EventBindings.Event;
-        this[webRequestEvent.name] =
-            new EventClass(
-                'webViewInternal.declarativeWebRequest.' + webRequestEvent.name,
-                webRequestEvent.parameters,
-                webRequestEvent.options,
-                this.view.viewInstanceId);
+        var newEvent;
+        var eventName =
+            'webViewInternal.declarativeWebRequest.' + webRequestEvent.name;
+        if (webRequestEvent.name === 'onMessage') {
+          // The onMessage event gets a special event type because we want the
+          // listener to fire only for messages targeted for this particular
+          // <webview>.
+          newEvent = createOnMessageEvent(eventName,
+                                          webRequestEvent.parameters,
+                                          webRequestEvent.options,
+                                          this.view.viewInstanceId);
+        } else {
+          newEvent =
+              createCustomDeclarativeEvent(eventName,
+                                           webRequestEvent.parameters,
+                                           webRequestEvent.options,
+                                           this.view.viewInstanceId);
+        }
+        this[webRequestEvent.name] = newEvent;
       }
       return this[webRequestEvent.name];
     });
@@ -275,30 +314,5 @@
   this.view.onSizeChanged(webViewEvent);
 };
 
-function DeclarativeWebRequestEvent(opt_eventName,
-                                    opt_argSchemas,
-                                    opt_eventOptions,
-                                    opt_webViewInstanceId) {
-  var subEventName = opt_eventName + '/' + IdGenerator.GetNextId();
-  EventBindings.Event.call(this,
-                           subEventName,
-                           opt_argSchemas,
-                           opt_eventOptions,
-                           opt_webViewInstanceId);
-
-  var view = GuestViewInternalNatives.GetViewFromID(opt_webViewInstanceId || 0);
-  if (!view) {
-    return;
-  }
-  view.events.addScopedListener(
-      WebRequestMessageEvent,
-      $Function.bind(function() {
-        // Re-dispatch to subEvent's listeners.
-        $Function.apply(this.dispatch, this, $Array.slice(arguments));
-      }, this), {instanceId: opt_webViewInstanceId || 0});
-}
-
-DeclarativeWebRequestEvent.prototype.__proto__ = EventBindings.Event.prototype;
-
 // Exports.
 exports.$set('WebViewEvents', WebViewEvents);
diff --git a/extensions/renderer/runtime_custom_bindings.cc b/extensions/renderer/runtime_custom_bindings.cc
index fb1e4814..f43aeaa 100644
--- a/extensions/renderer/runtime_custom_bindings.cc
+++ b/extensions/renderer/runtime_custom_bindings.cc
@@ -38,9 +38,7 @@
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   CHECK(context()->extension());
 
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  args.GetReturnValue().Set(converter->ToV8Value(
+  args.GetReturnValue().Set(content::V8ValueConverter::Create()->ToV8Value(
       context()->extension()->manifest()->value(), context()->v8_context()));
 }
 
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index 256381e1..13c4bdf6 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -33,8 +33,6 @@
 #include "third_party/WebKit/public/web/WebView.h"
 #include "v8/include/v8.h"
 
-using content::V8ValueConverter;
-
 namespace extensions {
 
 namespace {
@@ -372,13 +370,12 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   v8::HandleScope handle_scope(isolate());
 
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
   v8::Local<v8::Value> argv[] = {
       v8::Integer::New(isolate(), request_id),
       v8::String::NewFromUtf8(isolate(), name.c_str()),
       v8::Boolean::New(isolate(), success),
-      converter->ToV8Value(&response,
-                           v8::Local<v8::Context>::New(isolate(), v8_context_)),
+      content::V8ValueConverter::Create()->ToV8Value(
+          &response, v8::Local<v8::Context>::New(isolate(), v8_context_)),
       v8::String::NewFromUtf8(isolate(), error.c_str())};
 
   module_system()->CallModuleMethodSafe("sendRequest", "handleResponse",
diff --git a/extensions/renderer/script_injection.cc b/extensions/renderer/script_injection.cc
index cb0e8a1..a1090e5 100644
--- a/extensions/renderer/script_injection.cc
+++ b/extensions/renderer/script_injection.cc
@@ -352,15 +352,14 @@
   if (expects_results) {
     if (!results.empty() && !results[0].IsEmpty()) {
       // Right now, we only support returning single results (per frame).
-      std::unique_ptr<content::V8ValueConverter> v8_converter(
-          content::V8ValueConverter::create());
       // It's safe to always use the main world context when converting
       // here. V8ValueConverterImpl shouldn't actually care about the
       // context scope, and it switches to v8::Object's creation context
       // when encountered.
       v8::Local<v8::Context> context =
           render_frame_->GetWebFrame()->MainWorldScriptContext();
-      execution_result_ = v8_converter->FromV8Value(results[0], context);
+      execution_result_ =
+          content::V8ValueConverter::Create()->FromV8Value(results[0], context);
     }
     if (!execution_result_.get())
       execution_result_ = base::MakeUnique<base::Value>();
diff --git a/extensions/renderer/send_request_natives.cc b/extensions/renderer/send_request_natives.cc
index 5824bc0..a305b6a9 100644
--- a/extensions/renderer/send_request_natives.cc
+++ b/extensions/renderer/send_request_natives.cc
@@ -13,8 +13,6 @@
 #include "extensions/renderer/request_sender.h"
 #include "extensions/renderer/script_context.h"
 
-using content::V8ValueConverter;
-
 namespace extensions {
 
 SendRequestNatives::SendRequestNatives(RequestSender* request_sender,
@@ -42,7 +40,8 @@
   int request_id = request_sender_->GetNextRequestId();
   args.GetReturnValue().Set(static_cast<int32_t>(request_id));
 
-  std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+  std::unique_ptr<content::V8ValueConverter> converter =
+      content::V8ValueConverter::Create();
 
   // See http://crbug.com/149880. The context menus APIs relies on this, but
   // we shouldn't really be doing it (e.g. for the sake of the storage API).
diff --git a/extensions/renderer/test_features_native_handler.cc b/extensions/renderer/test_features_native_handler.cc
index 45c0244..6e25f0a9 100644
--- a/extensions/renderer/test_features_native_handler.cc
+++ b/extensions/renderer/test_features_native_handler.cc
@@ -23,10 +23,8 @@
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   std::unique_ptr<JSONFeatureProviderSource> source(
       ExtensionsClient::Get()->CreateAPIFeatureSource());
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
-  args.GetReturnValue().Set(
-      converter->ToV8Value(&source->dictionary(), context()->v8_context()));
+  args.GetReturnValue().Set(content::V8ValueConverter::Create()->ToV8Value(
+      &source->dictionary(), context()->v8_context()));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/web_request_hooks.cc b/extensions/renderer/web_request_hooks.cc
index ad2c3fb..0f833278 100644
--- a/extensions/renderer/web_request_hooks.cc
+++ b/extensions/renderer/web_request_hooks.cc
@@ -59,10 +59,8 @@
   DCHECK(event_spec);
   const base::ListValue* extra_params = nullptr;
   CHECK(event_spec->GetList("extraParameters", &extra_params));
-  std::unique_ptr<content::V8ValueConverter> converter(
-      content::V8ValueConverter::create());
   v8::Local<v8::Value> extra_parameters_spec =
-      converter->ToV8Value(extra_params, context);
+      content::V8ValueConverter::Create()->ToV8Value(extra_params, context);
 
   v8::Local<v8::Function> get_event = get_event_value.As<v8::Function>();
   v8::Local<v8::Value> args[] = {
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.h b/ios/chrome/browser/translate/chrome_ios_translate_client.h
index da7d420..94de42c 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client.h
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client.h
@@ -18,6 +18,10 @@
 
 class PrefService;
 
+namespace metrics {
+class TranslateEventProto;
+}  // namespace metrics
+
 namespace translate {
 class TranslateAcceptLanguages;
 class TranslatePrefs;
@@ -26,7 +30,7 @@
 
 namespace web {
 class WebState;
-}
+}  // namespace web
 
 class ChromeIOSTranslateClient
     : public translate::TranslateClient,
@@ -48,6 +52,7 @@
   std::unique_ptr<translate::TranslatePrefs> GetTranslatePrefs() override;
   translate::TranslateAcceptLanguages* GetTranslateAcceptLanguages() override;
   int GetInfobarIconID() const override;
+  void RecordTranslateEvent(const metrics::TranslateEventProto&) override;
   std::unique_ptr<infobars::InfoBar> CreateInfoBar(
       std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
       const override;
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.mm b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
index b474a280..b374335 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client.mm
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "components/infobars/core/infobar.h"
+#include "components/metrics/proto/translate_event.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/translate/core/browser/page_translated_details.h"
 #include "components/translate/core/browser/translate_accept_languages.h"
@@ -100,6 +101,11 @@
   return std::move(infobar);
 }
 
+void ChromeIOSTranslateClient::RecordTranslateEvent(
+    const metrics::TranslateEventProto&) {
+  // TODO(crbug.com/728491): Implementing gaia-keyed logging.
+}
+
 void ChromeIOSTranslateClient::ShowTranslateUI(
     translate::TranslateStep step,
     const std::string& source_language,
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index 142b4239..bb1c1571 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -40,6 +40,7 @@
 using chrome_test_util::ButtonWithAccessibilityLabel;
 using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsMenuBackButton;
 
 namespace {
 
@@ -155,7 +156,7 @@
                                           IDS_IOS_FIRSTRUN_TERMS_TITLE))]
       assertWithMatcher:grey_sufficientlyVisible()];
 
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"ic_arrow_back")]
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
 
   // Ensure we went back to the First Run screen.
diff --git a/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
index 07e92304..0a8a9520 100644
--- a/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
@@ -24,6 +24,7 @@
 using chrome_test_util::ButtonWithAccessibilityLabel;
 using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsMenuBackButton;
 
 namespace {
 
@@ -145,19 +146,10 @@
 
 // Close the settings.
 - (void)exitSettingsMenu {
-  NSString* backButtonA11yId = @"ic_arrow_back";
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(backButtonA11yId),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(backButtonA11yId),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
       performAction:grey_tap()];
   // Wait for UI components to finish loading.
diff --git a/ios/chrome/browser/ui/settings/block_popups_egtest.mm b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
index 58fbbaa..246f0b4 100644
--- a/ios/chrome/browser/ui/settings/block_popups_egtest.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
@@ -35,7 +35,9 @@
 #error "This file requires ARC support."
 #endif
 
+using chrome_test_util::ContentSettingsButton;
 using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsMenuBackButton;
 
 namespace {
 
@@ -53,45 +55,9 @@
 NSString* kOpenPopupScript = @"document.getElementById('open-window').click()";
 const std::string kOpenedWindowResponse = "Opened window";
 
-// Opens the block popups settings page.  Must be called from the NTP.
-void OpenBlockPopupsSettings() {
-  const CGFloat scroll_displacement = 50.0;
-  id<GREYMatcher> content_settings_button_matcher =
-      chrome_test_util::ButtonWithAccessibilityLabelId(
-          IDS_IOS_CONTENT_SETTINGS_TITLE);
-  id<GREYMatcher> settings_collection_view_matcher =
-      grey_accessibilityID(kSettingsCollectionViewId);
-  id<GREYMatcher> block_popups_button_matcher =
-      chrome_test_util::ButtonWithAccessibilityLabelId(IDS_IOS_BLOCK_POPUPS);
-
-  [ChromeEarlGreyUI openSettingsMenu];
-
-  [[[EarlGrey selectElementWithMatcher:content_settings_button_matcher]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  scroll_displacement)
-      onElementWithMatcher:settings_collection_view_matcher]
-      performAction:grey_tap()];
-
-  [[EarlGrey selectElementWithMatcher:block_popups_button_matcher]
-      performAction:grey_tap()];
-}
-
-// Exits out of settings.  Must be called from the block popups settings page.
-void CloseSettings() {
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(@"ic_arrow_back"),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(@"ic_arrow_back"),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
-      performAction:grey_tap()];
+// Returns matcher for the block popups settings menu button.
+id<GREYMatcher> BlockPopupsSettingsButton() {
+  return chrome_test_util::ButtonWithAccessibilityLabelId(IDS_IOS_BLOCK_POPUPS);
 }
 
 // ScopedBlockPopupsPref modifies the block popups preference and resets the
@@ -173,13 +139,23 @@
 // Opens the block popups settings page and verifies that accessibility is set
 // up properly.
 - (void)testAccessibilityOfBlockPopupSettings {
-  OpenBlockPopupsSettings();
+  [ChromeEarlGreyUI openSettingsMenu];
+  [ChromeEarlGreyUI tapSettingsMenuButton:ContentSettingsButton()];
+  [[EarlGrey selectElementWithMatcher:BlockPopupsSettingsButton()]
+      performAction:grey_tap()];
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(
                                    @"block_popups_settings_view_controller")]
       assertWithMatcher:grey_notNil()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
-  CloseSettings();
+
+  // Close the settings menu.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
+      performAction:grey_tap()];
 }
 
 // Tests that popups are opened in new tabs when the preference is set to ALLOW.
@@ -257,7 +233,10 @@
   ScopedBlockPopupsPref prefSetter(CONTENT_SETTING_BLOCK);
   ScopedBlockPopupsException exceptionSetter(allowedPattern);
 
-  OpenBlockPopupsSettings();
+  [ChromeEarlGreyUI openSettingsMenu];
+  [ChromeEarlGreyUI tapSettingsMenuButton:ContentSettingsButton()];
+  [[EarlGrey selectElementWithMatcher:BlockPopupsSettingsButton()]
+      performAction:grey_tap()];
 
   // Make sure that the "example.com" exception is listed.
   [[EarlGrey selectElementWithMatcher:grey_text(base::SysUTF8ToNSString(
@@ -294,7 +273,13 @@
                                    IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON))]
       assertWithMatcher:grey_sufficientlyVisible()];
 
-  CloseSettings();
+  // Close the settings menu.
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
+      performAction:grey_tap()];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/settings/settings_egtest.mm b/ios/chrome/browser/ui/settings/settings_egtest.mm
index edb30052..cc1dfcf2 100644
--- a/ios/chrome/browser/ui/settings/settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/settings_egtest.mm
@@ -58,7 +58,9 @@
 
 using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::ClearBrowsingDataCollectionView;
+using chrome_test_util::ContentSettingsButton;
 using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsMenuBackButton;
 
 namespace {
 
@@ -163,10 +165,6 @@
 id<GREYMatcher> SavePasswordButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_PASSWORD_MANAGER_SAVE_BUTTON);
 }
-// Matcher for the Content Settings button on the main Settings screen.
-id<GREYMatcher> ContentSettingsButton() {
-  return ButtonWithAccessibilityLabelId(IDS_IOS_CONTENT_SETTINGS_TITLE);
-}
 // Matcher for the Bandwidth Settings button on the main Settings screen.
 id<GREYMatcher> BandwidthSettingsButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_BANDWIDTH_MANAGEMENT_SETTINGS);
@@ -255,12 +253,8 @@
 
 // Closes a sub-settings menu, and then the general Settings menu.
 - (void)closeSubSettingsMenu {
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(@"ic_arrow_back"),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
       performAction:grey_tap()];
 }
@@ -922,16 +916,7 @@
       performAction:grey_tap()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
 
-  // Exit settings.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(@"ic_arrow_back"),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
-
-  [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
-      performAction:grey_tap()];
+  [self closeSubSettingsMenu];
 }
 
 // Verifies that the Settings UI registers keyboard commands when presented, but
diff --git a/ios/chrome/browser/ui/settings/translate_ui_egtest.mm b/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
index 132e507..2f64b38 100644
--- a/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
+++ b/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
@@ -18,12 +18,10 @@
 #error "This file requires ARC support."
 #endif
 
+using chrome_test_util::ContentSettingsButton;
 using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsMenuBackButton;
 
-namespace {
-// Displacement for scroll action.
-const CGFloat kScrollDisplacement = 50.0;
-}  // namespace
 @interface TranslateUITestCase : ChromeTestCase
 @end
 
@@ -36,13 +34,8 @@
   // TODO(crbug.com/606815): This and close settings is mostly shared with block
   // popups settings tests, and others. See if this can move to shared code.
   [ChromeEarlGreyUI openSettingsMenu];
-  [[[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_CONTENT_SETTINGS_TITLE)]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  kScrollDisplacement)
-      onElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI tapSettingsMenuButton:ContentSettingsButton()];
+
   [[EarlGrey
       selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
                                    IDS_IOS_TRANSLATE_SETTING)]
@@ -56,18 +49,10 @@
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
 
   // Close settings.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(@"ic_arrow_back"),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityID(@"ic_arrow_back"),
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitButton),
-                                   nil)] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
       performAction:grey_tap()];
 }
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index c2df86e..d9002c72 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -120,6 +120,12 @@
 // Returns matcher for the menu button to sync accounts.
 id<GREYMatcher> AccountsSyncButton();
 
+// Returns matcher for the Content Settings button on the main Settings screen.
+id<GREYMatcher> ContentSettingsButton();
+
+// Returns matcher for the back button on a settings menu.
+id<GREYMatcher> SettingsMenuBackButton();
+
 }  // namespace chrome_test_util
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index 6fd9cef6..c9aa5e42 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -213,4 +213,13 @@
   return grey_accessibilityID(kSettingsAccountsSyncCellId);
 }
 
+id<GREYMatcher> ContentSettingsButton() {
+  return ButtonWithAccessibilityLabelId(IDS_IOS_CONTENT_SETTINGS_TITLE);
+}
+
+id<GREYMatcher> SettingsMenuBackButton() {
+  return grey_allOf(grey_accessibilityID(@"ic_arrow_back"),
+                    grey_accessibilityTrait(UIAccessibilityTraitButton), nil);
+}
+
 }  // namespace chrome_test_util
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index fe7e3a0..01c9601a 100644
--- a/ios/third_party/material_components_ios/README.chromium
+++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Components for iOS
 URL: https://github.com/material-components/material-components-ios
 Version: 0
-Revision: 6bc1117ad8d9779e3d2f4006f28c675cfeada9eb
+Revision: 6d5844642b004594ba1607916032616f85c7c045
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/ios/web/public/referrer.h b/ios/web/public/referrer.h
index d001863..93833a4 100644
--- a/ios/web/public/referrer.h
+++ b/ios/web/public/referrer.h
@@ -17,7 +17,10 @@
   ReferrerPolicyNever,
   ReferrerPolicyOrigin,
   ReferrerPolicyOriginWhenCrossOrigin,
-  ReferrerPolicyLast = ReferrerPolicyOriginWhenCrossOrigin
+  ReferrerPolicySameOrigin,
+  ReferrerPolicyStrictOrigin,
+  ReferrerPolicyStrictOriginWhenCrossOrigin,
+  ReferrerPolicyLast = ReferrerPolicyStrictOriginWhenCrossOrigin
 };
 
 // This struct holds a referrer URL, as well as the referrer policy to be
diff --git a/ios/web/public/referrer_util.cc b/ios/web/public/referrer_util.cc
index 4bee17f..f572293 100644
--- a/ios/web/public/referrer_util.cc
+++ b/ios/web/public/referrer_util.cc
@@ -31,6 +31,20 @@
       if (referrer.url.GetOrigin() != destination.GetOrigin())
         return referrer.url.GetOrigin().spec();
       return referrer.url.GetAsReferrer().spec();
+    case ReferrerPolicySameOrigin:
+      if (referrer.url.GetOrigin() != destination.GetOrigin())
+        return std::string();
+      return referrer.url.GetAsReferrer().spec();
+    case ReferrerPolicyStrictOrigin:
+      if (is_downgrade)
+        return std::string();
+      return referrer.url.GetOrigin().spec();
+    case ReferrerPolicyStrictOriginWhenCrossOrigin:
+      if (is_downgrade)
+        return std::string();
+      if (referrer.url.GetOrigin() != destination.GetOrigin())
+        return referrer.url.GetOrigin().spec();
+      return referrer.url.GetAsReferrer().spec();
   }
   NOTREACHED();
   return std::string();
@@ -43,15 +57,25 @@
   // resource_dispatcher_host_impl.cc
   switch (referrer.policy) {
     case ReferrerPolicyAlways:
-    case ReferrerPolicyNever:
-    case ReferrerPolicyOrigin:
       return net::URLRequest::NEVER_CLEAR_REFERRER;
+    case ReferrerPolicyNever:
+      return net::URLRequest::NO_REFERRER;
+    case ReferrerPolicyOrigin:
+      return net::URLRequest::ORIGIN;
     case ReferrerPolicyNoReferrerWhenDowngrade:
     case ReferrerPolicyDefault:
       return net::URLRequest::
           CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
     case ReferrerPolicyOriginWhenCrossOrigin:
       return net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN;
+    case ReferrerPolicySameOrigin:
+      return net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN;
+    case ReferrerPolicyStrictOrigin:
+      return net::URLRequest::
+          ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
+    case ReferrerPolicyStrictOriginWhenCrossOrigin:
+      return net::URLRequest::
+          REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
   }
   NOTREACHED();
   return net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
@@ -69,8 +93,13 @@
     return ReferrerPolicyOriginWhenCrossOrigin;
   if (policy == "always" || policy == "unsafe-url")
     return ReferrerPolicyAlways;
-  // Note that this is *not* Default; per spec, anything unknown is Never.
-  return web::ReferrerPolicyNever;
+  if (policy == "same-origin")
+    return ReferrerPolicySameOrigin;
+  if (policy == "strict-origin")
+    return ReferrerPolicyStrictOrigin;
+  if (policy == "strict-origin-when-cross-origin")
+    return ReferrerPolicyStrictOriginWhenCrossOrigin;
+  return ReferrerPolicyDefault;
 }
 
 }  // namespace web
diff --git a/ios/web/public/referrer_util_unittest.cc b/ios/web/public/referrer_util_unittest.cc
index 0d4cb23..9da6fae 100644
--- a/ios/web/public/referrer_util_unittest.cc
+++ b/ios/web/public/referrer_util_unittest.cc
@@ -128,6 +128,65 @@
   }
 }
 
+// Tests that the same-origin policy works as expected.
+TEST(ReferrerUtilTest, SameOriginPolicy) {
+  for (unsigned int source = 0; source < arraysize(kTestUrls); ++source) {
+    for (unsigned int dest = 1; dest < arraysize(kTestUrls); ++dest) {
+      GURL source_url(kTestUrls[source]);
+      GURL dest_url(kTestUrls[dest]);
+      Referrer referrer(source_url, ReferrerPolicySameOrigin);
+      std::string value = ReferrerHeaderValueForNavigation(dest_url, referrer);
+
+      // Full URL for the same origin, and nothing for all other cases.
+      if (source_url.GetOrigin() == dest_url.GetOrigin())
+        EXPECT_EQ(source_url.GetAsReferrer().spec(), value);
+      else
+        EXPECT_EQ(std::string(), value);
+    }
+  }
+}
+
+// Tests that the strict-origin policy works as expected.
+TEST(ReferrerUtilTest, StrictOriginPolicy) {
+  for (unsigned int source = 0; source < arraysize(kTestUrls); ++source) {
+    for (unsigned int dest = 1; dest < arraysize(kTestUrls); ++dest) {
+      GURL source_url(kTestUrls[source]);
+      GURL dest_url(kTestUrls[dest]);
+      Referrer referrer(source_url, ReferrerPolicyStrictOrigin);
+      std::string value = ReferrerHeaderValueForNavigation(dest_url, referrer);
+
+      // No referrer when downgrading, and origin otherwise.
+      if (source_url.SchemeIsCryptographic() &&
+          !dest_url.SchemeIsCryptographic())
+        EXPECT_EQ("", value);
+      else
+        EXPECT_EQ(source_url.GetOrigin().spec(), value);
+    }
+  }
+}
+
+// Tests that the strict-origin-when-cross-origin policy works as expected.
+TEST(ReferrerUtilTest, StrictOriginWhenCrossOriginPolicy) {
+  for (unsigned int source = 0; source < arraysize(kTestUrls); ++source) {
+    for (unsigned int dest = 1; dest < arraysize(kTestUrls); ++dest) {
+      GURL source_url(kTestUrls[source]);
+      GURL dest_url(kTestUrls[dest]);
+      Referrer referrer(source_url, ReferrerPolicyStrictOriginWhenCrossOrigin);
+      std::string value = ReferrerHeaderValueForNavigation(dest_url, referrer);
+
+      // No referrer when downgrading, origin when cross-origin but not
+      // downgrading, and full referrer otherwise.
+      if (source_url.SchemeIsCryptographic() &&
+          !dest_url.SchemeIsCryptographic())
+        EXPECT_EQ("", value);
+      else if (source_url.GetOrigin() == dest_url.GetOrigin())
+        EXPECT_EQ(source_url.GetAsReferrer().spec(), value);
+      else
+        EXPECT_EQ(source_url.GetOrigin().spec(), value);
+    }
+  }
+}
+
 // Tests that PolicyForNavigation gives the right values.
 TEST(ReferrerUtilTest, PolicyForNavigation) {
   // The request and destination URLs are unused in the current implementation,
@@ -143,32 +202,33 @@
     // incorrect mappings.
     switch (net_request_policy) {
       case net::URLRequest::
-          REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
-        // Nothing currently maps to this policy on iOS.
-        FAIL();
-        break;
-      case net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
-        EXPECT_EQ(ReferrerPolicyOriginWhenCrossOrigin, policy);
-        break;
-      case net::URLRequest::NEVER_CLEAR_REFERRER:
-        // This request policy should be used when the referrer policy is always
-        // the same regardless of source and destination.
-        EXPECT_TRUE(policy == ReferrerPolicyAlways ||
-                    policy == ReferrerPolicyNever ||
-                    policy == ReferrerPolicyOrigin);
-        break;
-      case net::URLRequest::
           CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
         // This corresponds directly to ReferrerPolicyNoReferrerWhenDowngrade,
         // which is also how Default works on iOS.
         EXPECT_TRUE(policy == ReferrerPolicyDefault ||
                     policy == ReferrerPolicyNoReferrerWhenDowngrade);
         break;
+      case net::URLRequest::
+          REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
+        EXPECT_EQ(ReferrerPolicyStrictOriginWhenCrossOrigin, policy);
+        break;
+      case net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
+        EXPECT_EQ(ReferrerPolicyOriginWhenCrossOrigin, policy);
+        break;
+      case net::URLRequest::NEVER_CLEAR_REFERRER:
+        EXPECT_EQ(ReferrerPolicyAlways, policy);
+        break;
       case net::URLRequest::ORIGIN:
-        EXPECT_TRUE(policy == ReferrerPolicyOrigin);
+        EXPECT_EQ(ReferrerPolicyOrigin, policy);
+        break;
+      case net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN:
+        EXPECT_EQ(ReferrerPolicySameOrigin, policy);
+        break;
+      case net::URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+        EXPECT_EQ(ReferrerPolicyStrictOrigin, policy);
         break;
       case net::URLRequest::NO_REFERRER:
-        EXPECT_TRUE(policy == ReferrerPolicyNever);
+        EXPECT_EQ(ReferrerPolicyNever, policy);
         break;
       case net::URLRequest::MAX_REFERRER_POLICY:
         FAIL();
@@ -189,6 +249,9 @@
       "no-referrer",
       "origin",
       "origin-when-cross-origin",
+      "same-origin",
+      "strict-origin",
+      "strict-origin-when-cross-origin",
   };
   // Test that all the values are supported.
   for (int i = 0; i < ReferrerPolicyLast; ++i) {
@@ -210,9 +273,9 @@
             ReferrerPolicyFromString("default"));
   EXPECT_EQ(ReferrerPolicyAlways, ReferrerPolicyFromString("always"));
 
-  // Test that invalid values map to Never.
-  EXPECT_EQ(ReferrerPolicyNever, ReferrerPolicyFromString(""));
-  EXPECT_EQ(ReferrerPolicyNever, ReferrerPolicyFromString("made-up"));
+  // Test that invalid values map to Default.
+  EXPECT_EQ(ReferrerPolicyDefault, ReferrerPolicyFromString(""));
+  EXPECT_EQ(ReferrerPolicyDefault, ReferrerPolicyFromString("made-up"));
 }
 
 }  // namespace web
diff --git a/ios/web_view/internal/translate/web_view_translate_client.h b/ios/web_view/internal/translate/web_view_translate_client.h
index b214e5f..d38a71d 100644
--- a/ios/web_view/internal/translate/web_view_translate_client.h
+++ b/ios/web_view/internal/translate/web_view_translate_client.h
@@ -20,6 +20,10 @@
 
 class PrefService;
 
+namespace metrics {
+class TranslateEventProto;
+}  // namespace metrics
+
 namespace translate {
 class TranslateAcceptLanguages;
 class TranslatePrefs;
@@ -28,7 +32,7 @@
 
 namespace web {
 class WebState;
-}
+}  // namespace web
 
 namespace ios_web_view {
 
@@ -60,6 +64,7 @@
   std::unique_ptr<translate::TranslatePrefs> GetTranslatePrefs() override;
   translate::TranslateAcceptLanguages* GetTranslateAcceptLanguages() override;
   int GetInfobarIconID() const override;
+  void RecordTranslateEvent(const metrics::TranslateEventProto&) override;
   std::unique_ptr<infobars::InfoBar> CreateInfoBar(
       std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
       const override;
diff --git a/ios/web_view/internal/translate/web_view_translate_client.mm b/ios/web_view/internal/translate/web_view_translate_client.mm
index ddc9bed..7ade020 100644
--- a/ios/web_view/internal/translate/web_view_translate_client.mm
+++ b/ios/web_view/internal/translate/web_view_translate_client.mm
@@ -112,6 +112,11 @@
   return 0;
 }
 
+void WebViewTranslateClient::RecordTranslateEvent(
+    const metrics::TranslateEventProto&) {
+  // TODO(crbug.com/728491): Implementing gaia-keyed logging.
+}
+
 bool WebViewTranslateClient::IsTranslatableURL(const GURL& url) {
   return !url.is_empty() && !url.SchemeIs(url::kFtpScheme);
 }
diff --git a/media/base/android/mock_android_overlay.h b/media/base/android/mock_android_overlay.h
index a841e9f..7a7959a 100644
--- a/media/base/android/mock_android_overlay.h
+++ b/media/base/android/mock_android_overlay.h
@@ -45,12 +45,12 @@
   // Return callbacks that can be used to control the overlay.
   Callbacks GetCallbacks();
 
- private:
   // Send callbacks.
   void OnOverlayReady();
   void OnOverlayFailed();
   void OnSurfaceDestroyed();
 
+ private:
   // Initial configuration, mostly for callbacks.
   std::unique_ptr<AndroidOverlayConfig> config_;
 
diff --git a/media/base/audio_bus_unittest.cc b/media/base/audio_bus_unittest.cc
index 1dfb4b90..99f51034 100644
--- a/media/base/audio_bus_unittest.cc
+++ b/media/base/audio_bus_unittest.cc
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/aligned_memory.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
diff --git a/media/base/overlay_info.cc b/media/base/overlay_info.cc
index 1658f2a..8704658 100644
--- a/media/base/overlay_info.cc
+++ b/media/base/overlay_info.cc
@@ -18,4 +18,8 @@
   return routing_token.has_value();
 }
 
+bool OverlayInfo::RefersToSameOverlayAs(const OverlayInfo& other) {
+  return surface_id == other.surface_id && routing_token == other.routing_token;
+}
+
 }  // namespace media
diff --git a/media/base/overlay_info.h b/media/base/overlay_info.h
index 089edca..cc788be 100644
--- a/media/base/overlay_info.h
+++ b/media/base/overlay_info.h
@@ -28,6 +28,10 @@
   bool HasValidSurfaceId() const;
   bool HasValidRoutingToken() const;
 
+  // Whether |other| refers to the same (surface_id, routing_token) pair as
+  // |this|.
+  bool RefersToSameOverlayAs(const OverlayInfo& other);
+
   // This is the SurfaceManager surface id, or SurfaceManager::kNoSurfaceID to
   // indicate that no surface from SurfaceManager should be used.
   int surface_id = SurfaceManager::kNoSurfaceID;
@@ -39,7 +43,6 @@
   bool is_fullscreen = false;
 };
 
-// Request OverlayInformation.
 using ProvideOverlayInfoCB = base::Callback<void(const OverlayInfo&)>;
 using RequestOverlayInfoCB =
     base::Callback<void(bool, const ProvideOverlayInfoCB&)>;
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index af488adf..5895253 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -15,6 +15,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/md5.h"
+#include "base/memory/aligned_memory.h"
 #include "base/memory/shared_memory.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 1b58a14..649a0b4a3 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -180,6 +180,8 @@
 
   if (is_android) {
     sources += [
+      "android/device_info.cc",
+      "android/device_info.h",
       "android_video_decode_accelerator.cc",
       "android_video_decode_accelerator.h",
       "android_video_surface_chooser.h",
@@ -457,10 +459,14 @@
     sources = [
       "android/fake_codec_allocator.cc",
       "android/fake_codec_allocator.h",
+      "android/mock_device_info.cc",
+      "android/mock_device_info.h",
       "android_video_decode_accelerator_unittest.cc",
       "android_video_surface_chooser_impl_unittest.cc",
       "avda_codec_allocator_unittest.cc",
       "content_video_view_overlay_allocator_unittest.cc",
+      "fake_android_video_surface_chooser.cc",
+      "fake_android_video_surface_chooser.h",
       "mock_surface_texture_gl_owner.cc",
       "mock_surface_texture_gl_owner.h",
       "surface_texture_gl_owner_unittest.cc",
diff --git a/media/gpu/android/codec_wrapper.cc b/media/gpu/android/codec_wrapper.cc
index c738a72..e423879a 100644
--- a/media/gpu/android/codec_wrapper.cc
+++ b/media/gpu/android/codec_wrapper.cc
@@ -26,7 +26,7 @@
   std::unique_ptr<MediaCodecBridge> TakeCodec();
   bool HasValidCodecOutputBuffers() const;
   void DiscardCodecOutputBuffers();
-  bool SupportsFlush() const;
+  bool SupportsFlush(DeviceInfo* device_info) const;
   bool Flush();
   MediaCodecStatus QueueInputBuffer(int index,
                                     const uint8_t* data,
@@ -122,9 +122,9 @@
   buffer_ids_.clear();
 }
 
-bool CodecWrapperImpl::SupportsFlush() const {
+bool CodecWrapperImpl::SupportsFlush(DeviceInfo* device_info) const {
   base::AutoLock l(lock_);
-  return !MediaCodecUtil::CodecNeedsFlushWorkaround(codec_.get());
+  return !device_info->CodecNeedsFlushWorkaround(codec_.get());
 }
 
 bool CodecWrapperImpl::Flush() {
@@ -289,8 +289,8 @@
   impl_->DiscardCodecOutputBuffers();
 }
 
-bool CodecWrapper::SupportsFlush() const {
-  return impl_->SupportsFlush();
+bool CodecWrapper::SupportsFlush(DeviceInfo* device_info) const {
+  return impl_->SupportsFlush(device_info);
 }
 
 bool CodecWrapper::Flush() {
diff --git a/media/gpu/android/codec_wrapper.h b/media/gpu/android/codec_wrapper.h
index f8a483c..eab46bf 100644
--- a/media/gpu/android/codec_wrapper.h
+++ b/media/gpu/android/codec_wrapper.h
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
 #include "media/base/android/media_codec_bridge.h"
+#include "media/gpu/android/device_info.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/surface_texture_gl_owner.h"
 
@@ -76,7 +77,7 @@
   void DiscardCodecOutputBuffers();
 
   // Whether the codec supports Flush().
-  bool SupportsFlush() const;
+  bool SupportsFlush(DeviceInfo* device_info) const;
 
   // See MediaCodecBridge documentation for the following.
   bool Flush();
diff --git a/media/gpu/android/device_info.cc b/media/gpu/android/device_info.cc
new file mode 100644
index 0000000..a7f16357
--- /dev/null
+++ b/media/gpu/android/device_info.cc
@@ -0,0 +1,51 @@
+// 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 "media/gpu/android/device_info.h"
+
+#include "base/android/build_info.h"
+#include "media/base/android/media_codec_util.h"
+
+namespace media {
+
+DeviceInfo* DeviceInfo::GetInstance() {
+  static DeviceInfo* info = new DeviceInfo();
+  return info;
+}
+
+int DeviceInfo::SdkVersion() {
+  static int result = base::android::BuildInfo::GetInstance()->sdk_int();
+  return result;
+}
+
+bool DeviceInfo::IsVp8DecoderAvailable() {
+  static bool result = MediaCodecUtil::IsVp8DecoderAvailable();
+  return result;
+}
+
+bool DeviceInfo::IsVp9DecoderAvailable() {
+  static bool result = MediaCodecUtil::IsVp9DecoderAvailable();
+  return result;
+}
+
+bool DeviceInfo::IsDecoderKnownUnaccelerated(VideoCodec codec) {
+  return MediaCodecUtil::IsKnownUnaccelerated(codec,
+                                              MediaCodecDirection::DECODER);
+}
+
+bool DeviceInfo::IsSetOutputSurfaceSupported() {
+  static bool result = MediaCodecUtil::IsSetOutputSurfaceSupported();
+  return result;
+}
+
+bool DeviceInfo::SupportsOverlaySurfaces() {
+  static bool result = MediaCodecUtil::IsSurfaceViewOutputSupported();
+  return result;
+}
+
+bool DeviceInfo::CodecNeedsFlushWorkaround(MediaCodecBridge* codec) {
+  return MediaCodecUtil::CodecNeedsFlushWorkaround(codec);
+}
+
+}  // namespace media
diff --git a/media/gpu/android/device_info.h b/media/gpu/android/device_info.h
new file mode 100644
index 0000000..500b9e7e
--- /dev/null
+++ b/media/gpu/android/device_info.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_ANDROID_DEVICE_INFO_H_
+#define MEDIA_GPU_ANDROID_DEVICE_INFO_H_
+
+#include "media/base/video_codecs.h"
+
+namespace media {
+class MediaCodecBridge;
+
+// Info about the current platform and device with caching of the results that
+// don't change. Virtual for testing.
+struct DeviceInfo {
+  static DeviceInfo* GetInstance();
+
+  virtual int SdkVersion();
+  virtual bool IsVp8DecoderAvailable();
+  virtual bool IsVp9DecoderAvailable();
+  virtual bool IsDecoderKnownUnaccelerated(VideoCodec codec);
+  virtual bool IsSetOutputSurfaceSupported();
+  virtual bool SupportsOverlaySurfaces();
+  virtual bool CodecNeedsFlushWorkaround(MediaCodecBridge* codec);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_ANDROID_DEVICE_INFO_H_
diff --git a/media/gpu/android/fake_codec_allocator.cc b/media/gpu/android/fake_codec_allocator.cc
index 8a0843c..d957b7c 100644
--- a/media/gpu/android/fake_codec_allocator.cc
+++ b/media/gpu/android/fake_codec_allocator.cc
@@ -68,26 +68,22 @@
                         surface_bundle->surface_texture.get());
 }
 
-void FakeCodecAllocator::ProvideMockCodecAsync() {
+MockMediaCodecBridge* FakeCodecAllocator::ProvideMockCodecAsync() {
   // There must be a pending codec creation.
-  ASSERT_TRUE(client_);
-  if (!client_)
-    return;
+  DCHECK(client_);
 
   std::unique_ptr<MockMediaCodecBridge> codec =
-      base::MakeUnique<MockMediaCodecBridge>();
-  most_recent_codec_ = codec.get();
+      base::MakeUnique<NiceMock<MockMediaCodecBridge>>();
+  auto* raw_codec = codec.get();
+  most_recent_codec_ = raw_codec;
   most_recent_codec_destruction_observer_ = codec->CreateDestructionObserver();
-  most_recent_codec_destruction_observer_->DoNotAllowDestruction();
   client_->OnCodecConfigured(std::move(codec));
+  return raw_codec;
 }
 
 void FakeCodecAllocator::ProvideNullCodecAsync() {
   // There must be a pending codec creation.
-  ASSERT_TRUE(client_);
-  if (!client_)
-    return;
-
+  DCHECK(client_);
   most_recent_codec_ = nullptr;
   client_->OnCodecConfigured(nullptr);
 }
@@ -95,9 +91,7 @@
 void FakeCodecAllocator::CopyCodecAllocParams(
     scoped_refptr<CodecConfig> config) {
   config_ = config;
-  most_recent_bundle_ = config->surface_bundle;
   most_recent_overlay_ = config->surface_bundle->overlay.get();
-
   most_recent_surface_texture_ = config->surface_bundle->surface_texture.get();
 }
 
diff --git a/media/gpu/android/fake_codec_allocator.h b/media/gpu/android/fake_codec_allocator.h
index f14e7fc..80b0c14 100644
--- a/media/gpu/android/fake_codec_allocator.h
+++ b/media/gpu/android/fake_codec_allocator.h
@@ -47,15 +47,13 @@
       std::unique_ptr<MediaCodecBridge> media_codec,
       scoped_refptr<AVDASurfaceBundle> surface_bundle) override;
 
-  // Satisfies the pending codec creation with a mock codec.
-  void ProvideMockCodecAsync();
+  // Satisfies the pending codec creation with a mock codec and returns a raw
+  // pointer to it.
+  MockMediaCodecBridge* ProvideMockCodecAsync();
 
   // Satisfies the pending codec creation with a null codec.
   void ProvideNullCodecAsync();
 
-  // Returns the most recent bundle that we've received for codec allocation.
-  scoped_refptr<AVDASurfaceBundle> most_recent_bundle();
-
   // Returns the most recent codec that we provided, which might already have
   // been freed.  By default, the destruction observer will fail the test
   // if this happens, unless the expectation is explicitly changed.  If you
@@ -83,11 +81,6 @@
   std::unique_ptr<DestructionObservable::DestructionObserver>
       most_recent_codec_destruction_observer_;
 
-  // The most recent surface bundle that we've gotten during codec allocation.
-  // This should be the same as |config_->surface_bundle| initially, but AVDA
-  // might change it.
-  scoped_refptr<AVDASurfaceBundle> most_recent_bundle_;
-
   // The most recent overlay provided during codec allocation.
   AndroidOverlay* most_recent_overlay_ = nullptr;
 
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index 0e926fdf..a806c44 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -12,6 +12,7 @@
 #include "media/base/android/media_codec_bridge_impl.h"
 #include "media/base/android/media_codec_util.h"
 #include "media/base/bind_to_current_loop.h"
+#include "media/base/decoder_buffer.h"
 #include "media/base/video_codecs.h"
 #include "media/base/video_decoder_config.h"
 #include "media/gpu/android_video_surface_chooser.h"
@@ -28,7 +29,8 @@
          (config.codec() == kCodecVP8 || config.codec() == kCodecVP9);
 }
 
-bool ConfigSupported(const VideoDecoderConfig& config) {
+bool ConfigSupported(const VideoDecoderConfig& config,
+                     DeviceInfo* device_info) {
   // Don't support larger than 4k because it won't perform well on many devices.
   const auto size = config.coded_size();
   if (size.width() > 3840 || size.height() > 2160)
@@ -38,8 +40,7 @@
   // the stream is encrypted.
   const auto codec = config.codec();
   if (IsMediaCodecSoftwareDecodingForbidden(config) &&
-      MediaCodecUtil::IsKnownUnaccelerated(codec,
-                                           MediaCodecDirection::DECODER)) {
+      device_info->IsDecoderKnownUnaccelerated(codec)) {
     DVLOG(2) << "Config not supported: " << GetCodecName(codec)
              << " is not hardware accelerated";
     return false;
@@ -48,8 +49,8 @@
   switch (codec) {
     case kCodecVP8:
     case kCodecVP9: {
-      if ((codec == kCodecVP8 && !MediaCodecUtil::IsVp8DecoderAvailable()) ||
-          (codec == kCodecVP9 && !MediaCodecUtil::IsVp9DecoderAvailable())) {
+      if ((codec == kCodecVP8 && !device_info->IsVp8DecoderAvailable()) ||
+          (codec == kCodecVP9 && !device_info->IsVp9DecoderAvailable())) {
         return false;
       }
 
@@ -75,17 +76,9 @@
   }
 }
 
-std::unique_ptr<AndroidOverlay> CreateContentVideoViewOverlay(
-    int32_t surface_id,
-    AndroidOverlayConfig config) {
-  return base::MakeUnique<ContentVideoViewOverlay>(surface_id,
-                                                   std::move(config));
-}
-
 }  // namespace
 
 CodecAllocatorAdapter::CodecAllocatorAdapter() = default;
-
 CodecAllocatorAdapter::~CodecAllocatorAdapter() = default;
 
 void CodecAllocatorAdapter::OnCodecConfigured(
@@ -93,22 +86,31 @@
   codec_created_cb.Run(std::move(media_codec));
 }
 
+PendingDecode::PendingDecode(scoped_refptr<DecoderBuffer> buffer,
+                             VideoDecoder::DecodeCB decode_cb)
+    : buffer(buffer), decode_cb(decode_cb) {}
+PendingDecode::PendingDecode(PendingDecode&& other) = default;
+PendingDecode::~PendingDecode() = default;
+
 MediaCodecVideoDecoder::MediaCodecVideoDecoder(
     scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
-    base::Callback<gpu::GpuCommandBufferStub*()> get_command_buffer_stub_cb,
+    base::Callback<gpu::GpuCommandBufferStub*()> get_stub_cb,
+    DeviceInfo* device_info,
     AVDACodecAllocator* codec_allocator,
     std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser)
     : state_(State::kBeforeSurfaceInit),
       lazy_init_pending_(true),
       gpu_task_runner_(gpu_task_runner),
-      get_command_buffer_stub_cb_(get_command_buffer_stub_cb),
+      get_stub_cb_(get_stub_cb),
       codec_allocator_(codec_allocator),
       surface_chooser_(std::move(surface_chooser)),
+      device_info_(device_info),
       weak_factory_(this) {
   DVLOG(2) << __func__;
 }
 
 MediaCodecVideoDecoder::~MediaCodecVideoDecoder() {
+  ReleaseCodec();
   codec_allocator_->StopThread(&codec_allocator_adapter_);
 }
 
@@ -120,7 +122,7 @@
   DVLOG(2) << __func__;
   InitCB bound_init_cb = BindToCurrentLoop(init_cb);
 
-  if (!ConfigSupported(config)) {
+  if (!ConfigSupported(config, device_info_)) {
     bound_init_cb.Run(false);
     return;
   }
@@ -142,6 +144,8 @@
   codec_config_->initial_expected_coded_size = config.coded_size();
   // TODO(watk): Parse config.extra_data().
 
+  output_cb_ = output_cb;
+
   // We defer initialization of the Surface and MediaCodec until we
   // receive a Decode() call to avoid consuming those resources  in cases where
   // we'll be destructed before getting a Decode(). Failure to initialize those
@@ -158,28 +162,26 @@
   InitializeSurfaceChooser();
 }
 
+void MediaCodecVideoDecoder::SetOverlayInfo(const OverlayInfo& overlay_info) {
+  DVLOG(2) << __func__;
+  bool overlay_changed = !overlay_info_.RefersToSameOverlayAs(overlay_info);
+  overlay_info_ = overlay_info;
+  // Only update surface chooser if it's initialized and the overlay changed.
+  if (state_ != State::kBeforeSurfaceInit && overlay_changed)
+    surface_chooser_->ReplaceOverlayFactory(CreateOverlayFactoryCb());
+}
+
 void MediaCodecVideoDecoder::InitializeSurfaceChooser() {
   DVLOG(2) << __func__;
   DCHECK_EQ(state_, State::kBeforeSurfaceInit);
-
-  // If we have a surface or overlay id, create a factory cb.
-  AndroidOverlayFactoryCB factory;
-  // TODO(watk): Populate the surface id.
-  int surface_id = SurfaceManager::kNoSurfaceID;
-  if (surface_id != SurfaceManager::kNoSurfaceID)
-    factory = base::Bind(&CreateContentVideoViewOverlay, surface_id);
-  else if (overlay_routing_token_ && overlay_factory_cb_)
-    factory = base::Bind(overlay_factory_cb_, *overlay_routing_token_);
-
-  // Initialize |surface_chooser_| and wait for its decision.  Note: the
+  // Initialize |surface_chooser_| and wait for its decision. Note: the
   // callback may be reentrant.
-  // TODO(watk): Make it not reentrant.
   surface_chooser_->Initialize(
       base::Bind(&MediaCodecVideoDecoder::OnSurfaceChosen,
                  weak_factory_.GetWeakPtr()),
       base::Bind(&MediaCodecVideoDecoder::OnSurfaceChosen,
                  weak_factory_.GetWeakPtr(), nullptr),
-      std::move(factory));
+      CreateOverlayFactoryCb());
 }
 
 void MediaCodecVideoDecoder::OnSurfaceChosen(
@@ -191,18 +193,18 @@
                    weak_factory_.GetWeakPtr()));
   }
 
-  // If we're waiting for our first surface during initialization, then proceed
-  // immediately. Otherwise, DequeueOutput() will handle the transition.
+  // If we were waiting for our first surface during initialization, then
+  // proceed to create a codec with the chosen surface.
   if (state_ == State::kBeforeSurfaceInit) {
-    auto* bundle = overlay ? new AVDASurfaceBundle(std::move(overlay))
-                           : new AVDASurfaceBundle(surface_texture_);
-    TransitionToSurface(bundle);
+    codec_config_->surface_bundle =
+        overlay ? new AVDASurfaceBundle(std::move(overlay))
+                : new AVDASurfaceBundle(surface_texture_);
+    StartCodecCreation();
     return;
   }
 
   // If setOutputSurface() is not supported we can't do the transition.
-  // TODO(watk): Use PlatformConfig for testing.
-  if (!MediaCodecUtil::IsSetOutputSurfaceSupported())
+  if (!device_info_->IsSetOutputSurfaceSupported())
     return;
 
   // If we're told to switch to a SurfaceTexture but we're already using a
@@ -214,10 +216,8 @@
     return;
   }
 
-  scoped_refptr<AVDASurfaceBundle> bundle =
-      overlay ? new AVDASurfaceBundle(std::move(overlay))
-              : new AVDASurfaceBundle(surface_texture_);
-  incoming_surface_ = std::move(bundle);
+  incoming_surface_.emplace(overlay ? new AVDASurfaceBundle(std::move(overlay))
+                                    : new AVDASurfaceBundle(surface_texture_));
 }
 
 void MediaCodecVideoDecoder::OnSurfaceDestroyed(AndroidOverlay* overlay) {
@@ -235,13 +235,16 @@
     return;
   }
 
-  // If codec allocation is in progress, we go into state kSurfaceDestroyed.
-  // TODO(watk): When setOutputSurface() is not available, this is fine
-  // because |*this| will be destructed soon. However, if setOutputSurface() is
-  // available |*this| won't be destructed soon, and this will be a hung
-  // playback. We should handle this gracefully by allocating a new codec.
-  if (state_ == State::kWaitingForCodec ||
-      !MediaCodecUtil::IsSetOutputSurfaceSupported()) {
+  // TODO(watk): If setOutputSurface() is available we're supposed to
+  // transparently handle surface transitions, however we don't handle them
+  // while codec creation is in progress. It should be handled gracefully
+  // by allocating a new codec.
+  if (state_ == State::kWaitingForCodec) {
+    state_ = State::kSurfaceDestroyed;
+    return;
+  }
+
+  if (!device_info_->IsSetOutputSurfaceSupported()) {
     state_ = State::kSurfaceDestroyed;
     ReleaseCodecAndBundle();
     // TODO(watk): If we're draining, signal completion now because the drain
@@ -250,45 +253,206 @@
   }
 
   // If there isn't a pending overlay then transition to a SurfaceTexture.
-  scoped_refptr<AVDASurfaceBundle> new_surface =
-      incoming_surface_ ? *incoming_surface_
-                        : new AVDASurfaceBundle(surface_texture_);
-  incoming_surface_.reset();
-  TransitionToSurface(std::move(new_surface));
-  if (state_ == State::kError)
-    ReleaseCodecAndBundle();
+  if (!incoming_surface_)
+    incoming_surface_.emplace(new AVDASurfaceBundle(surface_texture_));
+  TransitionToIncomingSurface();
 }
 
-void MediaCodecVideoDecoder::TransitionToSurface(
-    scoped_refptr<AVDASurfaceBundle> surface_bundle) {
+void MediaCodecVideoDecoder::TransitionToIncomingSurface() {
   DVLOG(2) << __func__;
-  // If we have a codec, then call SetSurface().
-  if (media_codec_) {
-    if (media_codec_->SetSurface(surface_bundle->GetJavaSurface().obj())) {
-      codec_config_->surface_bundle = std::move(surface_bundle);
-    } else {
-      HandleError();
-    }
-    return;
+  DCHECK(incoming_surface_);
+  DCHECK(codec_);
+  auto surface_bundle = std::move(*incoming_surface_);
+  incoming_surface_.reset();
+  if (codec_->SetSurface(surface_bundle->GetJavaSurface().obj())) {
+    codec_config_->surface_bundle = std::move(surface_bundle);
+  } else {
+    ReleaseCodecAndBundle();
+    HandleError();
   }
-
-  // Create a codec with the surface bundle.
-  codec_config_->surface_bundle = std::move(surface_bundle);
-  StartCodecCreation();
 }
 
 void MediaCodecVideoDecoder::StartCodecCreation() {
-  DCHECK(!media_codec_);
+  DCHECK(!codec_);
+  DCHECK(state_ == State::kBeforeSurfaceInit);
   state_ = State::kWaitingForCodec;
+  codec_allocator_adapter_.codec_created_cb = base::Bind(
+      &MediaCodecVideoDecoder::OnCodecCreated, weak_factory_.GetWeakPtr());
   codec_allocator_->CreateMediaCodecAsync(codec_allocator_adapter_.AsWeakPtr(),
                                           codec_config_);
 }
 
+void MediaCodecVideoDecoder::OnCodecCreated(
+    std::unique_ptr<MediaCodecBridge> codec) {
+  DCHECK(state_ == State::kWaitingForCodec ||
+         state_ == State::kSurfaceDestroyed);
+  DCHECK(!codec_);
+  if (codec)
+    codec_ = base::MakeUnique<CodecWrapper>(std::move(codec));
+
+  // If we entered state kSurfaceDestroyed while we were waiting for
+  // the codec, then it's already invalid and we have to drop it.
+  if (state_ == State::kSurfaceDestroyed) {
+    ReleaseCodecAndBundle();
+    return;
+  }
+
+  // Handle the failure case after the kSurfaceDestroyed case to avoid
+  // transitioning from kSurfaceDestroyed to kError.
+  if (!codec_) {
+    HandleError();
+    return;
+  }
+
+  state_ = State::kOk;
+  ManageTimer(true);
+}
+
 void MediaCodecVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
                                     const DecodeCB& decode_cb) {
   DVLOG(2) << __func__;
+  pending_decodes_.emplace_back(buffer, std::move(decode_cb));
   if (lazy_init_pending_)
     StartLazyInit();
+  else
+    PumpCodec(true);
+}
+
+void MediaCodecVideoDecoder::PumpCodec(bool force_start_timer) {
+  DVLOG(4) << __func__;
+  if (state_ == State::kError || state_ == State::kWaitingForCodec ||
+      state_ == State::kSurfaceDestroyed ||
+      state_ == State::kBeforeSurfaceInit) {
+    return;
+  }
+
+  bool did_work = false, did_input = false, did_output = false;
+  do {
+    did_input = QueueInput();
+    did_output = DequeueOutput();
+    if (did_input || did_output)
+      did_work = true;
+  } while (did_input || did_output);
+
+  ManageTimer(did_work || force_start_timer);
+}
+
+void MediaCodecVideoDecoder::ManageTimer(bool start_timer) {
+  const base::TimeDelta kPollingPeriod = base::TimeDelta::FromMilliseconds(10);
+  const base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(1);
+
+  if (!idle_timer_ || start_timer)
+    idle_timer_ = base::ElapsedTimer();
+
+  if (!start_timer && idle_timer_->Elapsed() > kIdleTimeout) {
+    DVLOG(2) << __func__ << " Stopping timer; idle timeout hit";
+    pump_codec_timer_.Stop();
+  } else if (!pump_codec_timer_.IsRunning()) {
+    pump_codec_timer_.Start(FROM_HERE, kPollingPeriod,
+                            base::Bind(&MediaCodecVideoDecoder::PumpCodec,
+                                       base::Unretained(this), false));
+  }
+}
+
+bool MediaCodecVideoDecoder::QueueInput() {
+  DVLOG(4) << __func__;
+  if (state_ == State::kError || state_ == State::kWaitingForCodec ||
+      state_ == State::kWaitingForKey || state_ == State::kBeforeSurfaceInit) {
+    return false;
+  }
+  if (pending_decodes_.empty())
+    return false;
+
+  int input_buffer = -1;
+  MediaCodecStatus status =
+      codec_->DequeueInputBuffer(base::TimeDelta(), &input_buffer);
+  if (status == MEDIA_CODEC_ERROR) {
+    DVLOG(1) << "DequeueInputBuffer() error";
+    HandleError();
+    return false;
+  } else if (status == MEDIA_CODEC_TRY_AGAIN_LATER) {
+    return false;
+  }
+
+  DCHECK(status == MEDIA_CODEC_OK);
+  DCHECK_GE(input_buffer, 0);
+  PendingDecode pending_decode = std::move(pending_decodes_.front());
+  pending_decodes_.pop_front();
+
+  if (pending_decode.buffer->end_of_stream()) {
+    codec_->QueueEOS(input_buffer);
+    pending_decode.decode_cb.Run(DecodeStatus::OK);
+    // TODO(watk): Make CodecWrapper track this.
+    drain_type_ = DrainType::kFlush;
+    return true;
+  }
+
+  MediaCodecStatus queue_status = codec_->QueueInputBuffer(
+      input_buffer, pending_decode.buffer->data(),
+      pending_decode.buffer->data_size(), pending_decode.buffer->timestamp());
+  DVLOG(2) << ": QueueInputBuffer(pts="
+           << pending_decode.buffer->timestamp().InMilliseconds()
+           << ") status=" << queue_status;
+
+  DCHECK_NE(queue_status, MEDIA_CODEC_NO_KEY)
+      << "Encrypted support not yet implemented";
+  if (queue_status != MEDIA_CODEC_OK) {
+    pending_decode.decode_cb.Run(DecodeStatus::DECODE_ERROR);
+    HandleError();
+    return false;
+  }
+
+  pending_decode.decode_cb.Run(DecodeStatus::OK);
+  return true;
+}
+
+bool MediaCodecVideoDecoder::DequeueOutput() {
+  DVLOG(4) << __func__;
+  if (state_ == State::kError || state_ == State::kWaitingForCodec ||
+      state_ == State::kBeforeSurfaceInit) {
+    return false;
+  }
+
+  // Transition to the incoming surface when there are no unrendered codec
+  // buffers. This is so we can ensure we create the right kind of VideoFrame
+  // for the current surface.
+  if (incoming_surface_) {
+    if (codec_->HasValidCodecOutputBuffers())
+      return false;
+    TransitionToIncomingSurface();
+    return true;
+  }
+
+  base::TimeDelta presentation_time;
+  bool eos = false;
+  std::unique_ptr<CodecOutputBuffer> output_buffer;
+  MediaCodecStatus status = codec_->DequeueOutputBuffer(
+      base::TimeDelta(), &presentation_time, &eos, &output_buffer);
+  if (status == MEDIA_CODEC_ERROR) {
+    DVLOG(1) << "DequeueOutputBuffer() error";
+    HandleError();
+    // TODO(watk): Complete the pending drain.
+    return false;
+  } else if (status == MEDIA_CODEC_TRY_AGAIN_LATER) {
+    return false;
+  }
+
+  DCHECK(status == MEDIA_CODEC_OK);
+  DVLOG(2) << "DequeueOutputBuffer(): pts=" << presentation_time
+           << " size=" << output_buffer->size().ToString() << " eos=" << eos;
+
+  if (eos) {
+    // TODO(watk): Complete the pending drain.
+    return false;
+  }
+
+  if (drain_type_ == DrainType::kReset || drain_type_ == DrainType::kDestroy) {
+    // Returning here discards |output_buffer| without rendering it.
+    return true;
+  }
+
+  // TODO(watk): Create a VideoFrame backed by output_buffer.
+  return true;
 }
 
 void MediaCodecVideoDecoder::Reset(const base::Closure& closure) {
@@ -299,13 +463,17 @@
 void MediaCodecVideoDecoder::HandleError() {
   DVLOG(2) << __func__;
   state_ = State::kError;
+  for (auto& pending_decode : pending_decodes_)
+    pending_decode.decode_cb.Run(DecodeStatus::DECODE_ERROR);
+  pending_decodes_.clear();
 }
 
 void MediaCodecVideoDecoder::ReleaseCodec() {
-  if (!media_codec_)
+  if (!codec_)
     return;
-  codec_allocator_->ReleaseMediaCodec(std::move(media_codec_),
+  codec_allocator_->ReleaseMediaCodec(codec_->TakeCodec(),
                                       codec_config_->surface_bundle);
+  codec_ = nullptr;
 }
 
 void MediaCodecVideoDecoder::ReleaseCodecAndBundle() {
@@ -313,6 +481,16 @@
   codec_config_->surface_bundle = nullptr;
 }
 
+AndroidOverlayFactoryCB MediaCodecVideoDecoder::CreateOverlayFactoryCb() {
+  if (overlay_info_.HasValidSurfaceId()) {
+    return base::Bind(&ContentVideoViewOverlay::Create,
+                      overlay_info_.surface_id);
+  } else if (overlay_info_.HasValidRoutingToken() && overlay_factory_cb_) {
+    return base::Bind(overlay_factory_cb_, *overlay_info_.routing_token);
+  }
+  return AndroidOverlayFactoryCB();
+}
+
 std::string MediaCodecVideoDecoder::GetDisplayName() const {
   return "MediaCodecVideoDecoder";
 }
@@ -333,8 +511,8 @@
 }
 
 int MediaCodecVideoDecoder::GetMaxDecodeRequests() const {
-  // We indicate that we're done decoding a frame as soon as we submit it
-  // to MediaCodec so the number of parallel decode requests just sets the upper
+  // We indicate that we're done decoding a frame as soon as we submit it to
+  // MediaCodec so the number of parallel decode requests just sets the upper
   // limit of the size of our pending decode queue.
   return 2;
 }
diff --git a/media/gpu/android/media_codec_video_decoder.h b/media/gpu/android/media_codec_video_decoder.h
index 89ac416..3c652d6 100644
--- a/media/gpu/android/media_codec_video_decoder.h
+++ b/media/gpu/android/media_codec_video_decoder.h
@@ -5,17 +5,35 @@
 #ifndef MEDIA_GPU_ANDROID_MEDIA_CODEC_VIDEO_DECODER_H_
 #define MEDIA_GPU_ANDROID_MEDIA_CODEC_VIDEO_DECODER_H_
 
+#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
+#include "base/timer/elapsed_timer.h"
 #include "gpu/ipc/service/gpu_command_buffer_stub.h"
 #include "media/base/android_overlay_mojo_factory.h"
+#include "media/base/overlay_info.h"
 #include "media/base/video_decoder.h"
+#include "media/gpu/android/codec_wrapper.h"
+#include "media/gpu/android/device_info.h"
 #include "media/gpu/android_video_surface_chooser.h"
 #include "media/gpu/avda_codec_allocator.h"
 #include "media/gpu/media_gpu_export.h"
 
 namespace media {
 
+struct PendingDecode {
+  PendingDecode(scoped_refptr<DecoderBuffer> buffer,
+                VideoDecoder::DecodeCB decode_cb);
+  PendingDecode(PendingDecode&& other);
+  ~PendingDecode();
+
+  scoped_refptr<DecoderBuffer> buffer;
+  VideoDecoder::DecodeCB decode_cb;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PendingDecode);
+};
+
 // TODO(watk): Simplify the interface to AVDACodecAllocator.
 struct CodecAllocatorAdapter
     : public AVDACodecAllocatorClient,
@@ -37,6 +55,7 @@
   MediaCodecVideoDecoder(
       scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
       base::Callback<gpu::GpuCommandBufferStub*()> get_command_buffer_stub_cb,
+      DeviceInfo* device_info,
       AVDACodecAllocator* codec_allocator,
       std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser);
   ~MediaCodecVideoDecoder() override;
@@ -55,6 +74,10 @@
   bool CanReadWithoutStalling() const override;
   int GetMaxDecodeRequests() const override;
 
+  // Sets the overlay info to use. This can be called before Initialize() to
+  // set the first overlay.
+  void SetOverlayInfo(const OverlayInfo& overlay_info);
+
  private:
   enum class State {
     kOk,
@@ -84,47 +107,70 @@
   void InitializeSurfaceChooser();
   void OnSurfaceChosen(std::unique_ptr<AndroidOverlay> overlay);
   void OnSurfaceDestroyed(AndroidOverlay* overlay);
-  void TransitionToSurface(scoped_refptr<AVDASurfaceBundle> surface_bundle);
+
+  // Sets |codecs_|'s output surface to |incoming_surface_|. Releases the codec
+  // and both the current and incoming bundles on failure.
+  void TransitionToIncomingSurface();
   void StartCodecCreation();
+  void OnCodecCreated(std::unique_ptr<MediaCodecBridge> codec);
+
+  void PumpCodec(bool force_start_timer);
+  bool QueueInput();
+  bool DequeueOutput();
+  void ManageTimer(bool start_timer);
 
   // Sets |state_| and runs pending callbacks.
   void HandleError();
 
-  // Releases |media_codec_| if it's not null.
+  // Releases |codec_| if it's not null.
   void ReleaseCodec();
 
   // Calls ReleaseCodec() and drops the ref to its surface bundle.
   void ReleaseCodecAndBundle();
 
+  // Creates an overlay factory cb based on the value of overlay_info_.
+  AndroidOverlayFactoryCB CreateOverlayFactoryCb();
+
   State state_;
   bool lazy_init_pending_;
-  VideoDecoderConfig decoder_config_;
-
-  // |codec_config_| must not be modified while |state_| is kWaitingForCodec.
-  scoped_refptr<CodecConfig> codec_config_;
-  std::unique_ptr<MediaCodecBridge> media_codec_;
+  std::deque<PendingDecode> pending_decodes_;
+  OutputCB output_cb_;
 
   // The ongoing drain operation, if any.
-  base::Optional<DrainType> drain_kind_;
-
-  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
-  base::Callback<gpu::GpuCommandBufferStub*()> get_command_buffer_stub_cb_;
-  AVDACodecAllocator* codec_allocator_;
-  CodecAllocatorAdapter codec_allocator_adapter_;
-
-  // A SurfaceTexture that is kept for the lifetime of MCVD so that if we have
-  // to synchronously switch surfaces we always have one available.
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture_;
-  std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser_;
+  base::Optional<DrainType> drain_type_;
+  VideoDecoderConfig decoder_config_;
 
   // The surface bundle that we're transitioning to, if any.
   base::Optional<scoped_refptr<AVDASurfaceBundle>> incoming_surface_;
 
+  // |codec_config_| must not be modified while |state_| is kWaitingForCodec.
+  scoped_refptr<CodecConfig> codec_config_;
+  std::unique_ptr<CodecWrapper> codec_;
+  base::Optional<base::ElapsedTimer> idle_timer_;
+  base::RepeatingTimer pump_codec_timer_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
+  base::Callback<gpu::GpuCommandBufferStub*()> get_stub_cb_;
+
+  // An adapter to let us use AVDACodecAllocator.
+  CodecAllocatorAdapter codec_allocator_adapter_;
+  AVDACodecAllocator* codec_allocator_;
+
+  // A SurfaceTexture that is kept for the lifetime of MCVD so that if we have
+  // to synchronously switch surfaces we always have one available.
+  scoped_refptr<SurfaceTextureGLOwner> surface_texture_;
+
+  // The current overlay info, which possibly specifies an overlay to render to.
+  OverlayInfo overlay_info_;
+
+  // The surface chooser we use to decide which kind of surface to configure the
+  // codec with.
+  std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser_;
+
   // An optional factory callback for creating mojo AndroidOverlays.
   AndroidOverlayMojoFactoryCB overlay_factory_cb_;
 
-  // The routing token for a mojo AndroidOverlay.
-  base::Optional<base::UnguessableToken> overlay_routing_token_;
+  DeviceInfo* device_info_;
 
   base::WeakPtrFactory<MediaCodecVideoDecoder> weak_factory_;
 
diff --git a/media/gpu/android/media_codec_video_decoder_unittest.cc b/media/gpu/android/media_codec_video_decoder_unittest.cc
index be521950..bd876ff1 100644
--- a/media/gpu/android/media_codec_video_decoder_unittest.cc
+++ b/media/gpu/android/media_codec_video_decoder_unittest.cc
@@ -6,36 +6,35 @@
 #include "base/android/jni_android.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/base/android/media_codec_util.h"
 #include "media/base/android/media_jni_registrar.h"
+#include "media/base/android/mock_android_overlay.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/test_helpers.h"
 #include "media/gpu/android/fake_codec_allocator.h"
+#include "media/gpu/android/mock_device_info.h"
 #include "media/gpu/android_video_surface_chooser_impl.h"
+#include "media/gpu/fake_android_video_surface_chooser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/init/gl_factory.h"
 
-using ::testing::_;
+using testing::_;
+using testing::InvokeWithoutArgs;
+using testing::NiceMock;
+using testing::NotNull;
+using testing::Return;
+using testing::SaveArg;
 
 namespace media {
 namespace {
 
-#define SKIP_IF_MEDIA_CODEC_IS_BLACKLISTED()                                 \
-  do {                                                                       \
-    if (!MediaCodecUtil::IsMediaCodecAvailable()) {                          \
-      DVLOG(0) << "Skipping test: MediaCodec is blacklisted on this device"; \
-      return;                                                                \
-    }                                                                        \
-  } while (0)
-
 void OutputCb(const scoped_refptr<VideoFrame>& frame) {}
 
-void DecodeCb(DecodeStatus) {}
-
-gpu::GpuCommandBufferStub* GetCommandBufferStubCb() {
+gpu::GpuCommandBufferStub* GetStubCb() {
   return nullptr;
 }
 
@@ -43,24 +42,28 @@
 
 class MediaCodecVideoDecoderTest : public testing::Test {
  public:
-  MediaCodecVideoDecoderTest() {
+  MediaCodecVideoDecoderTest() = default;
+
+  void SetUp() override {
     JNIEnv* env = base::android::AttachCurrentThread();
     RegisterJni(env);
 
     // We set up GL so that we can create SurfaceTextures.
     // TODO(watk): Create a mock SurfaceTexture so we don't have to
     // do this.
-    DCHECK(gl::init::InitializeGLOneOff());
+    ASSERT_TRUE(gl::init::InitializeGLOneOff());
     surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(16, 16));
     context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
                                          gl::GLContextAttribs());
     context_->MakeCurrent(surface_.get());
 
     codec_allocator_ = base::MakeUnique<FakeCodecAllocator>();
+    device_info_ = base::MakeUnique<NiceMock<MockDeviceInfo>>();
+    auto surface_chooser = base::MakeUnique<NiceMock<FakeSurfaceChooser>>();
+    surface_chooser_ = surface_chooser.get();
     mcvd_ = base::MakeUnique<MediaCodecVideoDecoder>(
-        base::ThreadTaskRunnerHandle::Get(),
-        base::Bind(&GetCommandBufferStubCb), codec_allocator_.get(),
-        base::MakeUnique<AndroidVideoSurfaceChooserImpl>());
+        base::ThreadTaskRunnerHandle::Get(), base::Bind(&GetStubCb),
+        device_info_.get(), codec_allocator_.get(), std::move(surface_chooser));
   }
 
   ~MediaCodecVideoDecoderTest() override {
@@ -82,11 +85,27 @@
     return result;
   }
 
-  void TearDown() override {}
+  MockAndroidOverlay* InitializeWithOverlay() {
+    Initialize(TestVideoConfig::NormalH264());
+    mcvd_->Decode(nullptr, decode_cb_.Get());
+    auto overlay_ptr = base::MakeUnique<MockAndroidOverlay>();
+    auto* overlay = overlay_ptr.get();
+    surface_chooser_->ProvideOverlay(std::move(overlay_ptr));
+    return overlay;
+  }
+
+  void InitializeWithSurfaceTexture() {
+    Initialize(TestVideoConfig::NormalH264());
+    mcvd_->Decode(nullptr, decode_cb_.Get());
+    surface_chooser_->ProvideSurfaceTexture();
+  }
 
  protected:
   std::unique_ptr<MediaCodecVideoDecoder> mcvd_;
+  std::unique_ptr<MockDeviceInfo> device_info_;
   std::unique_ptr<FakeCodecAllocator> codec_allocator_;
+  FakeSurfaceChooser* surface_chooser_;
+  base::MockCallback<VideoDecoder::DecodeCB> decode_cb_;
 
   scoped_refptr<gl::GLSurface> surface_;
   scoped_refptr<gl::GLContext> context_;
@@ -98,34 +117,226 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, UnknownCodecIsRejected) {
-  SKIP_IF_MEDIA_CODEC_IS_BLACKLISTED();
   ASSERT_FALSE(Initialize(TestVideoConfig::Invalid()));
 }
 
 TEST_F(MediaCodecVideoDecoderTest, H264IsSupported) {
-  SKIP_IF_MEDIA_CODEC_IS_BLACKLISTED();
   // H264 is always supported by MCVD.
   ASSERT_TRUE(Initialize(TestVideoConfig::NormalH264()));
 }
 
 TEST_F(MediaCodecVideoDecoderTest, SmallVp8IsRejected) {
-  SKIP_IF_MEDIA_CODEC_IS_BLACKLISTED();
-  ASSERT_TRUE(Initialize(TestVideoConfig::NormalH264()));
+  ASSERT_FALSE(Initialize(TestVideoConfig::Normal()));
 }
 
-TEST_F(MediaCodecVideoDecoderTest, InitializeDoesNotCreateACodec) {
-  SKIP_IF_MEDIA_CODEC_IS_BLACKLISTED();
+TEST_F(MediaCodecVideoDecoderTest, InitializeDoesntInitSurfaceOrCodec) {
+  EXPECT_CALL(*surface_chooser_, MockInitialize()).Times(0);
   EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, _)).Times(0);
   Initialize(TestVideoConfig::NormalH264());
-  base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(MediaCodecVideoDecoderTest, DecodeTriggersLazyInit) {
-  SKIP_IF_MEDIA_CODEC_IS_BLACKLISTED();
+TEST_F(MediaCodecVideoDecoderTest, FirstDecodeTriggersSurfaceChooserInit) {
   Initialize(TestVideoConfig::NormalH264());
-  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, _));
-  mcvd_->Decode(nullptr, base::Bind(&DecodeCb));
-  base::RunLoop().RunUntilIdle();
+  EXPECT_CALL(*surface_chooser_, MockInitialize());
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+}
+
+TEST_F(MediaCodecVideoDecoderTest, CodecIsCreatedAfterSurfaceChosen) {
+  Initialize(TestVideoConfig::NormalH264());
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, NotNull()));
+  surface_chooser_->ProvideSurfaceTexture();
+}
+
+TEST_F(MediaCodecVideoDecoderTest, DecodeCbsAreRunOnError) {
+  InitializeWithSurfaceTexture();
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+  EXPECT_CALL(decode_cb_, Run(DecodeStatus::DECODE_ERROR)).Times(2);
+  // Failing to create a codec should put MCVD into an error state.
+  codec_allocator_->ProvideNullCodecAsync();
+}
+
+TEST_F(MediaCodecVideoDecoderTest, AfterInitCompletesTheCodecIsPolled) {
+  InitializeWithSurfaceTexture();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+  // Run a RunLoop until the first time the codec is polled for an available
+  // input buffer.
+  base::RunLoop loop;
+  EXPECT_CALL(*codec, DequeueInputBuffer(_, _))
+      .WillOnce(InvokeWithoutArgs([&loop]() {
+        loop.Quit();
+        return MEDIA_CODEC_TRY_AGAIN_LATER;
+      }));
+  loop.Run();
+}
+
+TEST_F(MediaCodecVideoDecoderTest, CodecIsReleasedOnDestruction) {
+  InitializeWithSurfaceTexture();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+  EXPECT_CALL(*codec_allocator_, MockReleaseMediaCodec(codec, _, _));
+}
+
+TEST_F(MediaCodecVideoDecoderTest,
+       SurfaceChooserNotInitializedWithOverlayFactory) {
+  InitializeWithSurfaceTexture();
+  // The surface chooser should not have an overlay factory because
+  // SetOverlayInfo() was not called before it was initialized.
+  ASSERT_FALSE(surface_chooser_->factory_);
+}
+
+TEST_F(MediaCodecVideoDecoderTest,
+       SurfaceChooserInitializedWithOverlayFactory) {
+  Initialize(TestVideoConfig::NormalH264());
+  OverlayInfo info;
+  info.surface_id = 123;
+  mcvd_->SetOverlayInfo(info);
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+  // The surface chooser should have an overlay factory because SetOverlayInfo()
+  // was called before it was initialized.
+  ASSERT_TRUE(surface_chooser_->factory_);
+}
+
+TEST_F(MediaCodecVideoDecoderTest, SetOverlayInfoIsValidBeforeInitialize) {
+  OverlayInfo info;
+  info.surface_id = 123;
+  mcvd_->SetOverlayInfo(info);
+  Initialize(TestVideoConfig::NormalH264());
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+  ASSERT_TRUE(surface_chooser_->factory_);
+}
+
+TEST_F(MediaCodecVideoDecoderTest, SetOverlayInfoReplacesTheOverlayFactory) {
+  InitializeWithOverlay();
+
+  EXPECT_CALL(*surface_chooser_, MockReplaceOverlayFactory()).Times(2);
+  OverlayInfo info;
+  info.surface_id = 123;
+  mcvd_->SetOverlayInfo(info);
+  info.surface_id = 456;
+  mcvd_->SetOverlayInfo(info);
+}
+
+TEST_F(MediaCodecVideoDecoderTest, DuplicateSetOverlayInfosAreIgnored) {
+  InitializeWithOverlay();
+
+  // The second SetOverlayInfo() should be ignored.
+  EXPECT_CALL(*surface_chooser_, MockReplaceOverlayFactory()).Times(1);
+  OverlayInfo info;
+  info.surface_id = 123;
+  mcvd_->SetOverlayInfo(info);
+  mcvd_->SetOverlayInfo(info);
+}
+
+TEST_F(MediaCodecVideoDecoderTest, CodecIsCreatedWithChosenOverlay) {
+  AndroidOverlay* overlay_passed_to_codec = nullptr;
+  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, _))
+      .WillOnce(SaveArg<0>(&overlay_passed_to_codec));
+  auto* overlay = InitializeWithOverlay();
+  DCHECK_EQ(overlay, overlay_passed_to_codec);
+}
+
+TEST_F(MediaCodecVideoDecoderTest,
+       SurfaceDestroyedBeforeCodecCreationDropsCodec) {
+  auto* overlay = InitializeWithOverlay();
+  overlay->OnSurfaceDestroyed();
+
+  // The codec is dropped as soon as it's ready.
+  EXPECT_CALL(*codec_allocator_, MockReleaseMediaCodec(_, _, _));
+  codec_allocator_->ProvideMockCodecAsync();
+  // Verify expectations before we delete the MCVD.
+  testing::Mock::VerifyAndClearExpectations(codec_allocator_.get());
+}
+
+TEST_F(MediaCodecVideoDecoderTest, SurfaceDestroyedDoesSyncSurfaceTransition) {
+  auto* overlay = InitializeWithOverlay();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+
+  // MCVD must synchronously switch the codec's surface (to surface
+  // texture), and delete the overlay.
+  EXPECT_CALL(*codec, SetSurface(_)).WillOnce(Return(true));
+  auto observer = overlay->CreateDestructionObserver();
+  observer->ExpectDestruction();
+  overlay->OnSurfaceDestroyed();
+}
+
+TEST_F(MediaCodecVideoDecoderTest,
+       SurfaceDestroyedReleasesCodecIfSetSurfaceIsNotSupported) {
+  auto* overlay = InitializeWithOverlay();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+
+  // MCVD must synchronously release the codec.
+  ON_CALL(*device_info_, IsSetOutputSurfaceSupported())
+      .WillByDefault(Return(false));
+  EXPECT_CALL(*codec, SetSurface(_)).Times(0);
+  EXPECT_CALL(*codec_allocator_, MockReleaseMediaCodec(codec, NotNull(), _));
+  overlay->OnSurfaceDestroyed();
+  // Verify expectations before we delete the MCVD.
+  testing::Mock::VerifyAndClearExpectations(codec_allocator_.get());
+}
+
+TEST_F(MediaCodecVideoDecoderTest, PumpCodecPerformsPendingSurfaceTransitions) {
+  InitializeWithOverlay();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+
+  // Set a pending surface transition and then call Decode() (which calls
+  // PumpCodec()).
+  surface_chooser_->ProvideSurfaceTexture();
+  EXPECT_CALL(*codec, SetSurface(_)).WillOnce(Return(true));
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+}
+
+TEST_F(MediaCodecVideoDecoderTest,
+       SetSurfaceFailureReleasesTheCodecAndSignalsError) {
+  InitializeWithOverlay();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+
+  surface_chooser_->ProvideSurfaceTexture();
+  EXPECT_CALL(*codec, SetSurface(_)).WillOnce(Return(false));
+  EXPECT_CALL(decode_cb_, Run(DecodeStatus::DECODE_ERROR)).Times(2);
+  EXPECT_CALL(*codec_allocator_, MockReleaseMediaCodec(codec, NotNull(), _));
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+  // Verify expectations before we delete the MCVD.
+  testing::Mock::VerifyAndClearExpectations(codec_allocator_.get());
+}
+
+TEST_F(MediaCodecVideoDecoderTest, SurfaceTransitionsCanBeCanceled) {
+  InitializeWithSurfaceTexture();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+
+  // Set a pending transition to an overlay, and then back to a surface texture.
+  // They should cancel each other out and leave the codec as-is.
+  EXPECT_CALL(*codec, SetSurface(_)).Times(0);
+  auto overlay = base::MakeUnique<MockAndroidOverlay>();
+  auto observer = overlay->CreateDestructionObserver();
+  surface_chooser_->ProvideOverlay(std::move(overlay));
+
+  // Switching back to surface texture should delete the pending overlay.
+  observer->ExpectDestruction();
+  surface_chooser_->ProvideSurfaceTexture();
+  observer.reset();
+
+  // Verify that Decode() does not transition the surface
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+}
+
+TEST_F(MediaCodecVideoDecoderTest, TransitionToSameSurfaceIsIgnored) {
+  InitializeWithSurfaceTexture();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+  EXPECT_CALL(*codec, SetSurface(_)).Times(0);
+  surface_chooser_->ProvideSurfaceTexture();
+  mcvd_->Decode(nullptr, decode_cb_.Get());
+}
+
+TEST_F(MediaCodecVideoDecoderTest,
+       SurfaceTransitionsAreIgnoredIfSetSurfaceIsNotSupported) {
+  InitializeWithSurfaceTexture();
+  auto* codec = codec_allocator_->ProvideMockCodecAsync();
+
+  EXPECT_CALL(*device_info_, IsSetOutputSurfaceSupported())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(*codec, SetSurface(_)).Times(0);
+  surface_chooser_->ProvideSurfaceTexture();
+  mcvd_->Decode(nullptr, decode_cb_.Get());
 }
 
 }  // namespace media
diff --git a/media/gpu/android/mock_device_info.cc b/media/gpu/android/mock_device_info.cc
new file mode 100644
index 0000000..eb8606c
--- /dev/null
+++ b/media/gpu/android/mock_device_info.cc
@@ -0,0 +1,27 @@
+// 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 "media/gpu/android/mock_device_info.h"
+
+#include "base/android/build_info.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace media {
+
+MockDeviceInfo::MockDeviceInfo() {
+  ON_CALL(*this, SdkVersion())
+      .WillByDefault(Return(base::android::SDK_VERSION_MARSHMALLOW));
+  ON_CALL(*this, IsVp8DecoderAvailable()).WillByDefault(Return(true));
+  ON_CALL(*this, IsVp9DecoderAvailable()).WillByDefault(Return(true));
+  ON_CALL(*this, IsDecoderKnownUnaccelerated(_)).WillByDefault(Return(false));
+  ON_CALL(*this, IsSetOutputSurfaceSupported()).WillByDefault(Return(true));
+  ON_CALL(*this, SupportsOverlaySurfaces()).WillByDefault(Return(true));
+  ON_CALL(*this, CodecNeedsFlushWorkaround(_)).WillByDefault(Return(false));
+}
+
+MockDeviceInfo::~MockDeviceInfo() = default;
+
+}  // namespace media
diff --git a/media/gpu/android/mock_device_info.h b/media/gpu/android/mock_device_info.h
new file mode 100644
index 0000000..dc3aff8c
--- /dev/null
+++ b/media/gpu/android/mock_device_info.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_ANDROID_MOCK_DEVICE_INFO_H_
+#define MEDIA_GPU_ANDROID_MOCK_DEVICE_INFO_H_
+
+#include "media/gpu/android/device_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// A mock DeviceInfo with reasonable defaults.
+class MockDeviceInfo : public DeviceInfo {
+ public:
+  MockDeviceInfo();
+  ~MockDeviceInfo();
+
+  MOCK_METHOD0(SdkVersion, int());
+  MOCK_METHOD0(IsVp8DecoderAvailable, bool());
+  MOCK_METHOD0(IsVp9DecoderAvailable, bool());
+  MOCK_METHOD1(IsDecoderKnownUnaccelerated, bool(VideoCodec codec));
+  MOCK_METHOD0(IsSetOutputSurfaceSupported, bool());
+  MOCK_METHOD0(SupportsOverlaySurfaces, bool());
+  MOCK_METHOD1(CodecNeedsFlushWorkaround, bool(MediaCodecBridge* codec));
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_ANDROID_MOCK_DEVICE_INFO_H_
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index fec2ce78..238f6323 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -34,6 +34,7 @@
 #include "media/base/media.h"
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_decoder_config.h"
+#include "media/gpu/android/device_info.h"
 #include "media/gpu/android_video_surface_chooser_impl.h"
 #include "media/gpu/avda_picture_buffer_manager.h"
 #include "media/gpu/content_video_view_overlay.h"
@@ -111,28 +112,17 @@
 // On low end devices (< KitKat is always low-end due to buggy MediaCodec),
 // defer the surface creation until the codec is actually used if we know no
 // software fallback exists.
-bool ShouldDeferSurfaceCreation(
-    AVDACodecAllocator* codec_allocator,
-    const OverlayInfo& overlay_info,
-    VideoCodec codec,
-    const AndroidVideoDecodeAccelerator::PlatformConfig& platform_config) {
-  if (platform_config.force_deferred_surface_creation)
-    return true;
-
+bool ShouldDeferSurfaceCreation(AVDACodecAllocator* codec_allocator,
+                                const OverlayInfo& overlay_info,
+                                VideoCodec codec,
+                                DeviceInfo* device_info) {
   // TODO(liberato): We might still want to defer if we've got a routing
   // token.  It depends on whether we want to use it right away or not.
   if (overlay_info.HasValidSurfaceId() || overlay_info.HasValidRoutingToken())
     return false;
 
   return codec == kCodecH264 && codec_allocator->IsAnyRegisteredAVDA() &&
-         platform_config.sdk_int <= base::android::SDK_VERSION_JELLY_BEAN_MR2;
-}
-
-std::unique_ptr<AndroidOverlay> CreateContentVideoViewOverlay(
-    int32_t surface_id,
-    AndroidOverlayConfig config) {
-  return base::MakeUnique<ContentVideoViewOverlay>(surface_id,
-                                                   std::move(config));
+         device_info->SdkVersion() <= base::android::SDK_VERSION_JELLY_BEAN_MR2;
 }
 
 }  // namespace
@@ -221,16 +211,6 @@
   return manager;
 }
 
-AndroidVideoDecodeAccelerator::PlatformConfig
-AndroidVideoDecodeAccelerator::PlatformConfig::CreateDefault() {
-  PlatformConfig config;
-
-  config.sdk_int = base::android::BuildInfo::GetInstance()->sdk_int();
-  config.allow_setsurface = MediaCodecUtil::IsSetOutputSurfaceSupported();
-
-  return config;
-}
-
 AndroidVideoDecodeAccelerator::BitstreamRecord::BitstreamRecord(
     const BitstreamBuffer& bitstream_buffer)
     : buffer(bitstream_buffer) {
@@ -250,7 +230,7 @@
     const MakeGLContextCurrentCallback& make_context_current_cb,
     const GetGLES2DecoderCallback& get_gles2_decoder_cb,
     const AndroidOverlayMojoFactoryCB& overlay_factory_cb,
-    const PlatformConfig& platform_config)
+    DeviceInfo* device_info)
     : client_(nullptr),
       codec_allocator_(codec_allocator),
       make_context_current_cb_(make_context_current_cb),
@@ -266,7 +246,8 @@
       codec_needs_reset_(false),
       defer_surface_creation_(false),
       surface_chooser_(std::move(surface_chooser)),
-      platform_config_(platform_config),
+      device_info_(device_info),
+      force_defer_surface_creation_for_testing_(false),
       overlay_factory_cb_(overlay_factory_cb),
       weak_this_factory_(this) {}
 
@@ -358,8 +339,9 @@
 
   // If we're low on resources, we may decide to defer creation of the surface
   // until the codec is actually used.
-  if (ShouldDeferSurfaceCreation(codec_allocator_, config_.overlay_info,
-                                 codec_config_->codec, platform_config_)) {
+  if (force_defer_surface_creation_for_testing_ ||
+      ShouldDeferSurfaceCreation(codec_allocator_, config_.overlay_info,
+                                 codec_config_->codec, device_info_)) {
     // We should never be here if a SurfaceView is required.
     // TODO(liberato): This really isn't true with AndroidOverlay.
     DCHECK(!config_.overlay_info.HasValidSurfaceId());
@@ -425,7 +407,7 @@
   // leave the factory blank.
   AndroidOverlayFactoryCB factory;
   if (config_.overlay_info.HasValidSurfaceId()) {
-    factory = base::Bind(&CreateContentVideoViewOverlay,
+    factory = base::Bind(&ContentVideoViewOverlay::Create,
                          config_.overlay_info.surface_id);
   } else if (config_.overlay_info.HasValidRoutingToken() &&
              overlay_factory_cb_) {
@@ -468,7 +450,7 @@
   // change our output surface pre-M, ignore it.  For example, if the
   // compositor tells us that it can't use an overlay, well, there's not much
   // that we can do here unless we start falling forward to keyframes.
-  if (!platform_config_.allow_setsurface)
+  if (!device_info_->IsSetOutputSurfaceSupported())
     return;
 
   // If we're using a SurfaceTexture and are told to switch to one, then just
@@ -1289,7 +1271,7 @@
   if (routing_token && overlay_factory_cb_)
     factory = base::Bind(overlay_factory_cb_, *routing_token);
   else if (surface_id != SurfaceManager::kNoSurfaceID)
-    factory = base::Bind(&CreateContentVideoViewOverlay, surface_id);
+    factory = base::Bind(&ContentVideoViewOverlay::Create, surface_id);
 
   surface_chooser_->ReplaceOverlayFactory(std::move(factory));
 }
@@ -1388,7 +1370,7 @@
   // If the API is available avoid having to restart the decoder in order to
   // leave fullscreen. If we don't clear the surface immediately during this
   // callback, the MediaCodec will throw an error as the surface is destroyed.
-  if (platform_config_.allow_setsurface) {
+  if (device_info_->IsSetOutputSurfaceSupported()) {
     // Since we can't wait for a transition, we must invalidate all outstanding
     // picture buffers to avoid putting the GL system in a broken state.
     picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_);
diff --git a/media/gpu/android_video_decode_accelerator.h b/media/gpu/android_video_decode_accelerator.h
index 6f1458447..cbc91cf 100644
--- a/media/gpu/android_video_decode_accelerator.h
+++ b/media/gpu/android_video_decode_accelerator.h
@@ -22,6 +22,7 @@
 #include "media/base/android/media_drm_bridge_cdm_context.h"
 #include "media/base/android_overlay_mojo_factory.h"
 #include "media/base/content_decryption_module.h"
+#include "media/gpu/android/device_info.h"
 #include "media/gpu/android_video_surface_chooser.h"
 #include "media/gpu/avda_codec_allocator.h"
 #include "media/gpu/avda_picture_buffer_manager.h"
@@ -47,28 +48,13 @@
   static VideoDecodeAccelerator::Capabilities GetCapabilities(
       const gpu::GpuPreferences& gpu_preferences);
 
-  // Various things that we normally ask the platform for, mostly for testing.
-  struct PlatformConfig {
-    int sdk_int = base::android::SDK_VERSION_MARSHMALLOW;
-
-    // Is SetSurface supported?  This is not the same as >= M, since we
-    // blacklist some devices.
-    bool allow_setsurface = true;
-
-    // For testing.
-    bool force_deferred_surface_creation = false;
-
-    // Create a default value that's appropriate for use in production.
-    static PlatformConfig CreateDefault();
-  };
-
   AndroidVideoDecodeAccelerator(
       AVDACodecAllocator* codec_allocator,
       std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser,
       const MakeGLContextCurrentCallback& make_context_current_cb,
       const GetGLES2DecoderCallback& get_gles2_decoder_cb,
       const AndroidOverlayMojoFactoryCB& overlay_factory_cb,
-      const PlatformConfig& platform_config);
+      DeviceInfo* device_info);
 
   ~AndroidVideoDecodeAccelerator() override;
 
@@ -397,7 +383,9 @@
 
   std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser_;
 
-  PlatformConfig platform_config_;
+  DeviceInfo* device_info_;
+
+  bool force_defer_surface_creation_for_testing_;
 
   // Optional factory to produce mojo AndroidOverlay instances.
   AndroidOverlayMojoFactoryCB overlay_factory_cb_;
diff --git a/media/gpu/android_video_decode_accelerator_unittest.cc b/media/gpu/android_video_decode_accelerator_unittest.cc
index 3a8f8b82..e7d7df1 100644
--- a/media/gpu/android_video_decode_accelerator_unittest.cc
+++ b/media/gpu/android_video_decode_accelerator_unittest.cc
@@ -24,9 +24,11 @@
 #include "media/base/android/mock_android_overlay.h"
 #include "media/base/android/mock_media_codec_bridge.h"
 #include "media/gpu/android/fake_codec_allocator.h"
+#include "media/gpu/android/mock_device_info.h"
 #include "media/gpu/android_video_decode_accelerator.h"
 #include "media/gpu/android_video_surface_chooser.h"
 #include "media/gpu/avda_codec_allocator.h"
+#include "media/gpu/fake_android_video_surface_chooser.h"
 #include "media/video/picture.h"
 #include "media/video/video_decode_accelerator.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -78,104 +80,56 @@
   DISALLOW_COPY_AND_ASSIGN(MockVDAClient);
 };
 
-// Surface chooser that calls mocked functions to allow counting calls, but
-// also records arguments.  Note that gmock can't mock out unique_ptrs
-// anyway, so we can't mock AndroidVideoSurfaceChooser directly.
-class FakeOverlayChooser : public NiceMock<AndroidVideoSurfaceChooser> {
- public:
-  FakeOverlayChooser() : weak_factory_(this) {}
-
-  // These are called by the real functions.  You may set expectations on
-  // them if you like.
-  MOCK_METHOD0(MockInitialize, void());
-  MOCK_METHOD0(MockReplaceOverlayFactory, void());
-
-  // We guarantee that we'll only clear this during destruction, so that you
-  // may treat it as "pointer that lasts as long as |this| does".
-  base::WeakPtr<FakeOverlayChooser> GetWeakPtrForTesting() {
-    return weak_factory_.GetWeakPtr();
-  }
-
-  void Initialize(UseOverlayCB use_overlay_cb,
-                  UseSurfaceTextureCB use_surface_texture_cb,
-                  AndroidOverlayFactoryCB initial_factory) override {
-    MockInitialize();
-
-    factory_ = std::move(initial_factory);
-    use_overlay_cb_ = std::move(use_overlay_cb);
-    use_surface_texture_cb_ = std::move(use_surface_texture_cb);
-  }
-
-  void ReplaceOverlayFactory(AndroidOverlayFactoryCB factory) override {
-    MockReplaceOverlayFactory();
-    factory_ = std::move(factory);
-  }
-
-  // Notify AVDA to use a surface texture.
-  void ProvideSurfaceTexture() {
-    use_surface_texture_cb_.Run();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void ProvideOverlay(std::unique_ptr<AndroidOverlay> overlay) {
-    use_overlay_cb_.Run(std::move(overlay));
-    base::RunLoop().RunUntilIdle();
-  }
-
-  UseOverlayCB use_overlay_cb_;
-  UseSurfaceTextureCB use_surface_texture_cb_;
-
-  AndroidOverlayFactoryCB factory_;
-
-  base::WeakPtrFactory<FakeOverlayChooser> weak_factory_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FakeOverlayChooser);
-};
-
 }  // namespace
 
 class AndroidVideoDecodeAcceleratorTest : public testing::Test {
  public:
-  // We pick this profile since it's always supported.
+  // Default to baseline H264 because it's always supported.
   AndroidVideoDecodeAcceleratorTest()
       : gl_decoder_(&command_buffer_service_), config_(H264PROFILE_BASELINE) {}
 
-  ~AndroidVideoDecodeAcceleratorTest() override {}
-
   void SetUp() override {
     JNIEnv* env = base::android::AttachCurrentThread();
     RegisterJni(env);
 
-    main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
-
-    gl::init::ShutdownGL();
     ASSERT_TRUE(gl::init::InitializeGLOneOff());
     surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(16, 16));
     context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
                                          gl::GLContextAttribs());
     context_->MakeCurrent(surface_.get());
 
-    chooser_that_is_usually_null_ = base::MakeUnique<FakeOverlayChooser>();
-    chooser_ = chooser_that_is_usually_null_->GetWeakPtrForTesting();
+    codec_allocator_ = base::MakeUnique<FakeCodecAllocator>();
+    device_info_ = base::MakeUnique<NiceMock<MockDeviceInfo>>();
 
-    platform_config_.sdk_int = base::android::SDK_VERSION_MARSHMALLOW;
-    platform_config_.allow_setsurface = true;
-    platform_config_.force_deferred_surface_creation = false;
+    chooser_that_is_usually_null_ =
+        base::MakeUnique<NiceMock<FakeSurfaceChooser>>();
+    chooser_ = chooser_that_is_usually_null_.get();
 
     // By default, allow deferred init.
     config_.is_deferred_initialization_allowed = true;
   }
 
+  ~AndroidVideoDecodeAcceleratorTest() override {
+    // ~AVDASurfaceBundle() might rely on GL being available, so we have to
+    // explicitly drop references to them before tearing down GL.
+    vda_ = nullptr;
+    codec_allocator_ = nullptr;
+    context_ = nullptr;
+    surface_ = nullptr;
+    gl::init::ShutdownGL();
+  }
+
   // Create and initialize AVDA with |config_|, and return the result.
-  bool InitializeAVDA() {
+  bool InitializeAVDA(bool force_defer_surface_creation = false) {
     // Because VDA has a custom deleter, we must assign it to |vda_| carefully.
     AndroidVideoDecodeAccelerator* avda = new AndroidVideoDecodeAccelerator(
-        &codec_allocator_, std::move(chooser_that_is_usually_null_),
+        codec_allocator_.get(), std::move(chooser_that_is_usually_null_),
         base::Bind(&MakeContextCurrent),
         base::Bind(&GetGLES2Decoder, gl_decoder_.AsWeakPtr()),
-        AndroidOverlayMojoFactoryCB(), platform_config_);
+        AndroidOverlayMojoFactoryCB(), device_info_.get());
     vda_.reset(avda);
+    avda->force_defer_surface_creation_for_testing_ =
+        force_defer_surface_creation;
 
     bool result = vda_->Initialize(config_, &client_);
     base::RunLoop().RunUntilIdle();
@@ -197,13 +151,13 @@
     overlay_callbacks_ = overlay->GetCallbacks();
 
     // Set the expectations first, since ProvideOverlay might cause callbacks.
-    EXPECT_CALL(codec_allocator_,
+    EXPECT_CALL(*codec_allocator_,
                 MockCreateMediaCodecAsync(overlay.get(), nullptr));
     chooser_->ProvideOverlay(std::move(overlay));
 
     // Provide the codec so that we can check if it's freed properly.
     EXPECT_CALL(client_, NotifyInitializationComplete(true));
-    codec_allocator_.ProvideMockCodecAsync();
+    codec_allocator_->ProvideMockCodecAsync();
     base::RunLoop().RunUntilIdle();
   }
 
@@ -214,13 +168,13 @@
     ASSERT_FALSE(chooser_->factory_);
 
     // Set the expectations first, since ProvideOverlay might cause callbacks.
-    EXPECT_CALL(codec_allocator_,
+    EXPECT_CALL(*codec_allocator_,
                 MockCreateMediaCodecAsync(nullptr, NotNull()));
     chooser_->ProvideSurfaceTexture();
 
     // Provide the codec so that we can check if it's freed properly.
     EXPECT_CALL(client_, NotifyInitializationComplete(true));
-    codec_allocator_.ProvideMockCodecAsync();
+    codec_allocator_->ProvideMockCodecAsync();
     base::RunLoop().RunUntilIdle();
   }
 
@@ -248,17 +202,17 @@
   gpu::FakeCommandBufferServiceBase command_buffer_service_;
   NiceMock<gpu::gles2::MockGLES2Decoder> gl_decoder_;
   NiceMock<MockVDAClient> client_;
-  FakeCodecAllocator codec_allocator_;
-  VideoDecodeAccelerator::Config config_;
+  std::unique_ptr<FakeCodecAllocator> codec_allocator_;
 
-  AndroidVideoDecodeAccelerator::PlatformConfig platform_config_;
+  // Only set until InitializeAVDA() is called.
+  std::unique_ptr<FakeSurfaceChooser> chooser_that_is_usually_null_;
+  FakeSurfaceChooser* chooser_;
+  VideoDecodeAccelerator::Config config_;
+  std::unique_ptr<MockDeviceInfo> device_info_;
 
   // Set by InitializeAVDAWithOverlay()
   MockAndroidOverlay::Callbacks overlay_callbacks_;
 
-  // We maintain a weak ref to this since AVDA owns it.
-  base::WeakPtr<FakeOverlayChooser> chooser_;
-
   // This must be a unique pointer to a VDA, not an AVDA, to ensure the
   // the default_delete specialization that calls Destroy() will be used.
   std::unique_ptr<VideoDecodeAccelerator> vda_;
@@ -266,11 +220,6 @@
   AndroidVideoDecodeAccelerator* avda() {
     return reinterpret_cast<AndroidVideoDecodeAccelerator*>(vda_.get());
   }
-
- private:
-  // This is the object that |chooser_| points to, or nullptr once we assign
-  // ownership to AVDA.
-  std::unique_ptr<FakeOverlayChooser> chooser_that_is_usually_null_;
 };
 
 TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureUnsupportedCodec) {
@@ -286,7 +235,7 @@
 
   config_.is_deferred_initialization_allowed = false;
 
-  EXPECT_CALL(codec_allocator_, MockCreateMediaCodecSync(_, _));
+  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecSync(_, _));
   ASSERT_TRUE(InitializeAVDA());
 }
 
@@ -295,9 +244,9 @@
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
   config_.is_deferred_initialization_allowed = false;
-  codec_allocator_.allow_sync_creation = false;
+  codec_allocator_->allow_sync_creation = false;
 
-  EXPECT_CALL(codec_allocator_, MockCreateMediaCodecSync(nullptr, NotNull()));
+  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecSync(nullptr, NotNull()));
   ASSERT_FALSE(InitializeAVDA());
 }
 
@@ -314,15 +263,15 @@
   // Note that if we somehow end up deferring surface creation, then this would
   // no longer be expected to fail.  It would signal success before asking for a
   // surface or codec.
-  EXPECT_CALL(codec_allocator_, MockCreateMediaCodecAsync(_, NotNull()));
+  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, NotNull()));
   EXPECT_CALL(client_, NotifyInitializationComplete(false));
 
   ASSERT_TRUE(InitializeAVDA());
   chooser_->ProvideSurfaceTexture();
-  codec_allocator_.ProvideNullCodecAsync();
+  codec_allocator_->ProvideNullCodecAsync();
 
   // Make sure that codec allocation has happened before destroying the VDA.
-  testing::Mock::VerifyAndClearExpectations(&codec_allocator_);
+  testing::Mock::VerifyAndClearExpectations(codec_allocator_.get());
 }
 
 TEST_F(AndroidVideoDecodeAcceleratorTest,
@@ -332,16 +281,16 @@
   // surface, though.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
-  // It would be nicer if we didn't just force this on, since we might do so
-  // in a state that AVDA isn't supposed to handle (e.g., if we give it a
-  // surface, then it would never decide to defer surface creation).
-  platform_config_.force_deferred_surface_creation = true;
   config_.overlay_info.surface_id = SurfaceManager::kNoSurfaceID;
 
   EXPECT_CALL(*chooser_, MockInitialize()).Times(0);
   EXPECT_CALL(client_, NotifyInitializationComplete(true));
 
-  InitializeAVDA();
+  // It would be nicer if we didn't just force this on, since we might do so
+  // in a state that AVDA isn't supposed to handle (e.g., if we give it a
+  // surface, then it would never decide to defer surface creation).
+  bool force_defer_surface_creation = true;
+  InitializeAVDA(force_defer_surface_creation);
 }
 
 TEST_F(AndroidVideoDecodeAcceleratorTest,
@@ -356,11 +305,11 @@
   // Delete the VDA, and make sure that it tries to free the codec and the right
   // surface texture.
   EXPECT_CALL(
-      codec_allocator_,
-      MockReleaseMediaCodec(codec_allocator_.most_recent_codec(),
-                            codec_allocator_.most_recent_overlay(),
-                            codec_allocator_.most_recent_surface_texture()));
-  codec_allocator_.codec_destruction_observer()->ExpectDestruction();
+      *codec_allocator_,
+      MockReleaseMediaCodec(codec_allocator_->most_recent_codec(),
+                            codec_allocator_->most_recent_overlay(),
+                            codec_allocator_->most_recent_surface_texture()));
+  codec_allocator_->codec_destruction_observer()->ExpectDestruction();
   vda_ = nullptr;
   base::RunLoop().RunUntilIdle();
 }
@@ -377,11 +326,11 @@
   // Delete the VDA, and make sure that it tries to free the codec and the
   // overlay that it provided to us.
   EXPECT_CALL(
-      codec_allocator_,
-      MockReleaseMediaCodec(codec_allocator_.most_recent_codec(),
-                            codec_allocator_.most_recent_overlay(),
-                            codec_allocator_.most_recent_surface_texture()));
-  codec_allocator_.codec_destruction_observer()->ExpectDestruction();
+      *codec_allocator_,
+      MockReleaseMediaCodec(codec_allocator_->most_recent_codec(),
+                            codec_allocator_->most_recent_overlay(),
+                            codec_allocator_->most_recent_surface_texture()));
+  codec_allocator_->codec_destruction_observer()->ExpectDestruction();
   vda_ = nullptr;
   base::RunLoop().RunUntilIdle();
 }
@@ -397,14 +346,14 @@
   // It would be nice if we knew that this was a surface texture.  As it is, we
   // just destroy the VDA and expect that we're provided with one.  Hopefully,
   // AVDA is actually calling SetSurface properly.
-  EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_))
+  EXPECT_CALL(*codec_allocator_->most_recent_codec(), SetSurface(_))
       .WillOnce(Return(true));
-  codec_allocator_.codec_destruction_observer()->DestructionIsOptional();
+  codec_allocator_->codec_destruction_observer()->DestructionIsOptional();
   overlay_callbacks_.SurfaceDestroyed.Run();
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_CALL(codec_allocator_,
-              MockReleaseMediaCodec(codec_allocator_.most_recent_codec(),
+  EXPECT_CALL(*codec_allocator_,
+              MockReleaseMediaCodec(codec_allocator_->most_recent_codec(),
                                     nullptr, NotNull()));
   vda_ = nullptr;
   base::RunLoop().RunUntilIdle();
@@ -417,7 +366,7 @@
 
   InitializeAVDAWithOverlay();
 
-  EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_))
+  EXPECT_CALL(*codec_allocator_->most_recent_codec(), SetSurface(_))
       .WillOnce(Return(true));
 
   // Note that it's okay if |avda_| switches before ProvideSurfaceTexture
@@ -426,10 +375,10 @@
   LetAVDAUpdateSurface();
 
   // Verify that we're now using some surface texture.
-  EXPECT_CALL(codec_allocator_,
-              MockReleaseMediaCodec(codec_allocator_.most_recent_codec(),
+  EXPECT_CALL(*codec_allocator_,
+              MockReleaseMediaCodec(codec_allocator_->most_recent_codec(),
                                     nullptr, NotNull()));
-  codec_allocator_.codec_destruction_observer()->ExpectDestruction();
+  codec_allocator_->codec_destruction_observer()->ExpectDestruction();
   vda_ = nullptr;
   base::RunLoop().RunUntilIdle();
 }
@@ -443,12 +392,12 @@
 
   InitializeAVDAWithOverlay();
 
-  EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_))
+  EXPECT_CALL(*codec_allocator_->most_recent_codec(), SetSurface(_))
       .WillOnce(Return(false));
   EXPECT_CALL(client_,
               NotifyError(AndroidVideoDecodeAccelerator::PLATFORM_FAILURE))
       .Times(1);
-  codec_allocator_.codec_destruction_observer()->DestructionIsOptional();
+  codec_allocator_->codec_destruction_observer()->DestructionIsOptional();
   chooser_->ProvideSurfaceTexture();
   LetAVDAUpdateSurface();
 }
@@ -464,7 +413,7 @@
   // Don't let AVDA switch immediately, else it could choose to SetSurface when
   // it first gets the overlay.
   SetHasUnrenderedPictureBuffers(true);
-  EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0);
+  EXPECT_CALL(*codec_allocator_->most_recent_codec(), SetSurface(_)).Times(0);
   std::unique_ptr<MockAndroidOverlay> overlay =
       base::MakeUnique<MockAndroidOverlay>();
   // Make sure that the overlay is not destroyed too soon.
@@ -496,9 +445,10 @@
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
   EXPECT_CALL(client_, NotifyError(_)).Times(0);
 
-  platform_config_.allow_setsurface = false;
+  ON_CALL(*device_info_, IsSetOutputSurfaceSupported())
+      .WillByDefault(Return(false));
   InitializeAVDAWithOverlay();
-  EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0);
+  EXPECT_CALL(*codec_allocator_->most_recent_codec(), SetSurface(_)).Times(0);
 
   // This should not switch to SurfaceTexture.
   chooser_->ProvideSurfaceTexture();
@@ -510,23 +460,24 @@
   // If AVDA receives OnSurfaceDestroyed without support for SetSurface, then it
   // should free the codec.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-  platform_config_.allow_setsurface = false;
+  ON_CALL(*device_info_, IsSetOutputSurfaceSupported())
+      .WillByDefault(Return(false));
   InitializeAVDAWithOverlay();
-  EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0);
+  EXPECT_CALL(*codec_allocator_->most_recent_codec(), SetSurface(_)).Times(0);
 
   // This should free the codec.
   EXPECT_CALL(
-      codec_allocator_,
-      MockReleaseMediaCodec(codec_allocator_.most_recent_codec(),
-                            codec_allocator_.most_recent_overlay(), nullptr));
-  codec_allocator_.codec_destruction_observer()->ExpectDestruction();
+      *codec_allocator_,
+      MockReleaseMediaCodec(codec_allocator_->most_recent_codec(),
+                            codec_allocator_->most_recent_overlay(), nullptr));
+  codec_allocator_->codec_destruction_observer()->ExpectDestruction();
   overlay_callbacks_.SurfaceDestroyed.Run();
   base::RunLoop().RunUntilIdle();
 
   // Verify that the codec has been released, since |vda_| will be destroyed
   // soon.  The expectations must be met before that.
   testing::Mock::VerifyAndClearExpectations(&codec_allocator_);
-  codec_allocator_.codec_destruction_observer()->DestructionIsOptional();
+  codec_allocator_->codec_destruction_observer()->DestructionIsOptional();
 }
 
 TEST_F(AndroidVideoDecodeAcceleratorTest,
@@ -537,7 +488,7 @@
   InitializeAVDAWithSurfaceTexture();
 
   // This should do nothing.
-  EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0);
+  EXPECT_CALL(*codec_allocator_->most_recent_codec(), SetSurface(_)).Times(0);
   chooser_->ProvideSurfaceTexture();
 
   base::RunLoop().RunUntilIdle();
diff --git a/media/gpu/content_video_view_overlay.cc b/media/gpu/content_video_view_overlay.cc
index 39df2f11..9a4702f 100644
--- a/media/gpu/content_video_view_overlay.cc
+++ b/media/gpu/content_video_view_overlay.cc
@@ -6,11 +6,20 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "gpu/ipc/common/gpu_surface_lookup.h"
 
 namespace media {
 
+// static
+std::unique_ptr<AndroidOverlay> ContentVideoViewOverlay::Create(
+    int surface_id,
+    AndroidOverlayConfig config) {
+  return base::MakeUnique<ContentVideoViewOverlay>(surface_id,
+                                                   std::move(config));
+}
+
 ContentVideoViewOverlay::ContentVideoViewOverlay(int surface_id,
                                                  AndroidOverlayConfig config)
     : surface_id_(surface_id), config_(std::move(config)), weak_factory_(this) {
diff --git a/media/gpu/content_video_view_overlay.h b/media/gpu/content_video_view_overlay.h
index 9657be9..24daa17 100644
--- a/media/gpu/content_video_view_overlay.h
+++ b/media/gpu/content_video_view_overlay.h
@@ -5,6 +5,8 @@
 #ifndef MEDIA_GPU_CONTENT_VIDEO_VIEW_OVERLAY_H_
 #define MEDIA_GPU_CONTENT_VIDEO_VIEW_OVERLAY_H_
 
+#include <memory>
+
 #include "base/memory/weak_ptr.h"
 #include "media/base/android/android_overlay.h"
 #include "media/gpu/content_video_view_overlay_allocator.h"
@@ -15,6 +17,11 @@
 class ContentVideoViewOverlay
     : public ContentVideoViewOverlayAllocator::Client {
  public:
+  // This exists so we can bind construction into a callback returning
+  // std::unique_ptr<AndroidOverlay>.
+  static std::unique_ptr<AndroidOverlay> Create(int surface_id,
+                                                AndroidOverlayConfig config);
+
   // |config| is ignored except for callbacks.  Callbacks will not be called
   // before this returns.
   ContentVideoViewOverlay(int surface_id, AndroidOverlayConfig config);
@@ -30,16 +37,13 @@
   void OnSurfaceDestroyed() override;
   int32_t GetSurfaceId() override;
 
- protected:
-  // For tests.
-  ContentVideoViewOverlay();
-
  private:
   int surface_id_;
   AndroidOverlayConfig config_;
   gl::ScopedJavaSurface surface_;
 
   base::WeakPtrFactory<ContentVideoViewOverlay> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(ContentVideoViewOverlay);
 };
 
 }  // namespace media
diff --git a/media/gpu/fake_android_video_surface_chooser.cc b/media/gpu/fake_android_video_surface_chooser.cc
new file mode 100644
index 0000000..803c139
--- /dev/null
+++ b/media/gpu/fake_android_video_surface_chooser.cc
@@ -0,0 +1,36 @@
+// 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 "media/gpu/fake_android_video_surface_chooser.h"
+
+namespace media {
+
+FakeSurfaceChooser::FakeSurfaceChooser() = default;
+FakeSurfaceChooser::~FakeSurfaceChooser() = default;
+
+void FakeSurfaceChooser::Initialize(UseOverlayCB use_overlay_cb,
+                                    UseSurfaceTextureCB use_surface_texture_cb,
+                                    AndroidOverlayFactoryCB initial_factory) {
+  MockInitialize();
+  use_overlay_cb_ = std::move(use_overlay_cb);
+  use_surface_texture_cb_ = std::move(use_surface_texture_cb);
+  factory_ = std::move(initial_factory);
+}
+
+void FakeSurfaceChooser::ReplaceOverlayFactory(
+    AndroidOverlayFactoryCB factory) {
+  MockReplaceOverlayFactory();
+  factory_ = std::move(factory);
+}
+
+void FakeSurfaceChooser::ProvideSurfaceTexture() {
+  use_surface_texture_cb_.Run();
+}
+
+void FakeSurfaceChooser::ProvideOverlay(
+    std::unique_ptr<AndroidOverlay> overlay) {
+  use_overlay_cb_.Run(std::move(overlay));
+}
+
+}  // namespace media
diff --git a/media/gpu/fake_android_video_surface_chooser.h b/media/gpu/fake_android_video_surface_chooser.h
new file mode 100644
index 0000000..d2087ad
--- /dev/null
+++ b/media/gpu/fake_android_video_surface_chooser.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_MOCK_ANDROID_VIDEO_SURFACE_CHOOSER_H_
+#define MEDIA_GPU_MOCK_ANDROID_VIDEO_SURFACE_CHOOSER_H_
+
+#include "media/gpu/android_video_surface_chooser.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// A fake surface chooser that lets tests choose the surface with
+// ProvideOverlay() and ProvideSurfaceTexture().
+class FakeSurfaceChooser : public AndroidVideoSurfaceChooser {
+ public:
+  FakeSurfaceChooser();
+  ~FakeSurfaceChooser() override;
+
+  // Mocks that are called by the fakes below.
+  MOCK_METHOD0(MockInitialize, void());
+  MOCK_METHOD0(MockReplaceOverlayFactory, void());
+
+  void Initialize(UseOverlayCB use_overlay_cb,
+                  UseSurfaceTextureCB use_surface_texture_cb,
+                  AndroidOverlayFactoryCB initial_factory) override;
+  void ReplaceOverlayFactory(AndroidOverlayFactoryCB factory) override;
+
+  // Calls the corresponding callback to choose the surface.
+  void ProvideOverlay(std::unique_ptr<AndroidOverlay> overlay);
+  void ProvideSurfaceTexture();
+
+  UseOverlayCB use_overlay_cb_;
+  UseSurfaceTextureCB use_surface_texture_cb_;
+  AndroidOverlayFactoryCB factory_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeSurfaceChooser);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_MOCK_ANDROID_VIDEO_SURFACE_CHOOSER_H_
diff --git a/media/gpu/gpu_video_decode_accelerator_factory.cc b/media/gpu/gpu_video_decode_accelerator_factory.cc
index e926ad640f..a232f174 100644
--- a/media/gpu/gpu_video_decode_accelerator_factory.cc
+++ b/media/gpu/gpu_video_decode_accelerator_factory.cc
@@ -28,6 +28,7 @@
 #include "ui/gl/gl_implementation.h"
 #endif
 #elif defined(OS_ANDROID)
+#include "media/gpu/android/device_info.h"
 #include "media/gpu/android_video_decode_accelerator.h"
 #include "media/gpu/android_video_surface_chooser_impl.h"
 #include "media/gpu/avda_codec_allocator.h"
@@ -184,8 +185,8 @@
   std::unique_ptr<VideoDecodeAccelerator> decoder;
   DVLOG(0) << "Initializing DXVA HW decoder for windows.";
   decoder.reset(new DXVAVideoDecodeAccelerator(
-      get_gl_context_cb_, make_context_current_cb_, bind_image_cb_,
-      workarounds, gpu_preferences));
+      get_gl_context_cb_, make_context_current_cb_, bind_image_cb_, workarounds,
+      gpu_preferences));
   return decoder;
 }
 #endif
@@ -254,7 +255,7 @@
       AVDACodecAllocator::GetInstance(),
       base::MakeUnique<AndroidVideoSurfaceChooserImpl>(),
       make_context_current_cb_, get_gles2_decoder_cb_, overlay_factory_cb_,
-      AndroidVideoDecodeAccelerator::PlatformConfig::CreateDefault()));
+      DeviceInfo::GetInstance()));
   return decoder;
 }
 #endif
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
index 4e57a46e..75af21a 100644
--- a/mojo/common/BUILD.gn
+++ b/mojo/common/BUILD.gn
@@ -16,6 +16,7 @@
   sources = [
     "file.mojom",
     "file_path.mojom",
+    "memory_allocator_dump_cross_process_uid.mojom",
     "process_id.mojom",
     "string16.mojom",
     "text_direction.mojom",
diff --git a/mojo/common/common_custom_types_struct_traits.cc b/mojo/common/common_custom_types_struct_traits.cc
index 62895048..06c22776 100644
--- a/mojo/common/common_custom_types_struct_traits.cc
+++ b/mojo/common/common_custom_types_struct_traits.cc
@@ -105,4 +105,17 @@
   return false;
 }
 
+// static
+bool StructTraits<common::mojom::MemoryAllocatorDumpCrossProcessUidDataView,
+                  base::trace_event::MemoryAllocatorDumpGuid>::
+    Read(common::mojom::MemoryAllocatorDumpCrossProcessUidDataView data,
+         base::trace_event::MemoryAllocatorDumpGuid* out) {
+  // Receiving a zeroed MemoryAllocatorDumpCrossProcessUid is a bug.
+  if (data.value() == 0)
+    return false;
+
+  *out = base::trace_event::MemoryAllocatorDumpGuid(data.value());
+  return true;
+}
+
 }  // namespace mojo
diff --git a/mojo/common/common_custom_types_struct_traits.h b/mojo/common/common_custom_types_struct_traits.h
index c400a5d4..e5c46c73 100644
--- a/mojo/common/common_custom_types_struct_traits.h
+++ b/mojo/common/common_custom_types_struct_traits.h
@@ -9,9 +9,11 @@
 #include "base/i18n/rtl.h"
 #include "base/process/process_handle.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/unguessable_token.h"
 #include "base/version.h"
 #include "mojo/common/file.mojom-shared.h"
+#include "mojo/common/memory_allocator_dump_cross_process_uid.mojom-shared.h"
 #include "mojo/common/mojo_common_export.h"
 #include "mojo/common/process_id.mojom-shared.h"
 #include "mojo/common/string16.mojom-shared.h"
@@ -116,6 +118,18 @@
                         base::i18n::TextDirection* out);
 };
 
+template <>
+struct StructTraits<common::mojom::MemoryAllocatorDumpCrossProcessUidDataView,
+                    base::trace_event::MemoryAllocatorDumpGuid> {
+  static uint64_t value(const base::trace_event::MemoryAllocatorDumpGuid& id) {
+    return id.ToUint64();
+  }
+
+  static bool Read(
+      common::mojom::MemoryAllocatorDumpCrossProcessUidDataView data,
+      base::trace_event::MemoryAllocatorDumpGuid* out);
+};
+
 }  // namespace mojo
 
 #endif  // MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
diff --git a/mojo/common/memory_allocator_dump_cross_process_uid.mojom b/mojo/common/memory_allocator_dump_cross_process_uid.mojom
new file mode 100644
index 0000000..6a254f77
--- /dev/null
+++ b/mojo/common/memory_allocator_dump_cross_process_uid.mojom
@@ -0,0 +1,11 @@
+// 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 mojo.common.mojom;
+
+// Corresponds to |base::trace_event::MemoryAllocatorDumpGuid| in base/trace_event/memory_allocator_dump_guid.h
+// TODO(ssid): MemoryAllocatorDumpGUID should be renamed. crbug.com/732653
+struct MemoryAllocatorDumpCrossProcessUid {
+  uint64 value;
+};
diff --git a/mojo/common/memory_allocator_dump_cross_process_uid.typemap b/mojo/common/memory_allocator_dump_cross_process_uid.typemap
new file mode 100644
index 0000000..8a73ec7b
--- /dev/null
+++ b/mojo/common/memory_allocator_dump_cross_process_uid.typemap
@@ -0,0 +1,12 @@
+# 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.
+
+mojom = "//mojo/common/memory_allocator_dump_cross_process_uid.mojom"
+public_headers = [ "//base/trace_event/memory_allocator_dump_guid.h" ]
+traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
+public_deps = [
+  "//mojo/common:struct_traits",
+]
+
+type_mappings = [ "mojo.common.mojom.MemoryAllocatorDumpCrossProcessUid=base::trace_event::MemoryAllocatorDumpGuid" ]
diff --git a/mojo/common/typemaps.gni b/mojo/common/typemaps.gni
index 1909b4bc..6890c8a6 100644
--- a/mojo/common/typemaps.gni
+++ b/mojo/common/typemaps.gni
@@ -5,6 +5,7 @@
 typemaps = [
   "//mojo/common/file.typemap",
   "//mojo/common/file_path.typemap",
+  "//mojo/common/memory_allocator_dump_cross_process_uid.typemap",
   "//mojo/common/process_id.typemap",
   "//mojo/common/string16.typemap",
   "//mojo/common/text_direction.typemap",
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc
index cfb35d3..6995a52 100644
--- a/mojo/edk/embedder/entrypoints.cc
+++ b/mojo/edk/embedder/entrypoints.cc
@@ -76,8 +76,19 @@
   return g_core->FreeMessage(message);
 }
 
-MojoResult MojoGetMessageBufferImpl(MojoMessageHandle message, void** buffer) {
-  return g_core->GetMessageBuffer(message, buffer);
+MojoResult MojoSerializeMessageImpl(MojoMessageHandle message) {
+  return g_core->SerializeMessage(message);
+}
+
+MojoResult MojoGetSerializedMessageContentsImpl(
+    MojoMessageHandle message,
+    void** buffer,
+    uint32_t* num_bytes,
+    MojoHandle* handles,
+    uint32_t* num_handles,
+    MojoGetSerializedMessageContentsFlags flags) {
+  return g_core->GetSerializedMessageContents(message, buffer, num_bytes,
+                                              handles, num_handles, flags);
 }
 
 MojoResult MojoReleaseMessageContextImpl(MojoMessageHandle message,
@@ -121,12 +132,8 @@
 
 MojoResult MojoReadMessageNewImpl(MojoHandle message_pipe_handle,
                                   MojoMessageHandle* message,
-                                  uint32_t* num_bytes,
-                                  MojoHandle* handles,
-                                  uint32_t* num_handles,
                                   MojoReadMessageFlags flags) {
-  return g_core->ReadMessageNew(
-      message_pipe_handle, message, num_bytes, handles, num_handles, flags);
+  return g_core->ReadMessageNew(message_pipe_handle, message, flags);
 }
 
 MojoResult MojoFuseMessagePipesImpl(MojoHandle handle0, MojoHandle handle1) {
@@ -281,7 +288,8 @@
                                     MojoAllocMessageImpl,
                                     MojoCreateMessageImpl,
                                     MojoFreeMessageImpl,
-                                    MojoGetMessageBufferImpl,
+                                    MojoSerializeMessageImpl,
+                                    MojoGetSerializedMessageContentsImpl,
                                     MojoReleaseMessageContextImpl,
                                     MojoWrapPlatformHandleImpl,
                                     MojoUnwrapPlatformHandleImpl,
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 4b55219f..1595add 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -235,8 +235,10 @@
       failed = true;
   }
   if (failed) {
-    for (auto d : dispatchers)
-      d.dispatcher->Close();
+    for (auto d : dispatchers) {
+      if (d.dispatcher)
+        d.dispatcher->Close();
+    }
     return false;
   }
   return true;
@@ -510,15 +512,17 @@
 MojoResult Core::CreateMessage(uintptr_t context,
                                const MojoMessageOperationThunks* thunks,
                                MojoMessageHandle* message_handle) {
-  if (!message_handle || !context || !thunks)
+  if (!message_handle || !context)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
-  if (thunks->struct_size != sizeof(MojoMessageOperationThunks))
-    return MOJO_RESULT_INVALID_ARGUMENT;
+  if (thunks) {
+    if (thunks->struct_size != sizeof(MojoMessageOperationThunks))
+      return MOJO_RESULT_INVALID_ARGUMENT;
 
-  if (!thunks->get_serialized_size || !thunks->serialize_handles ||
-      !thunks->serialize_payload || !thunks->destroy)
-    return MOJO_RESULT_INVALID_ARGUMENT;
+    if (!thunks->get_serialized_size || !thunks->serialize_handles ||
+        !thunks->serialize_payload || !thunks->destroy)
+      return MOJO_RESULT_INVALID_ARGUMENT;
+  }
 
   *message_handle = reinterpret_cast<MojoMessageHandle>(
       UserMessageImpl::CreateEventForNewMessageWithContext(context, thunks)
@@ -535,18 +539,55 @@
   return MOJO_RESULT_OK;
 }
 
-MojoResult Core::GetMessageBuffer(MojoMessageHandle message_handle,
-                                  void** buffer) {
+MojoResult Core::SerializeMessage(MojoMessageHandle message_handle) {
   if (!message_handle)
     return MOJO_RESULT_INVALID_ARGUMENT;
+  return reinterpret_cast<ports::UserMessageEvent*>(message_handle)
+      ->GetMessage<UserMessageImpl>()
+      ->SerializeIfNecessary();
+}
+
+MojoResult Core::GetSerializedMessageContents(
+    MojoMessageHandle message_handle,
+    void** buffer,
+    uint32_t* num_bytes,
+    MojoHandle* handles,
+    uint32_t* num_handles,
+    MojoGetSerializedMessageContentsFlags flags) {
+  if (!message_handle || (num_handles && *num_handles && !handles))
+    return MOJO_RESULT_INVALID_ARGUMENT;
 
   auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
                       ->GetMessage<UserMessageImpl>();
   if (!message->IsSerialized())
-    return MOJO_RESULT_NOT_FOUND;
+    return MOJO_RESULT_FAILED_PRECONDITION;
 
-  *buffer = message->user_payload();
-  return MOJO_RESULT_OK;
+  if (num_bytes) {
+    base::CheckedNumeric<uint32_t> payload_size = message->user_payload_size();
+    *num_bytes = payload_size.ValueOrDie();
+  }
+
+  if (message->user_payload_size() > 0) {
+    if (!num_bytes || !buffer)
+      return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+    *buffer = message->user_payload();
+  }
+
+  if (flags & MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES)
+    return MOJO_RESULT_OK;
+
+  uint32_t max_num_handles = 0;
+  if (num_handles) {
+    max_num_handles = *num_handles;
+    *num_handles = static_cast<uint32_t>(message->num_handles());
+  }
+
+  if (message->num_handles() > max_num_handles)
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+  return message->ExtractSerializedHandles(
+      UserMessageImpl::ExtractBadHandlePolicy::kAbort, handles);
 }
 
 MojoResult Core::ReleaseMessageContext(MojoMessageHandle message_handle,
@@ -595,11 +636,12 @@
   *message_pipe_handle1 = AddDispatcher(
       new MessagePipeDispatcher(GetNodeController(), port1, pipe_id, 1));
   if (*message_pipe_handle1 == MOJO_HANDLE_INVALID) {
-    scoped_refptr<Dispatcher> unused;
-    unused->Close();
-
-    base::AutoLock lock(handles_->GetLock());
-    handles_->GetAndRemoveDispatcher(*message_pipe_handle0, &unused);
+    scoped_refptr<Dispatcher> dispatcher0;
+    {
+      base::AutoLock lock(handles_->GetLock());
+      handles_->GetAndRemoveDispatcher(*message_pipe_handle0, &dispatcher0);
+    }
+    dispatcher0->Close();
     return MOJO_RESULT_RESOURCE_EXHAUSTED;
   }
 
@@ -615,26 +657,27 @@
   if (num_bytes && !bytes)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
-  MojoMessageHandle message;
+  MojoMessageHandle message_handle;
   MojoResult rv = AllocMessage(num_bytes, handles, num_handles,
-                               MOJO_ALLOC_MESSAGE_FLAG_NONE, &message);
+                               MOJO_ALLOC_MESSAGE_FLAG_NONE, &message_handle);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
   if (num_bytes) {
-    void* buffer = nullptr;
-    rv = GetMessageBuffer(message, &buffer);
-    DCHECK_EQ(rv, MOJO_RESULT_OK);
-    memcpy(buffer, bytes, num_bytes);
+    auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
+                        ->GetMessage<UserMessageImpl>();
+    memcpy(message->user_payload(), bytes, num_bytes);
   }
 
-  return WriteMessageNew(message_pipe_handle, message, flags);
+  return WriteMessageNew(message_pipe_handle, message_handle, flags);
 }
 
 MojoResult Core::WriteMessageNew(MojoHandle message_pipe_handle,
                                  MojoMessageHandle message_handle,
                                  MojoWriteMessageFlags flags) {
   RequestContext request_context;
+  if (!message_handle)
+    return MOJO_RESULT_INVALID_ARGUMENT;
   auto message = base::WrapUnique(
       reinterpret_cast<ports::UserMessageEvent*>(message_handle));
   auto dispatcher = GetDispatcher(message_pipe_handle);
@@ -656,9 +699,15 @@
   if (!dispatcher)
     return MOJO_RESULT_INVALID_ARGUMENT;
   std::unique_ptr<ports::UserMessageEvent> message_event;
+  const uint32_t max_payload_size = num_bytes ? *num_bytes : 0;
+  const uint32_t max_num_handles = num_handles ? *num_handles : 0;
+  auto discard_policy = flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD
+                            ? Dispatcher::ReadMessageDiscardPolicy::kMayDiscard
+                            : Dispatcher::ReadMessageDiscardPolicy::kNoDiscard;
   MojoResult rv =
-      dispatcher->ReadMessage(&message_event, num_bytes, handles, num_handles,
-                              flags, false /* ignore_num_bytes */);
+      dispatcher->ReadMessage(Dispatcher::ReadMessageSizePolicy::kLimitedSize,
+                              discard_policy, max_payload_size, max_num_handles,
+                              &message_event, num_bytes, num_handles);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
@@ -669,34 +718,42 @@
     return MOJO_RESULT_OK;
 
   auto* message = message_event->GetMessage<UserMessageImpl>();
-
-  // This must be true for ReadMessage to have succeeded above.
   DCHECK(message->IsSerialized());
-
-  if (message->user_payload_size())
+  if (message->num_handles()) {
+    DCHECK_LE(message->num_handles(), max_num_handles);
+    MojoResult extract_result = message->ExtractSerializedHandles(
+        UserMessageImpl::ExtractBadHandlePolicy::kAbort, handles);
+    if (extract_result != MOJO_RESULT_OK)
+      return extract_result;
+  }
+  if (message->user_payload_size()) {
+    DCHECK_LE(message->user_payload_size(), max_payload_size);
     memcpy(bytes, message->user_payload(), message->user_payload_size());
+  }
   return MOJO_RESULT_OK;
 }
 
 MojoResult Core::ReadMessageNew(MojoHandle message_pipe_handle,
                                 MojoMessageHandle* message_handle,
-                                uint32_t* num_bytes,
-                                MojoHandle* handles,
-                                uint32_t* num_handles,
                                 MojoReadMessageFlags flags) {
-  DCHECK(message_handle);
-  DCHECK(!num_handles || !*num_handles || handles);
   RequestContext request_context;
   auto dispatcher = GetDispatcher(message_pipe_handle);
-  if (!dispatcher)
+  if (!dispatcher || !message_handle)
     return MOJO_RESULT_INVALID_ARGUMENT;
+
   std::unique_ptr<ports::UserMessageEvent> message_event;
   MojoResult rv =
-      dispatcher->ReadMessage(&message_event, num_bytes, handles, num_handles,
-                              flags, true /* ignore_num_bytes */);
+      dispatcher->ReadMessage(Dispatcher::ReadMessageSizePolicy::kAnySize,
+                              Dispatcher::ReadMessageDiscardPolicy::kNoDiscard,
+                              0, 0, &message_event, nullptr, nullptr);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
+  // If there's nowhere to store the message handle and discard is allowed by
+  // the caller, we simply drop the message event.
+  if (flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD && !message_handle)
+    return MOJO_RESULT_OK;
+
   *message_handle =
       reinterpret_cast<MojoMessageHandle>(message_event.release());
   return MOJO_RESULT_OK;
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index f5df16e..5dd074e 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -199,7 +199,14 @@
                            const MojoMessageOperationThunks* thunks,
                            MojoMessageHandle* message_handle);
   MojoResult FreeMessage(MojoMessageHandle message_handle);
-  MojoResult GetMessageBuffer(MojoMessageHandle message_handle, void** buffer);
+  MojoResult SerializeMessage(MojoMessageHandle message_handle);
+  MojoResult GetSerializedMessageContents(
+      MojoMessageHandle message_handle,
+      void** buffer,
+      uint32_t* num_bytes,
+      MojoHandle* handles,
+      uint32_t* num_handles,
+      MojoGetSerializedMessageContentsFlags flags);
   MojoResult ReleaseMessageContext(MojoMessageHandle message_handle,
                                    uintptr_t* context);
   MojoResult GetProperty(MojoPropertyType type, void* value);
@@ -227,9 +234,6 @@
                          MojoReadMessageFlags flags);
   MojoResult ReadMessageNew(MojoHandle message_pipe_handle,
                             MojoMessageHandle* message_handle,
-                            uint32_t* num_bytes,
-                            MojoHandle* handles,
-                            uint32_t* num_handles,
                             MojoReadMessageFlags flags);
   MojoResult FuseMessagePipes(MojoHandle handle0, MojoHandle handle1);
   MojoResult NotifyBadMessage(MojoMessageHandle message_handle,
diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc
index e518f3d..148d82e 100644
--- a/mojo/edk/system/core_test_base.cc
+++ b/mojo/edk/system/core_test_base.cc
@@ -57,17 +57,14 @@
   }
 
   MojoResult ReadMessage(
+      ReadMessageSizePolicy size_policy,
+      ReadMessageDiscardPolicy discard_policy,
+      uint32_t max_payload_size,
+      uint32_t max_num_handles,
       std::unique_ptr<ports::UserMessageEvent>* message_event,
-      uint32_t* num_bytes,
-      MojoHandle* handle,
-      uint32_t* num_handles,
-      MojoReadMessageFlags /*flags*/,
-      bool ignore_num_bytes) override {
+      uint32_t* actual_payload_size,
+      uint32_t* actual_num_handles) override {
     info_->IncrementReadMessageCallCount();
-
-    if (num_handles)
-      *num_handles = 1;
-
     return MOJO_RESULT_OK;
   }
 
diff --git a/mojo/edk/system/dispatcher.cc b/mojo/edk/system/dispatcher.cc
index c144a00..f5cd9d3 100644
--- a/mojo/edk/system/dispatcher.cc
+++ b/mojo/edk/system/dispatcher.cc
@@ -47,12 +47,13 @@
 }
 
 MojoResult Dispatcher::ReadMessage(
+    ReadMessageSizePolicy size_policy,
+    ReadMessageDiscardPolicy discard_policy,
+    uint32_t max_payload_size,
+    uint32_t max_num_handles,
     std::unique_ptr<ports::UserMessageEvent>* message,
-    uint32_t* num_bytes,
-    MojoHandle* handles,
-    uint32_t* num_handles,
-    MojoReadMessageFlags flags,
-    bool read_any_size) {
+    uint32_t* actual_payload_size,
+    uint32_t* actual_num_handles) {
   return MOJO_RESULT_INVALID_ARGUMENT;
 }
 
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
index 0863da5..8275202 100644
--- a/mojo/edk/system/dispatcher.h
+++ b/mojo/edk/system/dispatcher.h
@@ -63,6 +63,35 @@
     PLATFORM_HANDLE = -1,
   };
 
+  // Transitional enum so that ReadMessage can service both MojoReadMessage and
+  // MojoReadMessageNew. To be deleted once MojoReadMessageNew replaces
+  // MojoReadMessage. This is used to decide whether ReadMessage should restrict
+  // the size of the next message it reads, according to its other arguments.
+  enum class ReadMessageSizePolicy {
+    // ReadMessage ignores its sizing arguments and reads any available message.
+    kAnySize,
+
+    // ReadMessage only reads the next available message if it fits within the
+    // size constraints given.
+    kLimitedSize,
+  };
+
+  // Transitional enum so that ReadMessage can service both MojoReadMessage and
+  // MojoReadMessageNew. To be deleted once MojoReadMessageNew replaces
+  // MojoReadMessage. If the selected |ReadMessageSizePolicy| is |kLimitedSize|,
+  // this chooses how to proceed when the provided size constraints are exceeded
+  // by the next available message. If |ReadMessageSizePolicy| is |kAnySize|
+  // this argument is ignored.
+  enum class ReadMessageDiscardPolicy {
+    // Never discard a message. ReadMessage will return an appropriate error
+    // code if the next available message exceeds the given size constraints.
+    kNoDiscard,
+
+    // Discards the next available message if it exceeds the given size
+    // constraints.
+    kMayDiscard,
+  };
+
   // All Dispatchers must minimally implement these methods.
 
   virtual Type GetType() const = 0;
@@ -86,12 +115,13 @@
       MojoWriteMessageFlags flags);
 
   virtual MojoResult ReadMessage(
+      ReadMessageSizePolicy size_policy,
+      ReadMessageDiscardPolicy discard_policy,
+      uint32_t max_payload_size,
+      uint32_t max_num_handles,
       std::unique_ptr<ports::UserMessageEvent>* message,
-      uint32_t* num_bytes,
-      MojoHandle* handles,
-      uint32_t* num_handles,
-      MojoReadMessageFlags flags,
-      bool read_any_size);
+      uint32_t* actual_payload_size,
+      uint32_t* actual_num_handles);
 
   ///////////// Shared buffer API /////////////
 
diff --git a/mojo/edk/system/handle_table.cc b/mojo/edk/system/handle_table.cc
index af986622..e9f2d2b 100644
--- a/mojo/edk/system/handle_table.cc
+++ b/mojo/edk/system/handle_table.cc
@@ -74,10 +74,13 @@
     return false;
 
   for (size_t i = 0; i < dispatchers.size(); ++i) {
-    MojoHandle handle = next_available_handle_++;
-    auto result = handles_.insert(
-        std::make_pair(handle, Entry(dispatchers[i].dispatcher)));
-    DCHECK(result.second);
+    MojoHandle handle = MOJO_HANDLE_INVALID;
+    if (dispatchers[i].dispatcher) {
+      handle = next_available_handle_++;
+      auto result = handles_.insert(
+          std::make_pair(handle, Entry(dispatchers[i].dispatcher)));
+      DCHECK(result.second);
+    }
     handles[i] = handle;
   }
 
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index ec4be91..2c313e9 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -165,34 +165,26 @@
 }
 
 MojoResult MessagePipeDispatcher::ReadMessage(
+    ReadMessageSizePolicy size_policy,
+    ReadMessageDiscardPolicy discard_policy,
+    uint32_t max_payload_size,
+    uint32_t max_num_handles,
     std::unique_ptr<ports::UserMessageEvent>* message,
-    uint32_t* num_bytes,
-    MojoHandle* handles,
-    uint32_t* num_handles,
-    MojoReadMessageFlags flags,
-    bool read_any_size) {
+    uint32_t* actual_payload_size,
+    uint32_t* actual_num_handles) {
   // We can't read from a port that's closed or in transit!
   if (port_closed_ || in_transit_)
     return MOJO_RESULT_INVALID_ARGUMENT;
 
-  const bool may_discard = flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD;
-
-  // Grab a message if the provided handles buffer is large enough. If the input
-  // |num_bytes| is provided and |read_any_size| is false, we also ensure
-  // that it specifies a size at least as large as the next available payload.
-  //
-  // If |read_any_size| is true, the input value of |*num_bytes| is ignored.
-  // This flag exists to support both new and old API behavior.
-  MojoResult read_result = UserMessageImpl::ReadMessageEventFromPort(
-      node_controller_, port_, read_any_size, may_discard, num_bytes, handles,
-      num_handles, message);
+  MojoResult rv = UserMessageImpl::ReadMessageEventFromPort(
+      port_, size_policy, discard_policy, max_payload_size, max_num_handles,
+      message, actual_payload_size, actual_num_handles);
 
   // We may need to update anyone watching our signals in case we just read the
   // last available message.
   base::AutoLock lock(signal_lock_);
   watchers_.NotifyState(GetHandleSignalsStateNoLock());
-
-  return read_result;
+  return rv;
 }
 
 HandleSignalsState
@@ -276,9 +268,10 @@
   const SerializedState* state = static_cast<const SerializedState*>(data);
 
   ports::PortRef port;
-  CHECK_EQ(
-      ports::OK,
-      internal::g_core->GetNodeController()->node()->GetPort(ports[0], &port));
+  if (internal::g_core->GetNodeController()->node()->GetPort(ports[0], &port) !=
+      ports::OK) {
+    return nullptr;
+  }
 
   return new MessagePipeDispatcher(internal::g_core->GetNodeController(), port,
                                    state->pipe_id, state->endpoint);
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index 6425f308..24cf42f 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -49,12 +49,13 @@
   MojoResult Close() override;
   MojoResult WriteMessage(std::unique_ptr<ports::UserMessageEvent> message,
                           MojoWriteMessageFlags flags) override;
-  MojoResult ReadMessage(std::unique_ptr<ports::UserMessageEvent>* message,
-                         uint32_t* num_bytes,
-                         MojoHandle* handles,
-                         uint32_t* num_handles,
-                         MojoReadMessageFlags flags,
-                         bool read_any_size) override;
+  MojoResult ReadMessage(ReadMessageSizePolicy size_policy,
+                         ReadMessageDiscardPolicy discard_policy,
+                         uint32_t max_payload_size,
+                         uint32_t max_num_handles,
+                         std::unique_ptr<ports::UserMessageEvent>* message,
+                         uint32_t* actual_payload_size,
+                         uint32_t* actual_num_handles) override;
   HandleSignalsState GetHandleSignalsState() const override;
   MojoResult AddWatcherRef(const scoped_refptr<WatcherDispatcher>& watcher,
                            uintptr_t context) override;
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
index 268a198..71b0195 100644
--- a/mojo/edk/system/message_pipe_unittest.cc
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -419,9 +419,14 @@
                              MOJO_ALLOC_MESSAGE_FLAG_NONE, &message));
   ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
 
+  uint32_t num_bytes;
   void* buffer = nullptr;
-  EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageBuffer(message, &buffer));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message, &buffer, &num_bytes, nullptr, nullptr,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
   ASSERT_TRUE(buffer);
+  EXPECT_EQ(kMessage.size(), num_bytes);
   memcpy(buffer, kMessage.data(), kMessage.size());
 
   MojoHandle a, b;
@@ -430,16 +435,16 @@
             MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
-  uint32_t num_bytes = 0;
   uint32_t num_handles = 0;
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(b, &message, &num_bytes, nullptr, &num_handles,
-                               MOJO_READ_MESSAGE_FLAG_NONE));
+            MojoReadMessageNew(b, &message, MOJO_READ_MESSAGE_FLAG_NONE));
   ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message, &buffer, &num_bytes, nullptr, &num_handles,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
   EXPECT_EQ(static_cast<uint32_t>(kMessage.size()), num_bytes);
   EXPECT_EQ(0u, num_handles);
-
-  EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageBuffer(message, &buffer));
   ASSERT_TRUE(buffer);
 
   EXPECT_EQ(0, strncmp(static_cast<const char*>(buffer), kMessage.data(),
diff --git a/mojo/edk/system/message_unittest.cc b/mojo/edk/system/message_unittest.cc
index 05714a4..5d01243 100644
--- a/mojo/edk/system/message_unittest.cc
+++ b/mojo/edk/system/message_unittest.cc
@@ -147,6 +147,16 @@
   ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
             MojoFreeMessage(MOJO_MESSAGE_HANDLE_INVALID));
 
+  // null message
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoGetSerializedMessageContents(
+                MOJO_MESSAGE_HANDLE_INVALID, nullptr, nullptr, nullptr, nullptr,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+
+  // null message
+  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+            MojoSerializeMessage(MOJO_MESSAGE_HANDLE_INVALID));
+
   // Non-zero num_handles with null handles array.
   ASSERT_EQ(
       MOJO_RESULT_INVALID_ARGUMENT,
@@ -168,9 +178,8 @@
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
   MojoMessageHandle read_message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(b, &read_message_handle, nullptr, nullptr,
-                               nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &read_message_handle,
+                                               MOJO_READ_MESSAGE_FLAG_NONE));
   message = TestMessageBase::UnwrapMessageHandle<NeverSerializedMessage>(
       &read_message_handle);
   EXPECT_EQ(original_message, message.get());
@@ -207,10 +216,9 @@
 
 TEST_F(MessageTest, SerializeSimpleMessageNoHandlesWithContext) {
   RUN_CHILD_ON_PIPE(ReceiveMessageNoHandles, h)
-    auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
-    MojoWriteMessageNew(h,
-                        TestMessageBase::MakeMessageHandle(std::move(message)),
-                        MOJO_WRITE_MESSAGE_FLAG_NONE);
+  auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
+  MojoWriteMessageNew(h, TestMessageBase::MakeMessageHandle(std::move(message)),
+                      MOJO_WRITE_MESSAGE_FLAG_NONE);
   END_CHILD()
 }
 
@@ -224,14 +232,13 @@
 
 TEST_F(MessageTest, SerializeSimpleMessageOneHandleWithContext) {
   RUN_CHILD_ON_PIPE(ReceiveMessageOneHandle, h)
-    auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
-    mojo::MessagePipe pipe;
-    message->AddMessagePipe(std::move(pipe.handle0));
-    MojoWriteMessageNew(h,
-                        TestMessageBase::MakeMessageHandle(std::move(message)),
-                        MOJO_WRITE_MESSAGE_FLAG_NONE);
-    EXPECT_EQ(kTestMessageWithContext2,
-              MojoTestBase::ReadMessage(pipe.handle1.get().value()));
+  auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
+  mojo::MessagePipe pipe;
+  message->AddMessagePipe(std::move(pipe.handle0));
+  MojoWriteMessageNew(h, TestMessageBase::MakeMessageHandle(std::move(message)),
+                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+  EXPECT_EQ(kTestMessageWithContext2,
+            MojoTestBase::ReadMessage(pipe.handle1.get().value()));
   END_CHILD()
 }
 
@@ -248,23 +255,22 @@
 
 TEST_F(MessageTest, SerializeSimpleMessageWithHandlesWithContext) {
   RUN_CHILD_ON_PIPE(ReceiveMessageWithHandles, h)
-    auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
-    mojo::MessagePipe pipes[4];
-    message->AddMessagePipe(std::move(pipes[0].handle0));
-    message->AddMessagePipe(std::move(pipes[1].handle0));
-    message->AddMessagePipe(std::move(pipes[2].handle0));
-    message->AddMessagePipe(std::move(pipes[3].handle0));
-    MojoWriteMessageNew(h,
-                        TestMessageBase::MakeMessageHandle(std::move(message)),
-                        MOJO_WRITE_MESSAGE_FLAG_NONE);
-    EXPECT_EQ(kTestMessageWithContext1,
-              MojoTestBase::ReadMessage(pipes[0].handle1.get().value()));
-    EXPECT_EQ(kTestMessageWithContext2,
-              MojoTestBase::ReadMessage(pipes[1].handle1.get().value()));
-    EXPECT_EQ(kTestMessageWithContext3,
-              MojoTestBase::ReadMessage(pipes[2].handle1.get().value()));
-    EXPECT_EQ(kTestMessageWithContext4,
-              MojoTestBase::ReadMessage(pipes[3].handle1.get().value()));
+  auto message = base::MakeUnique<SimpleMessage>(kTestMessageWithContext1);
+  mojo::MessagePipe pipes[4];
+  message->AddMessagePipe(std::move(pipes[0].handle0));
+  message->AddMessagePipe(std::move(pipes[1].handle0));
+  message->AddMessagePipe(std::move(pipes[2].handle0));
+  message->AddMessagePipe(std::move(pipes[3].handle0));
+  MojoWriteMessageNew(h, TestMessageBase::MakeMessageHandle(std::move(message)),
+                      MOJO_WRITE_MESSAGE_FLAG_NONE);
+  EXPECT_EQ(kTestMessageWithContext1,
+            MojoTestBase::ReadMessage(pipes[0].handle1.get().value()));
+  EXPECT_EQ(kTestMessageWithContext2,
+            MojoTestBase::ReadMessage(pipes[1].handle1.get().value()));
+  EXPECT_EQ(kTestMessageWithContext3,
+            MojoTestBase::ReadMessage(pipes[2].handle1.get().value()));
+  EXPECT_EQ(kTestMessageWithContext4,
+            MojoTestBase::ReadMessage(pipes[3].handle1.get().value()));
   END_CHILD()
 }
 
@@ -292,9 +298,8 @@
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
   MojoMessageHandle read_message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(b, &read_message_handle, nullptr, nullptr,
-                               nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &read_message_handle,
+                                               MOJO_READ_MESSAGE_FLAG_NONE));
   message =
       TestMessageBase::UnwrapMessageHandle<SimpleMessage>(&read_message_handle);
   EXPECT_EQ(original_message, message.get());
@@ -359,15 +364,17 @@
 
   // Try again, this time reading into a message object.
   MojoMessageHandle message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(b, &message_handle, nullptr, nullptr, nullptr,
-                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &message_handle,
+                                               MOJO_READ_MESSAGE_FLAG_NONE));
   EXPECT_FALSE(message_was_destroyed);
 
   // Not a serialized message, so we can't get serialized contents.
   void* buffer;
-  EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
-            MojoGetMessageBuffer(message_handle, &buffer));
+  uint32_t num_handles = 0;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoGetSerializedMessageContents(
+                message_handle, &buffer, &num_bytes, nullptr, &num_handles,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
   EXPECT_FALSE(message_was_destroyed);
 
   EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message_handle));
@@ -384,9 +391,8 @@
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
   MojoMessageHandle message_handle;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(b, &message_handle, nullptr, nullptr, nullptr,
-                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &message_handle,
+                                               MOJO_READ_MESSAGE_FLAG_NONE));
   uintptr_t context;
   EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
             MojoReleaseMessageContext(message_handle, &context));
@@ -394,6 +400,109 @@
   MojoClose(b);
 }
 
+TEST_F(MessageTest, ForceSerializeMessageWithContext) {
+  // Basic test - we can serialize a simple message.
+  bool message_was_destroyed = false;
+  auto message = base::MakeUnique<SimpleMessage>(
+      kTestMessageWithContext1,
+      base::Bind([](bool* was_destroyed) { *was_destroyed = true; },
+                 &message_was_destroyed));
+  auto message_handle = TestMessageBase::MakeMessageHandle(std::move(message));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle));
+  EXPECT_TRUE(message_was_destroyed);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message_handle));
+
+  // Serialize a message with a single handle. Freeing the message should close
+  // the handle.
+  message_was_destroyed = false;
+  message = base::MakeUnique<SimpleMessage>(
+      kTestMessageWithContext1,
+      base::Bind([](bool* was_destroyed) { *was_destroyed = true; },
+                 &message_was_destroyed));
+  MessagePipe pipe1;
+  message->AddMessagePipe(std::move(pipe1.handle0));
+  message_handle = TestMessageBase::MakeMessageHandle(std::move(message));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle));
+  EXPECT_TRUE(message_was_destroyed);
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message_handle));
+  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(pipe1.handle1.get().value(),
+                                           MOJO_HANDLE_SIGNAL_PEER_CLOSED));
+
+  // Serialize a message with a handle and extract its serialized contents.
+  message_was_destroyed = false;
+  message = base::MakeUnique<SimpleMessage>(
+      kTestMessageWithContext1,
+      base::Bind([](bool* was_destroyed) { *was_destroyed = true; },
+                 &message_was_destroyed));
+  MessagePipe pipe2;
+  message->AddMessagePipe(std::move(pipe2.handle0));
+  message_handle = TestMessageBase::MakeMessageHandle(std::move(message));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle));
+  EXPECT_TRUE(message_was_destroyed);
+  uint32_t num_bytes = 0;
+  void* buffer = nullptr;
+  uint32_t num_handles = 0;
+  MojoHandle extracted_handle;
+  EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+            MojoGetSerializedMessageContents(
+                message_handle, &buffer, &num_bytes, nullptr, &num_handles,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message_handle, &buffer, &num_bytes, nullptr, nullptr,
+                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoGetSerializedMessageContents(
+                message_handle, &buffer, &num_bytes, &extracted_handle,
+                &num_handles, MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
+  EXPECT_EQ(std::string(kTestMessageWithContext1).size(), num_bytes);
+  EXPECT_EQ(std::string(kTestMessageWithContext1),
+            base::StringPiece(static_cast<char*>(buffer), num_bytes));
+
+  // Confirm that the handle we extracted from the serialized message is still
+  // connected to the same peer, despite the fact that its handle value may have
+  // changed.
+  const char kTestMessage[] = "hey you";
+  MojoTestBase::WriteMessage(pipe2.handle1.get().value(), kTestMessage);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(extracted_handle, MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_EQ(kTestMessage, MojoTestBase::ReadMessage(extracted_handle));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message_handle));
+}
+
+TEST_F(MessageTest, DoubleSerialize) {
+  bool message_was_destroyed = false;
+  auto message = base::MakeUnique<SimpleMessage>(
+      kTestMessageWithContext1,
+      base::Bind([](bool* was_destroyed) { *was_destroyed = true; },
+                 &message_was_destroyed));
+  auto message_handle = TestMessageBase::MakeMessageHandle(std::move(message));
+
+  // Ensure we can safely call |MojoSerializeMessage()| twice on the same
+  // message handle.
+  EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle));
+  EXPECT_TRUE(message_was_destroyed);
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoSerializeMessage(message_handle));
+
+  // And also check that we can call it again after we've written and read the
+  // message object from a pipe.
+  MessagePipe pipe;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWriteMessageNew(pipe.handle0->value(), message_handle,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            WaitForSignals(pipe.handle1->value(), MOJO_HANDLE_SIGNAL_READABLE));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoReadMessageNew(pipe.handle1->value(), &message_handle,
+                               MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            MojoSerializeMessage(message_handle));
+
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message_handle));
+}
+
 }  // namespace
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index 37248d14..3bf7979 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -1324,11 +1324,9 @@
 
       // Read a message from the pipe we sent to child1 and flag it as bad.
       ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE));
-      uint32_t num_bytes = 0;
       MojoMessageHandle message;
       ASSERT_EQ(MOJO_RESULT_OK,
-                MojoReadMessageNew(a, &message, &num_bytes, nullptr, 0,
-                                   MOJO_READ_MESSAGE_FLAG_NONE));
+                MojoReadMessageNew(a, &message, MOJO_READ_MESSAGE_FLAG_NONE));
       EXPECT_EQ(MOJO_RESULT_OK,
                 MojoNotifyBadMessage(message, kFirstErrorMessage.data(),
                                      kFirstErrorMessage.size()));
@@ -1337,8 +1335,7 @@
       // Read a message from the pipe we sent to child2 and flag it as bad.
       ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE));
       ASSERT_EQ(MOJO_RESULT_OK,
-                MojoReadMessageNew(c, &message, &num_bytes, nullptr, 0,
-                                   MOJO_READ_MESSAGE_FLAG_NONE));
+                MojoReadMessageNew(c, &message, MOJO_READ_MESSAGE_FLAG_NONE));
       EXPECT_EQ(MOJO_RESULT_OK,
                 MojoNotifyBadMessage(message, kSecondErrorMessage.data(),
                                      kSecondErrorMessage.size()));
diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc
index 4eaed3a..e55f96f 100644
--- a/mojo/edk/system/node_controller.cc
+++ b/mojo/edk/system/node_controller.cc
@@ -111,12 +111,12 @@
   // in the first place.
   DCHECK_LE(event_size, size);
 
+  auto message_event = ports::Event::Cast<ports::UserMessageEvent>(&event);
   auto message = UserMessageImpl::CreateFromChannelMessage(
-      std::move(channel_message), static_cast<uint8_t*>(data) + event_size,
-      size - event_size);
+      message_event.get(), std::move(channel_message),
+      static_cast<uint8_t*>(data) + event_size, size - event_size);
   message->set_source_node(from_node);
 
-  auto message_event = ports::Event::Cast<ports::UserMessageEvent>(&event);
   message_event->AttachMessage(std::move(message));
   return std::move(message_event);
 }
diff --git a/mojo/edk/system/ports/event.cc b/mojo/edk/system/ports/event.cc
index b21a513..2cb0ed5 100644
--- a/mojo/edk/system/ports/event.cc
+++ b/mojo/edk/system/ports/event.cc
@@ -150,7 +150,7 @@
 
 bool UserMessageEvent::NotifyWillBeRoutedExternally() {
   DCHECK(message_);
-  return message_->WillBeRoutedExternally(this);
+  return message_->WillBeRoutedExternally();
 }
 
 // static
diff --git a/mojo/edk/system/ports/message_queue.cc b/mojo/edk/system/ports/message_queue.cc
index 08a7a10..f99f738 100644
--- a/mojo/edk/system/ports/message_queue.cc
+++ b/mojo/edk/system/ports/message_queue.cc
@@ -76,6 +76,11 @@
   }
 }
 
+void MessageQueue::TakeAllMessages(
+    std::vector<std::unique_ptr<UserMessageEvent>>* messages) {
+  *messages = std::move(heap_);
+}
+
 }  // namespace ports
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/ports/message_queue.h b/mojo/edk/system/ports/message_queue.h
index 278c0da4..4142999 100644
--- a/mojo/edk/system/ports/message_queue.h
+++ b/mojo/edk/system/ports/message_queue.h
@@ -59,6 +59,11 @@
   // Returns all of the ports referenced by messages in this message queue.
   void GetReferencedPorts(std::vector<PortName>* ports);
 
+  // Takes all messages from this queue. Used to safely destroy queued messages
+  // without holding any Port lock.
+  void TakeAllMessages(
+      std::vector<std::unique_ptr<UserMessageEvent>>* messages);
+
  private:
   std::vector<std::unique_ptr<UserMessageEvent>> heap_;
   uint64_t next_sequence_num_;
diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc
index a079fc8c..9f830e9 100644
--- a/mojo/edk/system/ports/node.cc
+++ b/mojo/edk/system/ports/node.cc
@@ -729,8 +729,23 @@
 
 void Node::ErasePort(const PortName& port_name) {
   PortLocker::AssertNoPortsLockedOnCurrentThread();
-  base::AutoLock lock(ports_lock_);
-  ports_.erase(port_name);
+  scoped_refptr<Port> port;
+  {
+    base::AutoLock lock(ports_lock_);
+    auto it = ports_.find(port_name);
+    if (it == ports_.end())
+      return;
+    port = std::move(it->second);
+    ports_.erase(it);
+  }
+  // NOTE: We are careful not to release the port's messages while holding any
+  // locks, since they may run arbitrary user code upon destruction.
+  std::vector<std::unique_ptr<UserMessageEvent>> messages;
+  {
+    PortRef port_ref(port_name, std::move(port));
+    SinglePortLocker locker(&port_ref);
+    locker.port()->message_queue.TakeAllMessages(&messages);
+  }
   DVLOG(2) << "Deleted port " << port_name << "@" << name_;
 }
 
@@ -1232,11 +1247,11 @@
         }
       }
     }
+  }
 
-    for (const auto& proxy_name : dead_proxies_to_broadcast) {
-      ports_.erase(proxy_name);
-      DVLOG(2) << "Forcibly deleted port " << proxy_name << "@" << name_;
-    }
+  for (const auto& proxy_name : dead_proxies_to_broadcast) {
+    ErasePort(proxy_name);
+    DVLOG(2) << "Forcibly deleted port " << proxy_name << "@" << name_;
   }
 
   // Wake up any receiving ports who have just observed simulated peer closure.
diff --git a/mojo/edk/system/ports/node.h b/mojo/edk/system/ports/node.h
index 78fdd35..561c6617 100644
--- a/mojo/edk/system/ports/node.h
+++ b/mojo/edk/system/ports/node.h
@@ -214,6 +214,10 @@
   // Guards |ports_|. This must never be acquired while an individual port's
   // lock is held on the same thread. Conversely, individual port locks may be
   // acquired while this one is held.
+  //
+  // Because UserMessage events may execute arbitrary user code during
+  // destruction, it is also important to ensure that such events are never
+  // destroyed while this (or any individual Port) lock is held.
   base::Lock ports_lock_;
   std::unordered_map<PortName, scoped_refptr<Port>> ports_;
 
diff --git a/mojo/edk/system/ports/user_message.cc b/mojo/edk/system/ports/user_message.cc
index e2675f3..1976dcb8 100644
--- a/mojo/edk/system/ports/user_message.cc
+++ b/mojo/edk/system/ports/user_message.cc
@@ -12,7 +12,7 @@
 
 UserMessage::~UserMessage() = default;
 
-bool UserMessage::WillBeRoutedExternally(UserMessageEvent* event) {
+bool UserMessage::WillBeRoutedExternally() {
   return true;
 }
 
diff --git a/mojo/edk/system/ports/user_message.h b/mojo/edk/system/ports/user_message.h
index d7ee82b..435e203c 100644
--- a/mojo/edk/system/ports/user_message.h
+++ b/mojo/edk/system/ports/user_message.h
@@ -11,8 +11,6 @@
 namespace edk {
 namespace ports {
 
-class UserMessageEvent;
-
 // Base type to use for any embedder-defined user message implementation. This
 // class is intentionally empty.
 //
@@ -33,13 +31,12 @@
   const TypeInfo* type_info() const { return type_info_; }
 
   // Invoked immediately before the system asks the embedder to forward this
-  // message to an external node. |message_event| is the event which owns this
-  // message and which will be routed externally.
+  // message to an external node.
   //
   // Returns |true| if the message is OK to route externally, or |false|
   // otherwise. Returning |false| implies an unrecoverable condition, and the
   // message event will be destroyed without further routing.
-  virtual bool WillBeRoutedExternally(UserMessageEvent* message_event);
+  virtual bool WillBeRoutedExternally();
 
  private:
   const TypeInfo* const type_info_;
diff --git a/mojo/edk/system/user_message_impl.cc b/mojo/edk/system/user_message_impl.cc
index 586870c..72334e09 100644
--- a/mojo/edk/system/user_message_impl.cc
+++ b/mojo/edk/system/user_message_impl.cc
@@ -179,18 +179,22 @@
   // Creates a new ReadMessageFilter which captures and potentially modifies
   // various (unowned) local state within
   // UserMessageImpl::ReadMessageEventFromPort.
-  ReadMessageFilter(bool read_any_size,
-                    bool may_discard,
-                    uint32_t* num_bytes,
-                    uint32_t* num_handles,
-                    bool* no_space,
-                    bool* invalid_message)
-      : read_any_size_(read_any_size),
-        may_discard_(may_discard),
-        num_bytes_(num_bytes),
-        num_handles_(num_handles),
-        no_space_(no_space),
-        invalid_message_(invalid_message) {}
+  ReadMessageFilter(Dispatcher::ReadMessageSizePolicy size_policy,
+                    Dispatcher::ReadMessageDiscardPolicy discard_policy,
+                    uint32_t max_payload_size,
+                    uint32_t max_num_handles,
+                    uint32_t* actual_payload_size,
+                    uint32_t* actual_num_handles,
+                    bool* exceeds_size_limit_flag,
+                    bool* invalid_message_flag)
+      : size_policy_(size_policy),
+        discard_policy_(discard_policy),
+        max_payload_size_(max_payload_size),
+        max_num_handles_(max_num_handles),
+        actual_payload_size_(actual_payload_size),
+        actual_num_handles_(actual_num_handles),
+        exceeds_size_limit_flag_(exceeds_size_limit_flag),
+        invalid_message_flag_(invalid_message_flag) {}
 
   ~ReadMessageFilter() override {}
 
@@ -201,8 +205,8 @@
       // Not a serialized message, so there's nothing to validate or filter
       // against. We only ensure that the caller expected a message object and
       // not a specific serialized buffer size.
-      if (!read_any_size_) {
-        *invalid_message_ = true;
+      if (size_policy_ != Dispatcher::ReadMessageSizePolicy::kAnySize) {
+        *invalid_message_flag_ = true;
         return false;
       }
       return true;
@@ -214,42 +218,43 @@
     DCHECK(message->header_);
     auto* header = static_cast<MessageHeader*>(message->header_);
 
-    uint32_t bytes_to_read = 0;
     base::CheckedNumeric<uint32_t> checked_bytes_available =
         message->user_payload_size();
     if (!checked_bytes_available.IsValid()) {
-      *invalid_message_ = true;
+      *invalid_message_flag_ = true;
       return true;
     }
     const uint32_t bytes_available = checked_bytes_available.ValueOrDie();
-    if (num_bytes_) {
-      bytes_to_read = std::min(*num_bytes_, bytes_available);
-      *num_bytes_ = bytes_available;
-    }
+    const uint32_t bytes_to_read = std::min(max_payload_size_, bytes_available);
+    if (actual_payload_size_)
+      *actual_payload_size_ = bytes_available;
 
-    uint32_t handles_to_read = 0;
-    uint32_t handles_available = header->num_dispatchers;
-    if (num_handles_) {
-      handles_to_read = std::min(*num_handles_, handles_available);
-      *num_handles_ = handles_available;
-    }
+    const uint32_t handles_available = header->num_dispatchers;
+    const uint32_t handles_to_read =
+        std::min(max_num_handles_, handles_available);
+    if (actual_num_handles_)
+      *actual_num_handles_ = handles_available;
 
-    if (handles_to_read < handles_available ||
-        (!read_any_size_ && bytes_to_read < bytes_available)) {
-      *no_space_ = true;
-      return may_discard_;
+    if ((handles_to_read < handles_available ||
+         bytes_to_read < bytes_available) &&
+        size_policy_ == Dispatcher::ReadMessageSizePolicy::kLimitedSize) {
+      *exceeds_size_limit_flag_ = true;
+      return discard_policy_ ==
+             Dispatcher::ReadMessageDiscardPolicy::kMayDiscard;
     }
 
     return true;
   }
 
  private:
-  const bool read_any_size_;
-  const bool may_discard_;
-  uint32_t* const num_bytes_;
-  uint32_t* const num_handles_;
-  bool* const no_space_;
-  bool* const invalid_message_;
+  const Dispatcher::ReadMessageSizePolicy size_policy_;
+  const Dispatcher::ReadMessageDiscardPolicy discard_policy_;
+  const uint32_t max_payload_size_;
+  const uint32_t max_num_handles_;
+  uint32_t* const actual_payload_size_;
+  uint32_t* const actual_num_handles_;
+  bool* const exceeds_size_limit_flag_;
+  bool* const invalid_message_flag_;
 
   DISALLOW_COPY_AND_ASSIGN(ReadMessageFilter);
 };
@@ -258,8 +263,24 @@
 const ports::UserMessage::TypeInfo UserMessageImpl::kUserMessageTypeInfo = {};
 
 UserMessageImpl::~UserMessageImpl() {
-  if (HasContext())
-    context_thunks_.destroy(context_);
+  if (HasContext()) {
+    if (context_thunks_.has_value())
+      context_thunks_->destroy(context_);
+    DCHECK(!channel_message_);
+    DCHECK(!has_serialized_handles_);
+  } else if (IsSerialized() && has_serialized_handles_) {
+    // Ensure that any handles still serialized within this message are
+    // extracted and closed so they don't leak.
+    std::vector<MojoHandle> handles(num_handles());
+    MojoResult result =
+        ExtractSerializedHandles(ExtractBadHandlePolicy::kSkip, handles.data());
+    if (result == MOJO_RESULT_OK) {
+      for (auto handle : handles) {
+        if (handle != MOJO_HANDLE_INVALID)
+          MojoClose(handle);
+      }
+    }
+  }
 }
 
 // static
@@ -268,8 +289,8 @@
     uintptr_t context,
     const MojoMessageOperationThunks* thunks) {
   auto message_event = base::MakeUnique<ports::UserMessageEvent>(0);
-  message_event->AttachMessage(
-      base::WrapUnique(new UserMessageImpl(context, thunks)));
+  message_event->AttachMessage(base::WrapUnique(
+      new UserMessageImpl(message_event.get(), context, thunks)));
   return message_event;
 }
 
@@ -288,14 +309,16 @@
                                         &header, &user_payload);
   if (rv != MOJO_RESULT_OK)
     return rv;
-  event->AttachMessage(base::WrapUnique(new UserMessageImpl(
-      std::move(channel_message), header, user_payload, num_bytes)));
+  event->AttachMessage(base::WrapUnique(
+      new UserMessageImpl(event.get(), std::move(channel_message), header,
+                          user_payload, num_bytes)));
   *out_event = std::move(event);
   return MOJO_RESULT_OK;
 }
 
 // static
 std::unique_ptr<UserMessageImpl> UserMessageImpl::CreateFromChannelMessage(
+    ports::UserMessageEvent* message_event,
     Channel::MessagePtr channel_message,
     void* payload,
     size_t payload_size) {
@@ -310,26 +333,30 @@
 
   void* user_payload = static_cast<uint8_t*>(payload) + header_size;
   const size_t user_payload_size = payload_size - header_size;
-  return base::WrapUnique(new UserMessageImpl(
-      std::move(channel_message), header, user_payload, user_payload_size));
+  return base::WrapUnique(
+      new UserMessageImpl(message_event, std::move(channel_message), header,
+                          user_payload, user_payload_size));
 }
 
 // static
 MojoResult UserMessageImpl::ReadMessageEventFromPort(
-    NodeController* node_controller,
     const ports::PortRef& port,
-    bool read_any_size,
-    bool may_discard,
-    uint32_t* num_bytes,
-    MojoHandle* handles,
-    uint32_t* num_handles,
-    std::unique_ptr<ports::UserMessageEvent>* out_event) {
-  bool no_space = false;
+    Dispatcher::ReadMessageSizePolicy size_policy,
+    Dispatcher::ReadMessageDiscardPolicy discard_policy,
+    uint32_t max_payload_size,
+    uint32_t max_num_handles,
+    std::unique_ptr<ports::UserMessageEvent>* out_event,
+    uint32_t* actual_payload_size,
+    uint32_t* actual_num_handles) {
+  bool exceeds_size_limit = false;
   bool invalid_message = false;
-  ReadMessageFilter filter(read_any_size, may_discard, num_bytes, num_handles,
-                           &no_space, &invalid_message);
+  ReadMessageFilter filter(size_policy, discard_policy, max_payload_size,
+                           max_num_handles, actual_payload_size,
+                           actual_num_handles, &exceeds_size_limit,
+                           &invalid_message);
   std::unique_ptr<ports::UserMessageEvent> message_event;
-  int rv = node_controller->node()->GetMessage(port, &message_event, &filter);
+  int rv = internal::g_core->GetNodeController()->node()->GetMessage(
+      port, &message_event, &filter);
   if (invalid_message)
     return MOJO_RESULT_NOT_FOUND;
 
@@ -339,13 +366,14 @@
       return MOJO_RESULT_INVALID_ARGUMENT;
 
     NOTREACHED();
-    return MOJO_RESULT_UNKNOWN;  // TODO: Add a better error code here?
+    return MOJO_RESULT_UNKNOWN;
   }
 
-  if (no_space) {
-    // |*num_handles| (and/or |*num_bytes| if |read_any_size| is false) wasn't
-    // sufficient to hold this message's data. The message will still be in
-    // queue unless MOJO_READ_MESSAGE_FLAG_MAY_DISCARD was set.
+  if (exceeds_size_limit) {
+    // |size_policy| is |kLimitedSize| and the next available message exceeded
+    // the constraints specified by |max_payload_size| and/or |max_num_handles|.
+    // The message may or may not have been read off the pipe, depending on the
+    // given value of |discard_policy|.
     return MOJO_RESULT_RESOURCE_EXHAUSTED;
   }
 
@@ -358,92 +386,6 @@
     return MOJO_RESULT_FAILED_PRECONDITION;
   }
 
-  // Alright! We have a message and the caller has provided sufficient storage
-  // in which to receive it, if applicable.
-
-  auto* message = message_event->GetMessage<UserMessageImpl>();
-  if (message->HasContext()) {
-    // Not a serialized message, so there's no more work to do.
-    *out_event = std::move(message_event);
-    return MOJO_RESULT_OK;
-  }
-
-  DCHECK(message->IsSerialized());
-
-  const MessageHeader* header =
-      static_cast<const MessageHeader*>(message->header_);
-  const DispatcherHeader* dispatcher_headers =
-      reinterpret_cast<const DispatcherHeader*>(header + 1);
-
-  if (header->num_dispatchers > std::numeric_limits<uint16_t>::max())
-    return MOJO_RESULT_UNKNOWN;
-
-  // Deserialize dispatchers.
-  if (header->num_dispatchers > 0) {
-    DCHECK(handles);
-    std::vector<Dispatcher::DispatcherInTransit> dispatchers(
-        header->num_dispatchers);
-
-    size_t data_payload_index =
-        sizeof(MessageHeader) +
-        header->num_dispatchers * sizeof(DispatcherHeader);
-    if (data_payload_index > header->header_size)
-      return MOJO_RESULT_UNKNOWN;
-    const char* dispatcher_data = reinterpret_cast<const char*>(
-        dispatcher_headers + header->num_dispatchers);
-    size_t port_index = 0;
-    size_t platform_handle_index = 0;
-    ScopedPlatformHandleVectorPtr msg_handles =
-        message->channel_message_->TakeHandles();
-    const size_t num_msg_handles = msg_handles ? msg_handles->size() : 0;
-    for (size_t i = 0; i < header->num_dispatchers; ++i) {
-      const DispatcherHeader& dh = dispatcher_headers[i];
-      auto type = static_cast<Dispatcher::Type>(dh.type);
-
-      base::CheckedNumeric<size_t> next_payload_index = data_payload_index;
-      next_payload_index += dh.num_bytes;
-      if (!next_payload_index.IsValid() ||
-          header->header_size < next_payload_index.ValueOrDie()) {
-        return MOJO_RESULT_UNKNOWN;
-      }
-
-      base::CheckedNumeric<size_t> next_port_index = port_index;
-      next_port_index += dh.num_ports;
-      if (!next_port_index.IsValid() ||
-          message_event->num_ports() < next_port_index.ValueOrDie()) {
-        return MOJO_RESULT_UNKNOWN;
-      }
-
-      base::CheckedNumeric<size_t> next_platform_handle_index =
-          platform_handle_index;
-      next_platform_handle_index += dh.num_platform_handles;
-      if (!next_platform_handle_index.IsValid() ||
-          num_msg_handles < next_platform_handle_index.ValueOrDie()) {
-        return MOJO_RESULT_UNKNOWN;
-      }
-
-      PlatformHandle* out_handles =
-          num_msg_handles ? msg_handles->data() + platform_handle_index
-                          : nullptr;
-      dispatchers[i].dispatcher = Dispatcher::Deserialize(
-          type, dispatcher_data, dh.num_bytes,
-          message_event->ports() + port_index, dh.num_ports, out_handles,
-          dh.num_platform_handles);
-      if (!dispatchers[i].dispatcher)
-        return MOJO_RESULT_UNKNOWN;
-
-      dispatcher_data += dh.num_bytes;
-      data_payload_index = next_payload_index.ValueOrDie();
-      port_index = next_port_index.ValueOrDie();
-      platform_handle_index = next_platform_handle_index.ValueOrDie();
-    }
-
-    if (!node_controller->core()->AddDispatchersFromTransit(dispatchers,
-                                                            handles)) {
-      return MOJO_RESULT_UNKNOWN;
-    }
-  }
-
   *out_event = std::move(message_event);
   return MOJO_RESULT_OK;
 }
@@ -480,21 +422,23 @@
   return static_cast<const MessageHeader*>(header_)->num_dispatchers;
 }
 
-MojoResult UserMessageImpl::SerializeIfNecessary(
-    ports::UserMessageEvent* message_event) {
+MojoResult UserMessageImpl::SerializeIfNecessary() {
   if (IsSerialized())
     return MOJO_RESULT_FAILED_PRECONDITION;
+  if (!context_thunks_.has_value())
+    return MOJO_RESULT_NOT_FOUND;
 
   DCHECK(HasContext());
+  DCHECK(!has_serialized_handles_);
   size_t num_bytes = 0;
   size_t num_handles = 0;
-  context_thunks_.get_serialized_size(context_, &num_bytes, &num_handles);
+  context_thunks_->get_serialized_size(context_, &num_bytes, &num_handles);
 
   std::vector<ScopedHandle> handles(num_handles);
   std::vector<Dispatcher::DispatcherInTransit> dispatchers;
   if (num_handles > 0) {
     auto* raw_handles = reinterpret_cast<MojoHandle*>(handles.data());
-    context_thunks_.serialize_handles(context_, raw_handles);
+    context_thunks_->serialize_handles(context_, raw_handles);
     MojoResult acquire_result = internal::g_core->AcquireDispatchersForTransit(
         raw_handles, num_handles, &dispatchers);
     if (acquire_result != MOJO_RESULT_OK)
@@ -503,7 +447,7 @@
 
   Channel::MessagePtr channel_message;
   MojoResult rv = SerializeEventMessage(
-      message_event, num_bytes, dispatchers.data(), num_handles,
+      message_event_, num_bytes, dispatchers.data(), num_handles,
       &channel_message, &header_, &user_payload_);
   if (num_handles > 0) {
     internal::g_core->ReleaseDispatchersForTransit(dispatchers,
@@ -517,35 +461,126 @@
   for (auto& handle : handles)
     ignore_result(handle.release());
 
-  context_thunks_.serialize_payload(context_, user_payload_);
+  if (num_bytes)
+    context_thunks_->serialize_payload(context_, user_payload_);
   user_payload_size_ = num_bytes;
   channel_message_ = std::move(channel_message);
 
-  context_thunks_.destroy(context_);
+  context_thunks_->destroy(context_);
   context_ = 0;
+  has_serialized_handles_ = true;
   return MOJO_RESULT_OK;
 }
 
-UserMessageImpl::UserMessageImpl(uintptr_t context,
+MojoResult UserMessageImpl::ExtractSerializedHandles(
+    ExtractBadHandlePolicy bad_handle_policy,
+    MojoHandle* handles) {
+  if (!IsSerialized())
+    return MOJO_RESULT_FAILED_PRECONDITION;
+
+  if (!has_serialized_handles_)
+    return MOJO_RESULT_NOT_FOUND;
+
+  const MessageHeader* header = static_cast<const MessageHeader*>(header_);
+  const DispatcherHeader* dispatcher_headers =
+      reinterpret_cast<const DispatcherHeader*>(header + 1);
+
+  if (header->num_dispatchers > std::numeric_limits<uint16_t>::max())
+    return MOJO_RESULT_ABORTED;
+
+  if (header->num_dispatchers == 0)
+    return MOJO_RESULT_OK;
+
+  has_serialized_handles_ = false;
+
+  std::vector<Dispatcher::DispatcherInTransit> dispatchers(
+      header->num_dispatchers);
+
+  size_t data_payload_index =
+      sizeof(MessageHeader) +
+      header->num_dispatchers * sizeof(DispatcherHeader);
+  if (data_payload_index > header->header_size)
+    return MOJO_RESULT_ABORTED;
+  const char* dispatcher_data = reinterpret_cast<const char*>(
+      dispatcher_headers + header->num_dispatchers);
+  size_t port_index = 0;
+  size_t platform_handle_index = 0;
+  ScopedPlatformHandleVectorPtr msg_handles = channel_message_->TakeHandles();
+  const size_t num_msg_handles = msg_handles ? msg_handles->size() : 0;
+  for (size_t i = 0; i < header->num_dispatchers; ++i) {
+    const DispatcherHeader& dh = dispatcher_headers[i];
+    auto type = static_cast<Dispatcher::Type>(dh.type);
+
+    base::CheckedNumeric<size_t> next_payload_index = data_payload_index;
+    next_payload_index += dh.num_bytes;
+    if (!next_payload_index.IsValid() ||
+        header->header_size < next_payload_index.ValueOrDie()) {
+      return MOJO_RESULT_ABORTED;
+    }
+
+    base::CheckedNumeric<size_t> next_port_index = port_index;
+    next_port_index += dh.num_ports;
+    if (!next_port_index.IsValid() ||
+        message_event_->num_ports() < next_port_index.ValueOrDie()) {
+      return MOJO_RESULT_ABORTED;
+    }
+
+    base::CheckedNumeric<size_t> next_platform_handle_index =
+        platform_handle_index;
+    next_platform_handle_index += dh.num_platform_handles;
+    if (!next_platform_handle_index.IsValid() ||
+        num_msg_handles < next_platform_handle_index.ValueOrDie()) {
+      return MOJO_RESULT_ABORTED;
+    }
+
+    PlatformHandle* out_handles =
+        num_msg_handles ? msg_handles->data() + platform_handle_index : nullptr;
+    dispatchers[i].dispatcher = Dispatcher::Deserialize(
+        type, dispatcher_data, dh.num_bytes,
+        message_event_->ports() + port_index, dh.num_ports, out_handles,
+        dh.num_platform_handles);
+    if (!dispatchers[i].dispatcher &&
+        bad_handle_policy == ExtractBadHandlePolicy::kAbort) {
+      return MOJO_RESULT_ABORTED;
+    }
+
+    dispatcher_data += dh.num_bytes;
+    data_payload_index = next_payload_index.ValueOrDie();
+    port_index = next_port_index.ValueOrDie();
+    platform_handle_index = next_platform_handle_index.ValueOrDie();
+  }
+
+  if (!internal::g_core->AddDispatchersFromTransit(dispatchers, handles))
+    return MOJO_RESULT_ABORTED;
+
+  return MOJO_RESULT_OK;
+}
+
+UserMessageImpl::UserMessageImpl(ports::UserMessageEvent* message_event,
+                                 uintptr_t context,
                                  const MojoMessageOperationThunks* thunks)
     : ports::UserMessage(&kUserMessageTypeInfo),
-      context_(context),
-      context_thunks_(*thunks) {}
+      message_event_(message_event),
+      context_(context) {
+  if (thunks)
+    context_thunks_ = *thunks;
+}
 
-UserMessageImpl::UserMessageImpl(Channel::MessagePtr channel_message,
+UserMessageImpl::UserMessageImpl(ports::UserMessageEvent* message_event,
+                                 Channel::MessagePtr channel_message,
                                  void* header,
                                  void* user_payload,
                                  size_t user_payload_size)
     : ports::UserMessage(&kUserMessageTypeInfo),
-      context_thunks_({}),
+      message_event_(message_event),
       channel_message_(std::move(channel_message)),
+      has_serialized_handles_(true),
       header_(header),
       user_payload_(user_payload),
       user_payload_size_(user_payload_size) {}
 
-bool UserMessageImpl::WillBeRoutedExternally(
-    ports::UserMessageEvent* message_event) {
-  MojoResult result = SerializeIfNecessary(message_event);
+bool UserMessageImpl::WillBeRoutedExternally() {
+  MojoResult result = SerializeIfNecessary();
   return result == MOJO_RESULT_OK || result == MOJO_RESULT_FAILED_PRECONDITION;
 }
 
diff --git a/mojo/edk/system/user_message_impl.h b/mojo/edk/system/user_message_impl.h
index 6ec9ee4..ce851b46 100644
--- a/mojo/edk/system/user_message_impl.h
+++ b/mojo/edk/system/user_message_impl.h
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/system/channel.h"
 #include "mojo/edk/system/dispatcher.h"
@@ -19,12 +20,11 @@
 #include "mojo/edk/system/system_impl_export.h"
 #include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/handle.h"
 
 namespace mojo {
 namespace edk {
 
-class NodeController;
-
 // UserMessageImpl is the sole implementation of ports::UserMessage used to
 // attach message data to any ports::UserMessageEvent.
 //
@@ -36,6 +36,18 @@
  public:
   static const TypeInfo kUserMessageTypeInfo;
 
+  // Determines how ExtractSerializedHandles should behave when it encounters an
+  // unrecoverable serialized handle.
+  enum ExtractBadHandlePolicy {
+    // Continue extracting handles upon encountering a bad handle. The bad
+    // handle will be extracted with an invalid handle value.
+    kSkip,
+
+    // Abort the extraction process, leaving any valid serialized handles still
+    // in the message.
+    kAbort,
+  };
+
   ~UserMessageImpl() override;
 
   // Creates a new ports::UserMessageEvent with an attached unserialized
@@ -61,40 +73,21 @@
   // |payload| and |payload_size| represent the range of bytes within
   // |channel_message| which should be parsed by this call.
   static std::unique_ptr<UserMessageImpl> CreateFromChannelMessage(
+      ports::UserMessageEvent* message_event,
       Channel::MessagePtr channel_message,
       void* payload,
       size_t payload_size);
 
-  // Reads a message from |port| on |node_controller|'s Node.
-  //
-  // The message may or may not require deserialization. If the read message is
-  // unserialized, it must have been sent from within the same process that's
-  // receiving it and this call merely passes ownership of the message object
-  // back out of the ports layer. In this case, |read_any_size| must be true,
-  // |*out_event| will own the read message upon return, and all other arguments
-  // are ignored.
-  //
-  // If the read message is still serialized, it must have been created by
-  // CreateFromChannelMessage() above whenever its bytes were first read from a
-  // Channel. In this case, the message will be taken form the port and returned
-  // in |*out_event|, if and only iff |read_any_size| is true or both
-  // |*num_bytes| and |*num_handles| are sufficiently large to contain the
-  // contents of the message. Upon success this returns |MOJO_RESULT_OK|, and
-  // updates |*num_bytes| and |*num_handles| with the actual size of the read
-  // message.
-  //
-  // Upon failure this returns any of various error codes detailed by the
-  // documentation for MojoReadMessage/MojoReadMessageNew in
-  // src/mojo/public/c/system/message_pipe.h.
+  // Attempts to read a message from the given |port|.
   static MojoResult ReadMessageEventFromPort(
-      NodeController* node_controller,
       const ports::PortRef& port,
-      bool read_any_size,
-      bool may_discard,
-      uint32_t* num_bytes,
-      MojoHandle* handles,
-      uint32_t* num_handles,
-      std::unique_ptr<ports::UserMessageEvent>* out_event);
+      Dispatcher::ReadMessageSizePolicy size_policy,
+      Dispatcher::ReadMessageDiscardPolicy discard_policy,
+      uint32_t max_payload_size,
+      uint32_t max_num_handles,
+      std::unique_ptr<ports::UserMessageEvent>* out_event,
+      uint32_t* actual_payload_size,
+      uint32_t* actual_num_handles);
 
   // Extracts the serialized Channel::Message from the UserMessageEvent in
   // |event|. |event| must have a serialized UserMessageImpl instance attached.
@@ -114,8 +107,7 @@
       return false;
     }
 
-    DCHECK(channel_message_);
-    return true;
+    return !!channel_message_;
   }
 
   void* user_payload() {
@@ -138,7 +130,21 @@
   void set_source_node(const ports::NodeName& name) { source_node_ = name; }
   const ports::NodeName& source_node() const { return source_node_; }
 
-  MojoResult SerializeIfNecessary(ports::UserMessageEvent* message_event);
+  // If this message is not already serialized, this serializes it.
+  MojoResult SerializeIfNecessary();
+
+  // Extracts handles from this (serialized) message.
+  //
+  // Returns |MOJO_RESULT_OK|
+  // if sucessful, |MOJO_RESULT_FAILED_PRECONDITION| if this isn't a serialized
+  // message, |MOJO_RESULT_NOT_FOUND| if all serialized handles have already
+  // been extracted, or |MOJO_RESULT_ABORTED| if one or more handles failed
+  // extraction.
+  //
+  // On success, |handles| is populated with |num_handles()| extracted handles,
+  // whose ownership is thereby transferred to the caller.
+  MojoResult ExtractSerializedHandles(ExtractBadHandlePolicy bad_handle_policy,
+                                      MojoHandle* handles);
 
  private:
   class ReadMessageFilter;
@@ -147,28 +153,38 @@
   // |thunks|. If the message is ever going to be routed to another node (see
   // |WillBeRoutedExternally()| below), it will be serialized at that time using
   // operations provided by |thunks|.
-  UserMessageImpl(uintptr_t context, const MojoMessageOperationThunks* thunks);
+  UserMessageImpl(ports::UserMessageEvent* message_event,
+                  uintptr_t context,
+                  const MojoMessageOperationThunks* thunks);
 
   // Creates a serialized UserMessageImpl backed by an existing Channel::Message
   // object. |header| and |user_payload| must be pointers into
   // |channel_message|'s own storage, and |user_payload_size| is the number of
   // bytes comprising the user message contents at |user_payload|.
-  UserMessageImpl(Channel::MessagePtr channel_message,
+  UserMessageImpl(ports::UserMessageEvent* message_event,
+                  Channel::MessagePtr channel_message,
                   void* header,
                   void* user_payload,
                   size_t user_payload_size);
 
   // UserMessage:
-  bool WillBeRoutedExternally(ports::UserMessageEvent* message_event) override;
+  bool WillBeRoutedExternally() override;
+
+  // The event which owns this serialized message. Not owned.
+  ports::UserMessageEvent* const message_event_;
 
   // Unserialized message state.
   uintptr_t context_ = 0;
-  const MojoMessageOperationThunks context_thunks_;
+  base::Optional<MojoMessageOperationThunks> context_thunks_;
 
   // Serialized message contents. May be null if this is not a serialized
   // message.
   Channel::MessagePtr channel_message_;
 
+  // Indicates whether any handles serialized within |channel_message_| have
+  // yet to be extracted.
+  bool has_serialized_handles_ = false;
+
   // Only valid if |channel_message_| is non-null. |header_| is the address
   // of the UserMessageImpl's internal MessageHeader structure within the
   // serialized message buffer. |user_payload_| is the address of the first byte
diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h
index 6635889..68316d2 100644
--- a/mojo/public/c/system/message_pipe.h
+++ b/mojo/public/c/system/message_pipe.h
@@ -95,6 +95,26 @@
 #define MOJO_ALLOC_MESSAGE_FLAG_NONE ((MojoAllocMessageFlags)0)
 #endif
 
+// |MojoGetSerializedMessageContentsFlags|: Used to specify different options
+// |MojoGetSerializedMessageContents()|.
+//   |MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE| - No flags; default mode.
+//   |MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES| - Don't attempt
+//       to extract any handles from the serialized message.
+
+typedef uint32_t MojoGetSerializedMessageContentsFlags;
+
+#ifdef __cplusplus
+const MojoGetSerializedMessageContentsFlags
+    MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE = 0;
+const MojoGetSerializedMessageContentsFlags
+    MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES = 1 << 0;
+#else
+#define MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE \
+  ((MojoGetSerializedMessageContentsFlags)0)
+#define MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES \
+  ((MojoGetSerializedMessageContentsFlags)1 << 0)
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -263,27 +283,21 @@
 // the message bytes. The returned message must eventually be freed using
 // |MojoFreeMessage()|.
 //
-// Message payload can be accessed using |MojoGetMessageBuffer()|.
+// Message payload and handles can be accessed using
+// |MojoGetSerializedMessageContents()|.
 //
-//   |message_pipe_handle|, |num_bytes|, |handles|, |num_handles|, and |flags|
-//       correspond to their use in |MojoReadMessage()| above, with the
-//       exception that |num_bytes| is only an output argument.
-//   |message| must be non-null unless |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| is
-//       set in flags.
+// |message| must be non-null unless |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| is set
+//     in |flags|.
 //
 // Return values correspond to the return values for |MojoReadMessage()| above.
 // On success (MOJO_RESULT_OK), |*message| will contain a handle to a message
-// object which may be passed to |MojoGetMessageBuffer()| or
+// object which may be passed to |MojoGetSerializedMessageContents()| or
 // |MojoReleaseMessageContext()|. The caller owns the message object and is
 // responsible for freeing it via |MojoFreeMessage()| or
 // |MojoReleaseMessageContext()|.
-MOJO_SYSTEM_EXPORT MojoResult
-    MojoReadMessageNew(MojoHandle message_pipe_handle,
-                       MojoMessageHandle* message,  // Optional out.
-                       uint32_t* num_bytes,         // Optional out.
-                       MojoHandle* handles,         // Optional out.
-                       uint32_t* num_handles,       // Optional in/out.
-                       MojoReadMessageFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult MojoReadMessageNew(MojoHandle message_pipe_handle,
+                                                 MojoMessageHandle* message,
+                                                 MojoReadMessageFlags flags);
 
 // Fuses two message pipe endpoints together. Given two pipes:
 //
@@ -310,8 +324,8 @@
     MojoFuseMessagePipes(MojoHandle handle0, MojoHandle handle1);
 
 // Allocates a new message whose ownership may be passed to
-// |MojoWriteMessageNew()|. Use |MojoGetMessageBuffer()| to retrieve the address
-// of the mutable message payload.
+// |MojoWriteMessageNew()|. Use |MojoGetSerializedMessageContents()| to retrieve
+// the address of the mutable message payload.
 //
 // |num_bytes|: The size of the message payload in bytes.
 // |handles|: An array of handles to transfer in the message. This takes
@@ -364,12 +378,16 @@
 // the system will invoke the serialization helper functions from |thunks| on
 // |context| to obtain a serialized representation of the message.
 //
+// If |thunks| is null, the created message cannot be serialized. Subsequent
+// calls to |MojoSerializeMessage()| on the created message, or any attempt to
+// transmit the message across a process boundary, will fail.
+//
 // Returns:
 //   |MOJO_RESULT_OK| if a new message was created. |*message| contains a handle
 //       to the new message object on return.
-//   |MOJO_RESULT_INVALID_ARGUMENT| if |context| is 0, |thunks| is null, or any
-//       element of |thunks| is invalid. In this case, the value of |message| is
-//       ignored.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |context| is 0, or |thunks| is non-null
+//       and any element of |thunks| is invalid. In this case, the value of
+//       |message| is ignored.
 MOJO_SYSTEM_EXPORT MojoResult
 MojoCreateMessage(uintptr_t context,
                   const struct MojoMessageOperationThunks* thunks,
@@ -387,22 +405,83 @@
 //   |MOJO_RESULT_INVALID_ARGUMENT| if |message| was not a valid message.
 MOJO_SYSTEM_EXPORT MojoResult MojoFreeMessage(MojoMessageHandle message);
 
-// Retrieves the address of mutable message bytes for a message allocated by
-// either |MojoAllocMessage()| or |MojoReadMessageNew()|.
+// Forces a message to be serialized in-place if not already serialized.
 //
 // Returns:
-//   |MOJO_RESULT_OK| if |message| is a valid message object with a serialized
-//       message buffer attached. |*buffer| will be updated to point to mutable
-//       message bytes.
-//   |MOJO_RESULT_NOT_FOUND| if |message| is a valid message object but has no
-//       serialized message buffer attached. In this case, callers should
-//       instead call |MojoReleaseMessageContext()| to retrieve its context
-//       value.
-//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message object.
+//   |MOJO_RESULT_OK| if |message| was not serialized and is now serialized.
+//       In this case its thunks were invoked to perform serialization and
+//       ultimately destroy its associated context. The message may still be
+//       written to a pipe or decomposed by MojoGetSerializedMessageContents().
+//   |MOJO_RESULT_FAILED_PRECONDITION| if |message| was already serialized.
+//   |MOJO_RESULT_NOT_FOUND| if |message| cannot be serialized (i.e. it was
+//       created with null |MojoMessageOperationThunks|.)
+//   |MOJO_RESULT_BUSY| if one or more handles provided by the user context
+//       reported itself as busy during the serialization attempt. In this case
+//       all serialized handles are closed automatically.
+//   |MOJO_RESULT_ABORTED| if some other unspecified error occurred during
+//       handle serialization. In this case all serialized handles are closed
+//       automatically.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message handle.
 //
-// NOTE: A returned buffer address is always guaranteed to be 8-byte aligned.
-MOJO_SYSTEM_EXPORT MojoResult MojoGetMessageBuffer(MojoMessageHandle message,
-                                                   void** buffer);  // Out
+// Note that unserialized messages may be successfully transferred from one
+// message pipe endpoint to another without ever being serialized. This function
+// allows callers to coerce eager serialization.
+MOJO_SYSTEM_EXPORT MojoResult MojoSerializeMessage(MojoMessageHandle message);
+
+// Retrieves the contents of a serialized message.
+//
+// |message|: The message whose contents are to be retrieved.
+// |num_bytes|: An output parameter which will receive the total size in bytes
+//     of the message's payload.
+// |buffer|: An output parameter which will receive the address of a buffer
+//     containing exactly |*num_bytes| bytes of payload data. This buffer
+//     address is not owned by the caller and is only valid as long as the
+//     message handle in |message| is valid.
+// |num_handles|: An input/output parameter. On input, if not null, this points
+//     to value specifying the available capacity (in number of handles) of
+//     |handles|. On output, if not null, this will point to a value specifying
+//     the actual number of handles available in the serialized message.
+// |handles|: A buffer to contain up to (input) |*num_handles| handles. May be
+//     null if |num_handles| is null or |*num_handles| is 0.
+// |flags|: Flags to affect the behavior of this API.
+//
+// If |MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES| is set in
+// |flags|, |num_handles| and |handles| arguments are ignored and only payload-
+// related outputs are updated.
+//
+// Returns:
+//   |MOJO_RESULT_OK| if |message| is a serialized message and the provided
+//       handle storage is sufficient to contain all handles attached to the
+//       message. In this case all non-null output parameters are filled in and
+//       ownership of any attached handles is transferred to the caller. It is
+//       no longer legal to call MojoGetSerializedMessageContents on this
+//       message handle.
+//   |MOJO_RESULT_INVALID_ARGUMENT| if |num_handles| is non-null and
+//       |*num_handles| is non-zero, but |handles| is null; or if |message| is
+//       not a valid message handle.
+//   |MOJO_RESULT_FAILED_PRECONDITION| if |message| is not a serialized message.
+//       The caller may either use |MojoSerializeMessage()| and try again, or
+//       use |MojoReleaseMessageContext()| to extract the message's unserialized
+//       context.
+//   |MOJO_RESULT_NOT_FOUND| if the message's serialized contents have already
+//       been extracted (or have failed to be extracted) by a previous call to
+//       |MojoGetSerializedMessageContents()|.
+//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if |num_handles| is null and there are
+//       handles attached to the message, or if |*num_handles| on input is less
+//       than the number of handles attached to the message. Also may be
+//       returned if |num_bytes| or |buffer| is null and the message has a non-
+//       empty payload.
+//   |MOJO_RESULT_ARBORTED| if the serialized message could not be parsed or
+//       its attached handles could not be decoded properly. The message is left
+//       intact but is effectively useless: future calls to this API on the same
+//       message handle will yield the same result.
+MOJO_SYSTEM_EXPORT MojoResult
+MojoGetSerializedMessageContents(MojoMessageHandle message,
+                                 void** buffer,
+                                 uint32_t* num_bytes,
+                                 MojoHandle* handles,
+                                 uint32_t* num_handles,
+                                 MojoGetSerializedMessageContentsFlags flags);
 
 // Detaches the user-provided context from a message and returns it to the
 // caller. This can only succeed if the message is not in a serialized form.
@@ -414,7 +493,7 @@
 //       |message|.
 //   |MOJO_RESULT_NOT_FOUND| if |message| is a valid message object which has no
 //       associated context value. In this case it must be a serialized message,
-//       and |MojoGetMessageBuffer()| should be called instead.
+//       and |MojoGetSerializedMessageContents()| should be called instead.
 //   |MOJO_RESULT_INVALID_ARGUMENT| if |message| is not a valid message object.
 MOJO_SYSTEM_EXPORT MojoResult
 MojoReleaseMessageContext(MojoMessageHandle message, uintptr_t* context);
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc
index 0323386..64344928 100644
--- a/mojo/public/c/system/thunks.cc
+++ b/mojo/public/c/system/thunks.cc
@@ -189,13 +189,9 @@
 
 MojoResult MojoReadMessageNew(MojoHandle message_pipe_handle,
                               MojoMessageHandle* message,
-                              uint32_t* num_bytes,
-                              MojoHandle* handles,
-                              uint32_t* num_handles,
                               MojoReadMessageFlags flags) {
   assert(g_thunks.ReadMessageNew);
-  return g_thunks.ReadMessageNew(message_pipe_handle, message, num_bytes,
-                                 handles, num_handles, flags);
+  return g_thunks.ReadMessageNew(message_pipe_handle, message, flags);
 }
 
 MojoResult MojoAllocMessage(uint32_t num_bytes,
@@ -220,9 +216,21 @@
   return g_thunks.FreeMessage(message);
 }
 
-MojoResult MojoGetMessageBuffer(MojoMessageHandle message, void** buffer) {
-  assert(g_thunks.GetMessageBuffer);
-  return g_thunks.GetMessageBuffer(message, buffer);
+MojoResult MojoSerializeMessage(MojoMessageHandle message) {
+  assert(g_thunks.SerializeMessage);
+  return g_thunks.SerializeMessage(message);
+}
+
+MojoResult MojoGetSerializedMessageContents(
+    MojoMessageHandle message,
+    void** buffer,
+    uint32_t* num_bytes,
+    MojoHandle* handles,
+    uint32_t* num_handles,
+    MojoGetSerializedMessageContentsFlags flags) {
+  assert(g_thunks.GetSerializedMessageContents);
+  return g_thunks.GetSerializedMessageContents(message, buffer, num_bytes,
+                                               handles, num_handles, flags);
 }
 
 MojoResult MojoReleaseMessageContext(MojoMessageHandle message,
diff --git a/mojo/public/c/system/thunks.h b/mojo/public/c/system/thunks.h
index 7bc24d5..ba921cf 100644
--- a/mojo/public/c/system/thunks.h
+++ b/mojo/public/c/system/thunks.h
@@ -96,9 +96,6 @@
                                 MojoWriteMessageFlags flags);
   MojoResult (*ReadMessageNew)(MojoHandle message_pipe_handle,
                                MojoMessageHandle* message,
-                               uint32_t* num_bytes,
-                               MojoHandle* handles,
-                               uint32_t* num_handles,
                                MojoReadMessageFlags flags);
   MojoResult (*AllocMessage)(uint32_t num_bytes,
                              const MojoHandle* handles,
@@ -109,7 +106,14 @@
                               const struct MojoMessageOperationThunks* thunks,
                               MojoMessageHandle* message);
   MojoResult (*FreeMessage)(MojoMessageHandle message);
-  MojoResult (*GetMessageBuffer)(MojoMessageHandle message, void** buffer);
+  MojoResult (*SerializeMessage)(MojoMessageHandle message);
+  MojoResult (*GetSerializedMessageContents)(
+      MojoMessageHandle message,
+      void** buffer,
+      uint32_t* num_bytes,
+      MojoHandle* handles,
+      uint32_t* num_handles,
+      MojoGetSerializedMessageContentsFlags flags);
   MojoResult (*ReleaseMessageContext)(MojoMessageHandle message,
                                       uintptr_t* context);
   MojoResult (*WrapPlatformHandle)(
diff --git a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
index 2f56194..1641d55e 100644
--- a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
+++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
@@ -117,7 +117,8 @@
                         Interface_Data* output,
                         SerializationContext* context) {
     InterfacePtrInfo<T> info = input.PassInterface();
-    output->handle = context->handles.AddHandle(info.PassHandle().release());
+    output->handle =
+        context->handles.AddHandle(ScopedHandle::From(info.PassHandle()));
     output->version = info.version();
   }
 
@@ -143,7 +144,8 @@
   static void Serialize(InterfaceRequest<T>& input,
                         Handle_Data* output,
                         SerializationContext* context) {
-    *output = context->handles.AddHandle(input.PassMessagePipe().release());
+    *output =
+        context->handles.AddHandle(ScopedHandle::From(input.PassMessagePipe()));
   }
 
   static bool Deserialize(Handle_Data* input,
@@ -165,7 +167,7 @@
   static void Serialize(ScopedHandleBase<T>& input,
                         Handle_Data* output,
                         SerializationContext* context) {
-    *output = context->handles.AddHandle(input.release());
+    *output = context->handles.AddHandle(ScopedHandle::From(std::move(input)));
   }
 
   static bool Deserialize(Handle_Data* input,
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index 50d6d67..445d0f2 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -44,9 +44,7 @@
       associated_endpoint_handles_(
           std::move(other.associated_endpoint_handles_)) {}
 
-Message::~Message() {
-  CloseHandles();
-}
+Message::~Message() {}
 
 Message& Message::operator=(Message&& other) {
   Reset();
@@ -57,7 +55,6 @@
 }
 
 void Message::Reset() {
-  CloseHandles();
   handles_.clear();
   associated_endpoint_handles_.clear();
   buffer_.reset();
@@ -70,7 +67,7 @@
 
 void Message::InitializeFromMojoMessage(ScopedMessageHandle message,
                                         uint32_t num_bytes,
-                                        std::vector<Handle>* handles) {
+                                        std::vector<ScopedHandle>* handles) {
   DCHECK(!buffer_);
   buffer_.reset(new internal::MessageBuffer(std::move(message), num_bytes));
   handles_.swap(*handles);
@@ -138,6 +135,10 @@
       MOJO_ALLOC_MESSAGE_FLAG_NONE,
       &new_message);
   CHECK_EQ(rv, MOJO_RESULT_OK);
+
+  // The handles are now owned by the message object.
+  for (auto& handle : handles_)
+    ignore_result(handle.release());
   handles_.clear();
 
   void* new_buffer = nullptr;
@@ -155,14 +156,6 @@
   buffer_->NotifyBadMessage(error);
 }
 
-void Message::CloseHandles() {
-  for (std::vector<Handle>::iterator it = handles_.begin();
-       it != handles_.end(); ++it) {
-    if (it->is_valid())
-      CloseRaw(*it);
-  }
-}
-
 void Message::SerializeAssociatedEndpointHandles(
     AssociatedGroupController* group_controller) {
   if (associated_endpoint_handles_.empty())
@@ -246,28 +239,18 @@
 }
 
 MojoResult ReadMessage(MessagePipeHandle handle, Message* message) {
-  MojoResult rv;
-
-  std::vector<Handle> handles;
   ScopedMessageHandle mojo_message;
-  uint32_t num_bytes = 0, num_handles = 0;
-  rv = ReadMessageNew(handle,
-                      &mojo_message,
-                      &num_bytes,
-                      nullptr,
-                      &num_handles,
-                      MOJO_READ_MESSAGE_FLAG_NONE);
-  if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) {
-    DCHECK_GT(num_handles, 0u);
-    handles.resize(num_handles);
-    rv = ReadMessageNew(handle,
-                        &mojo_message,
-                        &num_bytes,
-                        reinterpret_cast<MojoHandle*>(handles.data()),
-                        &num_handles,
-                        MOJO_READ_MESSAGE_FLAG_NONE);
-  }
+  MojoResult rv =
+      ReadMessageNew(handle, &mojo_message, MOJO_READ_MESSAGE_FLAG_NONE);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
 
+  uint32_t num_bytes = 0;
+  void* buffer;
+  std::vector<ScopedHandle> handles;
+  rv = GetSerializedMessageContents(
+      mojo_message.get(), &buffer, &num_bytes, &handles,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.cc b/mojo/public/cpp/bindings/lib/serialization_context.cc
index e2fd5c6..d17e3349 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.cc
+++ b/mojo/public/cpp/bindings/lib/serialization_context.cc
@@ -12,38 +12,31 @@
 namespace mojo {
 namespace internal {
 
-SerializedHandleVector::SerializedHandleVector() {}
+SerializedHandleVector::SerializedHandleVector() = default;
 
-SerializedHandleVector::~SerializedHandleVector() {
-  for (auto handle : handles_) {
-    if (handle.is_valid()) {
-      MojoResult rv = MojoClose(handle.value());
-      DCHECK_EQ(rv, MOJO_RESULT_OK);
-    }
-  }
-}
+SerializedHandleVector::~SerializedHandleVector() = default;
 
-Handle_Data SerializedHandleVector::AddHandle(mojo::Handle handle) {
+Handle_Data SerializedHandleVector::AddHandle(mojo::ScopedHandle handle) {
   Handle_Data data;
   if (!handle.is_valid()) {
     data.value = kEncodedInvalidHandleValue;
   } else {
     DCHECK_LT(handles_.size(), std::numeric_limits<uint32_t>::max());
     data.value = static_cast<uint32_t>(handles_.size());
-    handles_.push_back(handle);
+    handles_.emplace_back(std::move(handle));
   }
   return data;
 }
 
-mojo::Handle SerializedHandleVector::TakeHandle(
+mojo::ScopedHandle SerializedHandleVector::TakeHandle(
     const Handle_Data& encoded_handle) {
   if (!encoded_handle.is_valid())
-    return mojo::Handle();
+    return mojo::ScopedHandle();
   DCHECK_LT(encoded_handle.value, handles_.size());
-  return FetchAndReset(&handles_[encoded_handle.value]);
+  return std::move(handles_[encoded_handle.value]);
 }
 
-void SerializedHandleVector::Swap(std::vector<mojo::Handle>* other) {
+void SerializedHandleVector::Swap(std::vector<mojo::ScopedHandle>* other) {
   handles_.swap(*other);
 }
 
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.h b/mojo/public/cpp/bindings/lib/serialization_context.h
index a34fe3d..2d0d4b4a 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.h
+++ b/mojo/public/cpp/bindings/lib/serialization_context.h
@@ -29,24 +29,24 @@
   size_t size() const { return handles_.size(); }
 
   // Adds a handle to the handle list and returns its index for encoding.
-  Handle_Data AddHandle(mojo::Handle handle);
+  Handle_Data AddHandle(mojo::ScopedHandle handle);
 
   // Takes a handle from the list of serialized handle data.
-  mojo::Handle TakeHandle(const Handle_Data& encoded_handle);
+  mojo::ScopedHandle TakeHandle(const Handle_Data& encoded_handle);
 
   // Takes a handle from the list of serialized handle data and returns it in
   // |*out_handle| as a specific scoped handle type.
   template <typename T>
   ScopedHandleBase<T> TakeHandleAs(const Handle_Data& encoded_handle) {
-    return MakeScopedHandle(T(TakeHandle(encoded_handle).value()));
+    return ScopedHandleBase<T>::From(TakeHandle(encoded_handle));
   }
 
   // Swaps all owned handles out with another Handle vector.
-  void Swap(std::vector<mojo::Handle>* other);
+  void Swap(std::vector<mojo::ScopedHandle>* other);
 
  private:
   // Handles are owned by this object.
-  std::vector<mojo::Handle> handles_;
+  std::vector<mojo::ScopedHandle> handles_;
 
   DISALLOW_COPY_AND_ASSIGN(SerializedHandleVector);
 };
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 48e6900..1ec0119 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -59,7 +59,7 @@
   // Initializes a Message from an existing Mojo MessageHandle.
   void InitializeFromMojoMessage(ScopedMessageHandle message,
                                  uint32_t num_bytes,
-                                 std::vector<Handle>* handles);
+                                 std::vector<ScopedHandle>* handles);
 
   uint32_t data_num_bytes() const {
     return static_cast<uint32_t>(buffer_->size());
@@ -121,8 +121,8 @@
   const uint32_t* payload_interface_ids() const;
 
   // Access the handles.
-  const std::vector<Handle>* handles() const { return &handles_; }
-  std::vector<Handle>* mutable_handles() { return &handles_; }
+  const std::vector<ScopedHandle>* handles() const { return &handles_; }
+  std::vector<ScopedHandle>* mutable_handles() { return &handles_; }
 
   const std::vector<ScopedInterfaceEndpointHandle>*
   associated_endpoint_handles() const {
@@ -159,7 +159,7 @@
   void CloseHandles();
 
   std::unique_ptr<internal::MessageBuffer> buffer_;
-  std::vector<Handle> handles_;
+  std::vector<ScopedHandle> handles_;
   std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles_;
 
   DISALLOW_COPY_AND_ASSIGN(Message);
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 74ecb7a..8757d72 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -308,7 +308,8 @@
   AllocMessage(kText, &message1);
 
   MessagePipe pipe;
-  message1.mutable_handles()->push_back(pipe.handle0.release());
+  message1.mutable_handles()->emplace_back(
+      ScopedHandle::From(std::move(pipe.handle0)));
 
   connector0.Accept(&message1);
 
@@ -333,13 +334,10 @@
 
   // Now send a message to the transferred handle and confirm it's sent through
   // to the orginal pipe.
-  // TODO(vtl): Do we need a better way of "downcasting" the handle types?
-  ScopedMessagePipeHandle smph;
-  smph.reset(MessagePipeHandle(message_received.handles()->front().value()));
-  message_received.mutable_handles()->front() = Handle();
-  // |smph| now owns this handle.
-
-  Connector connector_received(std::move(smph), Connector::SINGLE_THREADED_SEND,
+  auto pipe_handle = ScopedMessagePipeHandle::From(
+      std::move(message_received.mutable_handles()->front()));
+  Connector connector_received(std::move(pipe_handle),
+                               Connector::SINGLE_THREADED_SEND,
                                base::ThreadTaskRunnerHandle::Get());
   Connector connector_original(std::move(pipe.handle1),
                                Connector::SINGLE_THREADED_SEND,
diff --git a/mojo/public/cpp/system/message.h b/mojo/public/cpp/system/message.h
index d4406ee8..a8078a1 100644
--- a/mojo/public/cpp/system/message.h
+++ b/mojo/public/cpp/system/message.h
@@ -6,6 +6,7 @@
 #define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_H_
 
 #include <limits>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
@@ -67,9 +68,35 @@
   return MOJO_RESULT_OK;
 }
 
-inline MojoResult GetMessageBuffer(MessageHandle message, void** buffer) {
+inline MojoResult GetSerializedMessageContents(
+    MessageHandle message,
+    void** buffer,
+    uint32_t* num_bytes,
+    std::vector<ScopedHandle>* handles,
+    MojoGetSerializedMessageContentsFlags flags) {
   DCHECK(message.is_valid());
-  return MojoGetMessageBuffer(message.value(), buffer);
+  DCHECK(num_bytes);
+  DCHECK(buffer);
+  uint32_t num_handles = 0;
+  MojoResult rv = MojoGetSerializedMessageContents(
+      message.value(), buffer, num_bytes, nullptr, &num_handles, flags);
+  if (rv != MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    if (handles)
+      handles->clear();
+    return rv;
+  }
+
+  handles->resize(num_handles);
+  return MojoGetSerializedMessageContents(
+      message.value(), buffer, num_bytes,
+      reinterpret_cast<MojoHandle*>(handles->data()), &num_handles, flags);
+}
+
+inline MojoResult GetMessageBuffer(MessageHandle message, void** buffer) {
+  uint32_t num_bytes;
+  return GetSerializedMessageContents(
+      message, buffer, &num_bytes, nullptr,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_IGNORE_HANDLES);
 }
 
 inline MojoResult NotifyBadMessage(MessageHandle message,
diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h
index 7fbe43f..acb5bcd0 100644
--- a/mojo/public/cpp/system/message_pipe.h
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -102,13 +102,9 @@
 // documentation.
 inline MojoResult ReadMessageNew(MessagePipeHandle message_pipe,
                                  ScopedMessageHandle* message,
-                                 uint32_t* num_bytes,
-                                 MojoHandle* handles,
-                                 uint32_t* num_handles,
                                  MojoReadMessageFlags flags) {
   MojoMessageHandle raw_message;
-  MojoResult rv = MojoReadMessageNew(message_pipe.value(), &raw_message,
-                                     num_bytes, handles, num_handles, flags);
+  MojoResult rv = MojoReadMessageNew(message_pipe.value(), &raw_message, flags);
   if (rv != MOJO_RESULT_OK)
     return rv;
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index c07dbab..7ec431f2 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -369,6 +369,7 @@
     ":traffic_annotation",
     "//crypto",
     "//crypto:platform",
+    "//third_party/boringssl",
   ]
 
   if (!is_nacl) {
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index eaaffe48..68aee907 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -95,28 +95,25 @@
 //-----------------------------------------------------------------------------
 
 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
-    : disk_entry(entry),
-      writer(NULL),
-      will_process_pending_queue(false),
-      doomed(false) {
-}
+    : disk_entry(entry) {}
 
 HttpCache::ActiveEntry::~ActiveEntry() {
   if (disk_entry) {
     disk_entry->Close();
-    disk_entry = NULL;
+    disk_entry = nullptr;
   }
 }
 
 size_t HttpCache::ActiveEntry::EstimateMemoryUsage() const {
   // Skip |disk_entry| which is tracked in simple_backend_impl; Skip |readers|
-  // and |pending_queue| because the Transactions are owned by their respective
-  // URLRequestHttpJobs.
+  // and |add_to_entry_queue| because the Transactions are owned by their
+  // respective URLRequestHttpJobs.
   return 0;
 }
 
 bool HttpCache::ActiveEntry::HasNoTransactions() {
-  return !writer && readers.empty() && pending_queue.empty();
+  return !writer && readers.empty() && add_to_entry_queue.empty() &&
+         done_headers_queue.empty() && !headers_transaction;
 }
 
 //-----------------------------------------------------------------------------
@@ -218,10 +215,7 @@
 class HttpCache::MetadataWriter {
  public:
   explicit MetadataWriter(HttpCache::Transaction* trans)
-      : transaction_(trans),
-        verified_(false),
-        buf_len_(0) {
-  }
+      : verified_(false), buf_len_(0), transaction_(trans) {}
 
   ~MetadataWriter() {}
 
@@ -236,12 +230,15 @@
   void SelfDestroy();
   void OnIOComplete(int result);
 
-  std::unique_ptr<HttpCache::Transaction> transaction_;
   bool verified_;
   scoped_refptr<IOBuffer> buf_;
   int buf_len_;
   base::Time expected_response_time_;
   HttpRequestInfo request_info_;
+
+  // |transaction_| to come after |request_info_| so that |request_info_| is not
+  // destroyed earlier.
+  std::unique_ptr<HttpCache::Transaction> transaction_;
   DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
 };
 
@@ -342,15 +339,17 @@
   weak_factory_.InvalidateWeakPtrs();
 
   // If we have any active entries remaining, then we need to deactivate them.
-  // We may have some pending calls to OnProcessPendingQueue, but since those
-  // won't run (due to our destruction), we can simply ignore the corresponding
-  // will_process_pending_queue flag.
+  // We may have some pending tasks to process queued transactions ,but since
+  // those won't run (due to our destruction), we can simply ignore the
+  // corresponding flags.
   while (!active_entries_.empty()) {
     ActiveEntry* entry = active_entries_.begin()->second.get();
-    entry->will_process_pending_queue = false;
-    entry->pending_queue.clear();
+    entry->will_process_queued_transactions = false;
+    entry->add_to_entry_queue.clear();
     entry->readers.clear();
-    entry->writer = NULL;
+    entry->done_headers_queue.clear();
+    entry->headers_transaction = nullptr;
+    entry->writer = nullptr;
     DeactivateEntry(entry);
   }
 
@@ -611,7 +610,8 @@
   entry_ptr->doomed = true;
 
   DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
-         entry_ptr->will_process_pending_queue);
+         entry_ptr->headers_transaction ||
+         entry_ptr->will_process_queued_transactions);
   return OK;
 }
 
@@ -667,7 +667,7 @@
 
 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
   auto it = active_entries_.find(key);
-  return it != active_entries_.end() ? it->second.get() : NULL;
+  return it != active_entries_.end() ? it->second.get() : nullptr;
 }
 
 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
@@ -679,7 +679,7 @@
 }
 
 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
-  DCHECK(!entry->will_process_pending_queue);
+  DCHECK(!entry->will_process_queued_transactions);
   DCHECK(!entry->doomed);
   DCHECK(entry->disk_entry);
   DCHECK(entry->HasNoTransactions());
@@ -809,121 +809,314 @@
   }
 }
 
-int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
+int HttpCache::AddTransactionToEntry(ActiveEntry* entry,
+                                     Transaction* transaction) {
   DCHECK(entry);
   DCHECK(entry->disk_entry);
-
-  // We implement a basic reader/writer lock for the disk cache entry.  If
-  // there is already a writer, then everyone has to wait for the writer to
-  // finish before they can access the cache entry.  There can be multiple
-  // readers.
-  //
-  // NOTE: If the transaction can only write, then the entry should not be in
-  // use (since any existing entry should have already been doomed).
-
-  if (entry->writer || entry->will_process_pending_queue) {
-    entry->pending_queue.push_back(trans);
-    return ERR_IO_PENDING;
-  }
-
-  if (trans->mode() & Transaction::WRITE) {
-    // transaction needs exclusive access to the entry
-    if (entry->readers.empty()) {
-      entry->writer = trans;
-    } else {
-      entry->pending_queue.push_back(trans);
-      return ERR_IO_PENDING;
-    }
-  } else {
-    // transaction needs read access to the entry
-    entry->readers.insert(trans);
-  }
-
-  // We do this before calling EntryAvailable to force any further calls to
-  // AddTransactionToEntry to add their transaction to the pending queue, which
-  // ensures FIFO ordering.
-  if (!entry->writer && !entry->pending_queue.empty())
-    ProcessPendingQueue(entry);
-
-  return OK;
+  // Always add a new transaction to the queue to maintain FIFO order.
+  entry->add_to_entry_queue.push_back(transaction);
+  ProcessQueuedTransactions(entry);
+  return ERR_IO_PENDING;
 }
 
-void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
-                              bool cancel) {
-  // If we already posted a task to move on to the next transaction and this was
-  // the writer, there is nothing to cancel.
-  if (entry->will_process_pending_queue && entry->readers.empty())
+int HttpCache::DoneWithResponseHeaders(ActiveEntry* entry,
+                                       Transaction* transaction,
+                                       bool is_partial) {
+  // If |transaction| is the current writer, do nothing. This can happen for
+  // range requests since they can go back to headers phase after starting to
+  // write.
+  if (entry->writer == transaction) {
+    DCHECK(is_partial);
+    return OK;
+  }
+
+  // TODO(crbug.com/715913, crbug.com/715974, crbug.com/715920,
+  // crbug.com/715911): Convert the CHECKs in this function to DCHECKs once
+  // canary is clear of any crashes.
+  CHECK_EQ(entry->headers_transaction, transaction);
+
+  entry->headers_transaction = nullptr;
+
+  // If transaction is responsible for writing the response body, then do not go
+  // through done_headers_queue for performance benefit. (Also, in case of
+  // writer transaction, the consumer sometimes depend on synchronous behaviour
+  // e.g. while computing raw headers size. (crbug.com/711766))
+  if (transaction->mode() & Transaction::WRITE) {
+    // Partial requests may have write mode even when there is a writer present
+    // since they may be reader for a particular range and writer for another
+    // range.
+    CHECK(is_partial || (!entry->writer && entry->done_headers_queue.empty()));
+
+    if (!entry->writer) {
+      entry->writer = transaction;
+      ProcessQueuedTransactions(entry);
+      return OK;
+    }
+  }
+
+  // If this is not the first transaction in done_headers_queue, it should be a
+  // read-mode transaction except if it is a partial request.
+  CHECK(is_partial || (entry->done_headers_queue.empty() ||
+                       !(transaction->mode() & Transaction::WRITE)));
+
+  entry->done_headers_queue.push_back(transaction);
+  ProcessQueuedTransactions(entry);
+  return ERR_IO_PENDING;
+}
+
+void HttpCache::DoneWithEntry(ActiveEntry* entry,
+                              Transaction* transaction,
+                              bool process_cancel,
+                              bool is_partial) {
+  // |should_restart| is true if there may be other transactions dependent on
+  // this transaction and they will need to be restarted.
+  bool should_restart = process_cancel && HasDependentTransactions(
+                                              entry, transaction, is_partial);
+  if (should_restart && is_partial)
+    entry->disk_entry->CancelSparseIO();
+
+  // Transaction is waiting in the done_headers_queue.
+  auto it = std::find(entry->done_headers_queue.begin(),
+                      entry->done_headers_queue.end(), transaction);
+  if (it != entry->done_headers_queue.end()) {
+    entry->done_headers_queue.erase(it);
+    if (should_restart)
+      ProcessEntryFailure(entry, transaction);
     return;
+  }
 
-  if (entry->writer) {
-    DCHECK(trans == entry->writer);
+  // Transaction is removed in the headers phase.
+  if (transaction == entry->headers_transaction) {
+    // If the response is not written (should_restart is true), consider it a
+    // failure.
+    DoneWritingToEntry(entry, !should_restart, transaction);
+    return;
+  }
 
+  // Transaction is removed in the writing phase.
+  if (transaction == entry->writer) {
     // Assume there was a failure.
     bool success = false;
-    if (cancel) {
+    bool did_truncate = false;
+    if (should_restart) {
       DCHECK(entry->disk_entry);
       // This is a successful operation in the sense that we want to keep the
       // entry.
-      success = trans->AddTruncatedFlag();
+      success = transaction->AddTruncatedFlag(&did_truncate);
       // The previous operation may have deleted the entry.
-      if (!trans->entry())
+      if (!transaction->entry())
         return;
     }
-    DoneWritingToEntry(entry, success);
-  } else {
-    DoneReadingFromEntry(entry, trans);
-  }
-}
-
-void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
-  DCHECK(entry->readers.empty());
-
-  entry->writer = NULL;
-
-  if (success) {
-    ProcessPendingQueue(entry);
-  } else {
-    DCHECK(!entry->will_process_pending_queue);
-
-    // We failed to create this entry.
-    TransactionList pending_queue;
-    pending_queue.swap(entry->pending_queue);
-
-    entry->disk_entry->Doom();
-    DestroyEntry(entry);
-
-    // We need to do something about these pending entries, which now need to
-    // be added to a new entry.
-    while (!pending_queue.empty()) {
-      // ERR_CACHE_RACE causes the transaction to restart the whole process.
-      pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
-      pending_queue.pop_front();
+    if (success && did_truncate) {
+      entry->writer = nullptr;
+      // Restart already validated transactions so that they are able to read
+      // the truncated status of the entry.
+      RestartHeadersPhaseTransactions(entry, transaction);
+      return;
     }
+    DoneWritingToEntry(entry, success && !did_truncate, transaction);
+    return;
   }
+
+  // Transaction is reading from the entry.
+  DoneReadingFromEntry(entry, transaction);
 }
 
-void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
+void HttpCache::DoneWritingToEntry(ActiveEntry* entry,
+                                   bool success,
+                                   Transaction* transaction) {
+  DCHECK(transaction == entry->writer ||
+         transaction == entry->headers_transaction);
+
+  if (transaction == entry->writer)
+    entry->writer = nullptr;
+  else
+    entry->headers_transaction = nullptr;
+
+  if (!success)
+    ProcessEntryFailure(entry, transaction);
+  else
+    ProcessQueuedTransactions(entry);
+}
+
+void HttpCache::DoneReadingFromEntry(ActiveEntry* entry,
+                                     Transaction* transaction) {
   DCHECK(!entry->writer);
-
-  auto it = entry->readers.find(trans);
+  auto it = entry->readers.find(transaction);
   DCHECK(it != entry->readers.end());
-
   entry->readers.erase(it);
 
-  ProcessPendingQueue(entry);
+  ProcessQueuedTransactions(entry);
 }
 
-void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
-  DCHECK(entry->writer);
-  DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
-  DCHECK(entry->readers.empty());
+void HttpCache::RemoveAllQueuedTransactions(ActiveEntry* entry,
+                                            TransactionList* list) {
+  // Process done_headers_queue before add_to_entry_queue to maintain FIFO
+  // order.
 
-  Transaction* trans = entry->writer;
+  for (auto* transaction : entry->done_headers_queue)
+    list->push_back(transaction);
+  entry->done_headers_queue.clear();
 
-  entry->writer = NULL;
-  entry->readers.insert(trans);
+  for (auto* pending_transaction : entry->add_to_entry_queue)
+    list->push_back(pending_transaction);
+  entry->add_to_entry_queue.clear();
+}
 
-  ProcessPendingQueue(entry);
+void HttpCache::ProcessEntryFailure(ActiveEntry* entry,
+                                    Transaction* transaction) {
+  // Failure case is either writer failing to completely write the response to
+  // the cache or validating transaction received a non-304 response.
+
+  if (entry->headers_transaction && transaction != entry->headers_transaction)
+    RestartHeadersTransaction(entry);
+
+  TransactionList list;
+  RemoveAllQueuedTransactions(entry, &list);
+
+  if (entry->HasNoTransactions() && !entry->will_process_queued_transactions) {
+    entry->disk_entry->Doom();
+    DestroyEntry(entry);
+  } else {
+    DoomActiveEntry(entry->disk_entry->GetKey());
+  }
+  // ERR_CACHE_RACE causes the transaction to restart the whole process.
+  for (auto* transaction : list)
+    transaction->io_callback().Run(net::ERR_CACHE_RACE);
+}
+
+void HttpCache::RestartHeadersPhaseTransactions(ActiveEntry* entry,
+                                                Transaction* transaction) {
+  if (entry->headers_transaction && transaction != entry->headers_transaction)
+    RestartHeadersTransaction(entry);
+
+  auto it = entry->done_headers_queue.begin();
+  while (it != entry->done_headers_queue.end()) {
+    Transaction* done_headers_transaction = *it;
+    DCHECK_NE(transaction, done_headers_transaction);
+    it = entry->done_headers_queue.erase(it);
+    done_headers_transaction->io_callback().Run(net::ERR_CACHE_RACE);
+  }
+}
+
+void HttpCache::RestartHeadersTransaction(ActiveEntry* entry) {
+  entry->headers_transaction->SetValidatingCannotProceed();
+  entry->headers_transaction = nullptr;
+}
+
+void HttpCache::ProcessQueuedTransactions(ActiveEntry* entry) {
+  // Multiple readers may finish with an entry at once, so we want to batch up
+  // calls to OnProcessQueuedTransactions. This flag also tells us that we
+  // should not delete the entry before OnProcessQueuedTransactions runs.
+  if (entry->will_process_queued_transactions)
+    return;
+
+  entry->will_process_queued_transactions = true;
+
+  // Post a task instead of invoking the io callback of another transaction here
+  // to avoid re-entrancy.
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::Bind(&HttpCache::OnProcessQueuedTransactions, GetWeakPtr(), entry));
+}
+
+void HttpCache::ProcessAddToEntryQueue(ActiveEntry* entry) {
+  DCHECK(!entry->add_to_entry_queue.empty());
+
+  // Note the entry may be new or may already have a response body written to
+  // it. In both cases, a transaction needs to wait since only one transaction
+  // can be in the headers phase at a time.
+  if (entry->headers_transaction) {
+    return;
+  }
+  Transaction* transaction = entry->add_to_entry_queue.front();
+  entry->add_to_entry_queue.erase(entry->add_to_entry_queue.begin());
+  entry->headers_transaction = transaction;
+
+  transaction->io_callback().Run(OK);
+}
+
+void HttpCache::ProcessDoneHeadersQueue(ActiveEntry* entry) {
+  DCHECK(!entry->writer);
+  DCHECK(!entry->done_headers_queue.empty());
+
+  Transaction* transaction = entry->done_headers_queue.front();
+
+  // If this transaction is responsible for writing the response body.
+  if (transaction->mode() & Transaction::WRITE) {
+    entry->writer = transaction;
+  } else {
+    // If a transaction is in front of this queue with only read mode set and
+    // there is no writer, it implies response body is already written, convert
+    // to a reader.
+    auto return_val = entry->readers.insert(transaction);
+    DCHECK_EQ(return_val.second, true);
+  }
+
+  // Post another task to give a chance to more transactions to either join
+  // readers or another transaction to start parallel validation.
+  ProcessQueuedTransactions(entry);
+
+  entry->done_headers_queue.erase(entry->done_headers_queue.begin());
+  transaction->io_callback().Run(OK);
+}
+
+bool HttpCache::CanTransactionWriteResponseHeaders(ActiveEntry* entry,
+                                                   Transaction* transaction,
+                                                   bool is_partial,
+                                                   bool is_match) const {
+  // If |transaction| is the current writer, do nothing. This can happen for
+  // range requests since they can go back to headers phase after starting to
+  // write.
+  if (entry->writer == transaction) {
+    DCHECK(is_partial);
+    return OK;
+  }
+
+  if (transaction != entry->headers_transaction)
+    return false;
+
+  if (!(transaction->mode() & Transaction::WRITE))
+    return false;
+
+  // If its not a match then check if it is the transaction responsible for
+  // writing the response body.
+  if (!is_match) {
+    return !entry->writer && entry->done_headers_queue.empty() &&
+           entry->readers.empty();
+  }
+
+  return true;
+}
+
+bool HttpCache::HasDependentTransactions(ActiveEntry* entry,
+                                         Transaction* transaction,
+                                         bool is_partial) const {
+  if (transaction->method() == "HEAD" || transaction->method() == "DELETE")
+    return false;
+
+  // Check if transaction is about to start writing to the cache.
+
+  // Transaction's mode may have been set to NONE if StopCaching was invoked but
+  // that should still be considered a writer failure.
+  bool writing_transaction = transaction->mode() & Transaction::WRITE ||
+                             transaction->mode() == Transaction::NONE;
+  if (!writing_transaction)
+    return false;
+
+  // If transaction is not in add_to_entry_queue and has a WRITE bit set or is
+  // NONE, then there may be other transactions depending on it to completely
+  // write the response.
+  for (auto* pending_transaction : entry->add_to_entry_queue) {
+    if (pending_transaction == transaction)
+      return false;
+  }
+
+  return true;
+}
+
+bool HttpCache::IsWritingInProgress(ActiveEntry* entry) const {
+  return entry->writer != nullptr;
 }
 
 LoadState HttpCache::GetLoadStateForPendingTransaction(
@@ -973,14 +1166,15 @@
 }
 
 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
-                                                  Transaction* trans) {
-  TransactionList& pending_queue = entry->pending_queue;
+                                                  Transaction* transaction) {
+  TransactionList& add_to_entry_queue = entry->add_to_entry_queue;
 
-  auto j = find(pending_queue.begin(), pending_queue.end(), trans);
-  if (j == pending_queue.end())
+  auto j =
+      find(add_to_entry_queue.begin(), add_to_entry_queue.end(), transaction);
+  if (j == add_to_entry_queue.end())
     return false;
 
-  pending_queue.erase(j);
+  add_to_entry_queue.erase(j);
   return true;
 }
 
@@ -1002,22 +1196,11 @@
   return false;
 }
 
-void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
-  // Multiple readers may finish with an entry at once, so we want to batch up
-  // calls to OnProcessPendingQueue.  This flag also tells us that we should
-  // not delete the entry before OnProcessPendingQueue runs.
-  if (entry->will_process_pending_queue)
-    return;
-  entry->will_process_pending_queue = true;
+void HttpCache::OnProcessQueuedTransactions(ActiveEntry* entry) {
+  entry->will_process_queued_transactions = false;
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
-}
-
-void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
-  entry->will_process_pending_queue = false;
-  DCHECK(!entry->writer);
+  // Note that this function should only invoke one transaction's IO callback
+  // since its possible for IO callbacks' consumers to destroy the cache/entry.
 
   // If no one is interested in this entry, then we can deactivate it.
   if (entry->HasNoTransactions()) {
@@ -1025,20 +1208,22 @@
     return;
   }
 
-  if (entry->pending_queue.empty())
+  if (entry->done_headers_queue.empty() && entry->add_to_entry_queue.empty())
     return;
 
-  // Promote next transaction from the pending queue.
-  Transaction* next = entry->pending_queue.front();
-  if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
-    return;  // Have to wait.
+  // To maintain FIFO order of transactions, done_headers_queue should be
+  // checked for processing before add_to_entry_queue.
 
-  entry->pending_queue.erase(entry->pending_queue.begin());
-
-  int rv = AddTransactionToEntry(entry, next);
-  if (rv != ERR_IO_PENDING) {
-    next->io_callback().Run(rv);
+  // If another transaction is writing the response, let validated transactions
+  // wait till the response is complete. If the response is not yet started, the
+  // done_headers_queue transaction should start writing it.
+  if (!entry->writer && !entry->done_headers_queue.empty()) {
+    ProcessDoneHeadersQueue(entry);
+    return;
   }
+
+  if (!entry->add_to_entry_queue.empty())
+    ProcessAddToEntryQueue(entry);
 }
 
 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 6aa26ae..91214c1 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -243,10 +243,32 @@
   friend class ViewCacheHelper;
   struct PendingOp;  // Info for an entry under construction.
 
+  // To help with testing.
+  friend class MockHttpCache;
+
   using TransactionList = std::list<Transaction*>;
   using TransactionSet = std::unordered_set<Transaction*>;
   typedef std::list<std::unique_ptr<WorkItem>> WorkItemList;
 
+  // We implement a basic reader/writer lock for the disk cache entry. If there
+  // is a writer, then all transactions must wait to read the body. But the
+  // waiting transactions can start their headers phase in parallel. Headers
+  // phase is allowed for one transaction at a time so that if it doesn't match
+  // the existing headers, remaining transactions do not also try to match the
+  // existing entry in parallel leading to wasted network requests. If the
+  // headers do not match, this entry will be doomed.
+  //
+  // A transaction goes through these state transitions.
+  //
+  // Write mode transactions:
+  // add_to_entry_queue-> headers_transaction -> writer
+  // add_to_entry_queue-> headers_transaction -> done_headers_queue -> readers
+  // (once the data is written to the cache by another writer)
+  //
+  // Read only transactions:
+  // add_to_entry_queue-> headers_transaction -> done_headers_queue -> readers
+  // (once the data is written to the cache by the writer)
+
   struct ActiveEntry {
     explicit ActiveEntry(disk_cache::Entry* entry);
     ~ActiveEntry();
@@ -255,12 +277,32 @@
     // Returns true if no transactions are associated with this entry.
     bool HasNoTransactions();
 
-    disk_cache::Entry* disk_entry;
-    Transaction*       writer;
+    disk_cache::Entry* disk_entry = nullptr;
+
+    // Transactions waiting to be added to entry.
+    TransactionList add_to_entry_queue;
+
+    // Transaction currently in the headers phase, either validating the
+    // response or getting new headers. This can exist simultaneously with
+    // writer or readers while validating existing headers.
+    Transaction* headers_transaction = nullptr;
+
+    // Transactions that have completed their headers phase and are waiting
+    // to read the response body or write the response body.
+    TransactionList done_headers_queue;
+
+    // Transaction currently reading from the network and writing to the cache.
+    Transaction* writer = nullptr;
+
+    // Transactions that can only read from the cache. Only one of writer or
+    // readers can exist at a time.
     TransactionSet readers;
-    TransactionList    pending_queue;
-    bool               will_process_pending_queue;
-    bool               doomed;
+
+    // The following variables are true if OnProcessQueuedTransactions is posted
+    bool will_process_queued_transactions = false;
+
+    // True if entry is doomed.
+    bool doomed = false;
   };
 
   using ActiveEntriesMap =
@@ -341,28 +383,90 @@
   // Destroys an ActiveEntry (active or doomed).
   void DestroyEntry(ActiveEntry* entry);
 
-  // Adds a transaction to an ActiveEntry. If this method returns ERR_IO_PENDING
-  // the transaction will be notified about completion via its IO callback. This
-  // method returns ERR_CACHE_RACE to signal the transaction that it cannot be
-  // added to the provided entry, and it should retry the process with another
-  // one (in this case, the entry is no longer valid).
-  int AddTransactionToEntry(ActiveEntry* entry, Transaction* trans);
+  // Adds a transaction to an ActiveEntry. This method returns ERR_IO_PENDING
+  // and the transaction will be notified about completion via its IO callback.
+  // In a failure case, the callback will be invoked with ERR_CACHE_RACE.
+  int AddTransactionToEntry(ActiveEntry* entry, Transaction* transaction);
 
-  // Called when the transaction has finished working with this entry. |cancel|
-  // is true if the operation was cancelled by the caller instead of running
-  // to completion.
-  void DoneWithEntry(ActiveEntry* entry, Transaction* trans, bool cancel);
+  // Transaction invokes this when its response headers phase is complete
+  // If the transaction is responsible for writing the response body,
+  // it becomes the writer and returns OK. In other cases ERR_IO_PENDING is
+  // returned and the transaction will be notified about completion via its
+  // IO callback. In a failure case, the callback will be invoked with
+  // ERR_CACHE_RACE.
+  int DoneWithResponseHeaders(ActiveEntry* entry,
+                              Transaction* transaction,
+                              bool is_partial);
+
+  // Called when the transaction has finished working with this entry.
+  // |process_cancel| is true if the transaction could have been writing the
+  // response body and was cancelled by the caller instead of running
+  // to completion. This will be confirmed and if true, its impact on queued
+  // transactions will be processed.
+  void DoneWithEntry(ActiveEntry* entry,
+                     Transaction* transaction,
+                     bool process_cancel,
+                     bool is_partial);
 
   // Called when the transaction has finished writing to this entry. |success|
   // is false if the cache entry should be deleted.
-  void DoneWritingToEntry(ActiveEntry* entry, bool success);
+  void DoneWritingToEntry(ActiveEntry* entry,
+                          bool success,
+                          Transaction* transaction);
 
   // Called when the transaction has finished reading from this entry.
-  void DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans);
+  void DoneReadingFromEntry(ActiveEntry* entry, Transaction* transaction);
 
-  // Converts the active writer transaction to a reader so that other
-  // transactions can start reading from this entry.
-  void ConvertWriterToReader(ActiveEntry* entry);
+  // Removes and returns all queued transactions in |entry| in FIFO order. This
+  // includes transactions that have completed the headers phase and those that
+  // have not been added to the entry yet in that order. |list| is the output
+  // argument.
+  void RemoveAllQueuedTransactions(ActiveEntry* entry, TransactionList* list);
+
+  // Processes either writer's failure to write response body or
+  // headers_transactions's failure to write headers. Also invoked when headers
+  // transaction's validation result is not a match.
+  void ProcessEntryFailure(ActiveEntry* entry, Transaction* transaction);
+
+  // Restarts headers_transaction and done_headers_queue transactions.
+  void RestartHeadersPhaseTransactions(ActiveEntry* entry,
+                                       Transaction* transaction);
+
+  // Restarts the headers_transaction by setting its state. Since the
+  // headers_transaction is awaiting an asynchronous operation completion,
+  // it will be restarted when it's IO callback is invoked.
+  void RestartHeadersTransaction(ActiveEntry* entry);
+
+  // Resumes processing the queued transactions of |entry|.
+  void ProcessQueuedTransactions(ActiveEntry* entry);
+
+  // Checks if a transaction can be added to the entry. If yes, it will
+  // invoke the IO callback of the transaction. This is a helper function for
+  // OnProcessQueuedTransactions. It will take a transaction from
+  // add_to_entry_queue and make it a headers_transaction, if one doesn't exist
+  // already.
+  void ProcessAddToEntryQueue(ActiveEntry* entry);
+
+  // Invoked when a transaction that has already completed the response headers
+  // phase can resume reading/writing the response body. It will invoke the IO
+  // callback of the transaction. This is a helper function for
+  // OnProcessQueuedTransactions.
+  void ProcessDoneHeadersQueue(ActiveEntry* entry);
+
+  // Returns true if this transaction can write headers to the entry.
+  bool CanTransactionWriteResponseHeaders(ActiveEntry* entry,
+                                          Transaction* transaction,
+                                          bool is_partial,
+                                          bool is_match) const;
+
+  // Returns true if any transactions in the ActiveEntry depend on this
+  // transaction to complete writing to the cache.
+  bool HasDependentTransactions(ActiveEntry* entry,
+                                Transaction* transaction,
+                                bool is_partial) const;
+
+  // Returns true if a transaction is currently writing the response body.
+  bool IsWritingInProgress(ActiveEntry* entry) const;
 
   // Returns the LoadState of the provided pending transaction.
   LoadState GetLoadStateForPendingTransaction(const Transaction* trans);
@@ -378,12 +482,10 @@
   // Removes the transaction |trans|, from the pending list of |pending_op|.
   bool RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
                                              Transaction* trans);
-  // Resumes processing the pending list of |entry|.
-  void ProcessPendingQueue(ActiveEntry* entry);
 
   // Events (called via PostTask) ---------------------------------------------
 
-  void OnProcessPendingQueue(ActiveEntry* entry);
+  void OnProcessQueuedTransactions(ActiveEntry* entry);
 
   // Callbacks ----------------------------------------------------------------
 
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 51894465..9bc8326 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -196,16 +196,8 @@
 
   if (cache_) {
     if (entry_) {
-      bool cancel_request = reading_ && response_.headers.get();
-      if (cancel_request) {
-        if (partial_) {
-          entry_->disk_entry->CancelSparseIO();
-        } else {
-          cancel_request &= (response_.headers->response_code() == 200);
-        }
-      }
-
-      cache_->DoneWithEntry(entry_, this, cancel_request);
+      cache_->DoneWithEntry(entry_, this, true /* process_cancel */,
+                            partial_ != nullptr);
     } else if (cache_pending_) {
       cache_->RemovePendingTransaction(this);
     }
@@ -228,7 +220,8 @@
                                        callback, true);
 }
 
-bool HttpCache::Transaction::AddTruncatedFlag() {
+bool HttpCache::Transaction::AddTruncatedFlag(bool* did_truncate) {
+  *did_truncate = false;
   DCHECK(mode_ & WRITE || mode_ == NONE);
 
   // Don't set the flag for sparse entries.
@@ -243,6 +236,7 @@
     return true;
 
   truncated_ = true;
+  *did_truncate = truncated_;
   next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE;
   DoLoop(OK);
   return true;
@@ -410,12 +404,14 @@
   // entry how it is (it will be marked as truncated at destruction), and let
   // the next piece of code that executes know that we are now reading directly
   // from the net.
-  // TODO(mmenke):  This doesn't release the lock on the cache entry, so a
-  //                future request for the resource will be blocked on this one.
-  //                Fix this.
   if (cache_.get() && entry_ && (mode_ & WRITE) && network_trans_.get() &&
       !is_sparse_ && !range_requested_) {
     mode_ = NONE;
+
+    // Let entry_ know so that any dependent transactions are restarted and this
+    // entry is doomed.
+    cache_->DoneWritingToEntry(entry_, false /* success */, this);
+    entry_ = nullptr;
   }
 }
 
@@ -451,7 +447,8 @@
       // It is necessary to check mode_ & READ because it is possible
       // for mode_ to be NONE and entry_ non-NULL with a write entry
       // if StopCaching was called.
-      cache_->DoneReadingFromEntry(entry_, this);
+      cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
+                            partial_ != nullptr);
       entry_ = NULL;
     }
   }
@@ -565,6 +562,15 @@
               old_connection_attempts_.end());
 }
 
+void HttpCache::Transaction::SetValidatingCannotProceed() {
+  DCHECK(!reading_);
+  // Ensure this transaction is waiting for a callback.
+  DCHECK_NE(STATE_UNSET, next_state_);
+
+  next_state_ = STATE_HEADERS_PHASE_CANNOT_PROCEED;
+  entry_ = nullptr;
+}
+
 size_t HttpCache::Transaction::EstimateMemoryUsage() const {
   // TODO(xunjieli): Consider improving the coverage. crbug.com/669108.
   return 0;
@@ -579,7 +585,7 @@
 //   GetBackend* -> InitEntry -> OpenEntry* -> CreateEntry* -> AddToEntry* ->
 //   SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
 //   CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* ->
-//   PartialHeadersReceived
+//   PartialHeadersReceived -> FinishHeaders*
 //
 //   Read():
 //   NetworkRead* -> CacheWriteData*
@@ -588,7 +594,7 @@
 //   Start():
 //   GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
 //   -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
-//   BeginCacheValidation() -> SetupEntryForRead()
+//   BeginCacheValidation() -> SetupEntryForRead() -> FinishHeaders*
 //
 //   Read():
 //   CacheReadData*
@@ -600,7 +606,7 @@
 //   BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
 //   UpdateCachedResponse -> CacheWriteUpdatedResponse* ->
 //   UpdateCachedResponseComplete -> OverwriteCachedResponse ->
-//   PartialHeadersReceived
+//   PartialHeadersReceived -> FinishHeaders*
 //
 //   Read():
 //   CacheReadData*
@@ -611,7 +617,7 @@
 //   -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
 //   BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
 //   OverwriteCachedResponse -> CacheWriteResponse* -> DoTruncateCachedData* ->
-//   TruncateCachedMetadata* -> PartialHeadersReceived
+//   TruncateCachedMetadata* -> PartialHeadersReceived -> FinishHeaders*
 //
 //   Read():
 //   NetworkRead* -> CacheWriteData*
@@ -625,7 +631,7 @@
 //   BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
 //   UpdateCachedResponse -> CacheWriteUpdatedResponse* ->
 //   UpdateCachedResponseComplete -> OverwriteCachedResponse ->
-//   PartialHeadersReceived
+//   PartialHeadersReceived -> FinishHeaders*
 //
 //   Read() 1:
 //   NetworkRead* -> CacheWriteData*
@@ -666,7 +672,7 @@
 //   GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
 //   -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
 //   BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
-//   OverwriteCachedResponse
+//   OverwriteCachedResponse -> FinishHeaders*
 //
 // 10. HEAD. Sparse entry, partially cached:
 //   Serve the request from the cache, as long as it doesn't require
@@ -691,7 +697,7 @@
 //   GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
 //   -> CacheToggleUnusedSincePrefetch* -> CacheDispatchValidation ->
 //   BeginPartialCacheValidation() -> BeginCacheValidation() ->
-//   SetupEntryForRead()
+//   SetupEntryForRead() -> FinishHeaders*
 //
 //   Read():
 //   CacheReadData*
@@ -705,8 +711,9 @@
   DCHECK(!in_do_loop_);
 
   int rv = result;
+  State state = next_state_;
   do {
-    State state = next_state_;
+    state = next_state_;
     next_state_ = STATE_UNSET;
     base::AutoReset<bool> scoped_in_do_loop(&in_do_loop_, true);
 
@@ -843,6 +850,15 @@
       case STATE_CACHE_READ_METADATA_COMPLETE:
         rv = DoCacheReadMetadataComplete(rv);
         break;
+      case STATE_HEADERS_PHASE_CANNOT_PROCEED:
+        rv = DoHeadersPhaseCannotProceed();
+        break;
+      case STATE_FINISH_HEADERS:
+        rv = DoFinishHeaders(rv);
+        break;
+      case STATE_FINISH_HEADERS_COMPLETE:
+        rv = DoFinishHeadersComplete(rv);
+        break;
       case STATE_NETWORK_READ:
         DCHECK_EQ(OK, rv);
         rv = DoNetworkRead();
@@ -879,8 +895,13 @@
 
   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
 
+  // Assert Start() state machine's allowed last state in successful cases when
+  // caching is happening.
+  DCHECK(reading_ || rv != OK || !entry_ ||
+         state == STATE_FINISH_HEADERS_COMPLETE);
+
   if (rv != ERR_IO_PENDING && !callback_.is_null()) {
-    read_buf_ = NULL;  // Release the buffer before invoking the callback.
+    read_buf_ = nullptr;  // Release the buffer before invoking the callback.
     base::ResetAndReturn(&callback_).Run(rv);
   }
 
@@ -907,7 +928,7 @@
     if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
       if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
         // The client has asked for nonsense.
-        TransitionToState(STATE_NONE);
+        TransitionToState(STATE_FINISH_HEADERS);
         return ERR_CACHE_MISS;
       }
       mode_ = READ;
@@ -930,8 +951,8 @@
   }
 
   // Use PUT and DELETE only to invalidate existing stored entries.
-  if ((request_->method == "PUT" || request_->method == "DELETE") &&
-      mode_ != READ_WRITE && mode_ != WRITE) {
+  if ((method_ == "PUT" || method_ == "DELETE") && mode_ != READ_WRITE &&
+      mode_ != WRITE) {
     mode_ = NONE;
   }
 
@@ -940,13 +961,13 @@
   // was not modified, the entry is updated and a response is not returned from
   // the cache. If we receive 200, it doesn't matter if there was a validation
   // header or not.
-  if (request_->method == "HEAD" && mode_ == WRITE)
+  if (method_ == "HEAD" && mode_ == WRITE)
     mode_ = NONE;
 
   // If must use cache, then we must fail.  This can happen for back/forward
   // navigations to a page generated via a form post.
   if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_CACHE_MISS;
   }
 
@@ -963,6 +984,9 @@
   // This is only set if we have something to do with the response.
   range_requested_ = (partial_.get() != NULL);
 
+  // mode_ may change later, save the initial mode in case we need to restart
+  // this request.
+  restart_info_.mode = mode_;
   return OK;
 }
 
@@ -971,7 +995,7 @@
   DCHECK(!new_entry_);
 
   if (!cache_.get()) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_UNEXPECTED;
   }
 
@@ -1008,13 +1032,13 @@
   }
 
   if (result == ERR_CACHE_RACE) {
-    TransitionToState(STATE_INIT_ENTRY);
+    TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
     return OK;
   }
 
-  if (request_->method == "PUT" || request_->method == "DELETE" ||
-      (request_->method == "HEAD" && mode_ == READ_WRITE)) {
-    DCHECK(mode_ == READ_WRITE || mode_ == WRITE || request_->method == "HEAD");
+  if (method_ == "PUT" || method_ == "DELETE" ||
+      (method_ == "HEAD" && mode_ == READ_WRITE)) {
+    DCHECK(mode_ == READ_WRITE || mode_ == WRITE || method_ == "HEAD");
     mode_ = NONE;
     TransitionToState(STATE_SEND_REQUEST);
     return OK;
@@ -1034,7 +1058,7 @@
 
   // The entry does not exist, and we are not permitted to create a new entry,
   // so we must fail.
-  TransitionToState(STATE_NONE);
+  TransitionToState(STATE_FINISH_HEADERS);
   return ERR_CACHE_MISS;
 }
 
@@ -1053,8 +1077,9 @@
   net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_DOOM_ENTRY,
                                     result);
   cache_pending_ = false;
-  TransitionToState(result == ERR_CACHE_RACE ? STATE_INIT_ENTRY
-                                             : STATE_CREATE_ENTRY);
+  TransitionToState(result == ERR_CACHE_RACE
+                        ? STATE_HEADERS_PHASE_CANNOT_PROCEED
+                        : STATE_CREATE_ENTRY);
   return OK;
 }
 
@@ -1081,7 +1106,7 @@
       break;
 
     case ERR_CACHE_RACE:
-      TransitionToState(STATE_INIT_ENTRY);
+      TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
       break;
 
     default:
@@ -1162,13 +1187,13 @@
   new_entry_ = NULL;
 
   if (result == ERR_CACHE_RACE) {
-    TransitionToState(STATE_INIT_ENTRY);
+    TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
     return OK;
   }
 
   if (result == ERR_CACHE_LOCK_TIMEOUT) {
     if (mode_ == READ) {
-      TransitionToState(STATE_NONE);
+      TransitionToState(STATE_FINISH_HEADERS);
       return ERR_CACHE_MISS;
     }
 
@@ -1182,12 +1207,16 @@
     return OK;
   }
 
-  open_entry_last_used_ = entry_->disk_entry->GetLastUsed();
+  // TODO(crbug.com/713354) Access timestamp for histograms only if entry is
+  // already written, to avoid data race since cache thread can also access
+  // this.
+  if (!cache_->IsWritingInProgress(entry_))
+    open_entry_last_used_ = entry_->disk_entry->GetLastUsed();
 
   // TODO(jkarlin): We should either handle the case or DCHECK.
   if (result != OK) {
     NOTREACHED();
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return result;
   }
 
@@ -1226,29 +1255,37 @@
     return OnCacheReadError(result, true);
   }
 
-  int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
-  int64_t full_response_length = response_.headers->GetContentLength();
+  // TODO(crbug.com/713354) Only get data size if there is no other transaction
+  // currently writing the response body due to the data race mentioned in the
+  // associated bug.
+  if (!cache_->IsWritingInProgress(entry_)) {
+    int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
+    int64_t full_response_length = response_.headers->GetContentLength();
 
-  // Some resources may have slipped in as truncated when they're not.
-  if (full_response_length == current_size)
-    truncated_ = false;
+    // Some resources may have slipped in as truncated when they're not.
+    if (full_response_length == current_size)
+      truncated_ = false;
 
-  // The state machine's handling of StopCaching unfortunately doesn't deal well
-  // with resources that are larger than 2GB when there is a truncated or sparse
-  // cache entry. While the state machine is reworked to resolve this, the
-  // following logic is put in place to defer such requests to the network. The
-  // cache should not be storing multi gigabyte resources. See
-  // http://crbug.com/89567.
-  if ((truncated_ || response_.headers->response_code() == 206) &&
-      !range_requested_ &&
-      full_response_length > std::numeric_limits<int32_t>::max()) {
-    // Does not release the cache entry. If another transaction wants to use
-    // this cache entry while this transaction is active, the second transaction
-    // will fall back to the network after the timeout.
-    DCHECK(!partial_);
-    mode_ = NONE;
-    TransitionToState(STATE_SEND_REQUEST);
-    return OK;
+    // The state machine's handling of StopCaching unfortunately doesn't deal
+    // well with resources that are larger than 2GB when there is a truncated or
+    // sparse cache entry. While the state machine is reworked to resolve this,
+    // the following logic is put in place to defer such requests to the
+    // network. The cache should not be storing multi gigabyte resources. See
+    // http://crbug.com/89567.
+    if ((truncated_ || response_.headers->response_code() == 206) &&
+        !range_requested_ &&
+        full_response_length > std::numeric_limits<int32_t>::max()) {
+      DCHECK(!partial_);
+
+      // Doom the entry so that no other transaction gets added to this entry
+      // and avoid a race of not being able to check this condition because
+      // writing is in progress.
+      cache_->DoneWritingToEntry(entry_, false, this);
+      entry_ = nullptr;
+      mode_ = NONE;
+      TransitionToState(STATE_SEND_REQUEST);
+      return OK;
+    }
   }
 
   if (response_.unused_since_prefetch !=
@@ -1330,7 +1367,7 @@
 int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
   DCHECK_EQ(OK, result);
   if (!cache_.get()) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_UNEXPECTED;
   }
 
@@ -1340,7 +1377,7 @@
 // We may end up here multiple times for a given request.
 int HttpCache::Transaction::DoStartPartialCacheValidation() {
   if (mode_ == NONE) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return OK;
   }
 
@@ -1354,15 +1391,16 @@
     if (mode_ & WRITE) {
       DoneWritingToEntry(true);
     } else {
-      cache_->DoneReadingFromEntry(entry_, this);
+      cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
+                            partial_ != nullptr);
       entry_ = NULL;
     }
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return result;
   }
 
   if (result < 0) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return result;
   }
 
@@ -1388,7 +1426,7 @@
   int rv =
       cache_->network_layer_->CreateTransaction(priority_, &network_trans_);
   if (rv != OK) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return rv;
   }
   network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_);
@@ -1410,7 +1448,7 @@
 int HttpCache::Transaction::DoSendRequestComplete(int result) {
   TRACE_EVENT0("io", "HttpCacheTransaction::DoSendRequestComplete");
   if (!cache_.get()) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_UNEXPECTED;
   }
 
@@ -1441,7 +1479,7 @@
     DoneWritingToEntry(true);
   }
 
-  TransitionToState(STATE_NONE);
+  TransitionToState(STATE_FINISH_HEADERS);
   return result;
 }
 
@@ -1455,7 +1493,7 @@
       new_response->headers->response_code() == 407) {
     SetAuthResponse(*new_response);
     if (!reading_) {
-      TransitionToState(STATE_NONE);
+      TransitionToState(STATE_FINISH_HEADERS);
       return OK;
     }
 
@@ -1480,7 +1518,7 @@
     mode_ = NONE;
     partial_.reset();
     ResetNetworkTransaction();
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_CACHE_AUTH_FAILURE_AFTER_READ;
   }
 
@@ -1512,20 +1550,18 @@
   }
 
   // Invalidate any cached GET with a successful PUT or DELETE.
-  if (mode_ == WRITE &&
-      (request_->method == "PUT" || request_->method == "DELETE")) {
+  if (mode_ == WRITE && (method_ == "PUT" || method_ == "DELETE")) {
     if (NonErrorResponse(new_response->headers->response_code())) {
       int ret = cache_->DoomEntry(cache_key_, NULL);
       DCHECK_EQ(OK, ret);
     }
-    cache_->DoneWritingToEntry(entry_, true);
+    cache_->DoneWritingToEntry(entry_, true, this);
     entry_ = NULL;
     mode_ = NONE;
   }
 
   // Invalidate any cached GET with a successful POST.
-  if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) &&
-      request_->method == "POST" &&
+  if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) && method_ == "POST" &&
       NonErrorResponse(new_response->headers->response_code())) {
     cache_->DoomMainEntryForUrl(request_->url);
   }
@@ -1533,10 +1569,10 @@
   RecordNoStoreHeaderHistogram(request_->load_flags, new_response);
 
   if (new_response_->headers->response_code() == 416 &&
-      (request_->method == "GET" || request_->method == "POST")) {
+      (method_ == "GET" || method_ == "POST")) {
     // If there is an active entry it may be destroyed with this transaction.
     SetResponse(*new_response_);
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return OK;
   }
 
@@ -1624,7 +1660,6 @@
   } else if (entry_ && !handling_206_) {
     DCHECK_EQ(READ_WRITE, mode_);
     if (!partial_ || partial_->IsLastRange()) {
-      cache_->ConvertWriterToReader(entry_);
       mode_ = READ;
     }
     // We no longer need the network transaction, so destroy it.
@@ -1657,12 +1692,12 @@
 
   SetResponse(*new_response_);
 
-  if (request_->method == "HEAD") {
+  if (method_ == "HEAD") {
     // This response is replacing the cached one.
     DoneWritingToEntry(false);
     mode_ = NONE;
     new_response_ = NULL;
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return OK;
   }
 
@@ -1683,6 +1718,20 @@
 int HttpCache::Transaction::DoCacheWriteResponse() {
   TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponse");
   TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE);
+
+  // Invalidate any current entry with a successful response if this transaction
+  // cannot write to this entry. This transaction then continues to read from
+  // the network without writing to the backend.
+  bool is_match = response_.headers->response_code() == 304;
+  if (entry_ && response_.headers &&
+      !cache_->CanTransactionWriteResponseHeaders(
+          entry_, this, partial_ != nullptr, is_match)) {
+    cache_->DoneWritingToEntry(entry_, false, this);
+    entry_ = nullptr;
+    mode_ = NONE;
+    return OK;
+  }
+
   return WriteResponseInfoToEntry(truncated_);
 }
 
@@ -1744,10 +1793,11 @@
   new_response_ = NULL;
 
   if (!partial_) {
-    if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex))
+    if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex)) {
       TransitionToState(STATE_CACHE_READ_METADATA);
-    else
-      TransitionToState(STATE_NONE);
+    } else {
+      TransitionToState(STATE_FINISH_HEADERS);
+    }
     return OK;
   }
 
@@ -1761,13 +1811,59 @@
     // We are about to return the headers for a byte-range request to the user,
     // so let's fix them.
     partial_->FixResponseHeaders(response_.headers.get(), true);
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
   } else {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
   }
   return OK;
 }
 
+int HttpCache::Transaction::DoHeadersPhaseCannotProceed() {
+  // If its the Start state machine and it cannot proceed due to a cache
+  // failure, restart this transaction.
+  DCHECK(!reading_);
+  TransitionToState(STATE_INIT_ENTRY);
+  cache_entry_status_ = restart_info_.cache_entry_status;
+  entry_ = nullptr;
+  mode_ = restart_info_.mode;
+  if (network_trans_)
+    network_trans_.reset();
+
+  return OK;
+}
+
+int HttpCache::Transaction::DoFinishHeaders(int result) {
+  if (!entry_ || result != OK) {
+    TransitionToState(STATE_NONE);
+    return result;
+  }
+
+  TransitionToState(STATE_FINISH_HEADERS_COMPLETE);
+
+  // If it was an auth failure or 416, this transaction should continue to be
+  // headers_transaction till consumer takes an action, so no need to do
+  // anything now.
+  if (auth_response_.headers.get() ||
+      (new_response_ && new_response_->headers &&
+       new_response_->headers->response_code() == 416))
+    return OK;
+
+  // If the transaction needs to wait because another transaction is still
+  // writing the response body, it will return ERR_IO_PENDING now and the
+  // io_callback_ will be invoked when the wait is done.
+  return cache_->DoneWithResponseHeaders(entry_, this, partial_ != nullptr);
+}
+
+int HttpCache::Transaction::DoFinishHeadersComplete(int rv) {
+  if (rv == ERR_CACHE_RACE) {
+    TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
+    return OK;
+  }
+
+  TransitionToState(STATE_NONE);
+  return rv;
+}
+
 int HttpCache::Transaction::DoCacheReadMetadata() {
   TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadMetadata");
   DCHECK(entry_);
@@ -1790,7 +1886,8 @@
                                     result);
   if (result != response_.metadata->size())
     return OnCacheReadError(result, false);
-  TransitionToState(STATE_NONE);
+
+  TransitionToState(STATE_FINISH_HEADERS);
   return OK;
 }
 
@@ -1823,7 +1920,7 @@
 int HttpCache::Transaction::DoCacheReadData() {
   TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadData");
 
-  if (request_->method == "HEAD") {
+  if (method_ == "HEAD") {
     TransitionToState(STATE_NONE);
     return 0;
   }
@@ -1961,6 +2058,7 @@
   net_log_ = net_log;
   request_ = request;
   effective_load_flags_ = request_->load_flags;
+  method_ = request_->method;
 
   if (cache_->mode() == DISABLE)
     effective_load_flags_ |= LOAD_DISABLE_CACHE;
@@ -2040,7 +2138,7 @@
   if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) {
     UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
     partial_.reset(new PartialData);
-    if (request_->method == "GET" && partial_->Init(request_->extra_headers)) {
+    if (method_ == "GET" && partial_->Init(request_->extra_headers)) {
       // We will be modifying the actual range requested to the server, so
       // let's remove the header here.
       custom_request_.reset(new HttpRequestInfo(*request_));
@@ -2054,6 +2152,7 @@
       partial_.reset(NULL);
     }
   }
+  restart_info_.cache_entry_status = cache_entry_status_;
 }
 
 bool HttpCache::Transaction::ShouldPassThrough() {
@@ -2065,18 +2164,18 @@
   if (effective_load_flags_ & LOAD_DISABLE_CACHE)
     return true;
 
-  if (request_->method == "GET" || request_->method == "HEAD")
+  if (method_ == "GET" || method_ == "HEAD")
     return false;
 
-  if (request_->method == "POST" && request_->upload_data_stream &&
+  if (method_ == "POST" && request_->upload_data_stream &&
       request_->upload_data_stream->identifier()) {
     return false;
   }
 
-  if (request_->method == "PUT" && request_->upload_data_stream)
+  if (method_ == "PUT" && request_->upload_data_stream)
     return false;
 
-  if (request_->method == "DELETE")
+  if (method_ == "DELETE")
     return false;
 
   return true;
@@ -2087,28 +2186,28 @@
   // TODO(jkarlin): Either handle this case or DCHECK.
   if (response_.headers->response_code() == 206 || partial_) {
     NOTREACHED();
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_CACHE_MISS;
   }
 
   // We don't have the whole resource.
   if (truncated_) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_CACHE_MISS;
   }
 
   if (RequiresValidation()) {
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
     return ERR_CACHE_MISS;
   }
 
-  if (request_->method == "HEAD")
+  if (method_ == "HEAD")
     FixHeadersForHead();
 
   if (entry_->disk_entry->GetDataSize(kMetadataIndex))
     TransitionToState(STATE_CACHE_READ_METADATA);
   else
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
 
   return OK;
 }
@@ -2118,7 +2217,7 @@
 
   bool skip_validation = !RequiresValidation();
 
-  if (request_->method == "HEAD" &&
+  if (method_ == "HEAD" &&
       (truncated_ || response_.headers->response_code() == 206)) {
     DCHECK(!partial_);
     if (skip_validation)
@@ -2176,7 +2275,7 @@
 
   // Partial requests should not be recorded in histograms.
   UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
-  if (request_->method == "HEAD")
+  if (method_ == "HEAD")
     return BeginCacheValidation();
 
   if (!range_requested_) {
@@ -2314,7 +2413,7 @@
     return true;
   }
 
-  if (request_->method == "PUT" || request_->method == "DELETE")
+  if (method_ == "PUT" || method_ == "DELETE")
     return true;
 
   bool validation_required_by_headers = response_.headers->RequiresValidation(
@@ -2340,7 +2439,7 @@
 bool HttpCache::Transaction::ConditionalizeRequest() {
   DCHECK(response_.headers.get());
 
-  if (request_->method == "PUT" || request_->method == "DELETE")
+  if (method_ == "PUT" || method_ == "DELETE")
     return false;
 
   // This only makes sense for cached 200 or 206 responses.
@@ -2434,7 +2533,7 @@
   bool partial_response = (response_code == 206);
   handling_206_ = false;
 
-  if (!entry_ || request_->method != "GET")
+  if (!entry_ || method_ != "GET")
     return true;
 
   if (invalid_range_) {
@@ -2541,7 +2640,8 @@
   if (mode_ & WRITE)
     DoneWritingToEntry(mode_ != WRITE);
   else if (mode_ & READ && entry_)
-    cache_->DoneReadingFromEntry(entry_, this);
+    cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
+                          partial_ != nullptr);
 
   partial_.reset(NULL);
   entry_ = NULL;
@@ -2568,16 +2668,16 @@
       partial_.reset();
     }
   }
-  cache_->ConvertWriterToReader(entry_);
+
   mode_ = READ;
 
-  if (request_->method == "HEAD")
+  if (method_ == "HEAD")
     FixHeadersForHead();
 
   if (entry_->disk_entry->GetDataSize(kMetadataIndex))
     TransitionToState(STATE_CACHE_READ_METADATA);
   else
-    TransitionToState(STATE_NONE);
+    TransitionToState(STATE_FINISH_HEADERS);
   return OK;
 }
 
@@ -2656,7 +2756,7 @@
 
   RecordHistograms();
 
-  cache_->DoneWritingToEntry(entry_, success);
+  cache_->DoneWritingToEntry(entry_, success, this);
   entry_ = NULL;
   mode_ = NONE;  // switch to 'pass through' mode
 }
@@ -2679,7 +2779,8 @@
   if (restart) {
     DCHECK(!reading_);
     DCHECK(!network_trans_.get());
-    cache_->DoneWithEntry(entry_, this, false);
+    cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
+                          partial_ != nullptr);
     entry_ = NULL;
     is_sparse_ = false;
     partial_.reset();
@@ -2708,7 +2809,8 @@
   DVLOG(2) << "DoomPartialEntry";
   int rv = cache_->DoomEntry(cache_key_, NULL);
   DCHECK_EQ(OK, rv);
-  cache_->DoneWithEntry(entry_, this, false);
+  cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
+                        partial_ != nullptr);
   entry_ = NULL;
   is_sparse_ = false;
   truncated_ = false;
@@ -2804,7 +2906,7 @@
   if (has_data && !entry_->disk_entry->GetDataSize(kResponseContentIndex))
     return false;
 
-  if (request_->method != "GET")
+  if (method_ != "GET")
     return false;
 
   // Note that if this is a 206, content-length was already fixed after calling
@@ -2853,7 +2955,7 @@
   DCHECK_NE(CacheEntryStatus::ENTRY_UNDEFINED, cache_entry_status_);
   if (!cache_.get() || !cache_->GetCurrentBackend() ||
       cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE ||
-      cache_->mode() != NORMAL || request_->method != "GET") {
+      cache_->mode() != NORMAL || method_ != "GET") {
     return;
   }
 
@@ -2867,10 +2969,14 @@
        cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE);
   int64_t freshness_periods_since_last_used = 0;
 
-  if (stale_request) {
+  if (stale_request && !open_entry_last_used_.is_null()) {
+    // Note that we are not able to capture those transactions' histograms which
+    // when added to entry, the response was being written by another
+    // transaction because getting the last used timestamp might lead to a data
+    // race in that case. TODO(crbug.com/713354).
+
     // For stale entries, record how many freshness periods have elapsed since
     // the entry was last used.
-    DCHECK(!open_entry_last_used_.is_null());
     DCHECK(!stale_entry_freshness_.is_zero());
     base::TimeDelta time_since_use = base::Time::Now() - open_entry_last_used_;
     freshness_periods_since_last_used =
@@ -2903,7 +3009,7 @@
     // response header mime type, which could be incorrect, so this is just an
     // estimate.
     if (mime_type == "text/html" &&
-        (request_->load_flags & LOAD_MAIN_FRAME_DEPRECATED)) {
+        (effective_load_flags_ & LOAD_MAIN_FRAME_DEPRECATED)) {
       CACHE_STATUS_HISTOGRAMS(".MainFrameHTML");
     } else if (mime_type == "text/html") {
       CACHE_STATUS_HISTOGRAMS(".NonMainFrameHTML");
@@ -2943,7 +3049,8 @@
 
   if (cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER)
     return;
-  DCHECK(!range_requested_);
+
+  DCHECK(!range_requested_) << "Cache entry status " << cache_entry_status_;
   DCHECK(!first_cache_access_since_.is_null());
 
   TimeDelta total_time = base::TimeTicks::Now() - first_cache_access_since_;
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index 51c0db7..080d8b9 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -80,6 +80,8 @@
 
   Mode mode() const { return mode_; }
 
+  std::string& method() { return method_; }
+
   const std::string& key() const { return cache_key_; }
 
   // Writes |buf_len| bytes of meta-data from the provided buffer |buf|. to the
@@ -104,8 +106,10 @@
   // This transaction is being deleted and we are not done writing to the cache.
   // We need to indicate that the response data was truncated.  Returns true on
   // success. Keep in mind that this operation may have side effects, such as
-  // deleting the active entry.
-  bool AddTruncatedFlag();
+  // deleting the active entry. This also returns success if the response was
+  // completely written, |*did_truncate| will be set to true if it was actually
+  // truncated.
+  bool AddTruncatedFlag(bool* did_truncate);
 
   HttpCache::ActiveEntry* entry() { return entry_; }
 
@@ -164,6 +168,10 @@
   int ResumeNetworkStart() override;
   void GetConnectionAttempts(ConnectionAttempts* out) const override;
 
+  // Invoked when parallel validation cannot proceed due to response failure
+  // and this transaction needs to be restarted.
+  void SetValidatingCannotProceed();
+
   // Returns the estimate of dynamically allocated memory in bytes.
   size_t EstimateMemoryUsage() const;
 
@@ -178,6 +186,14 @@
     bool initialized;
   };
 
+  // A snapshot of pieces of the transaction before entering the state machine
+  // so that the state can be restored when restarting the state machine.
+  struct RestartInfo {
+    Mode mode = NONE;
+    HttpResponseInfo::CacheEntryStatus cache_entry_status =
+        HttpResponseInfo::CacheEntryStatus::ENTRY_UNDEFINED;
+  };
+
   enum State {
     STATE_UNSET,
 
@@ -220,6 +236,9 @@
     STATE_PARTIAL_HEADERS_RECEIVED,
     STATE_CACHE_READ_METADATA,
     STATE_CACHE_READ_METADATA_COMPLETE,
+    STATE_HEADERS_PHASE_CANNOT_PROCEED,
+    STATE_FINISH_HEADERS,
+    STATE_FINISH_HEADERS_COMPLETE,
 
     // These states are entered from Read/AddTruncatedFlag.
     STATE_NETWORK_READ,
@@ -288,6 +307,9 @@
   int DoPartialHeadersReceived();
   int DoCacheReadMetadata();
   int DoCacheReadMetadataComplete(int result);
+  int DoHeadersPhaseCannotProceed();
+  int DoFinishHeaders(int result);
+  int DoFinishHeadersComplete(int result);
   int DoNetworkRead();
   int DoNetworkReadComplete(int result);
   int DoCacheReadData();
@@ -430,7 +452,12 @@
   void SyncCacheEntryStatusToResponse();
   void RecordHistograms();
 
-  // Called to signal completion of asynchronous IO.
+  // Called to signal completion of asynchronous IO. Note that this callback is
+  // used in the conventional sense where one layer calls the callback of the
+  // layer above it e.g. this callback gets called from the network transaction
+  // layer. In addition, it is also used for HttpCache layer to let this
+  // transaction know when it is out of a queued state in ActiveEntry and can
+  // continue its processing.
   void OnIOComplete(int result);
 
   // When in a DoLoop, use this to set the next state as it verifies that the
@@ -439,6 +466,7 @@
 
   State next_state_;
   const HttpRequestInfo* request_;
+  std::string method_;
   RequestPriority priority_;
   NetLogWithSource net_log_;
   std::unique_ptr<HttpRequestInfo> custom_request_;
@@ -514,6 +542,12 @@
   // True if the Transaction is currently processing the DoLoop.
   bool in_do_loop_;
 
+  // Used to restore some members when the state machine is restarted after it
+  // has already been added to an entry e.g after |this| has completed
+  // validation and the writer transaction fails to completely write the
+  // response to the cache.
+  RestartInfo restart_info_;
+
   base::WeakPtrFactory<Transaction> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Transaction);
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 13182e4..62269b79 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -147,6 +147,10 @@
   EXPECT_TRUE(load_timing_info.receive_headers_end.is_null());
 }
 
+void DeferNetworkStart(bool* defer) {
+  *defer = true;
+}
+
 class DeleteCacheCompletionCallback : public TestCompletionCallbackBase {
  public:
   explicit DeleteCacheCompletionCallback(MockHttpCache* cache)
@@ -182,6 +186,17 @@
   EXPECT_EQ(expected, content);
 }
 
+void ReadRemainingAndVerifyTransaction(HttpTransaction* trans,
+                                       std::string& already_read,
+                                       const MockTransaction& trans_info) {
+  std::string content;
+  int rv = ReadTransaction(trans, &content);
+  EXPECT_THAT(rv, IsOk());
+
+  std::string expected(trans_info.data);
+  EXPECT_EQ(expected, already_read + content);
+}
+
 void RunTransactionTestBase(HttpCache* cache,
                             const MockTransaction& trans_info,
                             const MockHttpRequest& request,
@@ -1327,8 +1342,7 @@
 
   MockTransaction transaction(kETagGET_Transaction);
   transaction.handler = PreserveRequestHeaders_Handler;
-  transaction.request_headers = "If-None-Match: \"foopy\"\r\n"
-                                EXTRA_HEADER;
+  transaction.request_headers = "If-None-Match: \"foopy\"\r\n" EXTRA_HEADER;
   AddMockTransaction(&transaction);
 
   RunTransactionTest(cache.http_cache(), transaction);
@@ -1349,7 +1363,7 @@
 
   for (int i = 0; i < kNumTransactions; ++i) {
     context_list.push_back(base::MakeUnique<Context>());
-    Context* c = context_list[i].get();
+    auto& c = context_list[i];
 
     c->result = cache.CreateTransaction(&c->trans);
     ASSERT_THAT(c->result, IsOk());
@@ -1360,16 +1374,19 @@
   }
 
   // All requests are waiting for the active entry.
-  for (int i = 0; i < kNumTransactions; ++i) {
-    Context* c = context_list[i].get();
-    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
   }
 
   // Allow all requests to move from the Create queue to the active entry.
   base::RunLoop().RunUntilIdle();
 
   // The first request should be a writer at this point, and the subsequent
-  // requests should be pending.
+  // requests should have passed the validation phase and waiting for the
+  // response to be written to the cache before they can read.
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(kNumTransactions - 1,
+            cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -1377,15 +1394,21 @@
 
   // All requests depend on the writer, and the writer is between Start and
   // Read, i.e. idle.
-  for (int i = 0; i < kNumTransactions; ++i) {
-    Context* c = context_list[i].get();
-    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
   }
 
   for (int i = 0; i < kNumTransactions; ++i) {
-    Context* c = context_list[i].get();
+    auto& c = context_list[i];
     if (c->result == ERR_IO_PENDING)
       c->result = c->callback.WaitForResult();
+
+    if (i > 0) {
+      EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+      EXPECT_EQ(kNumTransactions - i,
+                cache.GetCountReaders(kSimpleGET_Transaction.url));
+    }
+
     ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
   }
 
@@ -1396,6 +1419,871 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 }
 
+// Tests that we can have parallel validation on range requests.
+TEST(HttpCache, RangeGET_ParallelValidationNoMatch) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction transaction(kRangeGET_TransactionOK);
+  MockHttpRequest request(transaction);
+
+  std::vector<std::unique_ptr<Context>> context_list;
+  const int kNumTransactions = 5;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+  }
+
+  // All requests are waiting for the active entry.
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  // The first entry should have been doomed. Since the 1st transaction has not
+  // started writing to the cache, MockDiskEntry::CouldBeSparse() returns false
+  // leading to restarting the dooming the entry and restarting the second
+  // transaction.
+  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+
+  EXPECT_EQ(5, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(5, cache.disk_cache()->create_count());
+
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+  }
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    auto& c = context_list[i];
+    if (c->result == ERR_IO_PENDING)
+      c->result = c->callback.WaitForResult();
+
+    ReadAndVerifyTransaction(c->trans.get(), kRangeGET_TransactionOK);
+  }
+
+  EXPECT_EQ(5, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(5, cache.disk_cache()->create_count());
+}
+
+// Tests parallel validation on range requests with non-overlapping ranges.
+TEST(HttpCache, RangeGET_ParallelValidationDifferentRanges) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction transaction(kRangeGET_TransactionOK);
+
+  std::vector<std::unique_ptr<Context>> context_list;
+  const int kNumTransactions = 2;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+  }
+
+  // Let 1st transaction complete headers phase for ranges 40-49.
+  std::string first_read;
+  {
+    MockHttpRequest request(transaction);
+    auto& c = context_list[0];
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+    base::RunLoop().RunUntilIdle();
+
+    // Start writing to the cache so that MockDiskEntry::CouldBeSparse() returns
+    // true.
+    const int kBufferSize = 5;
+    scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
+    ReleaseBufferCompletionCallback cb(buffer.get());
+    c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
+    EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
+
+    std::string data_read(buffer->data(), kBufferSize);
+    first_read = data_read;
+
+    EXPECT_EQ(LOAD_STATE_READING_RESPONSE, c->trans->GetLoadState());
+  }
+
+  // 2nd transaction requests ranges 30-39.
+  {
+    transaction.request_headers = "Range: bytes = 30-39\r\n" EXTRA_HEADER;
+    MockHttpRequest request(transaction);
+    auto& c = context_list[1];
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+  }
+
+  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    auto& c = context_list[i];
+    if (c->result == ERR_IO_PENDING)
+      c->result = c->callback.WaitForResult();
+
+    if (i == 0) {
+      ReadRemainingAndVerifyTransaction(c->trans.get(), first_read,
+                                        transaction);
+      continue;
+    }
+
+    transaction.data = "rg: 30-39 ";
+    ReadAndVerifyTransaction(c->trans.get(), transaction);
+  }
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Fetch from the cache to check that ranges 30-49 have been successfully
+  // cached.
+  {
+    MockTransaction transaction(kRangeGET_TransactionOK);
+    transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
+    transaction.data = "rg: 30-39 rg: 40-49 ";
+    std::string headers;
+    RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+    Verify206Response(headers, 30, 49);
+  }
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests parallel validation on range requests with overlapping ranges.
+TEST(HttpCache, RangeGET_ParallelValidationOverlappingRanges) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction transaction(kRangeGET_TransactionOK);
+
+  std::vector<std::unique_ptr<Context>> context_list;
+  const int kNumTransactions = 2;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+  }
+
+  // Let 1st transaction complete headers phase for ranges 40-49.
+  std::string first_read;
+  {
+    MockHttpRequest request(transaction);
+    auto& c = context_list[0];
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+    base::RunLoop().RunUntilIdle();
+
+    // Start writing to the cache so that MockDiskEntry::CouldBeSparse() returns
+    // true.
+    const int kBufferSize = 5;
+    scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
+    ReleaseBufferCompletionCallback cb(buffer.get());
+    c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
+    EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
+
+    std::string data_read(buffer->data(), kBufferSize);
+    first_read = data_read;
+
+    EXPECT_EQ(LOAD_STATE_READING_RESPONSE, c->trans->GetLoadState());
+  }
+
+  // 2nd transaction requests ranges 30-49.
+  {
+    transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
+    MockHttpRequest request(transaction);
+    auto& c = context_list[1];
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+  }
+
+  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+
+  // Should have created another transaction for the uncached range.
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    auto& c = context_list[i];
+    if (c->result == ERR_IO_PENDING)
+      c->result = c->callback.WaitForResult();
+
+    if (i == 0) {
+      ReadRemainingAndVerifyTransaction(c->trans.get(), first_read,
+                                        transaction);
+      continue;
+    }
+
+    transaction.data = "rg: 30-39 rg: 40-49 ";
+    ReadAndVerifyTransaction(c->trans.get(), transaction);
+  }
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Fetch from the cache to check that ranges 30-49 have been successfully
+  // cached.
+  {
+    MockTransaction transaction(kRangeGET_TransactionOK);
+    transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
+    transaction.data = "rg: 30-39 rg: 40-49 ";
+    std::string headers;
+    RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+    Verify206Response(headers, 30, 49);
+  }
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Parallel validation results in 200.
+TEST(HttpCache, SimpleGET_ParallelValidationNoMatch) {
+  MockHttpCache cache;
+  MockHttpRequest request(kSimpleGET_Transaction);
+  request.load_flags |= LOAD_VALIDATE_CACHE;
+  std::vector<std::unique_ptr<Context>> context_list;
+  const int kNumTransactions = 5;
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+  }
+
+  // All requests are waiting for the active entry.
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  // The first request should be a writer at this point, and the subsequent
+  // requests should have passed the validation phase and created their own
+  // entries since none of them matched the headers of the earlier one.
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+
+  // Note that there are only 3 entries created and not 5 since every other
+  // transaction would have gone to the network.
+  EXPECT_EQ(5, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(3, cache.disk_cache()->create_count());
+
+  // All requests depend on the writer, and the writer is between Start and
+  // Read, i.e. idle.
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+  }
+
+  for (auto& context : context_list) {
+    if (context->result == ERR_IO_PENDING)
+      context->result = context->callback.WaitForResult();
+    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+  }
+
+  EXPECT_EQ(5, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(3, cache.disk_cache()->create_count());
+}
+
+// Tests that a GET followed by a DELETE results in DELETE immediately starting
+// the headers phase and the entry is doomed.
+TEST(HttpCache, SimpleGET_ParallelValidationDelete) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+  request.load_flags |= LOAD_VALIDATE_CACHE;
+
+  MockHttpRequest delete_request(kSimpleGET_Transaction);
+  delete_request.method = "DELETE";
+
+  std::vector<std::unique_ptr<Context>> context_list;
+  const int kNumTransactions = 2;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    MockHttpRequest* this_request = &request;
+    if (i == 1)
+      this_request = &delete_request;
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+    c->result = c->trans->Start(this_request, c->callback.callback(),
+                                NetLogWithSource());
+  }
+
+  // All requests are waiting for the active entry.
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  // The first request should be a writer at this point, and the subsequent
+  // request should have passed the validation phase and doomed the existing
+  // entry.
+  EXPECT_TRUE(
+      cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // All requests depend on the writer, and the writer is between Start and
+  // Read, i.e. idle.
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+  }
+
+  for (auto& context : context_list) {
+    if (context->result == ERR_IO_PENDING)
+      context->result = context->callback.WaitForResult();
+    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+  }
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that a transaction which is in validated queue can be destroyed without
+// any impact to other transactions.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelValidated) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  std::vector<std::unique_ptr<Context>> context_list;
+  const int kNumTransactions = 2;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  // The first request should be a writer at this point, and the subsequent
+  // requests should have completed validation.
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+  context_list[1].reset();
+
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+  // Complete the rest of the transactions.
+  for (auto& context : context_list) {
+    if (!context)
+      continue;
+    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+  }
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that a transaction which is in readers can be destroyed without
+// any impact to other transactions.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelReader) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  MockTransaction transaction(kSimpleGET_Transaction);
+  transaction.load_flags |= LOAD_VALIDATE_CACHE;
+  MockHttpRequest validate_request(transaction);
+
+  int kNumTransactions = 4;
+  std::vector<std::unique_ptr<Context>> context_list;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+
+    MockHttpRequest* this_request = &request;
+    if (i == 3) {
+      this_request = &validate_request;
+      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
+    }
+
+    c->result = c->trans->Start(this_request, c->callback.callback(),
+                                NetLogWithSource());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+  // Complete the response body.
+  auto& c = context_list[0];
+  ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+
+  // Rest of the transactions should move to readers.
+  EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(2, cache.GetCountReaders(kSimpleGET_Transaction.url));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+  // Add 2 new transactions.
+  kNumTransactions = 6;
+
+  for (int i = 4; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+  }
+
+  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
+
+  // Delete a reader.
+  context_list[1].reset();
+
+  // Deleting the reader did not impact any other transaction.
+  EXPECT_EQ(1, cache.GetCountReaders(kSimpleGET_Transaction.url));
+  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+  // Resume network start for headers_transaction. It will doom the entry as it
+  // will be a 200 and will go to network for the response body.
+  auto& context = context_list[3];
+  context->trans->ResumeNetworkStart();
+
+  // The pending transactions will be added to a new entry.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+
+  // Complete the rest of the transactions.
+  for (int i = 2; i < kNumTransactions; ++i) {
+    auto& c = context_list[i];
+    ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+  }
+
+  EXPECT_EQ(3, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+// Tests that a transaction is in validated queue and writer is destroyed
+// leading to restarting the validated transaction.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelWriter) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  MockTransaction transaction(kSimpleGET_Transaction);
+  transaction.load_flags |= LOAD_VALIDATE_CACHE;
+  MockHttpRequest validate_request(transaction);
+
+  const int kNumTransactions = 3;
+  std::vector<std::unique_ptr<Context>> context_list;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+
+    MockHttpRequest* this_request = &request;
+    if (i == 2) {
+      this_request = &validate_request;
+      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
+    }
+
+    c->result = c->trans->Start(this_request, c->callback.callback(),
+                                NetLogWithSource());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+  // Deleting the writer at this point will lead to destroying the entry and
+  // restarting the remaining transactions which will then create a new entry.
+  context_list[0].reset();
+
+  // Resume network start for headers_transaction. It should be restarted due to
+  // writer cancellation.
+  auto& c = context_list[2];
+  c->trans->ResumeNetworkStart();
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+  // Resume network start for the transaction the second time.
+  c->trans->ResumeNetworkStart();
+  base::RunLoop().RunUntilIdle();
+
+  // Headers transaction would have doomed the new entry created.
+  EXPECT_TRUE(
+      cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+
+  // Complete the rest of the transactions.
+  for (auto& context : context_list) {
+    if (!context)
+      continue;
+    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+  }
+
+  EXPECT_EQ(4, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+// Tests when a writer is destroyed mid-read and entry is marked as truncated,
+// it should lead to restarting the dependent transactions.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelWriterTruncateEntry) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction transaction(kSimpleGET_Transaction);
+  transaction.response_headers =
+      "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n"
+      "Content-Length: 22\n"
+      "Etag: \"foopy\"\n";
+  MockHttpRequest request(transaction);
+
+  const int kNumTransactions = 3;
+  std::vector<std::unique_ptr<Context>> context_list;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+  // Let first transaction read some bytes, so that entry can be marked as
+  // truncated.
+  {
+    auto& c = context_list[0];
+    const int kBufferSize = 5;
+    scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
+    ReleaseBufferCompletionCallback cb(buffer.get());
+    c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
+    EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
+  }
+
+  // Deleting the writer at this point will lead to restarting the remaining
+  // transactions which will then be able to read the truncated status of the
+  // entry.
+  context_list[0].reset();
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Complete the rest of the transactions.
+  for (auto& context : context_list) {
+    if (!context)
+      continue;
+    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+  }
+}
+
+// Tests that when StopCaching is invoked on a writer, dependent transactions
+// are restarted.
+TEST(HttpCache, SimpleGET_ParallelValidationStopCaching) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  MockTransaction transaction(kSimpleGET_Transaction);
+  transaction.load_flags |= LOAD_VALIDATE_CACHE;
+  MockHttpRequest validate_request(transaction);
+
+  const int kNumTransactions = 3;
+  std::vector<std::unique_ptr<Context>> context_list;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+
+    MockHttpRequest* this_request = &request;
+    if (i == 2) {
+      this_request = &validate_request;
+      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
+    }
+
+    c->result = c->trans->Start(this_request, c->callback.callback(),
+                                NetLogWithSource());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+  // Invoking StopCaching on the writer will lead to dooming the entry and
+  // restarting the remaining transactions which will then create a new entry.
+  context_list[0]->trans->StopCaching();
+
+  // Resume network start for headers_transaction. It should be restarted due to
+  // writer cancellation.
+  auto& c = context_list[2];
+  c->trans->ResumeNetworkStart();
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+  // Resume network start for the transaction the second time.
+  c->trans->ResumeNetworkStart();
+  base::RunLoop().RunUntilIdle();
+
+  // Headers transaction would have doomed the new entry created.
+  EXPECT_TRUE(
+      cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+
+  // Complete the rest of the transactions.
+  for (auto& context : context_list) {
+    if (!context)
+      continue;
+    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+  }
+
+  EXPECT_EQ(4, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+// Tests that a transaction is currently in headers phase and is destroyed
+// leading to destroying the entry.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelHeaders) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  const int kNumTransactions = 2;
+  std::vector<std::unique_ptr<Context>> context_list;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+
+    if (i == 0)
+      c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+  }
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Delete the headers transaction.
+  context_list[0].reset();
+
+  base::RunLoop().RunUntilIdle();
+
+  // Complete the rest of the transactions.
+  for (auto& context : context_list) {
+    if (!context)
+      continue;
+    ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+  }
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+// Similar to the above test, except here cache write fails and the
+// validated transactions should be restarted.
+TEST(HttpCache, SimpleGET_ParallelValidationFailWrite) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  const int kNumTransactions = 5;
+  std::vector<std::unique_ptr<Context>> context_list;
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    context_list.push_back(base::MakeUnique<Context>());
+    auto& c = context_list[i];
+
+    c->result = cache.CreateTransaction(&c->trans);
+    ASSERT_THAT(c->result, IsOk());
+    EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+    c->result =
+        c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+  }
+
+  // All requests are waiting for the active entry.
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+  }
+
+  // Allow all requests to move from the Create queue to the active entry.
+  base::RunLoop().RunUntilIdle();
+
+  // The first request should be a writer at this point, and the subsequent
+  // requests should have passed the validation phase and waiting for the
+  // response to be written to the cache before they can read.
+  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+  // All requests depend on the writer, and the writer is between Start and
+  // Read, i.e. idle.
+  for (auto& context : context_list) {
+    EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+  }
+
+  // The first request should be a writer at this point, and the subsequent
+  // requests should have passed the validation phase and waiting for the
+  // response to be written to the cache before they can read.
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Fail the request.
+  cache.disk_cache()->set_soft_failures(true);
+  // We have to open the entry again to propagate the failure flag.
+  disk_cache::Entry* en;
+  cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
+  en->Close();
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    auto& c = context_list[i];
+    if (c->result == ERR_IO_PENDING)
+      c->result = c->callback.WaitForResult();
+    if (i == 1) {
+      // The earlier entry must be destroyed and its disk entry doomed.
+      EXPECT_TRUE(
+          cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+    }
+    ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+  }
+
+  // Since validated transactions were restarted and new entry read/write
+  // operations would also fail, all requests would have gone to the network.
+  EXPECT_EQ(5, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(5, cache.disk_cache()->create_count());
+}
+
 // This is a test for http://code.google.com/p/chromium/issues/detail?id=4769.
 // If cancelling a request is racing with another request for the same resource
 // finishing, we have to make sure that we remove both transactions from the
@@ -1440,11 +2328,12 @@
   c->result = c->callback.WaitForResult();
   ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
 
-  // Now we have 2 active readers and two queued transactions.
-
+  // Now all transactions should be waiting for read to be invoked. Two readers
+  // are because of the load flags and remaining two transactions were converted
+  // to readers after skipping validation. Note that the remaining two went on
+  // to process the headers in parallel with readers present on the entry.
   EXPECT_EQ(LOAD_STATE_IDLE, context_list[2]->trans->GetLoadState());
-  EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE,
-            context_list[3]->trans->GetLoadState());
+  EXPECT_EQ(LOAD_STATE_IDLE, context_list[3]->trans->GetLoadState());
 
   c = context_list[1].get();
   ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
@@ -1504,12 +2393,16 @@
                                 NetLogWithSource());
   }
 
+  base::RunLoop().RunUntilIdle();
+
   // The first request should be a writer at this point, and the two subsequent
   // requests should be pending. The last request doomed the first entry.
 
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
 
-  // Cancel the first queued transaction.
+  // Cancel the second transaction. Note that this and the 3rd transactions
+  // would have completed their headers phase and would be waiting in the
+  // done_headers_queue when the 2nd transaction is cancelled.
   context_list[1].reset();
 
   for (int i = 0; i < kNumTransactions; ++i) {
@@ -1551,11 +2444,12 @@
   base::RunLoop().RunUntilIdle();
 
   // The first request should be a writer at this point, and the subsequent
-  // requests should be pending.
+  // requests should have completed validation. Since the validation does not
+  // result in a match, a new entry would be created.
 
-  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(3, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
-  EXPECT_EQ(1, cache.disk_cache()->create_count());
+  EXPECT_EQ(2, cache.disk_cache()->create_count());
 
   // Now, make sure that the second request asks for the entry not to be stored.
   request_handler.set_no_store(true);
@@ -1609,6 +2503,9 @@
     if (c->result == ERR_IO_PENDING)
       c->result = c->callback.WaitForResult();
     // Destroy only the first transaction.
+    // This should lead to all transactions to restart, even those that have
+    // validated themselves and were waiting for the writer transaction to
+    // complete writing to the cache.
     if (i == 0) {
       context_list[i].reset();
     }
@@ -2497,6 +3394,8 @@
 //     be returned.
 // (4) loads |kUrl| from cache only -- expects |cached_response_2| to be
 //     returned.
+// The entry will be created once and will be opened for the 3 subsequent
+// requests.
 static void ConditionalizedRequestUpdatesCacheHelper(
     const Response& net_response_1,
     const Response& net_response_2,
@@ -6069,13 +6968,14 @@
   rv = c->trans->Read(buf.get(), buf->size(), c->callback.callback());
   EXPECT_EQ(5, c->callback.GetResult(rv));
 
-  // Cancel the requests.
+  // Since |pending| is currently validating the already written headers
+  // it will be restarted as well.
   c.reset();
   pending.reset();
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
-  EXPECT_EQ(2, cache.disk_cache()->create_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   base::RunLoop().RunUntilIdle();
   RemoveMockTransaction(&transaction);
@@ -6800,46 +7700,6 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 }
 
-// Tests that if a metadata writer transaction hits cache lock timeout, it will
-// error out.
-TEST(HttpCache, WriteMetadata_CacheLockTimeout) {
-  MockHttpCache cache;
-
-  // Write to the cache
-  HttpResponseInfo response;
-  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
-                                     &response);
-  EXPECT_FALSE(response.metadata.get());
-
-  MockHttpRequest request(kSimpleGET_Transaction);
-  Context c1;
-  ASSERT_THAT(cache.CreateTransaction(&c1.trans), IsOk());
-  ASSERT_EQ(ERR_IO_PENDING, c1.trans->Start(&request, c1.callback.callback(),
-                                            NetLogWithSource()));
-
-  cache.SimulateCacheLockTimeout();
-
-  // Write meta data to the same entry.
-  scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(50));
-  memset(buf->data(), 0, buf->size());
-  base::strlcpy(buf->data(), "Hi there", buf->size());
-  cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
-                                    DEFAULT_PRIORITY, response.response_time,
-                                    buf.get(), buf->size());
-
-  // Release the buffer before the operation takes place.
-  buf = NULL;
-
-  // Makes sure we finish pending operations.
-  base::RunLoop().RunUntilIdle();
-
-  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
-                                     &response);
-
-  // The writer transaction should fail due to cache lock timeout.
-  ASSERT_FALSE(response.metadata.get());
-}
-
 // Tests that we ignore VARY checks when writing metadata since the request
 // headers for the WriteMetadata transaction are made up.
 TEST(HttpCache, WriteMetadata_IgnoreVary) {
@@ -7167,10 +8027,6 @@
     EXPECT_THAT(callback.GetResult(rv), IsOk());
 
     trans->StopCaching();
-
-    scoped_refptr<IOBuffer> buf(new IOBuffer(256));
-    rv = trans->Read(buf.get(), 10, callback.callback());
-    EXPECT_EQ(callback.GetResult(rv), 10);
   }
   RemoveMockTransaction(&mock_transaction);
 
@@ -7219,7 +8075,9 @@
   }
 
   // Verify that the entry is marked as incomplete.
-  VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
+  // VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
+  // Verify that the entry is doomed.
+  cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url);
 }
 
 // Tests that we handle truncated enries when StopCaching is called.
diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h
index eff45d1..17e2945 100644
--- a/net/http/http_transaction.h
+++ b/net/http/http_transaction.h
@@ -52,9 +52,10 @@
 
   // Starts the HTTP transaction (i.e., sends the HTTP request).
   //
-  // The consumer should ensure that request_info points to a valid value till
-  // final response headers are received; after that point, the HttpTransaction
-  // will not access |*request_info| and it may be deleted.
+  // TODO(crbug.com/723786) The consumer should ensure that request_info points
+  // to a valid value till final response headers are received; after that
+  // point, the HttpTransaction will not access |*request_info| and it may be
+  // deleted.
   //
   // Returns OK if the transaction could be started synchronously, which means
   // that the request was served from the cache.  ERR_IO_PENDING is returned to
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index a5b5746..2d728cf 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -409,13 +409,6 @@
   if (!t)
     return ERR_FAILED;
 
-  if (!before_network_start_callback_.is_null()) {
-    bool defer = false;
-    before_network_start_callback_.Run(&defer);
-    if (defer)
-      return net::ERR_IO_PENDING;
-  }
-
   test_mode_ = t->test_mode;
 
   // Return immediately if we're returning an error.
@@ -466,6 +459,16 @@
   if (request_->load_flags & LOAD_PREFETCH)
     response_.unused_since_prefetch = true;
 
+  // Pause and resume.
+  if (!before_network_start_callback_.is_null()) {
+    bool defer = false;
+    before_network_start_callback_.Run(&defer);
+    if (defer) {
+      resume_start_callback_ = callback;
+      return net::ERR_IO_PENDING;
+    }
+  }
+
   if (test_mode_ & TEST_MODE_SYNC_NET_START)
     return OK;
 
@@ -482,8 +485,9 @@
     const BeforeHeadersSentCallback& callback) {}
 
 int MockNetworkTransaction::ResumeNetworkStart() {
-  // Should not get here.
-  return ERR_FAILED;
+  DCHECK(!resume_start_callback_.is_null());
+  CallbackLater(resume_start_callback_, OK);
+  return ERR_IO_PENDING;
 }
 
 void MockNetworkTransaction::GetConnectionAttempts(
diff --git a/net/http/http_transaction_test_util.h b/net/http/http_transaction_test_util.h
index e936ac9d..96afead 100644
--- a/net/http/http_transaction_test_util.h
+++ b/net/http/http_transaction_test_util.h
@@ -280,6 +280,8 @@
 
   bool done_reading_called_;
 
+  CompletionCallback resume_start_callback_;  // used for pause and restart.
+
   base::WeakPtrFactory<MockNetworkTransaction> weak_factory_;
 
 };
diff --git a/net/http/mock_http_cache.cc b/net/http/mock_http_cache.cc
index 47125a30..e676122 100644
--- a/net/http/mock_http_cache.cc
+++ b/net/http/mock_http_cache.cc
@@ -534,6 +534,13 @@
       FROM_HERE, base::Bind(&CallbackForwader, callback, result));
 }
 
+bool MockDiskCache::IsDiskEntryDoomed(const std::string& key) {
+  auto it = entries_.find(key);
+  if (it == entries_.end())
+    return false;
+  return it->second->is_doomed();
+}
+
 //-----------------------------------------------------------------------------
 
 int MockBackendFactory::CreateBackend(
@@ -647,6 +654,41 @@
   g_test_mode = test_mode;
 }
 
+bool MockHttpCache::IsWriterPresent(const std::string& key) {
+  HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
+  if (entry)
+    return entry->writer;
+  return false;
+}
+
+bool MockHttpCache::IsHeadersTransactionPresent(const std::string& key) {
+  HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
+  if (entry)
+    return entry->headers_transaction;
+  return false;
+}
+
+int MockHttpCache::GetCountReaders(const std::string& key) {
+  HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
+  if (entry)
+    return entry->readers.size();
+  return false;
+}
+
+int MockHttpCache::GetCountAddToEntryQueue(const std::string& key) {
+  HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
+  if (entry)
+    return entry->add_to_entry_queue.size();
+  return false;
+}
+
+int MockHttpCache::GetCountDoneHeadersQueue(const std::string& key) {
+  HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
+  if (entry)
+    return entry->done_headers_queue.size();
+  return false;
+}
+
 //-----------------------------------------------------------------------------
 
 int MockDiskCacheNoCB::CreateEntry(const std::string& key,
diff --git a/net/http/mock_http_cache.h b/net/http/mock_http_cache.h
index a24200b..7718f351 100644
--- a/net/http/mock_http_cache.h
+++ b/net/http/mock_http_cache.h
@@ -158,6 +158,8 @@
 
   void ReleaseAll();
 
+  bool IsDiskEntryDoomed(const std::string& key);
+
  private:
   using EntryMap = std::unordered_map<std::string, MockDiskEntry*>;
   class NotImplementedIterator;
@@ -234,6 +236,14 @@
   // the test! (by setting test_mode to zero).
   static void SetTestMode(int test_mode);
 
+  // Functions to test the state of ActiveEntry.
+
+  bool IsWriterPresent(const std::string& key);
+  bool IsHeadersTransactionPresent(const std::string& key);
+  int GetCountReaders(const std::string& key);
+  int GetCountAddToEntryQueue(const std::string& key);
+  int GetCountDoneHeadersQueue(const std::string& key);
+
  private:
   HttpCache http_cache_;
 };
diff --git a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
index 72f03e6..9ebe2ba 100644
--- a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
+++ b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
@@ -76,14 +76,13 @@
     /** Bind the service that will run the native server object.
      *
      *  @param context The context to use to bind the service. This will also be used to unbind
-     #          the service at server destruction time.
+     *          the service at server destruction time.
      */
     public void initializeNative(Context context) throws InterruptedException {
         mContext = context;
 
         Intent intent = new Intent(EMBEDDED_TEST_SERVER_SERVICE);
-        intent.setClassName(
-                "org.chromium.net.test.support", "org.chromium.net.test.EmbeddedTestServerService");
+        setIntentClassName(intent);
         if (!mContext.bindService(intent, mConn, Context.BIND_AUTO_CREATE)) {
             throw new EmbeddedTestServerFailure(
                     "Unable to bind to the EmbeddedTestServer service.");
@@ -109,6 +108,15 @@
         }
     }
 
+    /** Set intent package and class name that will pass to the service.
+     *
+     *  @param intent The intent to use to pass into the service.
+     */
+    protected void setIntentClassName(Intent intent) {
+        intent.setClassName(
+                "org.chromium.net.test.support", "org.chromium.net.test.EmbeddedTestServerService");
+    }
+
     /** Add the default handlers and serve files from the provided directory relative to the
      *  external storage directory.
      *
@@ -233,6 +241,20 @@
                 + "the instantiation will hang forever waiting for tasks to post to UI thread",
                 Looper.getMainLooper(), Looper.myLooper());
         EmbeddedTestServer server = new EmbeddedTestServer();
+        return initializeAndStartServer(server, context);
+    }
+
+    /** Initialize a server with the default handlers.
+     *
+     *  This handles native object initialization, server configuration, and server initialization.
+     *  On returning, the server is ready for use.
+     *
+     *  @param server The server instance that will be initialized.
+     *  @param context The context in which the server will run.
+     *  @return The created server.
+     */
+    public static <T extends EmbeddedTestServer> T initializeAndStartServer(
+            T server, Context context) throws InterruptedException {
         server.initializeNative(context);
         server.addDefaultHandlers("");
         if (!server.start()) {
diff --git a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java
index defdd47..4630b44 100644
--- a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java
+++ b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java
@@ -122,6 +122,21 @@
         });
     }
 
+    /** Register multiple request handlers.
+     *  Handlers must be registered before starting the server.
+     *
+     *  @param handler The pointer of handler to be registered.
+     */
+    public void registerRequestHandler(final long handler) {
+        runOnHandlerThread(new Callable<Void>() {
+            @Override
+            public Void call() {
+                nativeRegisterRequestHandler(mNativeEmbeddedTestServer, handler);
+                return null;
+            }
+        });
+    }
+
     /** Serve files from the provided directory.
      *
      *  @param directoryPath The path of the directory from which files should be served.
@@ -213,9 +228,11 @@
     private native void nativeDestroy(long nativeEmbeddedTestServerAndroid);
     private native boolean nativeStart(long nativeEmbeddedTestServerAndroid);
     private native boolean nativeShutdownAndWaitUntilComplete(long nativeEmbeddedTestServerAndroid);
-    private native String nativeGetURL(long nativeEmbeddedTestServerAndroid, String relativeUrl);
     private native void nativeAddDefaultHandlers(
             long nativeEmbeddedTestServerAndroid, String directoryPath);
+    private native void nativeRegisterRequestHandler(
+            long nativeEmbeddedTestServerAndroid, long handler);
+    private native String nativeGetURL(long nativeEmbeddedTestServerAndroid, String relativeUrl);
     private native void nativeServeFilesFromDirectory(
             long nativeEmbeddedTestServerAndroid, String directoryPath);
 }
diff --git a/net/test/android/javatests/src/org/chromium/net/test/IEmbeddedTestServerInterface.aidl b/net/test/android/javatests/src/org/chromium/net/test/IEmbeddedTestServerInterface.aidl
index df0c3d18c..9cb0488 100644
--- a/net/test/android/javatests/src/org/chromium/net/test/IEmbeddedTestServerInterface.aidl
+++ b/net/test/android/javatests/src/org/chromium/net/test/IEmbeddedTestServerInterface.aidl
@@ -2,4 +2,4 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-interface org.chromium.net.test.IEmbedddedTestServerImpl;
+interface org.chromium.net.test.IEmbeddedTestServerImpl;
diff --git a/net/test/android/javatests/src/org/chromium/net/test/OWNERS b/net/test/android/javatests/src/org/chromium/net/test/OWNERS
new file mode 100644
index 0000000..89442abe
--- /dev/null
+++ b/net/test/android/javatests/src/org/chromium/net/test/OWNERS
@@ -0,0 +1,2 @@
+per-file *.aidl=set noparent
+per-file *.aidl=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/net/test/embedded_test_server/android/embedded_test_server_android.cc b/net/test/embedded_test_server/android/embedded_test_server_android.cc
index 0b25220c..fb49200 100644
--- a/net/test/embedded_test_server/android/embedded_test_server_android.cc
+++ b/net/test/embedded_test_server/android/embedded_test_server_android.cc
@@ -4,6 +4,7 @@
 
 #include "net/test/embedded_test_server/android/embedded_test_server_android.h"
 
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/bind.h"
@@ -14,6 +15,7 @@
 
 using base::android::JavaParamRef;
 using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
 
 namespace net {
 namespace test_server {
@@ -42,7 +44,7 @@
   return test_server_.ShutdownAndWaitUntilComplete();
 }
 
-base::android::ScopedJavaLocalRef<jstring> EmbeddedTestServerAndroid::GetURL(
+ScopedJavaLocalRef<jstring> EmbeddedTestServerAndroid::GetURL(
     JNIEnv* env,
     const JavaParamRef<jobject>& jobj,
     const JavaParamRef<jstring>& jrelative_url) const {
@@ -60,6 +62,17 @@
   test_server_.AddDefaultHandlers(directory);
 }
 
+typedef std::unique_ptr<HttpResponse> (*HandleRequestPtr)(
+    const HttpRequest& request);
+
+void EmbeddedTestServerAndroid::RegisterRequestHandler(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jobj,
+    jlong handler) {
+  HandleRequestPtr handler_ptr = reinterpret_cast<HandleRequestPtr>(handler);
+  test_server_.RegisterRequestHandler(base::Bind(handler_ptr));
+}
+
 void EmbeddedTestServerAndroid::ServeFilesFromDirectory(
     JNIEnv* env,
     const JavaParamRef<jobject>& jobj,
diff --git a/net/test/embedded_test_server/android/embedded_test_server_android.h b/net/test/embedded_test_server/android/embedded_test_server_android.h
index 1b730829..a0e7f25 100644
--- a/net/test/embedded_test_server/android/embedded_test_server_android.h
+++ b/net/test/embedded_test_server/android/embedded_test_server_android.h
@@ -42,6 +42,10 @@
       const base::android::JavaParamRef<jobject>& jobj,
       const base::android::JavaParamRef<jstring>& jdirectory_path);
 
+  void RegisterRequestHandler(JNIEnv* jenv,
+                              const base::android::JavaParamRef<jobject>& jobj,
+                              jlong handler);
+
   void ServeFilesFromDirectory(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jobj,
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 148c3e8..535e940e 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -648,7 +648,7 @@
   response_info_.was_cached = false;
 
   GURL referrer_url(referrer_);
-  if (referrer_url != URLRequestJob::ComputeReferrerForRedirect(
+  if (referrer_url != URLRequestJob::ComputeReferrerForPolicy(
                           referrer_policy_, referrer_url, url())) {
     if (!network_delegate_ ||
         !network_delegate_->CancelURLRequestWithPolicyViolatingReferrerHeader(
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index e5b06ad0..0dc5cac 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -87,25 +87,30 @@
   // setting the initial Referer, and the ReferrerPolicy only controls
   // what happens to the Referer while following redirects.
   enum ReferrerPolicy {
-    // Clear the referrer header if the protocol changes from HTTPS to
-    // HTTP. This is the default behavior of URLRequest.
+    // Clear the referrer header if the header value is HTTPS but the request
+    // destination is HTTP. This is the default behavior of URLRequest.
     CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
-    // A slight variant on
-    // CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE: If the
-    // request downgrades from HTTPS to HTTP, the referrer will be
-    // cleared. If the request transitions cross-origin (but does not
-    // downgrade), the referrer's granularity will be reduced (currently
-    // stripped down to an origin rather than a full URL). Same-origin
-    // requests will send the full referrer.
+    // A slight variant on CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+    // If the request destination is HTTP, an HTTPS referrer will be cleared. If
+    // the request's destination is cross-origin with the referrer (but does not
+    // downgrade), the referrer's granularity will be stripped down to an origin
+    // rather than a full URL. Same-origin requests will send the full referrer.
     REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-    // Strip the referrer down to an origin upon cross-origin navigation.
+    // Strip the referrer down to an origin when the origin of the referrer is
+    // different from the destination's origin.
     ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
     // Never change the referrer.
     NEVER_CLEAR_REFERRER,
     // Strip the referrer down to the origin regardless of the redirect
     // location.
     ORIGIN,
-    // Always clear the referrer regardless of the redirect location.
+    // Clear the referrer when the request's referrer is cross-origin with
+    // the request's destination.
+    CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN,
+    // Strip the referrer down to the origin, but clear it entirely if the
+    // referrer value is HTTPS and the destination is HTTP.
+    ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+    // Always clear the referrer regardless of the request destination.
     NO_REFERRER,
     MAX_REFERRER_POLICY
   };
@@ -314,16 +319,22 @@
   const std::string& method() const { return method_; }
   void set_method(const std::string& method);
 
-  // The referrer URL for the request.  This header may actually be suppressed
-  // from the underlying network request for security reasons (e.g., a HTTPS
-  // URL will not be sent as the referrer for a HTTP request).  The referrer
-  // may only be changed before Start() is called.
+  // The referrer URL for the request
   const std::string& referrer() const { return referrer_; }
-  // Referrer is sanitized to remove URL fragment, user name and password.
+  // Sets the referrer URL for the request. Can only be changed before Start()
+  // is called. |referrer| is sanitized to remove URL fragment, user name and
+  // password. If a referrer policy is set via set_referrer_policy(), then
+  // |referrer| should obey the policy; if it doesn't, it will be cleared when
+  // the request is started. The referrer URL may be suppressed or changed
+  // during the course of the request, for example because of a referrer policy
+  // set with set_referrer_policy().
   void SetReferrer(const std::string& referrer);
 
   // The referrer policy to apply when updating the referrer during redirects.
-  // The referrer policy may only be changed before Start() is called.
+  // The referrer policy may only be changed before Start() is called. Any
+  // referrer set via SetReferrer() is expected to obey the policy set via
+  // set_referrer_policy(); otherwise the referrer will be cleared when the
+  // request is started.
   ReferrerPolicy referrer_policy() const { return referrer_policy_; }
   void set_referrer_policy(ReferrerPolicy referrer_policy);
 
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index 564ce25..e3a17d7 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -67,6 +67,14 @@
     "Accept-Encoding: gzip, deflate\r\n"
     "Accept-Language: en-us,fr\r\n\r\n";
 
+const char kSimpleHeadMockWrite[] =
+    "HEAD / HTTP/1.1\r\n"
+    "Host: www.example.com\r\n"
+    "Connection: keep-alive\r\n"
+    "User-Agent:\r\n"
+    "Accept-Encoding: gzip, deflate\r\n"
+    "Accept-Language: en-us,fr\r\n\r\n";
+
 // Inherit from URLRequestHttpJob to expose the priority and some
 // other hidden functions.
 class TestURLRequestHttpJob : public URLRequestHttpJob {
@@ -303,6 +311,132 @@
             network_delegate_.total_network_bytes_received());
 }
 
+// Tests a successful HEAD request.
+TEST_F(URLRequestHttpJobWithMockSocketsTest, TestSuccessfulHead) {
+  MockWrite writes[] = {MockWrite(kSimpleHeadMockWrite)};
+  MockRead reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Content-Length: 0\r\n\r\n")};
+
+  StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
+                                       arraysize(writes));
+  socket_factory_.AddSocketDataProvider(&socket_data);
+
+  TestDelegate delegate;
+  std::unique_ptr<URLRequest> request = context_->CreateRequest(
+      GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate);
+
+  request->set_method("HEAD");
+  request->Start();
+  ASSERT_TRUE(request->is_pending());
+  base::RunLoop().Run();
+
+  EXPECT_THAT(delegate.request_status(), IsOk());
+  EXPECT_EQ(0, request->received_response_content_length());
+  EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)),
+            request->GetTotalSentBytes());
+  EXPECT_EQ(CountReadBytes(reads, arraysize(reads)),
+            request->GetTotalReceivedBytes());
+  EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)),
+            network_delegate_.total_network_bytes_sent());
+  EXPECT_EQ(CountReadBytes(reads, arraysize(reads)),
+            network_delegate_.total_network_bytes_received());
+}
+
+// Similar to above test but tests that even if response body is there in the
+// HEAD response stream, it should not be read due to HttpStreamParser's logic.
+TEST_F(URLRequestHttpJobWithMockSocketsTest, TestSuccessfulHeadWithContent) {
+  MockWrite writes[] = {MockWrite(kSimpleHeadMockWrite)};
+  MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
+                               "Content-Length: 12\r\n\r\n"),
+                      MockRead("Test Content")};
+
+  StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
+                                       arraysize(writes));
+  socket_factory_.AddSocketDataProvider(&socket_data);
+
+  TestDelegate delegate;
+  std::unique_ptr<URLRequest> request = context_->CreateRequest(
+      GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate);
+
+  request->set_method("HEAD");
+  request->Start();
+  ASSERT_TRUE(request->is_pending());
+  base::RunLoop().Run();
+
+  EXPECT_THAT(delegate.request_status(), IsOk());
+  EXPECT_EQ(0, request->received_response_content_length());
+  EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)),
+            request->GetTotalSentBytes());
+  EXPECT_EQ(CountReadBytes(reads, arraysize(reads)) - 12,
+            request->GetTotalReceivedBytes());
+  EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)),
+            network_delegate_.total_network_bytes_sent());
+  EXPECT_EQ(CountReadBytes(reads, arraysize(reads)) - 12,
+            network_delegate_.total_network_bytes_received());
+}
+
+TEST_F(URLRequestHttpJobWithMockSocketsTest, TestSuccessfulCachedHeadRequest) {
+  // Cache the response.
+  {
+    MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
+    MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
+                                 "Content-Length: 12\r\n\r\n"),
+                        MockRead("Test Content")};
+
+    StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
+                                         arraysize(writes));
+    socket_factory_.AddSocketDataProvider(&socket_data);
+
+    TestDelegate delegate;
+    std::unique_ptr<URLRequest> request = context_->CreateRequest(
+        GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate);
+
+    request->Start();
+    ASSERT_TRUE(request->is_pending());
+    base::RunLoop().Run();
+
+    EXPECT_THAT(delegate.request_status(), IsOk());
+    EXPECT_EQ(12, request->received_response_content_length());
+    EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)),
+              request->GetTotalSentBytes());
+    EXPECT_EQ(CountReadBytes(reads, arraysize(reads)),
+              request->GetTotalReceivedBytes());
+    EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)),
+              network_delegate_.total_network_bytes_sent());
+    EXPECT_EQ(CountReadBytes(reads, arraysize(reads)),
+              network_delegate_.total_network_bytes_received());
+  }
+
+  // Send a HEAD request for the cached response.
+  {
+    MockWrite writes[] = {MockWrite(kSimpleHeadMockWrite)};
+    MockRead reads[] = {
+        MockRead("HTTP/1.1 200 OK\r\n"
+                 "Content-Length: 0\r\n\r\n")};
+
+    StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
+                                         arraysize(writes));
+    socket_factory_.AddSocketDataProvider(&socket_data);
+
+    TestDelegate delegate;
+    std::unique_ptr<URLRequest> request = context_->CreateRequest(
+        GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate);
+
+    // Use the cached version.
+    request->SetLoadFlags(LOAD_SKIP_CACHE_VALIDATION);
+    request->set_method("HEAD");
+    request->Start();
+    ASSERT_TRUE(request->is_pending());
+    base::RunLoop().Run();
+
+    EXPECT_THAT(delegate.request_status(), IsOk());
+    EXPECT_EQ(0, request->received_response_content_length());
+    EXPECT_EQ(0, request->GetTotalSentBytes());
+    EXPECT_EQ(0, request->GetTotalReceivedBytes());
+  }
+}
+
 TEST_F(URLRequestHttpJobWithMockSocketsTest,
        TestContentLengthSuccessfulHttp09Request) {
   MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 8b5c002..e208252 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -87,6 +87,8 @@
   UMA_HISTOGRAM_BOOLEAN("Net.URLRequest.ReferrerPolicyHeaderPresentOnRedirect",
                         !policy_tokens.empty());
 
+  // Per https://w3c.github.io/webappsec-referrer-policy/#unknown-policy-values,
+  // use the last recognized policy value, and ignore unknown policies.
   for (const auto& token : policy_tokens) {
     if (base::CompareCaseInsensitiveASCII(token, "no-referrer") == 0) {
       new_policy = URLRequest::NO_REFERRER;
@@ -115,6 +117,24 @@
       new_policy = URLRequest::NEVER_CLEAR_REFERRER;
       continue;
     }
+
+    if (base::CompareCaseInsensitiveASCII(token, "same-origin") == 0) {
+      new_policy = URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN;
+      continue;
+    }
+
+    if (base::CompareCaseInsensitiveASCII(token, "strict-origin") == 0) {
+      new_policy =
+          URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
+      continue;
+    }
+
+    if (base::CompareCaseInsensitiveASCII(
+            token, "strict-origin-when-cross-origin") == 0) {
+      new_policy =
+          URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
+      continue;
+    }
   }
   return new_policy;
 }
@@ -362,16 +382,14 @@
 }
 
 // static
-GURL URLRequestJob::ComputeReferrerForRedirect(
-    URLRequest::ReferrerPolicy policy,
-    const GURL& original_referrer,
-    const GURL& redirect_destination) {
+GURL URLRequestJob::ComputeReferrerForPolicy(URLRequest::ReferrerPolicy policy,
+                                             const GURL& original_referrer,
+                                             const GURL& destination) {
   bool secure_referrer_but_insecure_destination =
       original_referrer.SchemeIsCryptographic() &&
-      !redirect_destination.SchemeIsCryptographic();
+      !destination.SchemeIsCryptographic();
   url::Origin referrer_origin(original_referrer);
-  bool same_origin =
-      referrer_origin.IsSameOriginWith(url::Origin(redirect_destination));
+  bool same_origin = referrer_origin.IsSameOriginWith(url::Origin(destination));
   switch (policy) {
     case URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
       return secure_referrer_but_insecure_destination ? GURL()
@@ -393,6 +411,14 @@
       return original_referrer;
     case URLRequest::ORIGIN:
       return referrer_origin.GetURL();
+    case URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN:
+      if (same_origin)
+        return original_referrer;
+      return GURL();
+    case URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
+      if (secure_referrer_but_insecure_destination)
+        return GURL();
+      return referrer_origin.GetURL();
     case URLRequest::NO_REFERRER:
       return GURL();
     case URLRequest::MAX_REFERRER_POLICY:
@@ -839,9 +865,9 @@
 
   // Alter the referrer if redirecting cross-origin (especially HTTP->HTTPS).
   redirect_info.new_referrer =
-      ComputeReferrerForRedirect(redirect_info.new_referrer_policy,
-                                 GURL(request_->referrer()),
-                                 redirect_info.new_url)
+      ComputeReferrerForPolicy(redirect_info.new_referrer_policy,
+                               GURL(request_->referrer()),
+                               redirect_info.new_url)
           .spec();
 
   std::string include_referer;
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index 913b879..52418e31 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -228,11 +228,11 @@
   // has failed or the response headers have been received.
   virtual void GetConnectionAttempts(ConnectionAttempts* out) const;
 
-  // Given |policy|, |referrer|, and |redirect_destination|, returns the
+  // Given |policy|, |referrer|, and |destination|, returns the
   // referrer URL mandated by |request|'s referrer policy.
-  static GURL ComputeReferrerForRedirect(URLRequest::ReferrerPolicy policy,
-                                         const GURL& original_referrer,
-                                         const GURL& redirect_destination);
+  static GURL ComputeReferrerForPolicy(URLRequest::ReferrerPolicy policy,
+                                       const GURL& original_referrer,
+                                       const GURL& destination);
 
  protected:
   // Notifies the job that a certificate is requested.
diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc
index cd40308..4546552e 100644
--- a/net/url_request/url_request_job_unittest.cc
+++ b/net/url_request/url_request_job_unittest.cc
@@ -388,6 +388,97 @@
            ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN /* expected final policy */,
        "https://foo.test/" /* expected final referrer */},
 
+      // If a redirect serves 'Referrer-Policy: same-origin', then the referrer
+      // should be untouched for a same-origin redirect,
+      {"https://foo.test/one" /* original url */,
+       "https://foo.test/referrer" /* original referrer */,
+       "Location: https://foo.test/two\n"
+       "Referrer-Policy: same-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN /* final policy */
+       ,
+       "https://foo.test/referrer" /* expected final referrer */},
+
+      // ... but should be cleared for a cross-origin redirect.
+      {"https://foo.test/one" /* original url */,
+       "https://foo.test/referrer" /* original referrer */,
+       "Location: https://bar.test/two\n"
+       "Referrer-Policy: same-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN,
+       "" /* expected final referrer */},
+
+      // If a redirect serves 'Referrer-Policy: strict-origin', then the
+      // referrer should be the origin only for a cross-origin non-downgrading
+      // redirect,
+      {"https://foo.test/one" /* original url */,
+       "https://foo.test/referrer" /* original referrer */,
+       "Location: https://bar.test/two\n"
+       "Referrer-Policy: strict-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+       "https://foo.test/" /* expected final referrer */},
+      {"http://foo.test/one" /* original url */,
+       "http://foo.test/referrer" /* original referrer */,
+       "Location: http://bar.test/two\n"
+       "Referrer-Policy: strict-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+       "http://foo.test/" /* expected final referrer */},
+
+      // ... but should be cleared for a downgrading redirect.
+      {"https://foo.test/one" /* original url */,
+       "https://foo.test/referrer" /* original referrer */,
+       "Location: http://foo.test/two\n"
+       "Referrer-Policy: strict-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+       "" /* expected final referrer */},
+
+      // If a redirect serves 'Referrer-Policy:
+      // strict-origin-when-cross-origin', then the referrer should be preserved
+      // for a same-origin redirect,
+      {"https://foo.test/one" /* original url */,
+       "https://foo.test/referrer" /* original referrer */,
+       "Location: https://foo.test/two\n"
+       "Referrer-Policy: strict-origin-when-cross-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+       "https://foo.test/referrer" /* expected final referrer */},
+      {"http://foo.test/one" /* original url */,
+       "http://foo.test/referrer" /* original referrer */,
+       "Location: http://foo.test/two\n"
+       "Referrer-Policy: strict-origin-when-cross-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+       "http://foo.test/referrer" /* expected final referrer */},
+
+      // ... but should be stripped to the origin for a cross-origin
+      // non-downgrading redirect,
+      {"https://foo.test/one" /* original url */,
+       "https://foo.test/referrer" /* original referrer */,
+       "Location: https://bar.test/two\n"
+       "Referrer-Policy: strict-origin-when-cross-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+       "https://foo.test/" /* expected final referrer */},
+      {"http://foo.test/one" /* original url */,
+       "http://foo.test/referrer" /* original referrer */,
+       "Location: http://bar.test/two\n"
+       "Referrer-Policy: strict-origin-when-cross-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+       "http://foo.test/" /* expected final referrer */},
+
+      // ... and should be cleared for a downgrading redirect.
+      {"https://foo.test/one" /* original url */,
+       "https://foo.test/referrer" /* original referrer */,
+       "Location: http://foo.test/two\n"
+       "Referrer-Policy: strict-origin-when-cross-origin\n",
+       URLRequest::NEVER_CLEAR_REFERRER /* original policy */,
+       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+       "" /* expected final referrer */},
+
       // If a redirect serves 'Referrer-Policy: unsafe-url', then the
       // referrer should remain, even if originally set to clear on
       // downgrade.
diff --git a/net/url_request/url_request_quic_unittest.cc b/net/url_request/url_request_quic_unittest.cc
index e28e714..a6c2d1a 100644
--- a/net/url_request/url_request_quic_unittest.cc
+++ b/net/url_request/url_request_quic_unittest.cc
@@ -306,15 +306,14 @@
 
   EXPECT_TRUE(entries[0].GetStringValue("push_url", &value));
   EXPECT_EQ(value, push_url_1);
-  // No net error code for this lookup transaction, the push is found.
-  EXPECT_FALSE(entries[1].GetIntegerValue("net_error", &net_error));
-
-  EXPECT_TRUE(entries[2].GetStringValue("push_url", &value));
+  EXPECT_TRUE(entries[1].GetStringValue("push_url", &value));
   EXPECT_EQ(value, push_url_2);
   // Net error code -400 is found for this lookup transaction, the push is not
   // found in the cache.
-  EXPECT_TRUE(entries[3].GetIntegerValue("net_error", &net_error));
+  EXPECT_TRUE(entries[2].GetIntegerValue("net_error", &net_error));
   EXPECT_EQ(net_error, -400);
+  // No net error code for this lookup transaction, the push is found.
+  EXPECT_FALSE(entries[3].GetIntegerValue("net_error", &net_error));
 
   // Verify the reset error count received on the server side.
   EXPECT_LE(1u, GetRstErrorCountReceivedByServer(QUIC_STREAM_CANCELLED));
@@ -393,11 +392,13 @@
 
   EXPECT_TRUE(entries[0].GetStringValue("push_url", &value));
   EXPECT_EQ(value, push_url_1);
-  // No net error code for this lookup transaction, the push is found.
-  EXPECT_FALSE(entries[1].GetIntegerValue("net_error", &net_error));
 
-  EXPECT_TRUE(entries[2].GetStringValue("push_url", &value));
+  EXPECT_TRUE(entries[1].GetStringValue("push_url", &value));
   EXPECT_EQ(value, push_url_2);
+
+  // No net error code for this lookup transaction, the push is found.
+  EXPECT_FALSE(entries[2].GetIntegerValue("net_error", &net_error));
+
   // No net error code for this lookup transaction, the push is found.
   EXPECT_FALSE(entries[3].GetIntegerValue("net_error", &net_error));
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index ea3a9d44..9494281 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -4362,14 +4362,14 @@
         url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->Start();
 
+    base::RunLoop().Run();
+
     {
       HttpRequestHeaders headers;
       EXPECT_TRUE(r->GetFullRequestHeaders(&headers));
-      EXPECT_FALSE(headers.HasHeader("Authorization"));
+      EXPECT_TRUE(headers.HasHeader("Authorization"));
     }
 
-    base::RunLoop().Run();
-
     EXPECT_EQ(OK, d.request_status());
     EXPECT_EQ(200, r->GetResponseCode());
     EXPECT_TRUE(d.auth_required_called());
@@ -8853,141 +8853,244 @@
 TEST_F(URLRequestTestReferrerPolicy, HTTPToSameOriginHTTP) {
   InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTP);
 
+  GURL referrer = origin_server()->GetURL("/path/to/file.html");
   VerifyReferrerAfterRedirect(
       URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      referrer, referrer);
 
   VerifyReferrerAfterRedirect(
       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      referrer, referrer);
 
   VerifyReferrerAfterRedirect(
-      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer);
 
-  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
-                              origin_server()->GetURL("/path/to/file.html"),
-                              origin_server()->GetURL("/path/to/file.html"));
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, referrer,
+                              referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(URLRequest::ORIGIN, referrer.GetOrigin(),
+                              referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN, referrer,
+      referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      referrer.GetOrigin(), referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(URLRequest::NO_REFERRER, GURL(), GURL());
 }
 
 TEST_F(URLRequestTestReferrerPolicy, HTTPToCrossOriginHTTP) {
   InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP,
                                 net::EmbeddedTestServer::TYPE_HTTP);
+  GURL referrer = origin_server()->GetURL("/path/to/file.html");
 
   VerifyReferrerAfterRedirect(
       URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      referrer, referrer);
 
   VerifyReferrerAfterRedirect(
       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/"));
+      referrer, referrer.GetOrigin());
 
   VerifyReferrerAfterRedirect(
-      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/"));
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer,
+      referrer.GetOrigin());
 
-  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
-                              origin_server()->GetURL("/path/to/file.html"),
-                              origin_server()->GetURL("/path/to/file.html"));
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, referrer,
+                              referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(URLRequest::ORIGIN, referrer.GetOrigin(),
+                              referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL());
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      referrer.GetOrigin(), referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(URLRequest::NO_REFERRER, GURL(), GURL());
 }
 
 TEST_F(URLRequestTestReferrerPolicy, HTTPSToSameOriginHTTPS) {
   InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTPS);
+  GURL referrer = origin_server()->GetURL("/path/to/file.html");
 
   VerifyReferrerAfterRedirect(
       URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      referrer, referrer);
 
   VerifyReferrerAfterRedirect(
       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      referrer, referrer);
 
   VerifyReferrerAfterRedirect(
-      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer);
 
-  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
-                              origin_server()->GetURL("/path/to/file.html"),
-                              origin_server()->GetURL("/path/to/file.html"));
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, referrer,
+                              referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(URLRequest::ORIGIN, referrer.GetOrigin(),
+                              referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN, referrer,
+      referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      referrer.GetOrigin(), referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(URLRequest::NO_REFERRER, GURL(), GURL());
 }
 
 TEST_F(URLRequestTestReferrerPolicy, HTTPSToCrossOriginHTTPS) {
   InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS,
                                 net::EmbeddedTestServer::TYPE_HTTPS);
+  GURL referrer = origin_server()->GetURL("/path/to/file.html");
 
   VerifyReferrerAfterRedirect(
       URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      referrer, referrer);
 
   VerifyReferrerAfterRedirect(
       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/"));
+      referrer, origin_server()->GetURL("/"));
 
   VerifyReferrerAfterRedirect(
-      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer,
       origin_server()->GetURL("/"));
 
-  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
-                              origin_server()->GetURL("/path/to/file.html"),
-                              origin_server()->GetURL("/path/to/file.html"));
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, referrer,
+                              referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(URLRequest::ORIGIN, referrer.GetOrigin(),
+                              referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL());
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      referrer.GetOrigin(), referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(URLRequest::NO_REFERRER, GURL(), GURL());
 }
 
 TEST_F(URLRequestTestReferrerPolicy, HTTPToHTTPS) {
   InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP,
                                 net::EmbeddedTestServer::TYPE_HTTPS);
+  GURL referrer = origin_server()->GetURL("/path/to/file.html");
 
   VerifyReferrerAfterRedirect(
       URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/path/to/file.html"));
+      referrer, referrer);
 
   VerifyReferrerAfterRedirect(
       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
-      origin_server()->GetURL("/"));
+      referrer, origin_server()->GetURL("/"));
 
   VerifyReferrerAfterRedirect(
-      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer,
       origin_server()->GetURL("/"));
 
-  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
-                              origin_server()->GetURL("/path/to/file.html"),
-                              origin_server()->GetURL("/path/to/file.html"));
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, referrer,
+                              referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(URLRequest::ORIGIN, referrer.GetOrigin(),
+                              referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL());
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      referrer.GetOrigin(), referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(URLRequest::NO_REFERRER, GURL(), GURL());
 }
 
 TEST_F(URLRequestTestReferrerPolicy, HTTPSToHTTP) {
   InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS,
                                 net::EmbeddedTestServer::TYPE_HTTP);
+  GURL referrer = origin_server()->GetURL("/path/to/file.html");
 
   VerifyReferrerAfterRedirect(
       URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
-      origin_server()->GetURL("/path/to/file.html"), GURL());
+      referrer, GURL());
 
   VerifyReferrerAfterRedirect(
       URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"), GURL());
+      referrer, GURL());
 
   VerifyReferrerAfterRedirect(
-      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
-      origin_server()->GetURL("/path/to/file.html"),
+      URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer,
       origin_server()->GetURL("/"));
 
-  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER,
-                              origin_server()->GetURL("/path/to/file.html"),
-                              origin_server()->GetURL("/path/to/file.html"));
+  VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, referrer,
+                              referrer);
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin; thus this test case just
+  // checks that this policy doesn't cause the referrer to change when following
+  // a redirect.
+  VerifyReferrerAfterRedirect(URLRequest::ORIGIN, referrer.GetOrigin(),
+                              referrer.GetOrigin());
+
+  VerifyReferrerAfterRedirect(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL());
+
+  // The original referrer set on the request is expected to obey the referrer
+  // policy and already be stripped to the origin, though it should be
+  // subsequently cleared during the downgrading redirect.
+  VerifyReferrerAfterRedirect(
+      URLRequest::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+      referrer.GetOrigin(), GURL());
+
+  VerifyReferrerAfterRedirect(URLRequest::NO_REFERRER, GURL(), GURL());
 }
 
 class HTTPSRequestTest : public testing::Test {
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index 92decad2..9f278538 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -94,7 +94,7 @@
   // /proc/tid directory for the thread (since /proc may not be aware of the
   // PID namespace). With a process, we can just use /proc/self.
   pid_t pid = -1;
-  char stack_buf[PTHREAD_STACK_MIN] ALIGNAS(16);
+  alignas(16) char stack_buf[PTHREAD_STACK_MIN];
 #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
     defined(ARCH_CPU_MIPS_FAMILY)
   // The stack grows downward.
diff --git a/services/ui/ws/platform_display_default_unittest.cc b/services/ui/ws/platform_display_default_unittest.cc
index d7e72ff..bc3b399 100644
--- a/services/ui/ws/platform_display_default_unittest.cc
+++ b/services/ui/ws/platform_display_default_unittest.cc
@@ -99,7 +99,11 @@
   DISALLOW_COPY_AND_ASSIGN(TestOzonePlatform);
 };
 
-TEST(PlatformDisplayDefaultTest, EventDispatch) {
+// Test fails in part because services_unittests appears to have its own ozone
+// platform that it initializes. For some reason, this only started failing
+// locally and on the trybots on 06/13/2017, while passing when run on the CQ
+// and the builders. crbug.com/732987
+TEST(PlatformDisplayDefaultTest, DISABLED_EventDispatch) {
   // Setup ozone so the display can be initialized.
   TestOzonePlatform platform;
 
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index 789fef2c0..be8833cb 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -37,8 +37,6 @@
     "receiver_mojo_to_media_adapter.h",
     "service_impl.cc",
     "service_impl.h",
-    "testing_controls_impl.cc",
-    "testing_controls_impl.h",
   ]
 
   public_deps = [
diff --git a/services/video_capture/public/interfaces/BUILD.gn b/services/video_capture/public/interfaces/BUILD.gn
index 36fc949..58034c9 100644
--- a/services/video_capture/public/interfaces/BUILD.gn
+++ b/services/video_capture/public/interfaces/BUILD.gn
@@ -10,7 +10,6 @@
     "device_factory.mojom",
     "device_factory_provider.mojom",
     "receiver.mojom",
-    "testing_controls.mojom",
   ]
 
   deps = [
diff --git a/services/video_capture/public/interfaces/testing_controls.mojom b/services/video_capture/public/interfaces/testing_controls.mojom
deleted file mode 100644
index fd5ac97..0000000
--- a/services/video_capture/public/interfaces/testing_controls.mojom
+++ /dev/null
@@ -1,11 +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 video_capture.mojom;
-
-// APIs for allowing tests to control service behavior for the purpose of
-// integration testing.
-interface TestingControls  {
-  Crash();
-};
diff --git a/services/video_capture/service_impl.cc b/services/video_capture/service_impl.cc
index 5c4aa54..16bfa06 100644
--- a/services/video_capture/service_impl.cc
+++ b/services/video_capture/service_impl.cc
@@ -8,7 +8,6 @@
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/video_capture/device_factory_provider_impl.h"
 #include "services/video_capture/public/interfaces/constants.mojom.h"
-#include "services/video_capture/testing_controls_impl.h"
 
 namespace video_capture {
 
@@ -29,10 +28,6 @@
       // Unretained |this| is safe because |registry_| is owned by |this|.
       base::Bind(&ServiceImpl::OnDeviceFactoryProviderRequest,
                  base::Unretained(this)));
-  registry_.AddInterface<mojom::TestingControls>(
-      // Unretained |this| is safe because |registry_| is owned by |this|.
-      base::Bind(&ServiceImpl::OnTestingControlsRequest,
-                 base::Unretained(this)));
 }
 
 void ServiceImpl::OnBindInterface(
@@ -65,15 +60,6 @@
       std::move(request));
 }
 
-void ServiceImpl::OnTestingControlsRequest(
-    const service_manager::BindSourceInfo& source_info,
-    mojom::TestingControlsRequest request) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  mojo::MakeStrongBinding(
-      base::MakeUnique<TestingControlsImpl>(ref_factory_->CreateRef()),
-      std::move(request));
-}
-
 void ServiceImpl::SetShutdownDelayInSeconds(float seconds) {
   DCHECK(thread_checker_.CalledOnValidThread());
   shutdown_delay_in_seconds_ = seconds;
diff --git a/services/video_capture/service_impl.h b/services/video_capture/service_impl.h
index 34d7079..fc40952 100644
--- a/services/video_capture/service_impl.h
+++ b/services/video_capture/service_impl.h
@@ -12,7 +12,6 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/interfaces/device_factory_provider.mojom.h"
-#include "services/video_capture/public/interfaces/testing_controls.mojom.h"
 
 #if defined(OS_WIN)
 #include "base/win/scoped_com_initializer.h"
@@ -36,9 +35,6 @@
   void OnDeviceFactoryProviderRequest(
       const service_manager::BindSourceInfo& source_info,
       mojom::DeviceFactoryProviderRequest request);
-  void OnTestingControlsRequest(
-      const service_manager::BindSourceInfo& source_info,
-      mojom::TestingControlsRequest request);
   void SetShutdownDelayInSeconds(float seconds);
   void MaybeRequestQuitDelayed();
   void MaybeRequestQuit();
diff --git a/services/video_capture/service_manifest.json b/services/video_capture/service_manifest.json
index dcd36230..62d76da 100644
--- a/services/video_capture/service_manifest.json
+++ b/services/video_capture/service_manifest.json
@@ -3,14 +3,11 @@
   "display_name": "Video Capture",
   "interface_provider_specs": {
     "service_manager:connector": {
-      "provides" : {
-        "capture" : ["video_capture::mojom::DeviceFactoryProvider"],
-        "tests" : [
-          "video_capture::mojom::DeviceFactoryProvider",
-          "video_capture::mojom::TestingControls"
-        ]
+      "provides": {
+        "capture": [ "video_capture::mojom::DeviceFactoryProvider" ],
+        "tests": [ "*" ]
       },
-      "requires" : {
+      "requires": {
         "service_manager": [ "service_manager:all_users" ]
       }
     }
diff --git a/services/video_capture/testing_controls_impl.cc b/services/video_capture/testing_controls_impl.cc
deleted file mode 100644
index e89eb86..0000000
--- a/services/video_capture/testing_controls_impl.cc
+++ /dev/null
@@ -1,19 +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 "services/video_capture/testing_controls_impl.h"
-
-namespace video_capture {
-
-TestingControlsImpl::TestingControlsImpl(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
-
-TestingControlsImpl::~TestingControlsImpl() = default;
-
-void TestingControlsImpl::Crash() {
-  CHECK(false) << "This is an intentional crash for the purpose of testing";
-}
-
-}  // namespace video_capture
diff --git a/services/video_capture/testing_controls_impl.h b/services/video_capture/testing_controls_impl.h
deleted file mode 100644
index f0a17fe..0000000
--- a/services/video_capture/testing_controls_impl.h
+++ /dev/null
@@ -1,30 +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 SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
-#define SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
-
-#include "services/service_manager/public/cpp/service_context_ref.h"
-#include "services/video_capture/public/interfaces/testing_controls.mojom.h"
-
-namespace video_capture {
-
-class TestingControlsImpl : public mojom::TestingControls {
- public:
-  TestingControlsImpl(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
-  ~TestingControlsImpl() override;
-
-  // mojom::TestingControls implementation.
-  void Crash() override;
-
- private:
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestingControlsImpl);
-};
-
-}  // namespace video_capture
-
-#endif  // SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index a68eeab6..a4692e8 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -54,10 +54,6 @@
     defines += [ "SK_HAS_JPEG_LIBRARY" ]
   }
 
-  if (!use_system_freetype) {
-    defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ]
-  }
-
   if (is_component_build) {
     defines += [
       "SKIA_DLL",
@@ -128,6 +124,10 @@
 
   defines = []
 
+  if (!use_system_freetype) {
+    defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ]
+  }
+
   if (is_component_build) {
     defines += [ "SKIA_IMPLEMENTATION=1" ]
   }
diff --git a/styleguide/c++/c++11.html b/styleguide/c++/c++11.html
index d62bf25..4fe82713d 100644
--- a/styleguide/c++/c++11.html
+++ b/styleguide/c++/c++11.html
@@ -78,6 +78,14 @@
 </tr>
 
 <tr>
+<td>Alignment Features</td>
+<td><code>alignas(alignof(T)) char[10];</code></td>
+<td>Specifies or queries storage alignment.</td>
+<td><a href="http://en.cppreference.com/w/chttp://en.cppreference.com/w/cpp/language/alignas">alignas</a>, <a href="http://en.cppreference.com/w/cpp/language/alignof">alignof</a></td>
+<td><code>alignof()</code> can be used. <code>alignas()</code> must be used with care because it does not interact well with export and packing specifiers. If your declaration contains any other attributes, use <code>ALIGNAS()</code> from <code>base/compiler_specific.h</code> instead. <a href="https://codereview.chromium.org/2670873002/">Patch where this was discussed</a></td>
+</tr>
+
+<tr>
 <td>Angle Bracket Parsing in Templates</td>
 <td><code>&gt;&gt;</code> for <code>&gt; &gt;</code>, <code>&lt;::</code> for <code>&lt; ::</code></td>
 <td>More intuitive parsing of template parameters</td>
@@ -618,6 +626,13 @@
 </tr>
 
 <tr>
+<td>Aligned storage</td>
+<td><code>std::aligned_storage&lt;10, 128&gt;</code></td>
+<td>Uninitialized storage for objects requiring specific alignment.</td>
+<td><a href="http://en.cppreference.com/w/cpp/types/aligned_storage">std::aligned_storage</a></td>
+<td>MSVC 2017's implementation does not align on boundaries greater than sizeof(double) = 8 bytes. Use <code>alignas(128) char foo[10];</code> instead. <a href="https://codereview.chromium.org/2932053002">Patch where this was discovered</a>.</td>
+</tr>
+
 <td>Bind Operations</td>
 <td><code>std::bind(<i>function</i>, <i>args</i>, ...)</code></td>
 <td>Declares a function object bound to certain arguments</td>
@@ -710,14 +725,6 @@
 </tr>
 
 <tr>
-<td>Alignment Features</td>
-<td><code>alignas</code> specifier, <code>alignof</code> operator</td>
-<td>Object alignment</td>
-<td><a href="http://en.cppreference.com/w/cpp/language/alignas">alignas specifier</a>, <a href="http://en.cppreference.com/w/cpp/language/alignof">alignof operator</a></td>
-<td>Reevaluate now that MSVS2015 is available. <a href="https://groups.google.com/a/chromium.org/forum/#!topic/cxx/rwXN02jzzq0">Discussion thread</a></td>
-</tr>
-
-<tr>
 <td>Attributes</td>
 <td><code>[[<i>attribute_name</i>]]</code></td>
 <td>Attaches properties to declarations that specific compiler implementations may use.</td>
@@ -769,11 +776,10 @@
 
 <tr>
 <td>Aligned Storage</td>
-<td><code>std::aligned_storage&lt;Size, Align&gt;::type</code><br />
-<code>std::alignment_of&lt;T&gt;</code>, <code>std::aligned_union&lt;Size, ...Types&gt;</code> <code>std::max_align_t</code></td>
+<td><code>std::alignment_of&lt;T&gt;</code>, <code>std::aligned_union&lt;Size, ...Types&gt;</code> <code>std::max_align_t</code></td>
 <td>Declare uninitialized storage having a specified alignment, or determine alignments.</td>
-<td><a href="http://en.cppreference.com/w/cpp/types/aligned_storage">std::aligned_storage</a></td>
-<td><code>std::aligned_storage</code> and <code>std::aligned_union</code> are disallowed in google3 over concerns about compatibility with internal cross-compiling toolchains.</td>
+<td><a href="http://en.cppreference.com/w/cpp/types/aligned_union">std::aligned_union</a></td>
+<td><code>std::aligned_union</code> is disallowed in google3 over concerns about compatibility with internal cross-compiling toolchains. <code>std::aligned_storage</code> is on the disallowed list due to compatibility concerns.</td>
 </tr>
 
 <tr>
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 7d5f36f11..7d7f5c8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -148,7 +148,7 @@
           ],
           "dimension_sets": [
             {
-              "android_devices": "4",
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -1174,7 +1174,7 @@
           ],
           "dimension_sets": [
             {
-              "android_devices": "4",
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5454,6 +5454,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5496,6 +5497,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5535,6 +5537,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5574,6 +5577,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5613,6 +5617,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5643,6 +5648,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5685,6 +5691,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "6",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5773,6 +5780,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "6",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5815,6 +5823,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5859,6 +5868,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5898,6 +5908,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5941,6 +5952,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -5980,6 +5992,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6023,6 +6036,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6062,6 +6076,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6105,6 +6120,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6147,6 +6163,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6191,6 +6208,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6230,6 +6248,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6273,6 +6292,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6312,6 +6332,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6351,6 +6372,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6390,6 +6412,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6429,6 +6452,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6468,6 +6492,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6507,6 +6532,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6546,6 +6572,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6585,6 +6612,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6624,6 +6652,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6663,6 +6692,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6702,6 +6732,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6741,6 +6772,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "1",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6780,6 +6812,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6819,6 +6852,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6858,6 +6892,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6897,6 +6932,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6936,6 +6972,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -6975,6 +7012,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -7018,6 +7056,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -7057,6 +7096,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "4",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -7096,6 +7136,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "6",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
@@ -7139,6 +7180,7 @@
           ],
           "dimension_sets": [
             {
+              "android_devices": "6",
               "device_os": "MMB29Q",
               "device_type": "bullhead"
             }
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index 8a1d3d9..667c0eab 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -59,14 +59,11 @@
 -DevToolsProtocolTest.CertificateError
 -DevToolsProtocolTest.SubresourceWithCertificateError
 -GenericSensorBrowserTest.AmbientLightSensorTest
+-GetUserMediaVideoCaptureBrowserTest.RecoverFromCrashInVideoCaptureProcess
 -HostZoomMapImplBrowserTest.GetZoomForView_Host
 -HostZoomMapImplBrowserTest.GetZoomForView_HostAndScheme
 -IndexedDBBrowserTestInstantiation/IndexedDBBrowserTest.OperationOnCorruptedOpenDatabase*
 -IsolateIcelandFrameTreeBrowserTest.ProcessSwitchForIsolatedBlob
--MemoryCoordinatorImplBrowserTest.CanSuspendRenderer
--MemoryCoordinatorImplBrowserTest.GetStateForProcess
--MemoryCoordinatorImplBrowserTest.HandleAdded
--MemoryCoordinatorWithServiceWorkerTest.CannotSuspendRendererWithServiceWorker
 -MHTMLGenerationTest.GenerateMHTMLIgnoreNoStore
 -MHTMLGenerationTest.GenerateMHTMLIgnoreNoStoreSubFrame
 -MHTMLGenerationTest.GenerateMHTMLObeyNoStoreMainFrame
@@ -76,6 +73,10 @@
 -MHTMLGenerationTest.ViewedMHTMLDoesNotContainNoStoreContent
 -ManifestBrowserTest.UseCredentialsSendCookies
 -MediaSessionImplVisibilityBrowserTestInstances/MediaSessionImplVisibilityBrowserTest*
+-MemoryCoordinatorImplBrowserTest.CanSuspendRenderer
+-MemoryCoordinatorImplBrowserTest.GetStateForProcess
+-MemoryCoordinatorImplBrowserTest.HandleAdded
+-MemoryCoordinatorWithServiceWorkerTest.CannotSuspendRendererWithServiceWorker
 -NavigationControllerBrowserTest.204Navigation
 -NavigationControllerBrowserTest.ErrorPageReplacement
 -NavigationControllerBrowserTest.NavigateFromLoadDataWithBaseURL
@@ -118,12 +119,12 @@
 -ResourceDispatcherHostBrowserTest.SniffHTMLWithNoContentType
 -ResourceDispatcherHostBrowserTest.SniffNoContentTypeNoData
 -ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest
+-SessionHistoryTest.CrossFrameFormBackForward
 -SessionHistoryTest.FrameFormBackForward
 -SessionHistoryTest.HistoryLength
--SessionHistoryTest.CrossFrameFormBackForward
+-SitePerProcessBrowserTest.CrossProcessFocusChangeFiresBlurEvents
 -SitePerProcessBrowserTest.CrossSiteIframeBlockedByCSPInheritedBySrcDocParent
 -SitePerProcessBrowserTest.CrossSiteIframeBlockedByParentCSPFromHeaders
--SitePerProcessBrowserTest.CrossProcessFocusChangeFiresBlurEvents
 -SitePerProcessBrowserTest.ProxyCreationSkipsSubtree
 -SitePerProcessBrowserTest.SubframeWindowFocus
 -SitePerProcessDownloadDevToolsBrowserTest.NotCommittedNavigationDoesNotBlockAgent
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index baee6c7..e992045f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1125,6 +1125,25 @@
             ]
         }
     ],
+    "GpuScheduler": [
+        {
+            "platforms": [
+                "mac",
+                "win",
+                "linux",
+                "chromeos",
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "GpuScheduler"
+                    ]
+                }
+            ]
+        }
+    ],
     "Html5ByDefault": [
         {
             "platforms": [
@@ -2023,7 +2042,7 @@
             ],
             "experiments": [
                 {
-                    "name": "SBEROnlyNoIncognito",
+                    "name": "SBEROnlyNoIncognito2",
                     "params": {
                         "extended_reporting": "true",
                         "history_sync": "false",
@@ -2046,11 +2065,10 @@
             ],
             "experiments": [
                 {
-                    "name": "SBEROnlyNoIncognito",
+                    "name": "AllPopulation",
                     "params": {
-                        "extended_reporting": "true",
-                        "history_sync": "false",
-                        "incognito": "false"
+                        "all_population": "true",
+                        "incognito": "true"
                     },
                     "enable_features": [
                         "ProtectedPasswordEntryPinging"
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index daf10943..d86d743 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -20,32 +20,32 @@
 Bug(none) bluetooth/server/connect/garbage-collection-ran-during-success.html [ Timeout ]
 Bug(none) bluetooth/server/connect/get-same-gatt-server.html [ Timeout ]
 Bug(none) bluetooth/server/device-same-object.html [ Timeout ]
-Bug(none) bluetooth/server/getPrimaryService/gen-disconnect-called-before.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryService/gen-disconnected-device.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html [ Failure ]
+Bug(none) bluetooth/server/getPrimaryService/gen-disconnect-called-before.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryService/gen-disconnected-device.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html [ Failure Timeout ]
 Bug(none) bluetooth/server/getPrimaryService/gen-invalid-service-name.html [ Timeout ]
-Bug(none) bluetooth/server/getPrimaryService/gen-no-permission-absent-service.html [ Failure ]
+Bug(none) bluetooth/server/getPrimaryService/gen-no-permission-absent-service.html [ Failure Timeout ]
 Bug(none) bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html [ Timeout ]
-Bug(none) bluetooth/server/getPrimaryService/gen-service-not-found.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-before.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-disconnected-device.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html [ Failure ]
+Bug(none) bluetooth/server/getPrimaryService/gen-service-not-found.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-before.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-disconnected-device.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html [ Failure Timeout ]
 Bug(none) bluetooth/server/getPrimaryServices/gen-invalid-service-name.html [ Timeout ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.html [ Failure ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.html [ Failure Timeout ]
 Bug(none) bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html [ Timeout ]
 Bug(none) bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html [ Timeout ]
-Bug(none) bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.html [ Failure ]
-Bug(none) bluetooth/server/getPrimaryServices/services-not-found.html [ Failure ]
+Bug(none) bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.html [ Failure Timeout ]
+Bug(none) bluetooth/server/getPrimaryServices/services-not-found.html [ Failure Timeout ]
 Bug(none) compositing/iframes/iframe-in-composited-layer.html [ Failure ]
 Bug(none) compositing/layers-inside-overflow-scroll.html [ Timeout ]
 Bug(none) compositing/self-painting-layers2.html [ Timeout ]
@@ -676,13 +676,13 @@
 Bug(none) external/wpt/payment-request/historical.https.html [ Timeout ]
 Bug(none) external/wpt/payment-request/interfaces.https.html [ Timeout ]
 Bug(none) external/wpt/payment-request/payment-request-abort-method.https.html [ Timeout ]
-Bug(none) external/wpt/payment-request/payment-request-constructor-crash.https.html [ Failure ]
+Bug(none) external/wpt/payment-request/payment-request-constructor-crash.https.html [ Failure Timeout ]
 Bug(none) external/wpt/payment-request/payment-request-constructor.https.html [ Timeout ]
 Bug(none) external/wpt/payment-request/payment-request-id.https.html [ Timeout ]
-Bug(none) external/wpt/payment-request/payment-request-onshippingaddresschange-attribute.https.html [ Failure ]
-Bug(none) external/wpt/payment-request/payment-request-onshippingoptionchange-attribute.https.html [ Failure ]
+Bug(none) external/wpt/payment-request/payment-request-onshippingaddresschange-attribute.https.html [ Failure Timeout ]
+Bug(none) external/wpt/payment-request/payment-request-onshippingoptionchange-attribute.https.html [ Failure Timeout ]
 Bug(none) external/wpt/payment-request/payment-request-show-method.https.html [ Timeout ]
-Bug(none) external/wpt/payment-request/payment-request-update-event-constructor.https.html [ Failure ]
+Bug(none) external/wpt/payment-request/payment-request-update-event-constructor.https.html [ Failure Timeout ]
 Bug(none) external/wpt/pointerevents/pointerevent_touch-action-table-test_touch-manual.html [ Timeout ]
 Bug(none) external/wpt/preload/delaying-onload-link-preload-after-discovery.html [ Timeout ]
 Bug(none) external/wpt/preload/download-resources.html [ Timeout ]
@@ -1563,7 +1563,7 @@
 Bug(none) http/tests/appcache/update-cache.html [ Timeout ]
 Bug(none) http/tests/appcache/video.html [ Failure Timeout ]
 Bug(none) http/tests/appcache/whitelist-wildcard.html [ Failure ]
-Bug(none) http/tests/appcache/xhr-foreign-resource.html [ Failure ]
+Bug(none) http/tests/appcache/xhr-foreign-resource.html [ Failure Timeout ]
 Bug(none) http/tests/background_fetch/background-fetch-click-event.https.html [ Timeout ]
 Bug(none) http/tests/background_fetch/background-fetch-event.https.html [ Timeout ]
 Bug(none) http/tests/background_fetch/background-fetch-fail-event.https.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index b30f5bb6..afabf3b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1822,11 +1822,6 @@
 crbug.com/694525 external/wpt/content-security-policy/script-src/script-src-1_10.html [ Skip ]
 crbug.com/694525 external/wpt/content-security-policy/script-src/script-src-strict_dynamic_in_img-src.html [ Skip ]
 
-# These policies are not implemented yet.
-crbug.com/627968 external/wpt/referrer-policy/same-origin/ [ Skip ]
-crbug.com/627968 external/wpt/referrer-policy/strict-origin/ [ Skip ]
-crbug.com/627968 external/wpt/referrer-policy/strict-origin-when-cross-origin/ [ Skip ]
-
 # These wpt/mixed-content tests have console errors that are different on different test runs.
 crbug.com/679742 external/wpt/mixed-content/allowed/http-csp/same-host-wss/websocket-request/top-level/keep-scheme-redirect/websocket-allowed.https.html [ Failure ]
 crbug.com/679742 external/wpt/mixed-content/allowed/meta-csp/same-host-wss/websocket-request/top-level/no-redirect/websocket-allowed.https.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/css-parser/offset-parsing.html b/third_party/WebKit/LayoutTests/css-parser/offset-parsing.html
deleted file mode 100644
index 2af7305..0000000
--- a/third_party/WebKit/LayoutTests/css-parser/offset-parsing.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="resources/property-parsing-test.js"></script>
-<script>
-// Verifies that offset shorthand values are properly parsed
-
-assert_valid_value("offset", "path('M 0 0 H 1') -200% auto");
-assert_valid_value("offset", "none 50px reverse 30deg");
-assert_invalid_value("offset", "path('M 0 0 H 1') reverse 30deg 50px");
-assert_invalid_value("offset", "path('M 0 0 H 1') auto");
-assert_invalid_value("offset", "none 30deg reverse");
-assert_invalid_value("offset", "path('M 0 0 H 1') -200%");
-assert_invalid_value("offset", "path('M 0 0 H 1') 50px");
-assert_invalid_value("offset", "path('M 0 0 H 1')", "path('M 0 0 H 1') 0px auto");
-assert_invalid_value("offset", "center center path('M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z') 100% 90deg/left bottom");
-assert_invalid_value("offset", "100px");
-assert_invalid_value("offset", "100px none auto 90deg");
-assert_invalid_value("offset", "auto");
-assert_invalid_value("offset", "30deg");
-assert_invalid_value("offset", "30deg path('M 20 30 A 60 70 80')");
-assert_invalid_value("offset", "auto 30deg 90px");
-assert_invalid_value("offset", "none /");
-assert_invalid_value("offset", "none / 10px 20px 30deg");
-assert_invalid_value("offset", "path('M 20 30 A 60 70 80') bottom");
-</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-same-origin-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-same-origin-expected.txt
deleted file mode 100644
index ec428d1e..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-same-origin-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS Test referer header /beacon/resources/ 
-FAIL Test referer header http://www1.web-platform.test:8001/beacon/resources/ assert_equals: Correct referrer header result expected "" but got "http://web-platform.test:8001/beacon/headers/header-referrer-same-origin.html"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin-when-cross-origin.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin-when-cross-origin.https-expected.txt
index d038c8e..1a33f92 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin-when-cross-origin.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin-when-cross-origin.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL Test referer header https://web-platform.test:8444/beacon/resources/ assert_equals: Correct referrer header result expected "https://web-platform.test:8444/" but got "https://web-platform.test:8444/beacon/headers/header-referrer-strict-origin-when-cross-origin.https.html"
+PASS Test referer header https://web-platform.test:8444/beacon/resources/ 
 FAIL Test referer header http://web-platform.test:8001/beacon/resources/ assert_true: SendBeacon Succeeded expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin.https-expected.txt
index 338dd5e..1a33f92 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/beacon/headers/header-referrer-strict-origin.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL Test referer header https://web-platform.test:8444/beacon/resources/ assert_equals: Correct referrer header result expected "https://web-platform.test:8444/" but got "https://web-platform.test:8444/beacon/headers/header-referrer-strict-origin.https.html"
+PASS Test referer header https://web-platform.test:8444/beacon/resources/ 
 FAIL Test referer header http://web-platform.test:8001/beacon/resources/ assert_true: SendBeacon Succeeded expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-invalid.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-invalid.html
index 6afd1371..54298ac 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-invalid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-invalid.html
@@ -12,9 +12,19 @@
 </head>
 <body>
 <script>
-test_invalid_value("offset", "path('m 0 0 h 100') 100px 0");
-test_invalid_value("offset", "ray(sides 0) 50% 90deg auto");
 test_invalid_value("offset", "100px 0deg path('m 0 0 h 100')");
+test_invalid_value("offset", "30deg path('M 20 30 A 60 70 80')");
+test_invalid_value("offset", "30deg");
+test_invalid_value("offset", "auto 30deg 90px");
+test_invalid_value("offset", "none / 10px 20px 30deg");
+test_invalid_value("offset", "none /");
+test_invalid_value("offset", "path('M 20 30 A 60 70 80') bottom");
+test_invalid_value("offset", "path('m 0 0 h 100') 100px 0");
+test_invalid_value("offset", "path('m 0 0 h 100') 100px 200px");
+test_invalid_value("offset", "path('m 0 0 h 100') 200% auto 100px");
+test_invalid_value("offset", "path('m 0 0 h 100') auto reverse");
+test_invalid_value("offset", "path('m 0 0 h 100') reverse 100px 30deg");
+test_invalid_value("offset", "ray(sides 0) 50% 90deg auto");
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-valid.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-valid.html
index e3dded8c..c7a4e64 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-valid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/parsing/offset-parsing-valid.html
@@ -12,9 +12,32 @@
 </head>
 <body>
 <script>
-test_valid_value("offset", "path('m 0 0 h 100') 100px 0deg");
-test_valid_value("offset", "ray(sides 0deg) 50% 90deg auto", "ray(0deg sides) 50% auto 90deg");
+test_valid_value("offset", "100px none auto 90deg", "100px center none auto 90deg");
+test_valid_value("offset", "100px", "100px center");
+test_valid_value("offset", "auto none reverse");
+test_valid_value("offset", "auto");
+test_valid_value("offset", "center bottom path('M 1 2 V 3 Z')");
+test_valid_value("offset", "center center path('M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z') 100% 90deg / left bottom");
+test_valid_value("offset", "left bottom ray(0rad closest-side) 10px auto 30deg / right bottom");
+test_valid_value("offset", "left top");
+test_valid_value("offset", "none 30deg reverse", "none reverse 30deg");
+test_valid_value("offset", "none 50px reverse 30deg");
 test_valid_value("offset", "none calc(10px + 20%) auto");
+test_valid_value("offset", "none reverse");
+test_valid_value("offset", "path('M 0 0 H 1') -200% auto");
+test_valid_value("offset", "path('M 0 0 H 1') -200%");
+test_valid_value("offset", "path('M 0 0 H 1') 50px");
+test_valid_value("offset", "path('M 0 0 H 1') auto");
+test_valid_value("offset", "path('M 0 0 H 1') reverse 30deg 50px", "path('M 0 0 H 1') 50px reverse 30deg");
+test_valid_value("offset", "path('M 0 0 H 1')");
+test_valid_value("offset", "path('m 0 0 h 100') -7rad 8px / auto", "path('m 0 0 h 100') 8px -7rad / auto");
+test_valid_value("offset", "path('m 0 0 h 100') -7rad 8px / left top", "path('m 0 0 h 100') 8px -7rad / left top");
+test_valid_value("offset", "path('m 0 0 h 100') -7rad 8px", "path('m 0 0 h 100') 8px -7rad");
+test_valid_value("offset", "path('m 0 0 h 100') 100px 0deg");
+test_valid_value("offset", "path('m 1 2 v 3 Z')");
+test_valid_value("offset", "ray(farthest-corner 90deg) 1%", "ray(90deg farthest-corner) 1%");
+test_valid_value("offset", "ray(sides 0deg) 50% 90deg auto", "ray(0deg sides) 50% auto 90deg");
+test_valid_value("offset", "right bottom / left top");
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/redirect/redirect-referrer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/redirect/redirect-referrer-expected.txt
deleted file mode 100644
index dce245f47..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/redirect/redirect-referrer-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-This is a testharness.js-based test.
-PASS Same origin redirection, empty init, unsafe-url redirect header  
-PASS Same origin redirection, empty init, no-referrer-when-downgrade redirect header  
-PASS Same origin redirection, empty init, same-origin redirect header  
-PASS Same origin redirection, empty init, origin redirect header  
-PASS Same origin redirection, empty init, origin-when-cross-origin redirect header  
-PASS Same origin redirection, empty init, no-referrer redirect header  
-FAIL Same origin redirection, empty init, strict-origin redirect header  assert_equals: Check referrer header expected "http://web-platform.test:8001/" but got "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.html"
-PASS Same origin redirection, empty init, strict-origin-when-cross-origin redirect header  
-PASS Same origin redirection, empty redirect header, unsafe-url init  
-PASS Same origin redirection, empty redirect header, no-referrer-when-downgrade init  
-FAIL Same origin redirection, empty redirect header, same-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid referrer policy"
-PASS Same origin redirection, empty redirect header, origin init  
-PASS Same origin redirection, empty redirect header, origin-when-cross-origin init  
-PASS Same origin redirection, empty redirect header, no-referrer init  
-FAIL Same origin redirection, empty redirect header, strict-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid referrer policy"
-FAIL Same origin redirection, empty redirect header, strict-origin-when-cross-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid referrer policy"
-PASS Cross origin redirection, empty init, unsafe-url redirect header  
-PASS Cross origin redirection, empty init, no-referrer-when-downgrade redirect header  
-FAIL Cross origin redirection, empty init, same-origin redirect header  assert_equals: Check referrer header expected (object) null but got (string) "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.html"
-PASS Cross origin redirection, empty init, origin redirect header  
-PASS Cross origin redirection, empty init, origin-when-cross-origin redirect header  
-PASS Cross origin redirection, empty init, no-referrer redirect header  
-FAIL Cross origin redirection, empty init, strict-origin redirect header  assert_equals: Check referrer header expected "http://web-platform.test:8001/" but got "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.html"
-FAIL Cross origin redirection, empty init, strict-origin-when-cross-origin redirect header  assert_equals: Check referrer header expected "http://web-platform.test:8001/" but got "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.html"
-PASS Cross origin redirection, empty redirect header, unsafe-url init  
-PASS Cross origin redirection, empty redirect header, no-referrer-when-downgrade init  
-FAIL Cross origin redirection, empty redirect header, same-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid referrer policy"
-PASS Cross origin redirection, empty redirect header, origin init  
-PASS Cross origin redirection, empty redirect header, origin-when-cross-origin init  
-PASS Cross origin redirection, empty redirect header, no-referrer init  
-FAIL Cross origin redirection, empty redirect header, strict-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid referrer policy"
-FAIL Cross origin redirection, empty redirect header, strict-origin-when-cross-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'Window': Invalid referrer policy"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/redirect/redirect-referrer-worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/redirect/redirect-referrer-worker-expected.txt
deleted file mode 100644
index a6d71ea..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/redirect/redirect-referrer-worker-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-This is a testharness.js-based test.
-PASS Same origin redirection, empty init, unsafe-url redirect header  
-PASS Same origin redirection, empty init, no-referrer-when-downgrade redirect header  
-PASS Same origin redirection, empty init, same-origin redirect header  
-PASS Same origin redirection, empty init, origin redirect header  
-PASS Same origin redirection, empty init, origin-when-cross-origin redirect header  
-PASS Same origin redirection, empty init, no-referrer redirect header  
-FAIL Same origin redirection, empty init, strict-origin redirect header  assert_equals: Check referrer header expected "http://web-platform.test:8001/" but got "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.js"
-PASS Same origin redirection, empty init, strict-origin-when-cross-origin redirect header  
-PASS Same origin redirection, empty redirect header, unsafe-url init  
-PASS Same origin redirection, empty redirect header, no-referrer-when-downgrade init  
-FAIL Same origin redirection, empty redirect header, same-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Invalid referrer policy"
-PASS Same origin redirection, empty redirect header, origin init  
-PASS Same origin redirection, empty redirect header, origin-when-cross-origin init  
-PASS Same origin redirection, empty redirect header, no-referrer init  
-FAIL Same origin redirection, empty redirect header, strict-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Invalid referrer policy"
-FAIL Same origin redirection, empty redirect header, strict-origin-when-cross-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Invalid referrer policy"
-PASS Cross origin redirection, empty init, unsafe-url redirect header  
-PASS Cross origin redirection, empty init, no-referrer-when-downgrade redirect header  
-FAIL Cross origin redirection, empty init, same-origin redirect header  assert_equals: Check referrer header expected (object) null but got (string) "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.js"
-PASS Cross origin redirection, empty init, origin redirect header  
-PASS Cross origin redirection, empty init, origin-when-cross-origin redirect header  
-PASS Cross origin redirection, empty init, no-referrer redirect header  
-FAIL Cross origin redirection, empty init, strict-origin redirect header  assert_equals: Check referrer header expected "http://web-platform.test:8001/" but got "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.js"
-FAIL Cross origin redirection, empty init, strict-origin-when-cross-origin redirect header  assert_equals: Check referrer header expected "http://web-platform.test:8001/" but got "http://web-platform.test:8001/fetch/api/redirect/redirect-referrer.js"
-PASS Cross origin redirection, empty redirect header, unsafe-url init  
-PASS Cross origin redirection, empty redirect header, no-referrer-when-downgrade init  
-FAIL Cross origin redirection, empty redirect header, same-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Invalid referrer policy"
-PASS Cross origin redirection, empty redirect header, origin init  
-PASS Cross origin redirection, empty redirect header, origin-when-cross-origin init  
-PASS Cross origin redirection, empty redirect header, no-referrer init  
-FAIL Cross origin redirection, empty redirect header, strict-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Invalid referrer policy"
-FAIL Cross origin redirection, empty redirect header, strict-origin-when-cross-origin init  promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Invalid referrer policy"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/request/request-init-001.sub-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/request/request-init-001.sub-expected.txt
index 5c099bf..e7c3fae 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/api/request/request-init-001.sub-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/api/request/request-init-001.sub-expected.txt
@@ -18,9 +18,9 @@
 PASS Check referrerPolicy init value of origin and associated getter 
 PASS Check referrerPolicy init value of origin-when-cross-origin and associated getter 
 PASS Check referrerPolicy init value of unsafe-url and associated getter 
-FAIL Check referrerPolicy init value of same-origin and associated getter Failed to construct 'Request': Invalid referrer policy
-FAIL Check referrerPolicy init value of strict-origin and associated getter Failed to construct 'Request': Invalid referrer policy
-FAIL Check referrerPolicy init value of strict-origin-when-cross-origin and associated getter Failed to construct 'Request': Invalid referrer policy
+PASS Check referrerPolicy init value of same-origin and associated getter 
+PASS Check referrerPolicy init value of strict-origin and associated getter 
+PASS Check referrerPolicy init value of strict-origin-when-cross-origin and associated getter 
 PASS Check mode init value of same-origin and associated getter 
 PASS Check mode init value of no-cors and associated getter 
 PASS Check mode init value of cors and associated getter 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-multiple-frames-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-multiple-frames-expected.txt
index 74b8ce0..58351f73 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-multiple-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-multiple-frames-expected.txt
@@ -14,7 +14,7 @@
 
 Running: createIframes
 Removed: 0 uiSourceCodes
-Workspace: 18 uiSourceCodes.
+Workspace: 17 uiSourceCodes.
     debugger:///VM[XXX] bindings-multiple-frames.html
     debugger:///VM[XXX] bindings-multiple-frames.html
     debugger:///VM[XXX] bindings-test.js
@@ -29,15 +29,13 @@
 [+] http://127.0.0.1:8000/inspector/bindings/resources/magic-script.js
 [+] http://127.0.0.1:8000/inspector/bindings/resources/magic-script.js
 [+] http://127.0.0.1:8000/inspector/bindings/resources/magic-style.css
-[+] http://127.0.0.1:8000/inspector/bindings/resources/magic-style.css
     http://127.0.0.1:8000/inspector/inspector-test.js
 [+] _test_create-iframe1.js
 [+] _test_create-iframe2.js
 
 Running: detachFrame1
-Removed: 2 uiSourceCodes
+Removed: 1 uiSourceCodes
 [-] http://127.0.0.1:8000/inspector/bindings/resources/magic-script.js
-[-] http://127.0.0.1:8000/inspector/bindings/resources/magic-style.css
 Workspace: 18 uiSourceCodes.
     debugger:///VM[XXX] bindings-multiple-frames.html
     debugger:///VM[XXX] bindings-multiple-frames.html
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-bindings-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-bindings-expected.txt
index ac9a412..14957526 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-bindings-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-bindings-expected.txt
@@ -52,9 +52,8 @@
 [+] _test_attachShadow2.js
 
 Running: detachShadow1
-Removed: 1 uiSourceCodes
-[-] sourcemap-style.css
-Workspace: 18 uiSourceCodes.
+Removed: 0 uiSourceCodes
+Workspace: 19 uiSourceCodes.
     debugger:///VM[XXX] bindings-test.js
     debugger:///VM[XXX] inspector-test.js
     debugger:///VM[XXX] shadowdom-bindings.html
@@ -70,13 +69,15 @@
     http://127.0.0.1:8000/inspector/bindings/shadowdom-bindings.html
     http://127.0.0.1:8000/inspector/inspector-test.js
     sourcemap-script.js
+    sourcemap-style.css
     _test_attachShadow1.js
     _test_attachShadow2.js
 [+] _test_detachShadow1.js
 
 Running: detachShadow2
-Removed: 1 uiSourceCodes
+Removed: 2 uiSourceCodes
 [-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-sass.scss
+[-] sourcemap-style.css
 Workspace: 19 uiSourceCodes.
     debugger:///VM[XXX] bindings-test.js
     debugger:///VM[XXX] inspector-test.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-navigator-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-navigator-expected.txt
index 8522edf..87802c2 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-navigator-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/shadowdom-navigator-expected.txt
@@ -53,6 +53,7 @@
       inspector-test.js
   (no domain)
     sourcemap-script.js
+    sourcemap-style.css
 
 Running: detachShadow2
 top
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/sourcemap-bindings-multiple-frames-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/sourcemap-bindings-multiple-frames-expected.txt
index cb3ebfd..477ce781 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/sourcemap-bindings-multiple-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/sourcemap-bindings-multiple-frames-expected.txt
@@ -14,7 +14,7 @@
 
 Running: createIframesAndWaitForSourceMaps
 Removed: 0 uiSourceCodes
-Workspace: 20 uiSourceCodes.
+Workspace: 19 uiSourceCodes.
     debugger:///VM[XXX] bindings-test.js
     debugger:///VM[XXX] inspector-test.js
     debugger:///VM[XXX] sourcemap-bindings-multiple-frames.html
@@ -29,7 +29,6 @@
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-script.js
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-script.js
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-style.css
-[+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-style.css
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-typescript.ts
     http://127.0.0.1:8000/inspector/bindings/sourcemap-bindings-multiple-frames.html
     http://127.0.0.1:8000/inspector/inspector-test.js
@@ -37,9 +36,8 @@
 [+] _test_create-iframe2.js
 
 Running: detachFrame1
-Removed: 2 uiSourceCodes
+Removed: 1 uiSourceCodes
 [-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-script.js
-[-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-style.css
 Workspace: 20 uiSourceCodes.
     debugger:///VM[XXX] bindings-test.js
     debugger:///VM[XXX] inspector-test.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/suspendtarget-bindings-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/suspendtarget-bindings-expected.txt
index 7a1f3953..748b529e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/suspendtarget-bindings-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/suspendtarget-bindings-expected.txt
@@ -15,7 +15,7 @@
 
 Running: createIframesAndWaitForSourceMaps
 Removed: 0 uiSourceCodes
-Workspace: 20 uiSourceCodes.
+Workspace: 19 uiSourceCodes.
     debugger:///VM[XXX] bindings-test.js
     debugger:///VM[XXX] inspector-test.js
 [+] debugger:///VM[XXX] sourcemap-script.js
@@ -30,7 +30,6 @@
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-script.js
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-script.js
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-style.css
-[+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-style.css
 [+] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-typescript.ts
     http://127.0.0.1:8000/inspector/bindings/suspendtarget-bindings.html
     http://127.0.0.1:8000/inspector/inspector-test.js
@@ -38,7 +37,7 @@
 [+] _test_create-iframe2.js
 
 Running: Suspending targets.
-Removed: 18 uiSourceCodes
+Removed: 17 uiSourceCodes
 [-] debugger:///VM[XXX] bindings-test.js
 [-] debugger:///VM[XXX] inspector-test.js
 [-] debugger:///VM[XXX] sourcemap-script.js
@@ -52,7 +51,6 @@
 [-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-script.js
 [-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-script.js
 [-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-style.css
-[-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-style.css
 [-] http://127.0.0.1:8000/inspector/bindings/resources/sourcemap-typescript.ts
 [-] http://127.0.0.1:8000/inspector/inspector-test.js
 [-] _test_create-iframe1.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-overwrite-css.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-overwrite-css.html
index 8390b16..62205c35 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-overwrite-css.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-overwrite-css.html
@@ -63,7 +63,7 @@
             InspectorTest.cssModel.setStyleSheetText(styleSheet.id, 'body {color: blue}');
             // Expect StylesSourceMapping to sync styleSheet with network UISourceCode.
             // Persistence acts synchronously.
-            InspectorTest.addSniffer(Bindings.StylesSourceMapping.prototype, '_styleFileSyncedForTest', next);
+            InspectorTest.addSniffer(Bindings.StyleFile.prototype, '_styleFileSyncedForTest', next);
 
             function throwProtocolError(styleSheetId) {
                 InspectorTest.addResult('Protocol Error: FAKE PROTOCOL ERROR');
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/referrer-policy-invalid-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/referrer-policy-invalid-expected.txt
index 304e985..75cd596 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/referrer-policy-invalid-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/referrer-policy-invalid-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 8: Failed to set referrer policy: The value 'invalid' is not one of 'always', 'default', 'never', 'origin-when-crossorigin', 'no-referrer', 'no-referrer-when-downgrade', 'origin', 'origin-when-cross-origin', or 'unsafe-url'. The referrer policy has been left unchanged.
+CONSOLE ERROR: line 8: Failed to set referrer policy: The value 'invalid' is not one of 'always', 'default', 'never', 'origin-when-crossorigin', 'no-referrer', 'no-referrer-when-downgrade', 'origin', 'origin-when-cross-origin', 'same-origin', 'strict-origin', 'strict-origin-when-cross-origin', or 'unsafe-url'. The referrer policy has been left unchanged.
 This test checks that an invalid referrer policy is ignored when navigating from an insecure URL to another insecure URL. The test passes if the printed referrer is the full URL.
 
 
diff --git a/third_party/WebKit/LayoutTests/inspector-enabled/sources/debugger/linkifier.html b/third_party/WebKit/LayoutTests/inspector-enabled/sources/debugger/linkifier.html
index 0edd90c..c1950ce 100644
--- a/third_party/WebKit/LayoutTests/inspector-enabled/sources/debugger/linkifier.html
+++ b/third_party/WebKit/LayoutTests/inspector-enabled/sources/debugger/linkifier.html
@@ -80,8 +80,9 @@
 
     function liveLocationsCount()
     {
-        var scriptInfo = Bindings.debuggerWorkspaceBinding._ensureInfoForScript(script);
-        return scriptInfo._locations ? scriptInfo._locations.size : 0;
+        var modelData = Bindings.debuggerWorkspaceBinding._debuggerModelToData.get(script.debuggerModel);
+        var locations = modelData._locations.get(script);
+        return locations.size;
     }
 }
 
diff --git a/third_party/WebKit/LayoutTests/inspector/components/linkifier.html b/third_party/WebKit/LayoutTests/inspector/components/linkifier.html
index c0f86a8..622ad644 100644
--- a/third_party/WebKit/LayoutTests/inspector/components/linkifier.html
+++ b/third_party/WebKit/LayoutTests/inspector/components/linkifier.html
@@ -52,8 +52,9 @@
 
     function dumpLiveLocationsCount()
     {
-        var scriptInfo = Bindings.debuggerWorkspaceBinding._ensureInfoForScript(script);
-        InspectorTest.addResult("Live locations count: " + (scriptInfo._locations ? scriptInfo._locations.size : 0));
+        var modelData = Bindings.debuggerWorkspaceBinding._debuggerModelToData.get(script.debuggerModel);
+        var locations = modelData._locations.get(script);
+        InspectorTest.addResult("Live locations count: " + locations.size);
         InspectorTest.addResult("");
     }
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
index d10a398..392ad45 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
@@ -1,23 +1,15 @@
 Tests that editing sourcecode which is referred by multiple stylesheets (via sourceURL comment) updates all stylesheets.
 
 Inspected node
-[expanded] 
-element.style { ()
 
-[expanded] 
-#inspected { (stylesheet.css:2 -> stylesheet.css:2:13)
-    font-size: 2em;
+Headers count: 3
 
-[expanded] 
-#inspected { (stylesheet.css:2 -> stylesheet.css:2:13)
-/-- overloaded --/     font-size: 2em;
+Running: Make edits with Sources Panel
+Both headers and uiSourceCode content:
+div{color:EDITED;}
 
-[expanded] 
-#inspected { (stylesheet.css:2 -> stylesheet.css:2:13)
-/-- overloaded --/     font-size: 2em;
 
-[expanded] 
-div { (user agent stylesheet)
-    display: block;
-
+Running: Make edits via css model
+Both headers and uiSourceCode content:
+* { --foo: "bar" }
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags.html
index 884db48..bf02e81 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags.html
@@ -4,38 +4,47 @@
 <script src="../../../http/tests/inspector/elements-test.js"></script>
 <script src="../../../http/tests/inspector/debugger-test.js"></script>
 <script src="../../../http/tests/inspector/live-edit-test.js"></script>
+<script src="../../../http/tests/inspector/bindings/bindings-test.js"></script>
 <script>
 
 function prepareTest()
 {
-    var text = document.querySelector(".stylesheet-text").textContent;
-    for (var i = 0; i < 3; ++i) {
-        var style = document.createElement("style");
-        style.textContent = text;
-        document.head.appendChild(style);
-    }
     runTest();
 }
 
-function test()
+async function test()
 {
-    InspectorTest.showScriptSource("stylesheet.css", onEditorOpened);
+    await InspectorTest.attachShadowDOM('shadow1', '#template'),
+    await InspectorTest.attachFrame('frame', './resources/frame.html');
+    var uiSourceCode = await InspectorTest.waitForUISourceCode('stylesheet.css');
+    var headers = InspectorTest.cssModel.styleSheetHeaders().filter(header => header.resourceURL().endsWith('stylesheet.css'));
+    InspectorTest.addResult('Headers count: ' + headers.length);
 
-    function onEditorOpened(sourceFrame)
-    {
-        InspectorTest.addSniffer(SDK.CSSModel.prototype, "_fireStyleSheetChanged", didEditStyleSheet);
-        InspectorTest.replaceInSource(sourceFrame, "100px", "2em");
-    }
+    InspectorTest.markStep('Make edits with Sources Panel');
+    var sourceFrame = await new Promise(x => InspectorTest.showScriptSource("stylesheet.css", x));
+    InspectorTest.replaceInSource(sourceFrame, 'red', 'EDITED');
+    await InspectorTest.addSnifferPromise(Bindings.StyleFile.prototype, '_styleFileSyncedForTest');
+    await checkHeadersContent();
 
-    function didEditStyleSheet()
-    {
-        InspectorTest.selectNodeAndWaitForStyles("inspected", didSelectNode);
-    }
 
-    function didSelectNode()
-    {
-        InspectorTest.dumpSelectedElementStyles(true, false);
-        InspectorTest.completeTest();
+    InspectorTest.markStep('Make edits via css model');
+    InspectorTest.cssModel.setStyleSheetText(headers[0].id, '* { --foo: "bar" }');
+    await InspectorTest.addSnifferPromise(Bindings.StyleFile.prototype, '_styleFileSyncedForTest');
+    await checkHeadersContent();
+    InspectorTest.completeTest();
+
+
+    async function checkHeadersContent(expected) {
+        var contents = await Promise.all(headers.map(header => header.requestContent()));
+        contents.push(uiSourceCode.workingCopy());
+        var dedup = new Set(contents);
+        if (dedup.size !== 1) {
+            InspectorTest.addResult('ERROR: contents are out of sync!');
+            InspectorTest.completeTest();
+            return;
+        }
+        InspectorTest.addResult('Both headers and uiSourceCode content:');
+        InspectorTest.addResult(dedup.firstValue());
     }
 }
 </script>
@@ -47,12 +56,16 @@
 
 <div id="inspected">Inspected node</div>
 
-<div class="stylesheet-text" hidden>
-#inspected {
-    font-size: 100px;
-}
+<style>div{color:red;}
 /*# sourceURL=stylesheet.css */
-</div>
+</style>
+
+<template id='template'>
+<style>div{color:red;}
+/*# sourceURL=stylesheet.css */
+</style>
+<p>Hi! I'm ShadowDOM v1!</p>
+</template>
 
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/resources/frame.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/resources/frame.html
new file mode 100644
index 0000000..dbca84d1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/resources/frame.html
@@ -0,0 +1,6 @@
+<style>div{color:red;}
+/*# sourceURL=stylesheet.css */
+</style>
+<div>
+Hi, i'm frame
+</div>
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-history.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-history.html
index 7f1f33a..d48e7640 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-history.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-history.html
@@ -31,7 +31,7 @@
 
             function testSetResourceContentMinor(next)
             {
-                InspectorTest.addSniffer(Bindings.StyleFile.prototype, "_styleContentSet", styleUpdatedMinor);
+                InspectorTest.addSniffer(Bindings.StyleFile.prototype, "_styleFileSyncedForTest", styleUpdatedMinor);
                 uiSourceCode.setWorkingCopy("body {\n  margin: 15px;\n  padding: 10px;\n}");
 
                 function styleUpdatedMinor()
@@ -42,12 +42,12 @@
 
             function testSetResourceContentMajor(next)
             {
-                InspectorTest.addSniffer(Bindings.StyleFile.prototype, "_styleContentSet", styleUpdatedMinor);
+                InspectorTest.addSniffer(Bindings.StyleFile.prototype, "_styleFileSyncedForTest", styleUpdatedMinor);
                 uiSourceCode.setWorkingCopy("body {\n  margin: 20px;\n  padding: 10px;\n}");
 
                 function styleUpdatedMinor()
                 {
-                    InspectorTest.addSniffer(Bindings.StyleFile.prototype, "_styleContentSet", styleUpdatedMajor);
+                    InspectorTest.addSniffer(Bindings.StyleFile.prototype, "_styleFileSyncedForTest", styleUpdatedMajor);
                     uiSourceCode.commitWorkingCopy(function() { });
 
                     function styleUpdatedMajor()
diff --git a/third_party/WebKit/LayoutTests/inspector/inspector-backend-commands-expected.txt b/third_party/WebKit/LayoutTests/inspector/inspector-backend-commands-expected.txt
index 41dd126..a2294ec7 100644
--- a/third_party/WebKit/LayoutTests/inspector/inspector-backend-commands-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/inspector-backend-commands-expected.txt
@@ -2,10 +2,10 @@
 
 error: Request Profiler.commandError failed. {"message":"this is the error message"}
 commandError: then result: null
-commandArgs0: then result: null
+commandArgs0: then result: undefined
 error: Protocol Error: Extra 1 arguments in a call to method 'Profiler.commandArgs0'.
 commandArgs0: then result: null
-commandArgs1Rets0: then result: null
+commandArgs1Rets0: then result: undefined
 error: Protocol Error: Extra 1 arguments in a call to method 'Profiler.commandArgs1Rets0'.
 commandArgs1Rets0: then result: null
 error: Protocol Error: Invalid type of argument 'arg1' for method 'Profiler.commandArgs1Rets0' call. It must be 'number' but it is 'string'.
diff --git a/third_party/WebKit/Source/build/scripts/templates/StylePropertyShorthand.cpp.tmpl b/third_party/WebKit/Source/build/scripts/templates/StylePropertyShorthand.cpp.tmpl
index 6bec053..db623c59 100644
--- a/third_party/WebKit/Source/build/scripts/templates/StylePropertyShorthand.cpp.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/StylePropertyShorthand.cpp.tmpl
@@ -40,6 +40,17 @@
 }
 {% endfor %}
 
+// TODO(ericwilligers): Retire this when offset-position and offset-anchor ship
+const StylePropertyShorthand& offsetShorthandWithoutPositionAnchor() {
+  static const CSSPropertyID offsetProperties[] = {
+    CSSPropertyOffsetPath,
+    CSSPropertyOffsetDistance,
+    CSSPropertyOffsetRotate,
+  };
+  DEFINE_STATIC_LOCAL(StylePropertyShorthand, offsetLonghands, (CSSPropertyOffset, offsetProperties, WTF_ARRAY_LENGTH(offsetProperties)));
+  return offsetLonghands;
+}
+
 // Returns an empty list if the property is not a shorthand
 const StylePropertyShorthand& shorthandForProperty(CSSPropertyID propertyID) {
   // FIXME: We shouldn't switch between shorthand/not shorthand based on a runtime flag
@@ -48,6 +59,9 @@
   if (propertyID == CSSPropertyTextDecoration &&
       !RuntimeEnabledFeatures::CSS3TextDecorationsEnabled())
     return emptyShorthand;
+  if (propertyID == CSSPropertyOffset &&
+      !RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled())
+    return offsetShorthandWithoutPositionAnchor();
   switch (propertyID) {
     {% for property_id, property in properties.items() %}
     case {{property_id}}:
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 75bdb885..95a409d 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -3349,7 +3349,7 @@
     },
     {
       name: "offset",
-      longhands: "offset-path;offset-distance;offset-rotate",
+      longhands: "offset-position;offset-path;offset-distance;offset-rotate;offset-anchor",
     },
     {
       name: "outline",
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
index 6e69f48..3713d852 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
+++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
@@ -1892,6 +1892,44 @@
   return list;
 }
 
+CSSValue* ComputedStyleCSSValueMapping::ValueForOffset(
+    const ComputedStyle& style,
+    const LayoutObject* layout_object,
+    Node* styled_node,
+    bool allow_visited_style) {
+  CSSValueList* list = CSSValueList::CreateSpaceSeparated();
+  if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
+    CSSValue* position = ValueForPosition(style.OffsetPosition(), style);
+    if (!position->IsIdentifierValue())
+      list->Append(*position);
+    else
+      DCHECK(ToCSSIdentifierValue(position)->GetValueID() == CSSValueAuto);
+  }
+
+  CSSPropertyID longhands[] = {CSSPropertyOffsetPath, CSSPropertyOffsetDistance,
+                               CSSPropertyOffsetRotate};
+  for (CSSPropertyID longhand : longhands) {
+    const CSSValue* value = ComputedStyleCSSValueMapping::Get(
+        longhand, style, layout_object, styled_node, allow_visited_style);
+    DCHECK(value);
+    list->Append(*value);
+  }
+
+  if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
+    CSSValue* anchor = ValueForPosition(style.OffsetAnchor(), style);
+    if (!anchor->IsIdentifierValue()) {
+      // Add a slash before anchor.
+      CSSValueList* result = CSSValueList::CreateSlashSeparated();
+      result->Append(*list);
+      result->Append(*anchor);
+      return result;
+    } else {
+      DCHECK(ToCSSIdentifierValue(anchor)->GetValueID() == CSSValueAuto);
+    }
+  }
+  return list;
+}
+
 CSSValue* ComputedStyleCSSValueMapping::ValueForFont(
     const ComputedStyle& style) {
   // Add a slash between size and line-height.
@@ -3428,8 +3466,8 @@
       return nullptr;
 
     case CSSPropertyOffset:
-      return ValuesForShorthandProperty(offsetShorthand(), style, layout_object,
-                                        styled_node, allow_visited_style);
+      return ValueForOffset(style, layout_object, styled_node,
+                            allow_visited_style);
 
     case CSSPropertyOffsetAnchor:
       return ValueForPosition(style.OffsetAnchor(), style);
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.h b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.h
index 9161a8b..39088809 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.h
+++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.h
@@ -52,6 +52,11 @@
   static CSSValue* ValueForFilter(const ComputedStyle&,
                                   const FilterOperations&);
   static CSSValue* ValueForFont(const ComputedStyle&);
+
+  static CSSValue* ValueForOffset(const ComputedStyle&,
+                                  const LayoutObject*,
+                                  Node* styled_node,
+                                  bool allow_visited_style);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp b/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp
index 44d2b01..21c3734 100644
--- a/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp
+++ b/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp
@@ -475,7 +475,7 @@
     case CSSPropertyMargin:
       return Get4Values(marginShorthand());
     case CSSPropertyOffset:
-      return GetShorthandValue(offsetShorthand());
+      return OffsetValue();
     case CSSPropertyWebkitMarginCollapse:
       return GetShorthandValue(webkitMarginCollapseShorthand());
     case CSSPropertyOverflow:
@@ -653,6 +653,48 @@
   return result.ToString();
 }
 
+String StylePropertySerializer::OffsetValue() const {
+  StringBuilder result;
+  if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
+    const CSSValue* position =
+        property_set_.GetPropertyCSSValue(CSSPropertyOffsetPosition);
+    if (!position->IsInitialValue()) {
+      result.Append(position->CssText());
+    }
+  }
+  const CSSValue* path =
+      property_set_.GetPropertyCSSValue(CSSPropertyOffsetPath);
+  const CSSValue* distance =
+      property_set_.GetPropertyCSSValue(CSSPropertyOffsetDistance);
+  const CSSValue* rotate =
+      property_set_.GetPropertyCSSValue(CSSPropertyOffsetRotate);
+  if (!path->IsInitialValue()) {
+    if (!result.IsEmpty())
+      result.Append(" ");
+    result.Append(path->CssText());
+    if (!distance->IsInitialValue()) {
+      result.Append(" ");
+      result.Append(distance->CssText());
+    }
+    if (!rotate->IsInitialValue()) {
+      result.Append(" ");
+      result.Append(rotate->CssText());
+    }
+  } else {
+    DCHECK(distance->IsInitialValue());
+    DCHECK(rotate->IsInitialValue());
+  }
+  if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
+    const CSSValue* anchor =
+        property_set_.GetPropertyCSSValue(CSSPropertyOffsetAnchor);
+    if (!anchor->IsInitialValue()) {
+      result.Append(" / ");
+      result.Append(anchor->CssText());
+    }
+  }
+  return result.ToString();
+}
+
 String StylePropertySerializer::Get4Values(
     const StylePropertyShorthand& shorthand) const {
   // Assume the properties are in the usual order top, right, bottom, left.
diff --git a/third_party/WebKit/Source/core/css/StylePropertySerializer.h b/third_party/WebKit/Source/core/css/StylePropertySerializer.h
index 77b10cf..cddd0193 100644
--- a/third_party/WebKit/Source/core/css/StylePropertySerializer.h
+++ b/third_party/WebKit/Source/core/css/StylePropertySerializer.h
@@ -55,6 +55,7 @@
   String FontVariantValue() const;
   void AppendFontLonghandValueIfNotNormal(CSSPropertyID,
                                           StringBuilder& result) const;
+  String OffsetValue() const;
   String BackgroundRepeatPropertyValue() const;
   String GetPropertyText(CSSPropertyID,
                          const String& value,
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index eda9dfb1..9fc136e 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -38,6 +38,8 @@
 #include "core/css/parser/CSSVariableParser.h"
 #include "core/css/parser/FontVariantLigaturesParser.h"
 #include "core/css/parser/FontVariantNumericParser.h"
+#include "core/css/properties/CSSPropertyAPIOffsetAnchor.h"
+#include "core/css/properties/CSSPropertyAPIOffsetPosition.h"
 #include "core/css/properties/CSSPropertyAlignmentUtils.h"
 #include "core/css/properties/CSSPropertyAnimationNameUtils.h"
 #include "core/css/properties/CSSPropertyBorderImageUtils.h"
@@ -703,23 +705,77 @@
   return list;
 }
 
-// offset: <offset-path> <offset-distance> <offset-rotate>
 bool CSSPropertyParser::ConsumeOffsetShorthand(bool important) {
   DCHECK(context_);
+  const CSSValue* offset_position =
+      CSSPropertyAPIOffsetPosition::parseSingleValue(range_, *context_,
+                                                     CSSParserLocalContext());
   const CSSValue* offset_path =
       CSSPropertyOffsetPathUtils::ConsumeOffsetPath(range_, *context_);
-  const CSSValue* offset_distance =
-      ConsumeLengthOrPercent(range_, context_->Mode(), kValueRangeAll);
-  const CSSValue* offset_rotate = ConsumeOffsetRotate(range_, *context_);
-  if (!offset_path || !offset_distance || !offset_rotate || !range_.AtEnd())
+  const CSSValue* offset_distance = nullptr;
+  const CSSValue* offset_rotate = nullptr;
+  if (offset_path) {
+    offset_distance =
+        ConsumeLengthOrPercent(range_, context_->Mode(), kValueRangeAll);
+    offset_rotate = ConsumeOffsetRotate(range_, *context_);
+    if (offset_rotate && !offset_distance) {
+      offset_distance =
+          ConsumeLengthOrPercent(range_, context_->Mode(), kValueRangeAll);
+    }
+  }
+  const CSSValue* offset_anchor = nullptr;
+  if (ConsumeSlashIncludingWhitespace(range_)) {
+    offset_anchor = CSSPropertyAPIOffsetAnchor::parseSingleValue(
+        range_, *context_, CSSParserLocalContext());
+    if (!offset_anchor)
+      return false;
+  }
+  if ((!offset_position && !offset_path) || !range_.AtEnd())
     return false;
 
-  AddParsedProperty(CSSPropertyOffsetPath, CSSPropertyOffset, *offset_path,
-                    important);
-  AddParsedProperty(CSSPropertyOffsetDistance, CSSPropertyOffset,
-                    *offset_distance, important);
-  AddParsedProperty(CSSPropertyOffsetRotate, CSSPropertyOffset, *offset_rotate,
-                    important);
+  if ((offset_position || offset_anchor) &&
+      !RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled())
+    return false;
+
+  if (offset_position) {
+    AddParsedProperty(CSSPropertyOffsetPosition, CSSPropertyOffset,
+                      *offset_position, important);
+  } else if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
+    AddParsedProperty(CSSPropertyOffsetPosition, CSSPropertyOffset,
+                      *CSSInitialValue::Create(), important);
+  }
+
+  if (offset_path) {
+    AddParsedProperty(CSSPropertyOffsetPath, CSSPropertyOffset, *offset_path,
+                      important);
+  } else {
+    AddParsedProperty(CSSPropertyOffsetPath, CSSPropertyOffset,
+                      *CSSInitialValue::Create(), important);
+  }
+
+  if (offset_distance) {
+    AddParsedProperty(CSSPropertyOffsetDistance, CSSPropertyOffset,
+                      *offset_distance, important);
+  } else {
+    AddParsedProperty(CSSPropertyOffsetDistance, CSSPropertyOffset,
+                      *CSSInitialValue::Create(), important);
+  }
+
+  if (offset_rotate) {
+    AddParsedProperty(CSSPropertyOffsetRotate, CSSPropertyOffset,
+                      *offset_rotate, important);
+  } else {
+    AddParsedProperty(CSSPropertyOffsetRotate, CSSPropertyOffset,
+                      *CSSInitialValue::Create(), important);
+  }
+
+  if (offset_anchor) {
+    AddParsedProperty(CSSPropertyOffsetAnchor, CSSPropertyOffset,
+                      *offset_anchor, important);
+  } else if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
+    AddParsedProperty(CSSPropertyOffsetAnchor, CSSPropertyOffset,
+                      *CSSInitialValue::Create(), important);
+  }
 
   return true;
 }
@@ -932,17 +988,17 @@
   }
 }
 
-static CSSValue* ConsumeCommaSeparatedBackgroundComponent(
+static CSSValueList* ConsumeCommaSeparatedBackgroundComponent(
     CSSPropertyID unresolved_property,
     CSSParserTokenRange& range,
     const CSSParserContext* context) {
-  CSSValue* result = nullptr;
+  CSSValueList* result = CSSValueList::CreateCommaSeparated();
   do {
     CSSValue* value =
         ConsumeBackgroundComponent(unresolved_property, range, context);
     if (!value)
       return nullptr;
-    AddBackgroundValue(result, value);
+    result->Append(*value);
   } while (ConsumeCommaIncludingWhitespace(range));
   return result;
 }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp
index bbaed41..01df3c9 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParserTest.cpp
@@ -24,7 +24,7 @@
   const CSSValue* value = CSSParser::ParseSingleValue(
       CSSPropertyBackgroundImage, "paint(foo, func1(1px, 3px), red)");
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsImageGeneratorValue());
+  ASSERT_TRUE(value->IsValueList());
   EXPECT_EQ(value->CssText(), "paint(foo, func1(1px, 3px), red)");
 }
 
@@ -32,7 +32,7 @@
   const CSSValue* value =
       CSSParser::ParseSingleValue(CSSPropertyBackgroundImage, "paint(foo)");
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsImageGeneratorValue());
+  ASSERT_TRUE(value->IsValueList());
   EXPECT_EQ(value->CssText(), "paint(foo)");
 }
 
@@ -40,7 +40,7 @@
   const CSSValue* value = CSSParser::ParseSingleValue(
       CSSPropertyBackgroundImage, "paint(bar, 10px, red)");
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsImageGeneratorValue());
+  ASSERT_TRUE(value->IsValueList());
   EXPECT_EQ(value->CssText(), "paint(bar, 10px, red)");
 }
 
diff --git a/third_party/WebKit/Source/core/dom/DocumentTest.cpp b/third_party/WebKit/Source/core/dom/DocumentTest.cpp
index 5b855f7..d06b908f 100644
--- a/third_party/WebKit/Source/core/dom/DocumentTest.cpp
+++ b/third_party/WebKit/Source/core/dom/DocumentTest.cpp
@@ -466,6 +466,10 @@
       {"origin", kReferrerPolicyOrigin, false},
       {"origin-when-crossorigin", kReferrerPolicyOriginWhenCrossOrigin, true},
       {"origin-when-cross-origin", kReferrerPolicyOriginWhenCrossOrigin, false},
+      {"same-origin", kReferrerPolicySameOrigin, false},
+      {"strict-origin", kReferrerPolicyStrictOrigin, false},
+      {"strict-origin-when-cross-origin",
+       kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin, false},
       {"unsafe-url", kReferrerPolicyAlways},
   };
 
diff --git a/third_party/WebKit/Source/core/dom/ExecutionContext.cpp b/third_party/WebKit/Source/core/dom/ExecutionContext.cpp
index 98c91f3..1a38ac5 100644
--- a/third_party/WebKit/Source/core/dom/ExecutionContext.cpp
+++ b/third_party/WebKit/Source/core/dom/ExecutionContext.cpp
@@ -215,7 +215,9 @@
                  ? "'always', 'default', 'never', 'origin-when-crossorigin', "
                  : "") +
             "'no-referrer', 'no-referrer-when-downgrade', 'origin', "
-            "'origin-when-cross-origin', or 'unsafe-url'. The referrer policy "
+            "'origin-when-cross-origin', 'same-origin', 'strict-origin', "
+            "'strict-origin-when-cross-origin', or 'unsafe-url'. The referrer "
+            "policy "
             "has been left unchanged."));
     return;
   }
diff --git a/third_party/WebKit/Source/core/frame/FrameSerializer.cpp b/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
index a225865..2b43599 100644
--- a/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
@@ -267,7 +267,14 @@
 
 FrameSerializer::FrameSerializer(Deque<SerializedResource>& resources,
                                  Delegate& delegate)
-    : resources_(&resources), is_serializing_css_(false), delegate_(delegate) {}
+    : resources_(&resources),
+      is_serializing_css_(false),
+      delegate_(delegate),
+      total_image_count_(0),
+      loaded_image_count_(0),
+      total_css_count_(0),
+      loaded_css_count_(0),
+      should_collect_problem_metric_(false) {}
 
 void FrameSerializer::SerializeFrame(const LocalFrame& frame) {
   TRACE_EVENT0("page-serialization", "FrameSerializer::serializeFrame");
@@ -299,6 +306,8 @@
         SharedBuffer::Create(frame_html.data(), frame_html.length())));
   }
 
+  should_collect_problem_metric_ =
+      delegate_.ShouldCollectProblemMetric() && frame.IsMainFrame();
   for (Node* node : serialized_nodes) {
     DCHECK(node);
     if (!node->IsElementNode())
@@ -341,6 +350,36 @@
         SerializeCSSStyleSheet(*sheet, KURL());
     }
   }
+  if (should_collect_problem_metric_) {
+    // Report detectors through UMA.
+    // We're having exact 21 buckets for percentage because we want to have 5%
+    // in each bucket to avoid potential spikes in the distribution.
+    UMA_HISTOGRAM_COUNTS_100(
+        "PageSerialization.ProblemDetection.TotalImageCount",
+        static_cast<int64_t>(total_image_count_));
+    if (total_image_count_ > 0) {
+      DCHECK_LE(loaded_image_count_, total_image_count_);
+      DEFINE_STATIC_LOCAL(
+          LinearHistogram, image_histogram,
+          ("PageSerialization.ProblemDetection.LoadedImagePercentage", 1, 100,
+           21));
+      image_histogram.Count(
+          static_cast<int64_t>(loaded_image_count_ * 100 / total_image_count_));
+    }
+
+    UMA_HISTOGRAM_COUNTS_100("PageSerialization.ProblemDetection.TotalCSSCount",
+                             static_cast<int64_t>(total_css_count_));
+    if (total_css_count_ > 0) {
+      DCHECK_LE(loaded_css_count_, total_css_count_);
+      DEFINE_STATIC_LOCAL(
+          LinearHistogram, css_histogram,
+          ("PageSerialization.ProblemDetection.LoadedCSSPercentage", 1, 100,
+           21));
+      css_histogram.Count(
+          static_cast<int64_t>(loaded_css_count_ * 100 / total_css_count_));
+    }
+    should_collect_problem_metric_ = false;
+  }
 }
 
 void FrameSerializer::SerializeCSSStyleSheet(CSSStyleSheet& style_sheet,
@@ -354,6 +393,13 @@
                          delegate_.ShouldSkipResourceWithURL(url))) {
     return;
   }
+  if (!is_inline_css)
+    resource_urls_.insert(url);
+  if (should_collect_problem_metric_ && !is_inline_css) {
+    total_css_count_++;
+    if (style_sheet.LoadCompleted())
+      loaded_css_count_++;
+  }
 
   TRACE_EVENT2("page-serialization", "FrameSerializer::serializeCSSStyleSheet",
                "type", "CSS", "url", url.ElidedString().Utf8().data());
@@ -390,7 +436,6 @@
     resources_->push_back(
         SerializedResource(url, String("text/css"),
                            SharedBuffer::Create(text.data(), text.length())));
-    resource_urls_.insert(url);
   }
 
   // Sub resources need to be serialized even if the CSS definition doesn't
@@ -473,14 +518,19 @@
   }
 
   resources_->push_back(SerializedResource(url, mime_type, std::move(data)));
-  resource_urls_.insert(url);
 }
 
 void FrameSerializer::AddImageToResources(ImageResourceContent* image,
                                           const KURL& url) {
-  if (!image || !image->HasImage() || image->ErrorOccurred() ||
-      !ShouldAddURL(url))
+  if (!ShouldAddURL(url))
     return;
+  resource_urls_.insert(url);
+  if (should_collect_problem_metric_)
+    total_image_count_++;
+  if (!image || !image->HasImage() || image->ErrorOccurred())
+    return;
+  if (should_collect_problem_metric_ && image->IsLoaded())
+    loaded_image_count_++;
 
   TRACE_EVENT2("page-serialization", "FrameSerializer::addImageToResources",
                "type", "image", "url", url.ElidedString().Utf8().data());
@@ -506,8 +556,10 @@
 }
 
 void FrameSerializer::AddFontToResources(FontResource* font) {
-  if (!font || !font->IsLoaded() || !font->ResourceBuffer() ||
-      !ShouldAddURL(font->Url()))
+  if (!font || !ShouldAddURL(font->Url()))
+    return;
+  resource_urls_.insert(font->Url());
+  if (!font || !font->IsLoaded() || !font->ResourceBuffer())
     return;
 
   RefPtr<const SharedBuffer> data(font->ResourceBuffer());
diff --git a/third_party/WebKit/Source/core/frame/FrameSerializer.h b/third_party/WebKit/Source/core/frame/FrameSerializer.h
index b53d02d..8a7b366 100644
--- a/third_party/WebKit/Source/core/frame/FrameSerializer.h
+++ b/third_party/WebKit/Source/core/frame/FrameSerializer.h
@@ -107,6 +107,8 @@
     virtual Vector<Attribute> GetCustomAttributes(const Element&) {
       return Vector<Attribute>();
     }
+
+    virtual bool ShouldCollectProblemMetric() { return false; }
   };
 
   // Constructs a serializer that will write output to the given deque of
@@ -146,11 +148,19 @@
   void RetrieveResourcesForCSSValue(const CSSValue&, Document&);
 
   Deque<SerializedResource>* resources_;
+  // This hashset is only used for de-duplicating resources to be serialized.
   HashSet<KURL> resource_urls_;
 
   bool is_serializing_css_;
 
   Delegate& delegate_;
+
+  // Variables for problem detection during serialization.
+  int total_image_count_;
+  int loaded_image_count_;
+  int total_css_count_;
+  int loaded_css_count_;
+  bool should_collect_problem_metric_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameClient.h b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
index 12070f1..46fe331 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameClient.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
@@ -347,6 +347,8 @@
   virtual TextCheckerClient& GetTextCheckerClient() const = 0;
 
   virtual std::unique_ptr<WebURLLoader> CreateURLLoader() = 0;
+
+  virtual void AnnotatedRegionsChanged() = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index f01be123..15b9b87 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -2826,8 +2826,9 @@
   if (new_regions == document->AnnotatedRegions())
     return;
   document->SetAnnotatedRegions(new_regions);
-  if (Page* page = frame_->GetPage())
-    page->GetChromeClient().AnnotatedRegionsChanged();
+
+  DCHECK(frame_->Client());
+  frame_->Client()->AnnotatedRegionsChanged();
 }
 
 void LocalFrameView::DidAttachDocument() {
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
index b0f7222..eac0d94b 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
@@ -704,6 +704,22 @@
        "bla.gif", "http://example.test/", Resource::kImage, 0,
        kReferrerPolicyOriginWhenCrossOrigin, nullptr},
       {"http://example.test",
+       "<link rel=preload as=image referrerpolicy='same-origin' "
+       "href='bla.gif'/>",
+       "bla.gif", "http://example.test/", Resource::kImage, 0,
+       kReferrerPolicySameOrigin, nullptr},
+      {"http://example.test",
+       "<link rel=preload as=image referrerpolicy='strict-origin' "
+       "href='bla.gif'/>",
+       "bla.gif", "http://example.test/", Resource::kImage, 0,
+       kReferrerPolicyStrictOrigin, nullptr},
+      {"http://example.test",
+       "<link rel=preload as=image "
+       "referrerpolicy='strict-origin-when-cross-origin' "
+       "href='bla.gif'/>",
+       "bla.gif", "http://example.test/", Resource::kImage, 0,
+       kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin, nullptr},
+      {"http://example.test",
        "<link rel='stylesheet' href='sheet.css' type='text/css'>", "sheet.css",
        "http://example.test/", Resource::kCSSStyleSheet, 0,
        kReferrerPolicyDefault, nullptr},
diff --git a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
index ff4d28eb..0b82cc7 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
@@ -296,7 +296,7 @@
     case kReferrerPolicyDefault:
       if (RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()) {
         return protocol::Network::Request::ReferrerPolicyEnum::
-            NoReferrerWhenDowngradeOriginWhenCrossOrigin;
+            StrictOriginWhenCrossOrigin;
       } else {
         return protocol::Network::Request::ReferrerPolicyEnum::
             NoReferrerWhenDowngrade;
@@ -311,9 +311,13 @@
     case kReferrerPolicyOriginWhenCrossOrigin:
       return protocol::Network::Request::ReferrerPolicyEnum::
           OriginWhenCrossOrigin;
+    case kReferrerPolicySameOrigin:
+      return protocol::Network::Request::ReferrerPolicyEnum::SameOrigin;
+    case kReferrerPolicyStrictOrigin:
+      return protocol::Network::Request::ReferrerPolicyEnum::StrictOrigin;
     case kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin:
       return protocol::Network::Request::ReferrerPolicyEnum::
-          NoReferrerWhenDowngradeOriginWhenCrossOrigin;
+          StrictOriginWhenCrossOrigin;
   }
 
   return protocol::Network::Request::ReferrerPolicyEnum::
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index 3e06181f..826b59f 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -1194,7 +1194,7 @@
                     { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." },
                     { "name": "mixedContentType", "optional": true, "type": "string", "enum": ["blockable", "optionally-blockable", "none"], "description": "The mixed content status of the request, as defined in http://www.w3.org/TR/mixed-content/" },
                     { "name": "initialPriority", "$ref": "ResourcePriority", "description": "Priority of the resource request at the time request is sent."},
-                    { "name": "referrerPolicy", "type": "string", "enum": [ "unsafe-url", "no-referrer-when-downgrade", "no-referrer", "origin", "origin-when-cross-origin", "no-referrer-when-downgrade-origin-when-cross-origin" ], "description": "The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/" },
+                    { "name": "referrerPolicy", "type": "string", "enum": [ "unsafe-url", "no-referrer-when-downgrade", "no-referrer", "origin", "origin-when-cross-origin", "same-origin", "strict-origin", "strict-origin-when-cross-origin" ], "description": "The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/" },
                     { "name": "isLinkPreload", "type": "boolean", "optional": true, "description": "Whether is loaded via link preload." }
                 ]
             },
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.h b/third_party/WebKit/Source/core/loader/EmptyClients.h
index f3e2ca5..d861f4b1 100644
--- a/third_party/WebKit/Source/core/loader/EmptyClients.h
+++ b/third_party/WebKit/Source/core/loader/EmptyClients.h
@@ -208,7 +208,6 @@
 
   void DidAssociateFormControlsAfterLoad(LocalFrame*) override {}
 
-  void AnnotatedRegionsChanged() override {}
   String AcceptLanguages() override;
 
   CompositorWorkerProxyClient* CreateCompositorWorkerProxyClient(
@@ -373,6 +372,8 @@
     return Platform::Current()->CreateURLLoader();
   }
 
+  void AnnotatedRegionsChanged() override {}
+
  protected:
   EmptyLocalFrameClient() {}
 
@@ -432,7 +433,7 @@
   USING_FAST_MALLOC(EmptyContextMenuClient);
 
  public:
-  EmptyContextMenuClient() {}
+  EmptyContextMenuClient() : ContextMenuClient() {}
   ~EmptyContextMenuClient() override {}
   bool ShowContextMenu(const ContextMenu*, bool) override { return false; }
   void ClearContextMenu() override {}
diff --git a/third_party/WebKit/Source/core/loader/LinkLoaderTest.cpp b/third_party/WebKit/Source/core/loader/LinkLoaderTest.cpp
index 6d9aaa319..b6c2d5343 100644
--- a/third_party/WebKit/Source/core/loader/LinkLoaderTest.cpp
+++ b/third_party/WebKit/Source/core/loader/LinkLoaderTest.cpp
@@ -255,6 +255,18 @@
      WebURLRequest::kRequestContextImage, true, true,
      kReferrerPolicyOriginWhenCrossOrigin},
     {"http://example.test/cat.gif", "image", "image/gif", "",
+     kReferrerPolicySameOrigin, kResourceLoadPriorityLow,
+     WebURLRequest::kRequestContextImage, true, true,
+     kReferrerPolicySameOrigin},
+    {"http://example.test/cat.gif", "image", "image/gif", "",
+     kReferrerPolicyStrictOrigin, kResourceLoadPriorityLow,
+     WebURLRequest::kRequestContextImage, true, true,
+     kReferrerPolicyStrictOrigin},
+    {"http://example.test/cat.gif", "image", "image/gif", "",
+     kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
+     kResourceLoadPriorityLow, WebURLRequest::kRequestContextImage, true, true,
+     kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin},
+    {"http://example.test/cat.gif", "image", "image/gif", "",
      kReferrerPolicyNever, kResourceLoadPriorityLow,
      WebURLRequest::kRequestContextImage, true, true, kReferrerPolicyNever}};
 
diff --git a/third_party/WebKit/Source/core/page/BUILD.gn b/third_party/WebKit/Source/core/page/BUILD.gn
index 1cd9cb3a..b210479 100644
--- a/third_party/WebKit/Source/core/page/BUILD.gn
+++ b/third_party/WebKit/Source/core/page/BUILD.gn
@@ -10,6 +10,7 @@
     "AutoscrollController.h",
     "ChromeClient.cpp",
     "ChromeClient.h",
+    "ContextMenuClient.cpp",
     "ContextMenuClient.h",
     "ContextMenuController.cpp",
     "ContextMenuController.h",
diff --git a/third_party/WebKit/Source/core/page/ChromeClient.h b/third_party/WebKit/Source/core/page/ChromeClient.h
index 49c34814..3cbd379 100644
--- a/third_party/WebKit/Source/core/page/ChromeClient.h
+++ b/third_party/WebKit/Source/core/page/ChromeClient.h
@@ -51,7 +51,6 @@
 
 namespace blink {
 
-class AXObject;
 class ColorChooser;
 class ColorChooserClient;
 class CompositorWorkerProxyClient;
@@ -208,8 +207,6 @@
 
   bool Print(LocalFrame*);
 
-  virtual void AnnotatedRegionsChanged() = 0;
-
   virtual ColorChooser* OpenColorChooser(LocalFrame*,
                                          ColorChooserClient*,
                                          const Color&) = 0;
@@ -277,8 +274,6 @@
   virtual void ClosePagePopup(PagePopup*) = 0;
   virtual DOMWindow* PagePopupWindowForTesting() const = 0;
 
-  virtual void PostAccessibilityNotification(AXObject*,
-                                             AXObjectCache::AXNotification) {}
   virtual String AcceptLanguages() = 0;
 
   enum DialogType {
diff --git a/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp b/third_party/WebKit/Source/core/page/ContextMenuClient.cpp
similarity index 96%
rename from third_party/WebKit/Source/web/ContextMenuClientImpl.cpp
rename to third_party/WebKit/Source/core/page/ContextMenuClient.cpp
index b138898..dbca4cd 100644
--- a/third_party/WebKit/Source/web/ContextMenuClientImpl.cpp
+++ b/third_party/WebKit/Source/core/page/ContextMenuClient.cpp
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "web/ContextMenuClientImpl.h"
+#include "core/page/ContextMenuClient.h"
 
 #include "bindings/core/v8/ExceptionState.h"
 #include "core/CSSPropertyNames.h"
@@ -163,8 +163,8 @@
 }
 
 // static
-int ContextMenuClientImpl::ComputeEditFlags(Document& selected_document,
-                                            Editor& editor) {
+int ContextMenuClient::ComputeEditFlags(Document& selected_document,
+                                        Editor& editor) {
   int edit_flags = WebContextMenuData::kCanDoNone;
   if (!selected_document.IsHTMLDocument() &&
       !selected_document.IsXHTMLDocument())
@@ -190,7 +190,7 @@
   return edit_flags;
 }
 
-bool ContextMenuClientImpl::ShouldShowContextMenuFromTouch(
+bool ContextMenuClient::ShouldShowContextMenuFromTouch(
     const WebContextMenuData& data) {
   return web_view_->GetPage()
              ->GetSettings()
@@ -254,8 +254,8 @@
   return ScanForForm(start);
 }
 
-bool ContextMenuClientImpl::ShowContextMenu(const ContextMenu* default_menu,
-                                            bool from_touch) {
+bool ContextMenuClient::ShowContextMenu(const ContextMenu* default_menu,
+                                        bool from_touch) {
   // Displaying the context menu in this function is a big hack as we don't
   // have context, i.e. whether this is being invoked via a script or in
   // response to user input (Mouse event WM_RBUTTONDOWN,
@@ -309,9 +309,10 @@
         isHTMLImageElement(r.InnerNodeOrImageMapImage())) {
       HTMLImageElement* image_element =
           toHTMLImageElement(r.InnerNodeOrImageMapImage());
-      if (image_element && image_element->CachedImage())
+      if (image_element && image_element->CachedImage()) {
         data.image_response = WrappedResourceResponse(
             image_element->CachedImage()->GetResponse());
+      }
     }
   } else if (!r.AbsoluteMediaURL().IsEmpty()) {
     data.src_url = r.AbsoluteMediaURL();
@@ -442,13 +443,15 @@
   }
 
   if (selected_frame->GetEditor().SelectionHasStyle(CSSPropertyDirection,
-                                                    "ltr") != kFalseTriState)
+                                                    "ltr") != kFalseTriState) {
     data.writing_direction_left_to_right |=
         WebContextMenuData::kCheckableMenuItemChecked;
+  }
   if (selected_frame->GetEditor().SelectionHasStyle(CSSPropertyDirection,
-                                                    "rtl") != kFalseTriState)
+                                                    "rtl") != kFalseTriState) {
     data.writing_direction_right_to_left |=
         WebContextMenuData::kCheckableMenuItemChecked;
+  }
 
   data.referrer_policy = static_cast<WebReferrerPolicy>(
       selected_frame->GetDocument()->GetReferrerPolicy());
@@ -507,7 +510,7 @@
   return true;
 }
 
-void ContextMenuClientImpl::ClearContextMenu() {
+void ContextMenuClient::ClearContextMenu() {
   HitTestResult r =
       web_view_->GetPage()->GetContextMenuController().GetHitTestResult();
   LocalFrame* selected_frame = r.InnerNodeFrame();
@@ -559,9 +562,8 @@
   sub_menu_items.Swap(output_items);
 }
 
-void ContextMenuClientImpl::PopulateCustomMenuItems(
-    const ContextMenu* default_menu,
-    WebContextMenuData* data) {
+void ContextMenuClient::PopulateCustomMenuItems(const ContextMenu* default_menu,
+                                                WebContextMenuData* data) {
   PopulateSubMenuItems(default_menu->Items(), data->custom_items);
 }
 
diff --git a/third_party/WebKit/Source/core/page/ContextMenuClient.h b/third_party/WebKit/Source/core/page/ContextMenuClient.h
index 56fa881..14be843 100644
--- a/third_party/WebKit/Source/core/page/ContextMenuClient.h
+++ b/third_party/WebKit/Source/core/page/ContextMenuClient.h
@@ -26,17 +26,33 @@
 #ifndef ContextMenuClient_h
 #define ContextMenuClient_h
 
+#include "core/CoreExport.h"
+
 namespace blink {
 
 class ContextMenu;
+class Document;
+class Editor;
+class WebViewBase;
+struct WebContextMenuData;
 
-class ContextMenuClient {
+class CORE_EXPORT ContextMenuClient {
  public:
+  explicit ContextMenuClient(WebViewBase& web_view) : web_view_(&web_view) {}
   virtual ~ContextMenuClient() {}
 
   // Returns whether a Context Menu was actually shown.
-  virtual bool ShowContextMenu(const ContextMenu*, bool from_touch) = 0;
-  virtual void ClearContextMenu() = 0;
+  virtual bool ShowContextMenu(const ContextMenu*, bool from_touch);
+  virtual void ClearContextMenu();
+
+ protected:
+  ContextMenuClient() : web_view_(nullptr) {}
+
+ private:
+  void PopulateCustomMenuItems(const ContextMenu*, WebContextMenuData*);
+  static int ComputeEditFlags(Document&, Editor&);
+  bool ShouldShowContextMenuFromTouch(const blink::WebContextMenuData&);
+  WebViewBase* web_view_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js b/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js
index c7d5c265..7f6501b 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js
@@ -54,9 +54,9 @@
    * @param {!SDK.Script} script
    */
   updateLocations(script) {
-    var info = this._infoForScript(script);
-    if (info)
-      info._updateLocations();
+    var modelData = this._debuggerModelToData.get(script.debuggerModel);
+    if (modelData)
+      modelData._updateLocations(script);
   }
 
   /**
@@ -66,13 +66,8 @@
    * @return {!Bindings.DebuggerWorkspaceBinding.Location}
    */
   createLiveLocation(rawLocation, updateDelegate, locationPool) {
-    var script = /** @type {!SDK.Script} */ (rawLocation.script());
-    console.assert(script);
-    var info = this._ensureInfoForScript(script);
-    var location =
-        new Bindings.DebuggerWorkspaceBinding.Location(info._script, rawLocation, this, updateDelegate, locationPool);
-    info._addLocation(location);
-    return location;
+    var modelData = this._debuggerModelToData.get(rawLocation.script().debuggerModel);
+    return modelData._createLiveLocation(rawLocation, updateDelegate, locationPool);
   }
 
   /**
@@ -100,7 +95,6 @@
     if (!script)
       return null;
     var debuggerModel = location.debuggerModel;
-    this._ensureInfoForScript(script);
     var liveLocation = this.createLiveLocation(location, updateDelegate, locationPool);
     this._registerCallFrameLiveLocation(debuggerModel, liveLocation);
     return liveLocation;
@@ -214,29 +208,6 @@
   }
 
   /**
-   * @param {!SDK.Script} script
-   * @return {!Bindings.DebuggerWorkspaceBinding.ScriptInfo}
-   */
-  _ensureInfoForScript(script) {
-    var info = script[Bindings.DebuggerWorkspaceBinding._scriptInfoSymbol];
-    if (!info) {
-      info = new Bindings.DebuggerWorkspaceBinding.ScriptInfo(script);
-      script[Bindings.DebuggerWorkspaceBinding._scriptInfoSymbol] = info;
-    }
-    return info;
-  }
-
-  /**
-   * @param {?SDK.Script} script
-   * @return {?Bindings.DebuggerWorkspaceBinding.ScriptInfo}
-   */
-  _infoForScript(script) {
-    if (!script)
-      return null;
-    return script[Bindings.DebuggerWorkspaceBinding._scriptInfoSymbol] || null;
-  }
-
-  /**
    * @param {!SDK.DebuggerModel} debuggerModel
    * @param {!Bindings.DebuggerWorkspaceBinding.Location} location
    */
@@ -249,9 +220,9 @@
    * @param {!Bindings.DebuggerWorkspaceBinding.Location} location
    */
   _removeLiveLocation(location) {
-    var info = this._infoForScript(location._script);
-    if (info)
-      info._removeLocation(location);
+    var modelData = this._debuggerModelToData.get(location._script.debuggerModel);
+    if (modelData)
+      modelData._disposeLocation(location);
   }
 
   /**
@@ -263,8 +234,6 @@
   }
 };
 
-Bindings.DebuggerWorkspaceBinding._scriptInfoSymbol = Symbol('scriptDataMap');
-
 /**
  * @unrestricted
  */
@@ -286,6 +255,9 @@
     this._resourceMapping = new Bindings.ResourceScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
     this._compilerMapping = new Bindings.CompilerScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
 
+    /** @type {!Multimap<!SDK.Script, !Bindings.DebuggerWorkspaceBinding.Location>} */
+    this._locations = new Multimap();
+
     debuggerModel.setBeforePausedCallback(this._beforePaused.bind(this));
     this._eventListeners = [
       debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this),
@@ -298,6 +270,37 @@
 
   /**
    * @param {!SDK.DebuggerModel.Location} rawLocation
+   * @param {function(!Bindings.LiveLocation)} updateDelegate
+   * @param {!Bindings.LiveLocationPool} locationPool
+   * @return {!Bindings.DebuggerWorkspaceBinding.Location}
+   */
+  _createLiveLocation(rawLocation, updateDelegate, locationPool) {
+    var script = /** @type {!SDK.Script} */ (rawLocation.script());
+    console.assert(script);
+    var location = new Bindings.DebuggerWorkspaceBinding.Location(
+        script, rawLocation, this._debuggerWorkspaceBinding, updateDelegate, locationPool);
+    this._locations.set(script, location);
+    location.update();
+    return location;
+  }
+
+  /**
+   * @param {!Bindings.DebuggerWorkspaceBinding.Location} location
+   */
+  _disposeLocation(location) {
+    this._locations.delete(location._script, location);
+  }
+
+  /**
+   * @param {!SDK.Script} script
+   */
+  _updateLocations(script) {
+    for (var location of this._locations.get(script))
+      location.update();
+  }
+
+  /**
+   * @param {!SDK.DebuggerModel.Location} rawLocation
    * @return {?Workspace.UILocation}
    */
   _rawLocationToUILocation(rawLocation) {
@@ -362,47 +365,6 @@
 /**
  * @unrestricted
  */
-Bindings.DebuggerWorkspaceBinding.ScriptInfo = class {
-  /**
-   * @param {!SDK.Script} script
-   */
-  constructor(script) {
-    this._script = script;
-    // We create a lot of these, do not add arrays/collections/expensive data structures.
-  }
-
-  /**
-   * @param {!Bindings.LiveLocation} location
-   */
-  _addLocation(location) {
-    if (!this._locations) {
-      /** @type {!Set<!Bindings.LiveLocation>} */
-      this._locations = new Set();
-    }
-    this._locations.add(location);
-    location.update();
-  }
-
-  /**
-   * @param {!Bindings.LiveLocation} location
-   */
-  _removeLocation(location) {
-    if (!this._locations)
-      return;
-    this._locations.delete(location);
-  }
-
-  _updateLocations() {
-    if (!this._locations)
-      return;
-    for (var location of this._locations)
-      location.update();
-  }
-};
-
-/**
- * @unrestricted
- */
 Bindings.DebuggerWorkspaceBinding.Location = class extends Bindings.LiveLocationWithPool {
   /**
    * @param {!SDK.Script} script
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js b/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js
index 4f3b51f9..cac03cf 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js
@@ -47,7 +47,7 @@
    * @param {!SDK.Target} target
    */
   targetAdded(target) {
-    new Bindings.NetworkProject(target, this._workspace, target.model(SDK.ResourceTreeModel));
+    new Bindings.NetworkProject(target, this._workspace);
   }
 
   /**
@@ -71,14 +71,12 @@
   /**
    * @param {!SDK.Target} target
    * @param {!Workspace.Workspace} workspace
-   * @param {?SDK.ResourceTreeModel} resourceTreeModel
    */
-  constructor(target, workspace, resourceTreeModel) {
+  constructor(target, workspace) {
     this._target = target;
     this._workspace = workspace;
     /** @type {!Map<string, !Bindings.ContentProviderBasedProject>} */
     this._workspaceProjects = new Map();
-    this._resourceTreeModel = resourceTreeModel;
     target[Bindings.NetworkProject._networkProjectSymbol] = this;
 
     this._eventListeners = [];
@@ -98,12 +96,6 @@
           this._debuggerModel.addEventListener(
               SDK.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this));
     }
-    var cssModel = target.model(SDK.CSSModel);
-    if (cssModel) {
-      this._eventListeners.push(
-          cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, this._styleSheetAdded, this),
-          cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this));
-    }
   }
 
   /**
@@ -286,7 +278,7 @@
     var frameId = Bindings.frameIdForScript(script);
     script[Bindings.NetworkProject._frameIdSymbol] = frameId;
     var uiSourceCode = this._createFile(originalContentProvider, frameId, script.isContentScript());
-    var metadata = this._fetchMetadata(frameId, uiSourceCode.url());
+    var metadata = Bindings.metadataForURL(this._target, frameId, uiSourceCode.url());
     this._addUISourceCodeWithProvider(uiSourceCode, originalContentProvider, metadata, 'text/javascript');
   }
 
@@ -320,42 +312,6 @@
   }
 
   /**
-   * @param {!SDK.CSSStyleSheetHeader} header
-   */
-  _acceptsHeader(header) {
-    if (header.isInline && !header.hasSourceURL && header.origin !== 'inspector')
-      return false;
-    if (!header.resourceURL())
-      return false;
-    return true;
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _styleSheetAdded(event) {
-    var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data);
-    if (!this._acceptsHeader(header))
-      return;
-
-    var originalContentProvider = header.originalContentProvider();
-    var uiSourceCode = this._createFile(originalContentProvider, header.frameId, false);
-    uiSourceCode[Bindings.NetworkProject._styleSheetSymbol] = header;
-    var metadata = this._fetchMetadata(header.frameId, uiSourceCode.url());
-    this._addUISourceCodeWithProvider(uiSourceCode, originalContentProvider, metadata, 'text/css');
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _styleSheetRemoved(event) {
-    var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data);
-    if (!this._acceptsHeader(header))
-      return;
-    this._removeFileForURL(header.resourceURL(), header.frameId, false);
-  }
-
-  /**
    * @param {!Common.ContentProvider} contentProvider
    * @param {string} frameId
    * @param {boolean} isContentScript
@@ -370,20 +326,6 @@
     return uiSourceCode;
   }
 
-  /**
-   * @param {string} frameId
-   * @param {string} url
-   * @return {?Workspace.UISourceCodeMetadata}
-   */
-  _fetchMetadata(frameId, url) {
-    if (!this._resourceTreeModel)
-      return null;
-    var frame = this._resourceTreeModel.frameForId(frameId);
-    if (!frame)
-      return null;
-    return Bindings.resourceMetadata(frame.resourceForURL(url));
-  }
-
   _dispose() {
     for (var project of this._workspaceProjects.values())
       project.removeProject();
@@ -411,30 +353,10 @@
     return workspace.uiSourceCode(Bindings.NetworkProject.projectId(target, frameId, false), url) ||
         workspace.uiSourceCode(Bindings.NetworkProject.projectId(target, frameId, true), url);
   }
-
-  /**
-   * @param {!Workspace.Workspace} workspace
-   * @param {string} url
-   * @param {!SDK.CSSStyleSheetHeader} header
-   * @return {?Workspace.UISourceCode}
-   */
-  static uiSourceCodeForStyleURL(workspace, url, header) {
-    return workspace.uiSourceCode(
-        Bindings.NetworkProject.projectId(header.cssModel().target(), header.frameId, false), url);
-  }
-
-  /**
-   * @param {!Workspace.UISourceCode} uiSourceCode
-   * @return {?SDK.CSSStyleSheetHeader}
-   */
-  static styleHeaderForUISourceCode(uiSourceCode) {
-    return uiSourceCode[Bindings.NetworkProject._styleSheetSymbol];
-  }
 };
 
 Bindings.NetworkProject._networkProjectSymbol = Symbol('networkProject');
-Bindings.NetworkProject._styleSheetSymbol = Symbol('styleSheet');
 Bindings.NetworkProject._targetSymbol = Symbol('target');
 Bindings.NetworkProject._frameIdSymbol = Symbol('frameid');
 
-Bindings.NetworkProject._frameAttributionSymbol = Symbol('Bindings.NetworkProject._frameAttributionSymbol');
\ No newline at end of file
+Bindings.NetworkProject._frameAttributionSymbol = Symbol('Bindings.NetworkProject._frameAttributionSymbol');
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/ResourceUtils.js b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceUtils.js
index 006a493..3e465e30 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/ResourceUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceUtils.js
@@ -86,6 +86,22 @@
 };
 
 /**
+ * @param {!SDK.Target} target
+ * @param {string} frameId
+ * @param {string} url
+ * @return {?Workspace.UISourceCodeMetadata}
+ */
+Bindings.metadataForURL = function(target, frameId, url) {
+  var resourceTreeModel = target.model(SDK.ResourceTreeModel);
+  if (!resourceTreeModel)
+    return null;
+  var frame = resourceTreeModel.frameForId(frameId);
+  if (!frame)
+    return null;
+  return Bindings.resourceMetadata(frame.resourceForURL(url));
+};
+
+/**
  * @param {?SDK.Resource} resource
  * @return {?Workspace.UISourceCodeMetadata}
  */
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js
index 0bfe380..4b42606 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js
@@ -39,24 +39,17 @@
    */
   constructor(cssModel, workspace) {
     this._cssModel = cssModel;
-    this._workspace = workspace;
+    var target = this._cssModel.target();
+    this._project = new Bindings.ContentProviderBasedProject(
+        workspace, 'css:' + target.id(), Workspace.projectTypes.Network, '', false /* isServiceProject */);
+    Bindings.NetworkProject.setTargetForProject(this._project, target);
 
-    /** @type {!Map<string, !Map<string, !Map<string, !SDK.CSSStyleSheetHeader>>>} */
-    this._urlToHeadersByFrameId = new Map();
-    /** @type {!Map.<!Workspace.UISourceCode, !Bindings.StyleFile>} */
+    /** @type {!Map.<string, !Bindings.StyleFile>} */
     this._styleFiles = new Map();
-
     this._eventListeners = [
-      this._workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved, this._projectRemoved, this),
-      this._workspace.addEventListener(
-          Workspace.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this),
-      this._workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this),
       this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, this._styleSheetAdded, this),
       this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this),
       this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetChanged, this._styleSheetChanged, this),
-      cssModel.target()
-          .model(SDK.ResourceTreeModel)
-          .addEventListener(SDK.ResourceTreeModel.Events.MainFrameNavigated, this._unbindAllUISourceCodes, this)
     ];
   }
 
@@ -67,10 +60,10 @@
    */
   rawLocationToUILocation(rawLocation) {
     var header = rawLocation.header();
-    if (!header)
+    if (!header || !this._acceptsHeader(header))
       return null;
-    var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this._workspace, rawLocation.url, header);
-    if (!uiSourceCode)
+    var styleFile = this._styleFiles.get(header.resourceURL());
+    if (!styleFile)
       return null;
     var lineNumber = rawLocation.lineNumber;
     var columnNumber = rawLocation.columnNumber;
@@ -78,7 +71,7 @@
       lineNumber -= header.lineNumberInSource(0);
       columnNumber -= header.columnNumberInSource(lineNumber, 0);
     }
-    return uiSourceCode.uiLocation(lineNumber, columnNumber);
+    return styleFile._uiSourceCode.uiLocation(lineNumber, columnNumber);
   }
 
   /**
@@ -87,17 +80,31 @@
    * @return {!Array<!SDK.CSSLocation>}
    */
   uiLocationToRawLocations(uiLocation) {
-    // TODO(caseq,lushnikov): return multiple raw locations.
-    var header = Bindings.NetworkProject.styleHeaderForUISourceCode(uiLocation.uiSourceCode);
-    if (!header)
+    var styleFile = uiLocation.uiSourceCode[Bindings.StyleFile._symbol];
+    if (!styleFile)
       return [];
-    var lineNumber = uiLocation.lineNumber;
-    var columnNumber = uiLocation.columnNumber;
-    if (header.isInline && header.hasSourceURL) {
-      columnNumber = header.columnNumberInSource(lineNumber, columnNumber);
-      lineNumber = header.lineNumberInSource(lineNumber);
+    var rawLocations = [];
+    for (var header of styleFile._headers) {
+      var lineNumber = uiLocation.lineNumber;
+      var columnNumber = uiLocation.columnNumber;
+      if (header.isInline && header.hasSourceURL) {
+        columnNumber = header.columnNumberInSource(lineNumber, columnNumber);
+        lineNumber = header.lineNumberInSource(lineNumber);
+      }
+      rawLocations.push(new SDK.CSSLocation(header, lineNumber, columnNumber));
     }
-    return [new SDK.CSSLocation(header, lineNumber, columnNumber)];
+    return rawLocations;
+  }
+
+  /**
+   * @param {!SDK.CSSStyleSheetHeader} header
+   */
+  _acceptsHeader(header) {
+    if (header.isInline && !header.hasSourceURL && header.origin !== 'inspector')
+      return false;
+    if (!header.resourceURL())
+      return false;
+    return true;
   }
 
   /**
@@ -105,24 +112,17 @@
    */
   _styleSheetAdded(event) {
     var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data);
-    var url = header.resourceURL();
-    if (!url)
+    if (!this._acceptsHeader(header))
       return;
 
-    var map = this._urlToHeadersByFrameId.get(url);
-    if (!map) {
-      map = /** @type {!Map.<string, !Map.<string, !SDK.CSSStyleSheetHeader>>} */ (new Map());
-      this._urlToHeadersByFrameId.set(url, map);
+    var url = header.resourceURL();
+    var styleFile = this._styleFiles.get(url);
+    if (!styleFile) {
+      styleFile = new Bindings.StyleFile(this._cssModel, this._project, header);
+      this._styleFiles.set(url, styleFile);
+    } else {
+      styleFile.addHeader(header);
     }
-    var headersById = map.get(header.frameId);
-    if (!headersById) {
-      headersById = /** @type {!Map.<string, !SDK.CSSStyleSheetHeader>} */ (new Map());
-      map.set(header.frameId, headersById);
-    }
-    headersById.set(header.id, header);
-    var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this._workspace, url, header);
-    if (uiSourceCode)
-      this._bindUISourceCode(uiSourceCode, header);
   }
 
   /**
@@ -130,201 +130,109 @@
    */
   _styleSheetRemoved(event) {
     var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data);
+    if (!this._acceptsHeader(header))
+      return;
     var url = header.resourceURL();
-    if (!url)
-      return;
-
-    var map = this._urlToHeadersByFrameId.get(url);
-    console.assert(map);
-    var headersById = map.get(header.frameId);
-    console.assert(headersById);
-    headersById.delete(header.id);
-
-    if (!headersById.size) {
-      map.delete(header.frameId);
-      if (!map.size) {
-        this._urlToHeadersByFrameId.delete(url);
-        var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this._workspace, url, header);
-        if (uiSourceCode)
-          this._unbindUISourceCode(uiSourceCode);
-      }
-    }
-  }
-
-  /**
-   * @param {!Workspace.UISourceCode} uiSourceCode
-   */
-  _unbindUISourceCode(uiSourceCode) {
-    var styleFile = this._styleFiles.get(uiSourceCode);
-    if (!styleFile)
-      return;
-    styleFile.dispose();
-    this._styleFiles.delete(uiSourceCode);
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _unbindAllUISourceCodes(event) {
-    for (var styleFile of this._styleFiles.values())
+    var styleFile = this._styleFiles.get(url);
+    if (styleFile._headers.size === 1) {
       styleFile.dispose();
-    this._styleFiles.clear();
-    this._urlToHeadersByFrameId = new Map();
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _uiSourceCodeAddedToWorkspace(event) {
-    var uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
-    if (!this._urlToHeadersByFrameId.has(uiSourceCode.url()))
-      return;
-    this._bindUISourceCode(
-        uiSourceCode, this._urlToHeadersByFrameId.get(uiSourceCode.url()).valuesArray()[0].valuesArray()[0]);
-  }
-
-  /**
-   * @param {!Workspace.UISourceCode} uiSourceCode
-   * @param {!SDK.CSSStyleSheetHeader} header
-   */
-  _bindUISourceCode(uiSourceCode, header) {
-    if (this._styleFiles.get(uiSourceCode) || (header.isInline && !header.hasSourceURL))
-      return;
-    this._styleFiles.set(uiSourceCode, new Bindings.StyleFile(uiSourceCode, this));
-    Bindings.cssWorkspaceBinding.updateLocations(header);
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _projectRemoved(event) {
-    var project = /** @type {!Workspace.Project} */ (event.data);
-    var uiSourceCodes = project.uiSourceCodes();
-    for (var i = 0; i < uiSourceCodes.length; ++i)
-      this._unbindUISourceCode(uiSourceCodes[i]);
-  }
-
-  /**
-   * @param {!Common.Event} event
-   */
-  _uiSourceCodeRemoved(event) {
-    var uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
-    this._unbindUISourceCode(uiSourceCode);
-  }
-
-  /**
-   * @param {!Workspace.UISourceCode} uiSourceCode
-   * @param {string} content
-   * @param {boolean} majorChange
-   * @return {!Promise<?string>}
-   */
-  async _setStyleContent(uiSourceCode, content, majorChange) {
-    var styleSheetIds = this._cssModel.styleSheetIdsForURL(uiSourceCode.url());
-    if (!styleSheetIds.length)
-      return 'No stylesheet found: ' + uiSourceCode.url();
-    this._isSettingContent = true;
-    var promises = styleSheetIds.map(id => this._cssModel.setStyleSheetText(id, content, majorChange));
-
-    var results = await Promise.all(promises);
-
-    delete this._isSettingContent;
-    return results.find(error => !!error);
+      this._styleFiles.delete(url);
+    } else {
+      styleFile.removeHeader(header);
+    }
   }
 
   /**
    * @param {!Common.Event} event
    */
   _styleSheetChanged(event) {
-    if (this._isSettingContent)
+    var header = this._cssModel.styleSheetHeaderForId(event.data.styleSheetId);
+    if (!header || !this._acceptsHeader(header))
       return;
-
-    this._updateStyleSheetTextSoon(event.data.styleSheetId);
-  }
-
-  /**
-   * @param {!Protocol.CSS.StyleSheetId} styleSheetId
-   */
-  _updateStyleSheetTextSoon(styleSheetId) {
-    if (this._updateStyleSheetTextTimer)
-      clearTimeout(this._updateStyleSheetTextTimer);
-
-    this._updateStyleSheetTextTimer = setTimeout(
-        this._updateStyleSheetText.bind(this, styleSheetId), Bindings.StylesSourceMapping.ChangeUpdateTimeoutMs);
-  }
-
-  /**
-   * @param {!Protocol.CSS.StyleSheetId} styleSheetId
-   */
-  _updateStyleSheetText(styleSheetId) {
-    if (this._updateStyleSheetTextTimer) {
-      clearTimeout(this._updateStyleSheetTextTimer);
-      delete this._updateStyleSheetTextTimer;
-    }
-
-    var header = this._cssModel.styleSheetHeaderForId(styleSheetId);
-    if (!header)
-      return;
-    var styleSheetURL = header.resourceURL();
-    if (!styleSheetURL)
-      return;
-    var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this._workspace, styleSheetURL, header);
-    if (!uiSourceCode)
-      return;
-    header.requestContent().then(callback.bind(this, uiSourceCode));
-
-    /**
-     * @param {!Workspace.UISourceCode} uiSourceCode
-     * @param {?string} content
-     * @this {Bindings.StylesSourceMapping}
-     */
-    function callback(uiSourceCode, content) {
-      var styleFile = this._styleFiles.get(uiSourceCode);
-      if (typeof content === 'string' && styleFile)
-        styleFile.addRevision(content);
-      this._styleFileSyncedForTest();
-    }
-  }
-
-  _styleFileSyncedForTest() {
+    var styleFile = this._styleFiles.get(header.resourceURL());
+    styleFile._styleSheetChanged(header);
   }
 
   dispose() {
+    for (var styleFile of this._styleFiles.values())
+      styleFile.dispose();
+    this._styleFiles.clear();
     Common.EventTarget.removeEventListeners(this._eventListeners);
+    this._project.removeProject();
   }
 };
 
-Bindings.StylesSourceMapping.ChangeUpdateTimeoutMs = 200;
-
 /**
+ * @implements {Common.ContentProvider}
  * @unrestricted
  */
 Bindings.StyleFile = class {
   /**
-   * @param {!Workspace.UISourceCode} uiSourceCode
-   * @param {!Bindings.StylesSourceMapping} mapping
+   * @param {!SDK.CSSModel} cssModel
+   * @param {!Bindings.ContentProviderBasedProject} project
+   * @param {!SDK.CSSStyleSheetHeader} header
    */
-  constructor(uiSourceCode, mapping) {
-    this._uiSourceCode = uiSourceCode;
-    this._mapping = mapping;
+  constructor(cssModel, project, header) {
+    this._cssModel = cssModel;
+    this._project = project;
+    /** @type {!Set<!SDK.CSSStyleSheetHeader>} */
+    this._headers = new Set([header]);
+
+    var target = cssModel.target();
+
+    var url = header.resourceURL();
+    var metadata = Bindings.metadataForURL(target, header.frameId, url);
+
+    this._uiSourceCode = this._project.createUISourceCode(url, header.contentType());
+    this._uiSourceCode[Bindings.StyleFile._symbol] = this;
+    Bindings.NetworkProject.setInitialFrameAttribution(this._uiSourceCode, header.frameId);
+    this._project.addUISourceCodeWithProvider(this._uiSourceCode, this, metadata, 'text/css');
+
     this._eventListeners = [
       this._uiSourceCode.addEventListener(
           Workspace.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this),
       this._uiSourceCode.addEventListener(
           Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this)
     ];
-    this._commitThrottler = new Common.Throttler(Bindings.StyleFile.updateTimeout);
+    this._throttler = new Common.Throttler(Bindings.StyleFile.updateTimeout);
     this._terminated = false;
   }
 
   /**
+   * @param {!SDK.CSSStyleSheetHeader} header
+   */
+  addHeader(header) {
+    this._headers.add(header);
+    Bindings.NetworkProject.addFrameAttribution(this._uiSourceCode, header.frameId);
+  }
+
+  /**
+   * @param {!SDK.CSSStyleSheetHeader} header
+   */
+  removeHeader(header) {
+    this._headers.delete(header);
+    Bindings.NetworkProject.removeFrameAttribution(this._uiSourceCode, header.frameId);
+  }
+
+  /**
+   * @param {!SDK.CSSStyleSheetHeader} header
+   */
+  _styleSheetChanged(header) {
+    console.assert(this._headers.has(header));
+    if (this._isUpdatingHeaders || !this._headers.has(header))
+      return;
+    var mirrorContentBound = this._mirrorContent.bind(this, header, true /* majorChange */);
+    this._throttler.schedule(mirrorContentBound, false /* asSoonAsPossible */);
+  }
+
+  /**
    * @param {!Common.Event} event
    */
   _workingCopyCommitted(event) {
     if (this._isAddingRevision)
       return;
-
-    this._isMajorChangePending = true;
-    this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), true);
+    var mirrorContentBound = this._mirrorContent.bind(this, this._uiSourceCode, true /* majorChange */);
+    this._throttler.schedule(mirrorContentBound, true /* asSoonAsPossible */);
   }
 
   /**
@@ -333,43 +241,100 @@
   _workingCopyChanged(event) {
     if (this._isAddingRevision)
       return;
-
-    this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), false);
+    var mirrorContentBound = this._mirrorContent.bind(this, this._uiSourceCode, false /* majorChange */);
+    this._throttler.schedule(mirrorContentBound, false /* asSoonAsPossible */);
   }
 
-  _commitIncrementalEdit() {
-    if (this._terminated)
+  /**
+   * @param {!Common.ContentProvider} fromProvider
+   * @param {boolean} majorChange
+   * @return {!Promise}
+   */
+  async _mirrorContent(fromProvider, majorChange) {
+    if (this._terminated) {
+      this._styleFileSyncedForTest();
       return;
-    var promise =
-        this._mapping._setStyleContent(this._uiSourceCode, this._uiSourceCode.workingCopy(), this._isMajorChangePending)
-            .then(this._styleContentSet.bind(this));
-    this._isMajorChangePending = false;
-    return promise;
+    }
+
+    var newContent = null;
+    if (fromProvider === this._uiSourceCode) {
+      newContent = this._uiSourceCode.workingCopy();
+    } else {
+      // ------ ASYNC ------
+      newContent = await fromProvider.requestContent();
+    }
+
+    if (newContent === null || this._terminated) {
+      this._styleFileSyncedForTest();
+      return;
+    }
+
+    if (fromProvider !== this._uiSourceCode) {
+      this._isAddingRevision = true;
+      this._uiSourceCode.addRevision(newContent);
+      this._isAddingRevision = false;
+    }
+
+    this._isUpdatingHeaders = true;
+    var promises = [];
+    for (var header of this._headers) {
+      if (header === fromProvider)
+        continue;
+      promises.push(this._cssModel.setStyleSheetText(header.id, newContent, majorChange));
+    }
+    // ------ ASYNC ------
+    await Promise.all(promises);
+    this._isUpdatingHeaders = false;
+    this._styleFileSyncedForTest();
   }
 
-  /**
-   * @param {?string} error
-   */
-  _styleContentSet(error) {
-    if (error)
-      console.error(error);
-  }
-
-  /**
-   * @param {string} content
-   */
-  addRevision(content) {
-    this._isAddingRevision = true;
-    this._uiSourceCode.addRevision(content);
-    delete this._isAddingRevision;
+  _styleFileSyncedForTest() {
   }
 
   dispose() {
     if (this._terminated)
       return;
     this._terminated = true;
+    this._project.removeFile(this._uiSourceCode.url());
     Common.EventTarget.removeEventListeners(this._eventListeners);
   }
+
+  /**
+   * @override
+   * @return {string}
+   */
+  contentURL() {
+    return this._headers.firstValue().originalContentProvider().contentURL();
+  }
+
+  /**
+   * @override
+   * @return {!Common.ResourceType}
+   */
+  contentType() {
+    return this._headers.firstValue().originalContentProvider().contentType();
+  }
+
+  /**
+   * @override
+   * @return {!Promise<?string>}
+   */
+  requestContent() {
+    return this._headers.firstValue().originalContentProvider().requestContent();
+  }
+
+  /**
+   * @override
+   * @param {string} query
+   * @param {boolean} caseSensitive
+   * @param {boolean} isRegex
+   * @return {!Promise<!Array<!Common.ContentProvider.SearchMatch>>}
+   */
+  searchInContent(query, caseSensitive, isRegex) {
+    return this._headers.firstValue().originalContentProvider().searchInContent(query, caseSensitive, isRegex);
+  }
 };
 
+Bindings.StyleFile._symbol = Symbol('Bindings.StyleFile._symbol');
+
 Bindings.StyleFile.updateTimeout = 200;
diff --git a/third_party/WebKit/Source/devtools/front_end/color_picker/Spectrum.js b/third_party/WebKit/Source/devtools/front_end/color_picker/Spectrum.js
index cf12fc1..f45965c 100644
--- a/third_party/WebKit/Source/devtools/front_end/color_picker/Spectrum.js
+++ b/third_party/WebKit/Source/devtools/front_end/color_picker/Spectrum.js
@@ -917,9 +917,6 @@
 ColorPicker.Spectrum.Palette;
 ColorPicker.Spectrum.GeneratedPaletteTitle = 'Page colors';
 
-/**
- * @unrestricted
- */
 ColorPicker.Spectrum.PaletteGenerator = class {
   /**
    * @param {function(!ColorPicker.Spectrum.Palette)} callback
@@ -931,7 +928,7 @@
     var stylesheetPromises = [];
     for (var cssModel of SDK.targetManager.models(SDK.CSSModel)) {
       for (var stylesheet of cssModel.allStyleSheets())
-        stylesheetPromises.push(new Promise(this._processStylesheet.bind(this, stylesheet)));
+        stylesheetPromises.push(this._processStylesheet(stylesheet));
     }
     Promise.all(stylesheetPromises).catchException(null).then(this._finish.bind(this));
   }
@@ -992,25 +989,16 @@
 
   /**
    * @param {!SDK.CSSStyleSheetHeader} stylesheet
-   * @param {function(?)} resolve
-   * @this {ColorPicker.Spectrum.PaletteGenerator}
+   * @return {!Promise}
    */
-  _processStylesheet(stylesheet, resolve) {
-    /**
-     * @param {?string} text
-     * @this {ColorPicker.Spectrum.PaletteGenerator}
-     */
-    function parseContent(text) {
-      text = text.toLowerCase();
-      var regexResult = text.match(/((?:rgb|hsl)a?\([^)]+\)|#[0-9a-f]{6}|#[0-9a-f]{3})/g) || [];
-      for (var c of regexResult) {
-        var frequency = this._frequencyMap.get(c) || 0;
-        this._frequencyMap.set(c, ++frequency);
-      }
-      resolve(null);
+  async _processStylesheet(stylesheet) {
+    var text = await stylesheet.requestContent() || '';
+    text = text.toLowerCase();
+    var regexResult = text.match(/((?:rgb|hsl)a?\([^)]+\)|#[0-9a-f]{6}|#[0-9a-f]{3})/g) || [];
+    for (var c of regexResult) {
+      var frequency = this._frequencyMap.get(c) || 0;
+      this._frequencyMap.set(c, ++frequency);
     }
-
-    stylesheet.requestContent().then(parseContent.bind(this));
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/protocol/InspectorBackend.js b/third_party/WebKit/Source/devtools/front_end/protocol/InspectorBackend.js
index 8e792a3..051b5d5 100644
--- a/third_party/WebKit/Source/devtools/front_end/protocol/InspectorBackend.js
+++ b/third_party/WebKit/Source/devtools/front_end/protocol/InspectorBackend.js
@@ -362,8 +362,7 @@
       var domainName = method[0];
       if (!(domainName in this._dispatchers)) {
         Protocol.InspectorBackend.reportProtocolError(
-            'Protocol Error: the message ' + messageObject.method + ' is for non-existing domain \'' + domainName +
-                '\'',
+            `Protocol Error: the message ${messageObject.method} is for non-existing domain '${domainName}'`,
             messageObject);
         return;
       }
@@ -600,7 +599,7 @@
           return;
         }
         var args = this._replyArgs[method];
-        resolve(result && args.length && result[args[0]] || null);
+        resolve(result && args.length ? result[args[0]] : undefined);
       });
     });
   }
@@ -675,8 +674,7 @@
 
     if (!this._eventArgs[messageObject.method]) {
       Protocol.InspectorBackend.reportProtocolError(
-          'Protocol Error: Attempted to dispatch an unspecified method \'' + messageObject.method + '\'',
-          messageObject);
+          `Protocol Error: Attempted to dispatch an unspecified method '${messageObject.method}'`, messageObject);
       return;
     }
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
index 970a804..65292efc 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
@@ -37,6 +37,7 @@
 #include "core/frame/LocalFrame.h"
 #include "core/frame/LocalFrameView.h"
 #include "core/frame/Settings.h"
+#include "core/frame/WebLocalFrameBase.h"
 #include "core/html/HTMLAreaElement.h"
 #include "core/html/HTMLCanvasElement.h"
 #include "core/html/HTMLFrameOwnerElement.h"
@@ -83,6 +84,7 @@
 #include "modules/accessibility/AXTableRow.h"
 #include "platform/wtf/PassRefPtr.h"
 #include "platform/wtf/PtrUtil.h"
+#include "public/web/WebFrameClient.h"
 
 namespace blink {
 
@@ -1113,10 +1115,13 @@
   if (!obj || !obj->GetDocument() || !obj->DocumentFrameView() ||
       !obj->DocumentFrameView()->GetFrame().GetPage())
     return;
-
-  ChromeClient& client =
-      obj->GetDocument()->AxObjectCacheOwner().GetPage()->GetChromeClient();
-  client.PostAccessibilityNotification(obj, notification);
+  // Send via WebFrameClient
+  WebLocalFrameBase* webframe = WebLocalFrameBase::FromFrame(
+      obj->GetDocument()->AxObjectCacheOwner().GetFrame());
+  if (webframe && webframe->Client()) {
+    webframe->Client()->PostAccessibilityEvent(
+        WebAXObject(obj), static_cast<WebAXEvent>(notification));
+  }
 }
 
 void AXObjectCacheImpl::HandleFocusedUIElementChanged(Node* old_focused_node,
diff --git a/third_party/WebKit/Source/modules/fetch/Request.cpp b/third_party/WebKit/Source/modules/fetch/Request.cpp
index dd35a7f..6e97144 100644
--- a/third_party/WebKit/Source/modules/fetch/Request.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Request.cpp
@@ -609,9 +609,12 @@
       return "origin";
     case kReferrerPolicyOriginWhenCrossOrigin:
       return "origin-when-cross-origin";
+    case kReferrerPolicySameOrigin:
+      return "same-origin";
+    case kReferrerPolicyStrictOrigin:
+      return "strict-origin";
     case kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin:
-      DCHECK(RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled());
-      return "no-referrer-when-downgrade-origin-when-cross-origin";
+      return "strict-origin-when-cross-origin";
   }
   NOTREACHED();
   return String();
diff --git a/third_party/WebKit/Source/modules/fetch/RequestInit.cpp b/third_party/WebKit/Source/modules/fetch/RequestInit.cpp
index ea5cd44..e745b1c 100644
--- a/third_party/WebKit/Source/modules/fetch/RequestInit.cpp
+++ b/third_party/WebKit/Source/modules/fetch/RequestInit.cpp
@@ -91,11 +91,13 @@
         referrer.referrer_policy = kReferrerPolicyOrigin;
       } else if (referrer_policy_string == "origin-when-cross-origin") {
         referrer.referrer_policy = kReferrerPolicyOriginWhenCrossOrigin;
+      } else if (referrer_policy_string == "same-origin") {
+        referrer.referrer_policy = kReferrerPolicySameOrigin;
+      } else if (referrer_policy_string == "strict-origin") {
+        referrer.referrer_policy = kReferrerPolicyStrictOrigin;
       } else if (referrer_policy_string == "unsafe-url") {
         referrer.referrer_policy = kReferrerPolicyAlways;
-      } else if (referrer_policy_string ==
-                     "no-referrer-when-downgrade-origin-when-cross-origin" &&
-                 RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()) {
+      } else if (referrer_policy_string == "strict-origin-when-cross-origin") {
         referrer.referrer_policy =
             kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin;
       } else {
diff --git a/third_party/WebKit/Source/platform/Histogram.cpp b/third_party/WebKit/Source/platform/Histogram.cpp
index cb87116..48997ee5 100644
--- a/third_party/WebKit/Source/platform/Histogram.cpp
+++ b/third_party/WebKit/Source/platform/Histogram.cpp
@@ -49,4 +49,15 @@
   histogram_->Add(sample);
 }
 
+LinearHistogram::LinearHistogram(const char* name,
+                                 base::HistogramBase::Sample min,
+                                 base::HistogramBase::Sample max,
+                                 int32_t bucket_count)
+    : CustomCountHistogram(base::LinearHistogram::FactoryGet(
+          name,
+          min,
+          max,
+          bucket_count,
+          base::HistogramBase::kUmaTargetedHistogramFlag)) {}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/Histogram.h b/third_party/WebKit/Source/platform/Histogram.h
index 190149e..ec4c13a 100644
--- a/third_party/WebKit/Source/platform/Histogram.h
+++ b/third_party/WebKit/Source/platform/Histogram.h
@@ -54,6 +54,14 @@
   base::HistogramBase* histogram_;
 };
 
+class PLATFORM_EXPORT LinearHistogram : public CustomCountHistogram {
+ public:
+  explicit LinearHistogram(const char* name,
+                           base::HistogramBase::Sample min,
+                           base::HistogramBase::Sample max,
+                           int32_t bucket_count);
+};
+
 class PLATFORM_EXPORT ScopedUsHistogramTimer {
  public:
   ScopedUsHistogramTimer(CustomCountHistogram& counter)
diff --git a/third_party/WebKit/Source/platform/weborigin/ReferrerPolicy.h b/third_party/WebKit/Source/platform/weborigin/ReferrerPolicy.h
index ad105f2..af31390 100644
--- a/third_party/WebKit/Source/platform/weborigin/ReferrerPolicy.h
+++ b/third_party/WebKit/Source/platform/weborigin/ReferrerPolicy.h
@@ -47,8 +47,16 @@
   kReferrerPolicyOrigin,
   // https://w3c.github.io/webappsec/specs/referrer-policy/#referrer-policy-state-origin-when-cross-origin
   kReferrerPolicyOriginWhenCrossOrigin,
-  // Not spec conformant. set only when reduced-referrer-granularity is enabled.
+  // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin-when-cross-origin
+  // Also used as the default policy when reduced-referrer-grnaularity is
+  // enabled (not spec conformant).
+  // TODO(estark): rename to kReferrerPolicyStrictOriginWhenCrossOrigin to
+  // match spec.
   kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
+  // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-same-origin
+  kReferrerPolicySameOrigin,
+  // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin
+  kReferrerPolicyStrictOrigin,
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp
index 483674f..0b411ea 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp
@@ -123,9 +123,23 @@
       }
       break;
     }
+    case kReferrerPolicySameOrigin: {
+      RefPtr<SecurityOrigin> referrer_origin =
+          SecurityOrigin::Create(referrer_url);
+      RefPtr<SecurityOrigin> url_origin = SecurityOrigin::Create(url);
+      if (!url_origin->IsSameSchemeHostPort(referrer_origin.Get())) {
+        return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
+      }
+      return Referrer(referrer, referrer_policy_no_default);
+    }
+    case kReferrerPolicyStrictOrigin: {
+      String origin = SecurityOrigin::Create(referrer_url)->ToString();
+      return Referrer(ShouldHideReferrer(url, referrer_url)
+                          ? Referrer::NoReferrer()
+                          : origin + "/",
+                      referrer_policy_no_default);
+    }
     case kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin: {
-      // If the flag is enabled, and we're dealing with a cross-origin request,
-      // strip it.  Otherwise fall through to NoReferrerWhenDowngrade behavior.
       RefPtr<SecurityOrigin> referrer_origin =
           SecurityOrigin::Create(referrer_url);
       RefPtr<SecurityOrigin> url_origin = SecurityOrigin::Create(url);
@@ -286,6 +300,18 @@
     *result = kReferrerPolicyOriginWhenCrossOrigin;
     return true;
   }
+  if (EqualIgnoringASCIICase(policy, "same-origin")) {
+    *result = kReferrerPolicySameOrigin;
+    return true;
+  }
+  if (EqualIgnoringASCIICase(policy, "strict-origin")) {
+    *result = kReferrerPolicyStrictOrigin;
+    return true;
+  }
+  if (EqualIgnoringASCIICase(policy, "strict-origin-when-cross-origin")) {
+    *result = kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin;
+    return true;
+  }
   if (EqualIgnoringASCIICase(policy, "no-referrer-when-downgrade") ||
       (support_legacy_keywords && EqualIgnoringASCIICase(policy, "default"))) {
     *result = kReferrerPolicyNoReferrerWhenDowngrade;
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp
index da69eab..8e0a598 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp
@@ -102,6 +102,11 @@
       {kReferrerPolicyOrigin, kInsecureURLA, kInsecureURLA, kInsecureOriginA},
       {kReferrerPolicyOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLA,
        kInsecureURLA},
+      {kReferrerPolicySameOrigin, kInsecureURLA, kInsecureURLA, kInsecureURLA},
+      {kReferrerPolicyStrictOrigin, kInsecureURLA, kInsecureURLA,
+       kInsecureOriginA},
+      {kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
+       kInsecureURLA, kInsecureURLA, kInsecureURLA},
 
       // HTTP -> HTTP: Cross Origin
       {kReferrerPolicyAlways, kInsecureURLA, kInsecureURLB, kInsecureURLA},
@@ -112,6 +117,11 @@
       {kReferrerPolicyOrigin, kInsecureURLA, kInsecureURLB, kInsecureOriginA},
       {kReferrerPolicyOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLB,
        kInsecureOriginA},
+      {kReferrerPolicySameOrigin, kInsecureURLA, kInsecureURLB, 0},
+      {kReferrerPolicyStrictOrigin, kInsecureURLA, kInsecureURLB,
+       kInsecureOriginA},
+      {kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
+       kInsecureURLA, kInsecureURLB, kInsecureOriginA},
 
       // HTTPS -> HTTPS: Same Origin
       {kReferrerPolicyAlways, kSecureURLA, kSecureURLA, kSecureURLA},
@@ -122,6 +132,10 @@
       {kReferrerPolicyOrigin, kSecureURLA, kSecureURLA, kSecureOriginA},
       {kReferrerPolicyOriginWhenCrossOrigin, kSecureURLA, kSecureURLA,
        kSecureURLA},
+      {kReferrerPolicySameOrigin, kSecureURLA, kSecureURLA, kSecureURLA},
+      {kReferrerPolicyStrictOrigin, kSecureURLA, kSecureURLA, kSecureOriginA},
+      {kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin, kSecureURLA,
+       kSecureURLA, kSecureURLA},
 
       // HTTPS -> HTTPS: Cross Origin
       {kReferrerPolicyAlways, kSecureURLA, kSecureURLB, kSecureURLA},
@@ -132,6 +146,10 @@
       {kReferrerPolicyOrigin, kSecureURLA, kSecureURLB, kSecureOriginA},
       {kReferrerPolicyOriginWhenCrossOrigin, kSecureURLA, kSecureURLB,
        kSecureOriginA},
+      {kReferrerPolicySameOrigin, kSecureURLA, kSecureURLB, 0},
+      {kReferrerPolicyStrictOrigin, kSecureURLA, kSecureURLB, kSecureOriginA},
+      {kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin, kSecureURLA,
+       kSecureURLB, kSecureOriginA},
 
       // HTTP -> HTTPS
       {kReferrerPolicyAlways, kInsecureURLA, kSecureURLB, kInsecureURLA},
@@ -142,6 +160,11 @@
       {kReferrerPolicyOrigin, kInsecureURLA, kSecureURLB, kInsecureOriginA},
       {kReferrerPolicyOriginWhenCrossOrigin, kInsecureURLA, kSecureURLB,
        kInsecureOriginA},
+      {kReferrerPolicySameOrigin, kInsecureURLA, kSecureURLB, 0},
+      {kReferrerPolicyStrictOrigin, kInsecureURLA, kSecureURLB,
+       kInsecureOriginA},
+      {kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
+       kInsecureURLA, kSecureURLB, kInsecureOriginA},
 
       // HTTPS -> HTTP
       {kReferrerPolicyAlways, kSecureURLA, kInsecureURLB, kSecureURLA},
@@ -151,6 +174,10 @@
       {kReferrerPolicyOrigin, kSecureURLA, kInsecureURLB, kSecureOriginA},
       {kReferrerPolicyOriginWhenCrossOrigin, kSecureURLA, kSecureURLB,
        kSecureOriginA},
+      {kReferrerPolicySameOrigin, kSecureURLA, kInsecureURLB, 0},
+      {kReferrerPolicyStrictOrigin, kSecureURLA, kInsecureURLB, 0},
+      {kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin, kSecureURLA,
+       kInsecureURLB, 0},
 
       // blob and filesystem URL handling
       {kReferrerPolicyAlways, kInsecureURLA, kBlobURL, 0},
diff --git a/third_party/WebKit/Source/platform/wtf/StdLibExtras.h b/third_party/WebKit/Source/platform/wtf/StdLibExtras.h
index 1cf4967..092d2ee6 100644
--- a/third_party/WebKit/Source/platform/wtf/StdLibExtras.h
+++ b/third_party/WebKit/Source/platform/wtf/StdLibExtras.h
@@ -27,7 +27,6 @@
 #define WTF_StdLibExtras_h
 
 #include <cstddef>
-#include "base/memory/aligned_memory.h"
 #include "base/numerics/safe_conversions.h"
 #include "platform/wtf/Assertions.h"
 #include "platform/wtf/CPU.h"
@@ -163,12 +162,12 @@
    public:
     template <typename HeapNew, typename PlacementNew>
     InstanceStorage(const HeapNew&, const PlacementNew& placement_new) {
-      placement_new(object_.void_data());
+      placement_new(&object_);
     }
-    T* Get() { return object_.template data_as<T>(); }
+    T* Get() { return reinterpret_cast<T*>(object_); }
 
    private:
-    base::AlignedMemory<sizeof(T), ALIGNOF(T)> object_;
+    alignas(T) char object_[sizeof(T)];
   };
 
   InstanceStorage<WrapperType> instance_;
diff --git a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
index 16646c3f..b30c167 100644
--- a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
+++ b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
@@ -726,6 +726,8 @@
 STATIC_ASSERT_ENUM(kWebReferrerPolicyOrigin, kReferrerPolicyOrigin);
 STATIC_ASSERT_ENUM(kWebReferrerPolicyOriginWhenCrossOrigin,
                    kReferrerPolicyOriginWhenCrossOrigin);
+STATIC_ASSERT_ENUM(kWebReferrerPolicySameOrigin, kReferrerPolicySameOrigin);
+STATIC_ASSERT_ENUM(kWebReferrerPolicyStrictOrigin, kReferrerPolicyStrictOrigin);
 STATIC_ASSERT_ENUM(
     kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
     kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin);
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index b9b2315..3c11a1b2 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -39,8 +39,6 @@
     "AssertMatchingEnums.cpp",
     "ChromeClientImpl.cpp",
     "ChromeClientImpl.h",
-    "ContextMenuClientImpl.cpp",
-    "ContextMenuClientImpl.h",
     "DedicatedWorkerMessagingProxyProviderImpl.cpp",
     "DedicatedWorkerMessagingProxyProviderImpl.h",
     "EditorClientImpl.cpp",
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index bc3fbd7..b3f87fa 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -34,7 +34,6 @@
 #include <memory>
 #include "bindings/core/v8/ScriptController.h"
 #include "core/HTMLNames.h"
-#include "core/dom/AXObjectCache.h"
 #include "core/dom/Document.h"
 #include "core/dom/Fullscreen.h"
 #include "core/dom/Node.h"
@@ -65,7 +64,6 @@
 #include "core/page/ChromeClient.h"
 #include "core/page/Page.h"
 #include "core/page/PopupOpeningObserver.h"
-#include "modules/accessibility/AXObjectImpl.h"
 #include "platform/Cursor.h"
 #include "platform/FileChooser.h"
 #include "platform/Histogram.h"
@@ -88,7 +86,6 @@
 #include "public/platform/WebFloatRect.h"
 #include "public/platform/WebRect.h"
 #include "public/platform/WebURLRequest.h"
-#include "public/web/WebAXObject.h"
 #include "public/web/WebAutofillClient.h"
 #include "public/web/WebColorChooser.h"
 #include "public/web/WebColorSuggestion.h"
@@ -155,11 +152,6 @@
 
 class CompositorAnimationTimeline;
 
-// Converts a AXObjectCache::AXNotification to a WebAXEvent
-static WebAXEvent ToWebAXEvent(AXObjectCache::AXNotification notification) {
-  // These enums have the same values; enforced in AssertMatchingEnums.cpp.
-  return static_cast<WebAXEvent>(notification);
-}
 
 ChromeClientImpl::ChromeClientImpl(WebViewBase* web_view)
     : web_view_(web_view),
@@ -656,22 +648,6 @@
   cursor_overridden_ = overridden;
 }
 
-void ChromeClientImpl::PostAccessibilityNotification(
-    AXObject* axObject,
-    AXObjectCache::AXNotification notification) {
-  AXObjectImpl* obj = ToAXObjectImpl(axObject);
-
-  // Alert assistive technology about the accessibility object notification.
-  if (!obj || !obj->GetDocument())
-    return;
-
-  WebLocalFrameImpl* webframe = WebLocalFrameImpl::FromFrame(
-      obj->GetDocument()->AxObjectCacheOwner().GetFrame());
-  if (webframe && webframe->Client())
-    webframe->Client()->PostAccessibilityEvent(WebAXObject(obj),
-                                               ToWebAXEvent(notification));
-}
-
 String ChromeClientImpl::AcceptLanguages() {
   return web_view_->Client()->AcceptLanguages();
 }
@@ -969,11 +945,6 @@
       ->RequestPointerUnlock();
 }
 
-void ChromeClientImpl::AnnotatedRegionsChanged() {
-  if (WebViewClient* client = web_view_->Client())
-    client->DraggableRegionsChanged();
-}
-
 void ChromeClientImpl::DidAssociateFormControlsAfterLoad(LocalFrame* frame) {
   WebLocalFrameImpl* webframe = WebLocalFrameImpl::FromFrame(frame);
   if (webframe->AutofillClient())
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.h b/third_party/WebKit/Source/web/ChromeClientImpl.h
index 2ee20f3..7d79131 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.h
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.h
@@ -118,7 +118,6 @@
   void DispatchViewportPropertiesDidChange(
       const ViewportDescription&) const override;
   void PrintDelegate(LocalFrame*) override;
-  void AnnotatedRegionsChanged() override;
   ColorChooser* OpenColorChooser(LocalFrame*,
                                  ColorChooserClient*,
                                  const Color&) override;
@@ -166,8 +165,6 @@
                                  const CompositedSelection&) override;
 
   // ChromeClient methods:
-  void PostAccessibilityNotification(AXObject*,
-                                     AXObjectCache::AXNotification) override;
   String AcceptLanguages() override;
   void SetCursorForPlugin(const WebCursorInfo&, LocalFrame*) override;
 
diff --git a/third_party/WebKit/Source/web/ContextMenuClientImpl.h b/third_party/WebKit/Source/web/ContextMenuClientImpl.h
deleted file mode 100644
index c54d532..0000000
--- a/third_party/WebKit/Source/web/ContextMenuClientImpl.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef ContextMenuClientImpl_h
-#define ContextMenuClientImpl_h
-
-#include "core/page/ContextMenuClient.h"
-
-namespace blink {
-
-class Document;
-class Editor;
-class WebViewBase;
-struct WebContextMenuData;
-
-class ContextMenuClientImpl final : public ContextMenuClient {
- public:
-  explicit ContextMenuClientImpl(WebViewBase* web_view) : web_view_(web_view) {}
-  ~ContextMenuClientImpl() override {}
-  bool ShowContextMenu(const ContextMenu*, bool from_touch) override;
-  void ClearContextMenu() override;
-
- private:
-  void PopulateCustomMenuItems(const ContextMenu*, WebContextMenuData*);
-  static int ComputeEditFlags(Document&, Editor&);
-  bool ShouldShowContextMenuFromTouch(const blink::WebContextMenuData&);
-  WebViewBase* web_view_;
-};
-
-}  // namespace blink
-
-#endif  // ContextMenuClientImpl_h
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 069ae44..b6d1616 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -1059,4 +1059,8 @@
   return web_frame_->Client()->GetInterfaceProvider();
 }
 
+void LocalFrameClientImpl::AnnotatedRegionsChanged() {
+  web_frame_->Client()->DraggableRegionsChanged();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.h b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
index 5cef012..da335fb 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.h
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
@@ -236,6 +236,8 @@
 
   service_manager::InterfaceProvider* GetInterfaceProvider() override;
 
+  void AnnotatedRegionsChanged() override;
+
  private:
   explicit LocalFrameClientImpl(WebLocalFrameBase*);
 
diff --git a/third_party/WebKit/Source/web/WebFrameSerializer.cpp b/third_party/WebKit/Source/web/WebFrameSerializer.cpp
index fce3201..19e50a9 100644
--- a/third_party/WebKit/Source/web/WebFrameSerializer.cpp
+++ b/third_party/WebKit/Source/web/WebFrameSerializer.cpp
@@ -62,6 +62,7 @@
 #include "platform/wtf/HashMap.h"
 #include "platform/wtf/HashSet.h"
 #include "platform/wtf/Noncopyable.h"
+#include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/StringConcatenate.h"
 #include "public/platform/WebString.h"
@@ -95,6 +96,7 @@
   bool ShouldSkipResource(
       FrameSerializer::ResourceHasCacheControlNoStoreHeader) override;
   Vector<Attribute> GetCustomAttributes(const Element&) override;
+  bool ShouldCollectProblemMetric() override;
 
  private:
   bool ShouldIgnoreHiddenElement(const Element&);
@@ -280,6 +282,10 @@
   return attributes;
 }
 
+bool MHTMLFrameSerializerDelegate::ShouldCollectProblemMetric() {
+  return web_delegate_.UsePageProblemDetectors();
+}
+
 void MHTMLFrameSerializerDelegate::GetCustomAttributesForImageElement(
     const HTMLImageElement& element,
     Vector<Attribute>* attributes) {
diff --git a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
index fab006a..80293a5e 100644
--- a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
+++ b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
@@ -61,7 +61,6 @@
 #include "public/platform/WebCompositeAndReadbackAsyncCallback.h"
 #include "public/platform/WebCursorInfo.h"
 #include "public/platform/WebFloatRect.h"
-#include "public/web/WebAXObject.h"
 #include "public/web/WebFrameClient.h"
 #include "public/web/WebViewClient.h"
 #include "public/web/WebWidgetClient.h"
@@ -218,18 +217,6 @@
     popup_->SetRootGraphicsLayer(graphics_layer);
   }
 
-  void PostAccessibilityNotification(
-      AXObject* obj,
-      AXObjectCache::AXNotification notification) override {
-    WebLocalFrameImpl* frame = WebLocalFrameImpl::FromFrame(
-        popup_->popup_client_->OwnerElement().GetDocument().GetFrame());
-    if (obj && frame && frame->Client()) {
-      frame->Client()->PostAccessibilityEvent(
-          WebAXObject(ToAXObjectImpl(obj)),
-          static_cast<WebAXEvent>(notification));
-    }
-  }
-
   void SetToolTip(LocalFrame&,
                   const String& tooltip_text,
                   TextDirection dir) override {
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index d93adb1..551f670 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -337,7 +337,7 @@
     : client_(client),
       spell_check_client_(nullptr),
       chrome_client_(WebFactory::GetInstance().CreateChromeClient(this)),
-      context_menu_client_impl_(this),
+      context_menu_client_(*this),
       editor_client_impl_(this),
       spell_checker_client_impl_(this),
       storage_client_impl_(this),
@@ -385,7 +385,7 @@
       override_compositor_visibility_(false) {
   Page::PageClients page_clients;
   page_clients.chrome_client = chrome_client_.Get();
-  page_clients.context_menu_client = &context_menu_client_impl_;
+  page_clients.context_menu_client = &context_menu_client_;
   page_clients.editor_client = &editor_client_impl_;
   page_clients.spell_checker_client = &spell_checker_client_impl_;
 
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index 15a2ca38..77f7f39 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -36,6 +36,7 @@
 #include "core/exported/WebViewBase.h"
 #include "core/frame/ResizeViewportAnchor.h"
 #include "core/page/ChromeClient.h"
+#include "core/page/ContextMenuClient.h"
 #include "core/page/ContextMenuProvider.h"
 #include "core/page/EventWithHitTestResults.h"
 #include "core/page/PageWidgetDelegate.h"
@@ -63,7 +64,6 @@
 #include "public/platform/WebVector.h"
 #include "public/web/WebNavigationPolicy.h"
 #include "public/web/WebPageImportanceSignals.h"
-#include "web/ContextMenuClientImpl.h"
 #include "web/EditorClientImpl.h"
 #include "web/MediaKeysClientImpl.h"
 #include "web/StorageClientImpl.h"
@@ -593,7 +593,7 @@
   WebSpellCheckClient* spell_check_client_;
 
   Persistent<ChromeClient> chrome_client_;
-  ContextMenuClientImpl context_menu_client_impl_;
+  ContextMenuClient context_menu_client_;
   EditorClientImpl editor_client_impl_;
   SpellCheckerClientImpl spell_checker_client_impl_;
   StorageClientImpl storage_client_impl_;
diff --git a/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp
index 64cb0bf..52bf13d 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp
@@ -70,6 +70,7 @@
 
   bool UseBinaryEncoding() final { return false; }
   bool RemovePopupOverlay() final { return remove_popup_overlay_; }
+  bool UsePageProblemDetectors() final { return false; }
 
   bool remove_popup_overlay_;
 };
diff --git a/third_party/WebKit/public/platform/OWNERS b/third_party/WebKit/public/platform/OWNERS
index 1ac05ee..c9f498f3 100644
--- a/third_party/WebKit/public/platform/OWNERS
+++ b/third_party/WebKit/public/platform/OWNERS
@@ -1,5 +1,7 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *EnumTraits*.*=set noparent
+per-file *EnumTraits*.*=file://ipc/SECURITY_OWNERS
 per-file *StructTraits*.*=set noparent
 per-file *StructTraits*.*=file://ipc/SECURITY_OWNERS
 per-file *.typemap=set noparent
diff --git a/third_party/WebKit/public/platform/ReferrerPolicyEnumTraits.h b/third_party/WebKit/public/platform/ReferrerPolicyEnumTraits.h
index ef7b8b4..b100e38 100644
--- a/third_party/WebKit/public/platform/ReferrerPolicyEnumTraits.h
+++ b/third_party/WebKit/public/platform/ReferrerPolicyEnumTraits.h
@@ -29,6 +29,10 @@
         return ::blink::mojom::ReferrerPolicy::ORIGIN;
       case ::blink::kWebReferrerPolicyOriginWhenCrossOrigin:
         return ::blink::mojom::ReferrerPolicy::ORIGIN_WHEN_CROSS_ORIGIN;
+      case ::blink::kWebReferrerPolicySameOrigin:
+        return ::blink::mojom::ReferrerPolicy::SAME_ORIGIN;
+      case ::blink::kWebReferrerPolicyStrictOrigin:
+        return ::blink::mojom::ReferrerPolicy::STRICT_ORIGIN;
       case ::blink::
           kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin:
         return ::blink::mojom::ReferrerPolicy::
@@ -60,6 +64,12 @@
       case ::blink::mojom::ReferrerPolicy::ORIGIN_WHEN_CROSS_ORIGIN:
         *out = ::blink::kWebReferrerPolicyOriginWhenCrossOrigin;
         return true;
+      case ::blink::mojom::ReferrerPolicy::SAME_ORIGIN:
+        *out = ::blink::kWebReferrerPolicySameOrigin;
+        return true;
+      case ::blink::mojom::ReferrerPolicy::STRICT_ORIGIN:
+        *out = ::blink::kWebReferrerPolicyStrictOrigin;
+        return true;
       case ::blink::mojom::ReferrerPolicy::
           NO_REFERRER_WHEN_DOWNGRADE_ORIGIN_WHEN_CROSS_ORIGIN:
         *out = ::blink::
diff --git a/third_party/WebKit/public/platform/WebReferrerPolicy.h b/third_party/WebKit/public/platform/WebReferrerPolicy.h
index 1f45f15..a948f716d 100644
--- a/third_party/WebKit/public/platform/WebReferrerPolicy.h
+++ b/third_party/WebKit/public/platform/WebReferrerPolicy.h
@@ -33,6 +33,8 @@
 
 namespace blink {
 
+// These values are serialized and persisted, so do not remove values and add
+// new ones at the end.
 enum WebReferrerPolicy {
   kWebReferrerPolicyAlways,
   kWebReferrerPolicyDefault,
@@ -40,9 +42,12 @@
   kWebReferrerPolicyNever,
   kWebReferrerPolicyOrigin,
   kWebReferrerPolicyOriginWhenCrossOrigin,
+  // This policy corresponds to strict-origin-when-cross-origin.
+  // TODO(estark): rename to match the spec.
   kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
-  kWebReferrerPolicyLast =
-      kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin
+  kWebReferrerPolicySameOrigin,
+  kWebReferrerPolicyStrictOrigin,
+  kWebReferrerPolicyLast = kWebReferrerPolicyStrictOrigin
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/platform/referrer.mojom b/third_party/WebKit/public/platform/referrer.mojom
index 961640e..003ba32 100644
--- a/third_party/WebKit/public/platform/referrer.mojom
+++ b/third_party/WebKit/public/platform/referrer.mojom
@@ -18,6 +18,8 @@
     NEVER,
     ORIGIN,
     ORIGIN_WHEN_CROSS_ORIGIN,
+    SAME_ORIGIN,
+    STRICT_ORIGIN,
     NO_REFERRER_WHEN_DOWNGRADE_ORIGIN_WHEN_CROSS_ORIGIN,
 };
 
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index 142c8b1..fe89d1b 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -649,6 +649,9 @@
   // notify that the <body> will be attached soon.
   virtual void WillInsertBody(WebLocalFrame*) {}
 
+  // Informs the browser that the draggable regions have been updated.
+  virtual void DraggableRegionsChanged() {}
+
   // Find-in-page notifications ------------------------------------------
 
   // Notifies how many matches have been found in this frame so far, for a
@@ -804,6 +807,7 @@
   }
 
   // Loading --------------------------------------------------------------
+
   virtual std::unique_ptr<blink::WebURLLoader> CreateURLLoader() {
     NOTREACHED();
     return nullptr;
diff --git a/third_party/WebKit/public/web/WebFrameSerializer.h b/third_party/WebKit/public/web/WebFrameSerializer.h
index b6fd3198..74d5473 100644
--- a/third_party/WebKit/public/web/WebFrameSerializer.h
+++ b/third_party/WebKit/public/web/WebFrameSerializer.h
@@ -68,6 +68,8 @@
     virtual bool UseBinaryEncoding() = 0;
 
     virtual bool RemovePopupOverlay() = 0;
+
+    virtual bool UsePageProblemDetectors() = 0;
   };
 
   // Generates and returns an MHTML header.
diff --git a/third_party/WebKit/public/web/WebViewClient.h b/third_party/WebKit/public/web/WebViewClient.h
index 8be346c..0ff83fe 100644
--- a/third_party/WebKit/public/web/WebViewClient.h
+++ b/third_party/WebKit/public/web/WebViewClient.h
@@ -227,10 +227,7 @@
   // Informs the browser that the page scale has changed.
   virtual void PageScaleFactorChanged() {}
 
-  // Draggable regions ----------------------------------------------------
-
-  // Informs the browser that the draggable regions have been updated.
-  virtual void DraggableRegionsChanged() {}
+  // Gestures -------------------------------------------------------------
 
   virtual bool CanHandleGestureEvent() { return false; }
 
diff --git a/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.h b/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.h
index 04d3dc4..e8b72c7 100644
--- a/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.h
+++ b/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.h
@@ -23,7 +23,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "build/build_config.h"
 #include "util/stdlib/cxx.h"
 
@@ -55,7 +54,7 @@
 //! This is similar to `std::allocator<T>`, with the addition of an alignment
 //! guarantee. \a Alignment must be a power of 2. If \a Alignment is not
 //! specified, the default alignment for type \a T is used.
-template <class T, size_t Alignment = ALIGNOF(T)>
+template <class T, size_t Alignment = alignof(T)>
 struct AlignedAllocator {
  public:
   using value_type = T;
@@ -130,7 +129,7 @@
 //! This is similar to `std::vector<T>`, with the addition of an alignment
 //! guarantee. \a Alignment must be a power of 2. If \a Alignment is not
 //! specified, the default alignment for type \a T is used.
-template <typename T, size_t Alignment = ALIGNOF(T)>
+template <typename T, size_t Alignment = alignof(T)>
 using AlignedVector = std::vector<T, AlignedAllocator<T, Alignment>>;
 
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc b/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc
index 66ee6e6c..2799558e 100644
--- a/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc
@@ -40,30 +40,30 @@
   AlignedVector<NaturalAlignedStruct> natural_aligned_vector;
   natural_aligned_vector.push_back(NaturalAlignedStruct());
   EXPECT_TRUE(
-      IsAligned(&natural_aligned_vector[0], ALIGNOF(NaturalAlignedStruct)));
+      IsAligned(&natural_aligned_vector[0], alignof(NaturalAlignedStruct)));
 
   natural_aligned_vector.resize(3);
   EXPECT_TRUE(
-      IsAligned(&natural_aligned_vector[0], ALIGNOF(NaturalAlignedStruct)));
+      IsAligned(&natural_aligned_vector[0], alignof(NaturalAlignedStruct)));
   EXPECT_TRUE(
-      IsAligned(&natural_aligned_vector[1], ALIGNOF(NaturalAlignedStruct)));
+      IsAligned(&natural_aligned_vector[1], alignof(NaturalAlignedStruct)));
   EXPECT_TRUE(
-      IsAligned(&natural_aligned_vector[2], ALIGNOF(NaturalAlignedStruct)));
+      IsAligned(&natural_aligned_vector[2], alignof(NaturalAlignedStruct)));
 
   // Test a structure that declares its own alignment.
-  struct ALIGNAS(32) AlignedStruct {
+  struct alignas(32) AlignedStruct {
     int i;
   };
-  ASSERT_EQ(ALIGNOF(AlignedStruct), 32u);
+  ASSERT_EQ(alignof(AlignedStruct), 32u);
 
   AlignedVector<AlignedStruct> aligned_vector;
   aligned_vector.push_back(AlignedStruct());
-  EXPECT_TRUE(IsAligned(&aligned_vector[0], ALIGNOF(AlignedStruct)));
+  EXPECT_TRUE(IsAligned(&aligned_vector[0], alignof(AlignedStruct)));
 
   aligned_vector.resize(3);
-  EXPECT_TRUE(IsAligned(&aligned_vector[0], ALIGNOF(AlignedStruct)));
-  EXPECT_TRUE(IsAligned(&aligned_vector[1], ALIGNOF(AlignedStruct)));
-  EXPECT_TRUE(IsAligned(&aligned_vector[2], ALIGNOF(AlignedStruct)));
+  EXPECT_TRUE(IsAligned(&aligned_vector[0], alignof(AlignedStruct)));
+  EXPECT_TRUE(IsAligned(&aligned_vector[1], alignof(AlignedStruct)));
+  EXPECT_TRUE(IsAligned(&aligned_vector[2], alignof(AlignedStruct)));
 
   // Try a custom alignment. Since the structure itself doesn’t specify an
   // alignment constraint, only the base address will be aligned to the
@@ -73,19 +73,19 @@
   EXPECT_TRUE(IsAligned(&custom_aligned_vector[0], 64));
 
   // Try a structure with a pretty big alignment request.
-  struct ALIGNAS(1024) BigAlignedStruct {
+  struct alignas(1024) BigAlignedStruct {
     int i;
   };
-  ASSERT_EQ(ALIGNOF(BigAlignedStruct), 1024u);
+  ASSERT_EQ(alignof(BigAlignedStruct), 1024u);
 
   AlignedVector<BigAlignedStruct> big_aligned_vector;
   big_aligned_vector.push_back(BigAlignedStruct());
-  EXPECT_TRUE(IsAligned(&big_aligned_vector[0], ALIGNOF(BigAlignedStruct)));
+  EXPECT_TRUE(IsAligned(&big_aligned_vector[0], alignof(BigAlignedStruct)));
 
   big_aligned_vector.resize(3);
-  EXPECT_TRUE(IsAligned(&big_aligned_vector[0], ALIGNOF(BigAlignedStruct)));
-  EXPECT_TRUE(IsAligned(&big_aligned_vector[1], ALIGNOF(BigAlignedStruct)));
-  EXPECT_TRUE(IsAligned(&big_aligned_vector[2], ALIGNOF(BigAlignedStruct)));
+  EXPECT_TRUE(IsAligned(&big_aligned_vector[0], alignof(BigAlignedStruct)));
+  EXPECT_TRUE(IsAligned(&big_aligned_vector[1], alignof(BigAlignedStruct)));
+  EXPECT_TRUE(IsAligned(&big_aligned_vector[2], alignof(BigAlignedStruct)));
 }
 
 void BadAlignmentTest() {
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 5b9fc97..6a32012 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -27,7 +27,7 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION = '303910'
+CLANG_REVISION = '305281'
 
 use_head_revision = 'LLVM_FORCE_HEAD_REVISION' in os.environ
 if use_head_revision:
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 27406e8..6f8ac11 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5646,6 +5646,7 @@
   <int value="31" label="Permission autoblocker data setting"/>
   <int value="32" label="Ads setting"/>
   <int value="33" label="Ads metadata"/>
+  <int value="34" label="Password protection setting"/>
 </enum>
 
 <enum name="ContentTypeParseableResult" type="int">
@@ -21990,6 +21991,8 @@
   <int value="-1940806558" label="enable-syncfs-directory-operation"/>
   <int value="-1940377152" label="MacRTL:enabled"/>
   <int value="-1940291343" label="SpeculativeResourcePrefetching:enabled"/>
+  <int value="-1939016096"
+      label="OmniboxUIExperimentHideSuggestionUrlTrivialSubdomains:enabled"/>
   <int value="-1938263248" label="enable-extension-info-dialog"/>
   <int value="-1937077699" label="http-form-warning"/>
   <int value="-1934673791" label="gl-composited-texture-quad-border"/>
@@ -22195,10 +22198,6 @@
   <int value="-1212273428" label="enable-experimental-app-list"/>
   <int value="-1212167260" label="disable-app-window-cycling"/>
   <int value="-1208501269" label="AutofillScanThemeDialog:enabled"/>
-  <int value="-1203955801" label="enable-password-change-support:disabled"/>
-  <int value="-1203742042" label="enable-gesture-selection"/>
-  <int value="-1201183153" label="enable-centered-app-list"/>
-  <int value="-1197035323" label="ZeroSuggestRedirectToChrome:disabled"/>
 <!--
 Values in LoginCustomFlags are:  value=(uint32_t)MD5(label).
 This enum is verified by AboutFlagsHistogramTest unit test.
@@ -22210,6 +22209,12 @@
 -->
 
   <summary>Chrome flags that lead to chrome restart on ChromeOS.</summary>
+  <int value="-1206337150"
+      label="OmniboxUIExperimentHideSuggestionUrlScheme:disabled"/>
+  <int value="-1203955801" label="enable-password-change-support:disabled"/>
+  <int value="-1203742042" label="enable-gesture-selection"/>
+  <int value="-1201183153" label="enable-centered-app-list"/>
+  <int value="-1197035323" label="ZeroSuggestRedirectToChrome:disabled"/>
   <int value="-1195194959" label="XGEOVisibleNetworks:disabled"/>
   <int value="-1190174011" label="enable-hdr"/>
   <int value="-1184904651" label="enable-npapi"/>
@@ -22423,6 +22428,8 @@
   <int value="-418868128" label="enable-experimental-web-platform-features"/>
   <int value="-410852857" label="ImprovedA2HS:disabled"/>
   <int value="-405380243" label="enable-encryption-migration"/>
+  <int value="-401170566"
+      label="OmniboxUIExperimentHideSuggestionUrlPath:disabled"/>
   <int value="-400584764" label="ChromeHomeNtpRedesign:enabled"/>
   <int value="-396994784" label="enable-vr-shell"/>
   <int value="-396496344" label="ViewsTaskManager:enabled"/>
@@ -22470,6 +22477,8 @@
   <int value="-279493876" label="WebVRExperimentalRendering:enabled"/>
   <int value="-278347667" label="default-tile-height"/>
   <int value="-277144896" label="enable-viewport-meta"/>
+  <int value="-275870837"
+      label="OmniboxUIExperimentHideSuggestionUrlPath:enabled"/>
   <int value="-275164173" label="QuickUnlockPinSignin:enabled"/>
   <int value="-268357961" label="enable-feature-policy"/>
   <int value="-254887599" label="google-profile-info"/>
@@ -22534,6 +22543,8 @@
   <int value="-16824589" label="ash-shelf-color"/>
   <int value="-13918890" label="disable-download-notification"/>
   <int value="-11260186" label="enable-offline-pages-as-saved-pages"/>
+  <int value="-10709540"
+      label="OmniboxUIExperimentHideSuggestionUrlScheme:enabled"/>
   <int value="-5052940" label="enable-simplified-fullscreen"/>
   <int value="-2953333" label="AndroidHistoryManager:disabled"/>
   <int value="-2371418" label="disable-display-list-2d-canvas"/>
@@ -22928,6 +22939,8 @@
   <int value="1382500494" label="disable-drive-apps-in-app-list"/>
   <int value="1383591631" label="enable-gesture-typing"/>
   <int value="1384614036" label="disable-unified-media-pipeline"/>
+  <int value="1386300777"
+      label="OmniboxUIExperimentHideSuggestionUrlTrivialSubdomains:disabled"/>
   <int value="1389729816" label="data-reduction-proxy-lo-fi"/>
   <int value="1405459667" label="enable-fast-text-autosizing"/>
   <int value="1406354320" label="MacViewsWebUIDialogs:enabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 416eb16..b155d0f40 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -13900,6 +13900,9 @@
 </histogram>
 
 <histogram name="DomDistiller.BarCloseButtonUsage" enum="BooleanPanelWasOpen">
+  <obsolete>
+    UI rewrite to be an InfoBar makes this metric irrelevant as of 05/2017.
+  </obsolete>
   <owner>mdjones@chromium.org</owner>
   <summary>
     Records if the close button was used before or after opening and viewing
@@ -13937,6 +13940,11 @@
   </summary>
 </histogram>
 
+<histogram name="DomDistiller.InfoBarUsage" enum="BooleanUsage">
+  <owner>mdjones@chromium.org</owner>
+  <summary>Records if the infobar was used or closed.</summary>
+</histogram>
+
 <histogram name="DomDistiller.LongArticleScoreNMF.Negative" units="score">
   <owner>wychen@chromium.org</owner>
   <summary>
@@ -14077,6 +14085,9 @@
 </histogram>
 
 <histogram name="DomDistiller.Time.SwipeToPaint" units="ms">
+  <obsolete>
+    UI rewrite to be an InfoBar makes this metric irrelevant as of 05/2017.
+  </obsolete>
   <owner>wychen@chromium.org</owner>
   <summary>
     Records the time from a swipe-up gesture on ReaderModePanel to the first
@@ -14091,7 +14102,17 @@
   </summary>
 </histogram>
 
+<histogram name="DomDistiller.Time.ViewingReaderModePage" units="ms">
+  <owner>mdjones@chromium.org</owner>
+  <summary>
+    Records the amount of time a user spent on a Reader Mode Page.
+  </summary>
+</histogram>
+
 <histogram name="DomDistiller.Time.ViewingReaderModePanel" units="ms">
+  <obsolete>
+    UI rewrite to be an InfoBar makes this metric irrelevant as of 05/2017.
+  </obsolete>
   <owner>mdjones@chromium.org</owner>
   <summary>
     Records the amount of time between the Reader Mode panel opening and it
@@ -50430,6 +50451,35 @@
   </summary>
 </histogram>
 
+<histogram name="PageSerialization.ProblemDetection.LoadedCSSPercentage"
+    units="%">
+  <owner>romax@chromium.org</owner>
+  <summary>
+    Percentage of loaded CSS elements in the main frame at the time of
+    serialization.
+  </summary>
+</histogram>
+
+<histogram name="PageSerialization.ProblemDetection.LoadedImagePercentage"
+    units="%">
+  <owner>romax@chromium.org</owner>
+  <summary>
+    Percentage of loaded images in the main frame at the time of serialization.
+  </summary>
+</histogram>
+
+<histogram name="PageSerialization.ProblemDetection.TotalCSSCount">
+  <owner>romax@chromium.org</owner>
+  <summary>
+    Total number of CSS elements in the main frame for serialization.
+  </summary>
+</histogram>
+
+<histogram name="PageSerialization.ProblemDetection.TotalImageCount">
+  <owner>romax@chromium.org</owner>
+  <summary>Total number of images in the main frame for serialization.</summary>
+</histogram>
+
 <histogram name="PageSerialization.SerializationTime.CSSElement"
     units="microseconds">
   <owner>carlosk@chromium.org</owner>
@@ -51622,8 +51672,8 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordProtection.NumberOfCachedVerdictBeforeShutdown"
-    units="count">
+<histogram base="true"
+    name="PasswordProtection.NumberOfCachedVerdictBeforeShutdown" units="count">
   <owner>jialiul@chromium.org</owner>
   <owner>nparker@chromium.org</owner>
   <summary>
@@ -92989,6 +93039,8 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="PasswordProtectionTrigger" separator=".">
+  <affected-histogram
+      name="PasswordProtection.NumberOfCachedVerdictBeforeShutdown"/>
   <suffix name="PasswordFieldOnFocus"
       label="Password protection triggered by password field on focus event."/>
   <suffix name="ProtectedPasswordEntry"
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index ce45400..e8c7624c 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -4,6 +4,18 @@
 
 import("//build/config/ui.gni")
 import("//testing/test.gni")
+import("//ui/vector_icons/vector_icons.gni")
+
+aggregate_vector_icons("app_list_vector_icons") {
+  icon_directory = "vector_icons"
+
+  icons = [
+    "ic_google_black.1x.icon",
+    "ic_google_black.icon",
+    "ic_mic_black.1x.icon",
+    "ic_mic_black.icon",
+  ]
+}
 
 component("app_list") {
   sources = [
@@ -68,9 +80,12 @@
     "speech_ui_model_observer.h",
   ]
 
+  sources += get_target_outputs(":app_list_vector_icons")
+
   defines = [ "APP_LIST_IMPLEMENTATION" ]
 
   deps = [
+    ":app_list_vector_icons",
     "//base",
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
@@ -130,8 +145,11 @@
       "views/folder_header_view_delegate.h",
       "views/image_shadow_animator.cc",
       "views/image_shadow_animator.h",
-      "views/page_switcher.cc",
       "views/page_switcher.h",
+      "views/page_switcher_horizontal.cc",
+      "views/page_switcher_horizontal.h",
+      "views/page_switcher_vertical.cc",
+      "views/page_switcher_vertical.h",
       "views/pulsing_block_view.cc",
       "views/pulsing_block_view.h",
       "views/search_box_view.cc",
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index 4241f46..4fad4ef 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -10,7 +10,7 @@
 namespace app_list {
 
 const SkColor kContentsBackgroundColor = SkColorSetRGB(0xF2, 0xF2, 0xF2);
-const SkColor kSearchBoxBackground = SK_ColorWHITE;
+const SkColor kSearchBoxBackgroundDefault = SK_ColorWHITE;
 
 const SkColor kSearchTextColor = SkColorSetRGB(0x33, 0x33, 0x33);
 
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index a3927e9..1ee7dcb 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -17,7 +17,7 @@
 namespace app_list {
 
 APP_LIST_EXPORT extern const SkColor kContentsBackgroundColor;
-APP_LIST_EXPORT extern const SkColor kSearchBoxBackground;
+APP_LIST_EXPORT extern const SkColor kSearchBoxBackgroundDefault;
 
 APP_LIST_EXPORT extern const SkColor kSearchTextColor;
 
diff --git a/ui/app_list/vector_icons/ic_google_black.1x.icon b/ui/app_list/vector_icons/ic_google_black.1x.icon
new file mode 100644
index 0000000..dd91373
--- /dev/null
+++ b/ui/app_list/vector_icons/ic_google_black.1x.icon
@@ -0,0 +1,21 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 12, 10,
+LINE_TO, 21.69f, 10,
+CUBIC_TO, 21.83f, 10.63f, 22, 11.4f, 22, 12.23f,
+CUBIC_TO, 22, 17.94f, 18.09f, 22, 12.21f, 22,
+CUBIC_TO, 6.57f, 22, 2, 17.52f, 2, 12,
+CUBIC_TO, 2, 6.48f, 6.57f, 2, 12.2f, 2,
+CUBIC_TO, 14.95f, 2, 17.26f, 2.99f, 19.02f, 4.61f,
+LINE_TO, 16.13f, 7.37f,
+CUBIC_TO, 15.39f, 6.69f, 14.12f, 5.88f, 12.2f, 5.88f,
+CUBIC_TO, 8.82f, 5.88f, 6.07f, 8.63f, 6.07f, 12,
+CUBIC_TO, 6.07f, 15.37f, 8.83f, 18, 12.21f, 18,
+CUBIC_TO, 16.12f, 18, 17.54f, 15.57f, 17.81f, 14,
+LINE_TO, 12, 14,
+LINE_TO, 12, 10,
+CLOSE,
+END
diff --git a/ui/app_list/vector_icons/ic_google_black.icon b/ui/app_list/vector_icons/ic_google_black.icon
new file mode 100644
index 0000000..6e55988
--- /dev/null
+++ b/ui/app_list/vector_icons/ic_google_black.icon
@@ -0,0 +1,20 @@
+// 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.
+
+MOVE_TO, 24, 20,
+LINE_TO, 43.37f, 20,
+CUBIC_TO, 43.66f, 21.26f, 44, 22.8f, 44, 24.46f,
+CUBIC_TO, 44, 35.88f, 36.19f, 44, 24.42f, 44,
+CUBIC_TO, 13.14f, 44, 4, 35.04f, 4, 24,
+CUBIC_TO, 4, 12.96f, 13.14f, 4, 24.4f, 4,
+CUBIC_TO, 29.91f, 4, 34.52f, 5.98f, 38.04f, 9.22f,
+LINE_TO, 32.25f, 14.74f,
+CUBIC_TO, 30.78f, 13.38f, 28.23f, 11.76f, 24.4f, 11.76f,
+CUBIC_TO, 17.65f, 11.76f, 12.14f, 17.26f, 12.14f, 24,
+CUBIC_TO, 12.14f, 30.74f, 17.67f, 36, 24.42f, 36,
+CUBIC_TO, 32.23f, 36, 35.09f, 31.14f, 35.62f, 28,
+LINE_TO, 24, 28,
+LINE_TO, 24, 20,
+CLOSE,
+END
diff --git a/ui/app_list/vector_icons/ic_mic_black.1x.icon b/ui/app_list/vector_icons/ic_mic_black.1x.icon
new file mode 100644
index 0000000..24b28ac
--- /dev/null
+++ b/ui/app_list/vector_icons/ic_mic_black.1x.icon
@@ -0,0 +1,25 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 12, 15,
+CUBIC_TO, 13.66f, 15, 14.99f, 13.66f, 14.99f, 12,
+LINE_TO, 15, 6,
+CUBIC_TO, 15, 4.34f, 13.66f, 3, 12, 3,
+CUBIC_TO, 10.34f, 3, 9, 4.34f, 9, 6,
+LINE_TO, 9, 12,
+CUBIC_TO, 9, 13.66f, 10.34f, 15, 12, 15,
+CLOSE,
+MOVE_TO, 17.3f, 12,
+CUBIC_TO, 17.3f, 15, 14.76f, 17.1f, 12, 17.1f,
+CUBIC_TO, 9.24f, 17.1f, 6.7f, 15, 6.7f, 12,
+LINE_TO, 5, 12,
+CUBIC_TO, 5, 15.41f, 7.72f, 18.23f, 11, 18.72f,
+LINE_TO, 11, 22,
+LINE_TO, 13, 22,
+LINE_TO, 13, 18.72f,
+CUBIC_TO, 16.28f, 18.24f, 19, 15.42f, 19, 12,
+LINE_TO, 17.3f, 12,
+CLOSE,
+END
diff --git a/ui/app_list/vector_icons/ic_mic_black.icon b/ui/app_list/vector_icons/ic_mic_black.icon
new file mode 100644
index 0000000..ca365a0
--- /dev/null
+++ b/ui/app_list/vector_icons/ic_mic_black.icon
@@ -0,0 +1,24 @@
+// 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.
+
+MOVE_TO, 24, 29,
+CUBIC_TO, 27.32f, 29, 29.98f, 26.32f, 29.98f, 23,
+LINE_TO, 30, 11,
+CUBIC_TO, 30, 7.68f, 27.32f, 5, 24, 5,
+CUBIC_TO, 20.68f, 5, 18, 7.68f, 18, 11,
+LINE_TO, 18, 23,
+CUBIC_TO, 18, 26.32f, 20.68f, 29, 24, 29,
+CLOSE,
+MOVE_TO, 34.6f, 23,
+CUBIC_TO, 34.6f, 29, 29.52f, 33.2f, 24, 33.2f,
+CUBIC_TO, 18.48f, 33.2f, 13.4f, 29, 13.4f, 23,
+LINE_TO, 10, 23,
+CUBIC_TO, 10, 29.82f, 15.44f, 35.46f, 22, 36.44f,
+LINE_TO, 22, 43,
+LINE_TO, 26, 43,
+LINE_TO, 26, 36.44f,
+CUBIC_TO, 32.56f, 35.48f, 38, 29.84f, 38, 23,
+LINE_TO, 34.6f, 23,
+CLOSE,
+END
diff --git a/ui/app_list/vector_icons/vector_icons.cc.template b/ui/app_list/vector_icons/vector_icons.cc.template
new file mode 100644
index 0000000..7af5d5b4
--- /dev/null
+++ b/ui/app_list/vector_icons/vector_icons.cc.template
@@ -0,0 +1,25 @@
+// 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.
+
+// vector_icons.cc.template is used to generate vector_icons.cc. Edit the former
+// rather than the latter.
+
+#include "ui/app_list/vector_icons.h"
+
+#include "base/logging.h"
+#include "ui/gfx/vector_icon_types.h"
+
+#define PATH_ELEMENT_TEMPLATE(path_name, ...) \
+static constexpr gfx::PathElement path_name[] = {__VA_ARGS__};
+
+#define VECTOR_ICON_TEMPLATE(icon_name, path_name, path_name_1x) \
+const gfx::VectorIcon icon_name = { path_name , path_name_1x };
+
+namespace app_list {
+
+using namespace gfx;
+
+TEMPLATE_PLACEHOLDER
+
+}  // namespace app_list
diff --git a/ui/app_list/vector_icons/vector_icons.h.template b/ui/app_list/vector_icons/vector_icons.h.template
new file mode 100644
index 0000000..c7943b1
--- /dev/null
+++ b/ui/app_list/vector_icons/vector_icons.h.template
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// vector_icons.h.template is used to generate vector_icons.h. Edit the former
+// rather than the latter.
+
+#ifndef UI_APP_LIST_VECTOR_ICONS_H_
+#define UI_APP_LIST_VECTOR_ICONS_H_
+
+namespace gfx {
+struct VectorIcon;
+}
+
+#define VECTOR_ICON_TEMPLATE_H(icon_name) \
+extern const gfx::VectorIcon icon_name;
+
+namespace app_list {
+
+TEMPLATE_PLACEHOLDER
+
+}  // namespace app_list
+
+#undef VECTOR_ICON_TEMPLATE_H
+
+#endif  // UI_APP_LIST_VECTOR_ICONS_H_
diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc
index 85023e9..b8cc2fa 100644
--- a/ui/app_list/views/apps_grid_view.cc
+++ b/ui/app_list/views/apps_grid_view.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_folder_item.h"
 #include "ui/app_list/app_list_item.h"
 #include "ui/app_list/app_list_switches.h"
@@ -20,7 +21,8 @@
 #include "ui/app_list/views/app_list_folder_view.h"
 #include "ui/app_list/views/app_list_item_view.h"
 #include "ui/app_list/views/apps_grid_view_delegate.h"
-#include "ui/app_list/views/page_switcher.h"
+#include "ui/app_list/views/page_switcher_horizontal.h"
+#include "ui/app_list/views/page_switcher_vertical.h"
 #include "ui/app_list/views/pulsing_block_view.h"
 #include "ui/app_list/views/top_icon_animation_view.h"
 #include "ui/aura/window.h"
@@ -229,9 +231,16 @@
                                            kOverscrollPageTransitionDurationMs);
 
   pagination_model_.AddObserver(this);
-  pagination_controller_.reset(new PaginationController(
-      &pagination_model_, PaginationController::SCROLL_AXIS_HORIZONTAL));
-  page_switcher_view_ = new PageSwitcher(&pagination_model_);
+
+  if (features::IsFullscreenAppListEnabled()) {
+    page_switcher_view_ = new PageSwitcherVertical(&pagination_model_);
+    pagination_controller_.reset(new PaginationController(
+        &pagination_model_, PaginationController::SCROLL_AXIS_VERTICAL));
+  } else {
+    page_switcher_view_ = new PageSwitcherHorizontal(&pagination_model_);
+    pagination_controller_.reset(new PaginationController(
+        &pagination_model_, PaginationController::SCROLL_AXIS_HORIZONTAL));
+  }
   AddChildView(page_switcher_view_);
 }
 
@@ -630,11 +639,18 @@
 
 gfx::Size AppsGridView::CalculatePreferredSize() const {
   const gfx::Insets insets(GetInsets());
-  // If we are in a folder, ignore the page switcher for height calculations.
-  int page_switcher_height =
-      folder_delegate_ ? 0 : page_switcher_view_->GetPreferredSize().height();
   gfx::Size size = GetTileGridSize();
-  size.Enlarge(insets.width(), insets.height() + page_switcher_height);
+  if (features::IsFullscreenAppListEnabled()) {
+    // If we are in a folder, ignore the page switcher for width calculations.
+    int page_switcher_width =
+        folder_delegate_ ? 0 : page_switcher_view_->GetPreferredSize().width();
+    size.Enlarge(insets.width() + page_switcher_width, insets.height());
+  } else {
+    // If we are in a folder, ignore the page switcher for height calculations.
+    int page_switcher_height =
+        folder_delegate_ ? 0 : page_switcher_view_->GetPreferredSize().height();
+    size.Enlarge(insets.width(), insets.height() + page_switcher_height);
+  }
   return size;
 }
 
@@ -666,11 +682,18 @@
   }
   views::ViewModelUtils::SetViewBoundsToIdealBounds(pulsing_blocks_model_);
 
-  const int page_switcher_height =
-      page_switcher_view_->GetPreferredSize().height();
   gfx::Rect rect(GetContentsBounds());
-  rect.set_y(rect.bottom() - page_switcher_height);
-  rect.set_height(page_switcher_height);
+  if (features::IsFullscreenAppListEnabled()) {
+    const int page_switcher_width =
+        page_switcher_view_->GetPreferredSize().width();
+    rect.set_x(rect.right() - page_switcher_width);
+    rect.set_width(page_switcher_width);
+  } else {
+    const int page_switcher_height =
+        page_switcher_view_->GetPreferredSize().height();
+    rect.set_y(rect.bottom() - page_switcher_height);
+    rect.set_height(page_switcher_height);
+  }
   page_switcher_view_->SetBoundsRect(rect);
 }
 
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index ff96d24..8508e106 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -210,13 +210,15 @@
   DCHECK(start_page_view_);
 
   // Set the visibility of the search box's back button.
-  app_list_main_view_->search_box_view()->back_button()->SetVisible(
-      state != AppListModel::STATE_START);
-  app_list_main_view_->search_box_view()->Layout();
-  bool folder_active = (state == AppListModel::STATE_APPS)
-                           ? apps_container_view_->IsInFolderView()
-                           : false;
-  app_list_main_view_->search_box_view()->SetBackButtonLabel(folder_active);
+  if (!features::IsFullscreenAppListEnabled()) {
+    app_list_main_view_->search_box_view()->back_button()->SetVisible(
+        state != AppListModel::STATE_START);
+    app_list_main_view_->search_box_view()->Layout();
+    bool folder_active = (state == AppListModel::STATE_APPS)
+                             ? apps_container_view_->IsInFolderView()
+                             : false;
+    app_list_main_view_->search_box_view()->SetBackButtonLabel(folder_active);
+  }
 
   // Whenever the page changes, the custom launcher page is considered to have
   // been reset.
diff --git a/ui/app_list/views/page_switcher.h b/ui/app_list/views/page_switcher.h
index 127124d0..6785c54c 100644
--- a/ui/app_list/views/page_switcher.h
+++ b/ui/app_list/views/page_switcher.h
@@ -5,54 +5,23 @@
 #ifndef UI_APP_LIST_VIEWS_PAGE_SWITCHER_H_
 #define UI_APP_LIST_VIEWS_PAGE_SWITCHER_H_
 
-#include "base/macros.h"
-#include "ui/app_list/pagination_model_observer.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
 namespace app_list {
 
-class PaginationModel;
-
 // PageSwitcher represents its underlying PaginationModel with a button strip.
 // Each page in the PageinationModel has a button in the strip and when the
 // button is clicked, the corresponding page becomes selected.
-class PageSwitcher : public views::View,
-                     public views::ButtonListener,
-                     public PaginationModelObserver {
+class PageSwitcher : public views::View {
  public:
-  explicit PageSwitcher(PaginationModel* model);
-  ~PageSwitcher() override;
-
   // Returns the page index of the page switcher button under the point. If no
   // page switcher button is under the point, -1 is return. |point| is in
   // PageSwitcher's coordinates.
-  int GetPageForPoint(const gfx::Point& point) const;
+  virtual int GetPageForPoint(const gfx::Point& point) const = 0;
 
   // Shows hover for button under the point. |point| is in PageSwitcher's
   // coordinates.
-  void UpdateUIForDragPoint(const gfx::Point& point);
-
-  // Overridden from views::View:
-  gfx::Size CalculatePreferredSize() const override;
-  void Layout() override;
-
- private:
-  void CalculateButtonWidthAndSpacing(int contents_width);
-
-  // Overridden from views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
-  // Overridden from PaginationModelObserver:
-  void TotalPagesChanged() override;
-  void SelectedPageChanged(int old_selected, int new_selected) override;
-  void TransitionStarted() override;
-  void TransitionChanged() override;
-
-  PaginationModel* model_;  // Owned by AppsGridView.
-  views::View* buttons_;  // Owned by views hierarchy.
-
-  DISALLOW_COPY_AND_ASSIGN(PageSwitcher);
+  virtual void UpdateUIForDragPoint(const gfx::Point& point) = 0;
 };
 
 }  // namespace app_list
diff --git a/ui/app_list/views/page_switcher.cc b/ui/app_list/views/page_switcher_horizontal.cc
similarity index 82%
rename from ui/app_list/views/page_switcher.cc
rename to ui/app_list/views/page_switcher_horizontal.cc
index a2c427f..52f6f63 100644
--- a/ui/app_list/views/page_switcher.cc
+++ b/ui/app_list/views/page_switcher_horizontal.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 "ui/app_list/views/page_switcher.h"
+#include "ui/app_list/views/page_switcher_horizontal.h"
 
 #include <algorithm>
 
@@ -36,8 +36,7 @@
   explicit PageSwitcherButton(views::ButtonListener* listener)
       : views::CustomButton(listener),
         button_width_(kMaxButtonWidth),
-        selected_range_(0) {
-  }
+        selected_range_(0) {}
   ~PageSwitcherButton() override {}
 
   void SetSelectedRange(double selected_range) {
@@ -95,7 +94,7 @@
     if (selected_range_ > 0) {
       selected_width = selected_range_ * rect.width();
     } else if (selected_range_ < 0) {
-      selected_width =  -selected_range_ * rect.width();
+      selected_width = -selected_range_ * rect.width();
       selected_start_x = rect.right() - selected_width;
     }
 
@@ -130,9 +129,8 @@
 
 }  // namespace
 
-PageSwitcher::PageSwitcher(PaginationModel* model)
-    : model_(model),
-      buttons_(new views::View) {
+PageSwitcherHorizontal::PageSwitcherHorizontal(PaginationModel* model)
+    : model_(model), buttons_(new views::View) {
   AddChildView(buttons_);
 
   TotalPagesChanged();
@@ -140,11 +138,11 @@
   model_->AddObserver(this);
 }
 
-PageSwitcher::~PageSwitcher() {
+PageSwitcherHorizontal::~PageSwitcherHorizontal() {
   model_->RemoveObserver(this);
 }
 
-int PageSwitcher::GetPageForPoint(const gfx::Point& point) const {
+int PageSwitcherHorizontal::GetPageForPoint(const gfx::Point& point) const {
   if (!buttons_->bounds().Contains(point))
     return -1;
 
@@ -160,7 +158,7 @@
   return -1;
 }
 
-void PageSwitcher::UpdateUIForDragPoint(const gfx::Point& point) {
+void PageSwitcherHorizontal::UpdateUIForDragPoint(const gfx::Point& point) {
   int page = GetPageForPoint(point);
 
   const int button_count = buttons_->child_count();
@@ -178,14 +176,13 @@
   }
 }
 
-gfx::Size PageSwitcher::CalculatePreferredSize() const {
+gfx::Size PageSwitcherHorizontal::CalculatePreferredSize() const {
   // Always return a size with correct height so that container resize is not
   // needed when more pages are added.
-  return gfx::Size(buttons_->GetPreferredSize().width(),
-                   kPreferredHeight);
+  return gfx::Size(buttons_->GetPreferredSize().width(), kPreferredHeight);
 }
 
-void PageSwitcher::Layout() {
+void PageSwitcherHorizontal::Layout() {
   gfx::Rect rect(GetContentsBounds());
 
   CalculateButtonWidthAndSpacing(rect.width());
@@ -193,13 +190,12 @@
   // Makes |buttons_| horizontally center and vertically fill.
   gfx::Size buttons_size(buttons_->GetPreferredSize());
   gfx::Rect buttons_bounds(rect.CenterPoint().x() - buttons_size.width() / 2,
-                           rect.y(),
-                           buttons_size.width(),
-                           rect.height());
+                           rect.y(), buttons_size.width(), rect.height());
   buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds));
 }
 
-void PageSwitcher::CalculateButtonWidthAndSpacing(int contents_width) {
+void PageSwitcherHorizontal::CalculateButtonWidthAndSpacing(
+    int contents_width) {
   const int button_count = buttons_->child_count();
   if (!button_count)
     return;
@@ -209,16 +205,16 @@
   int button_width = kMinButtonWidth;
   int button_spacing = kMinButtonSpacing;
   if (button_count > 1) {
-    button_spacing = (contents_width - button_width * button_count) /
-        (button_count - 1);
+    button_spacing =
+        (contents_width - button_width * button_count) / (button_count - 1);
     button_spacing = std::min(kMaxButtonSpacing,
                               std::max(kMinButtonSpacing, button_spacing));
   }
 
-  button_width = (contents_width - (button_count - 1) * button_spacing) /
-      button_count;
-  button_width = std::min(kMaxButtonWidth,
-                          std::max(kMinButtonWidth, button_width));
+  button_width =
+      (contents_width - (button_count - 1) * button_spacing) / button_count;
+  button_width =
+      std::min(kMaxButtonWidth, std::max(kMinButtonWidth, button_width));
 
   buttons_->SetLayoutManager(new views::BoxLayout(
       views::BoxLayout::kHorizontal, gfx::Insets(0, kButtonStripPadding),
@@ -230,8 +226,8 @@
   }
 }
 
-void PageSwitcher::ButtonPressed(views::Button* sender,
-                                 const ui::Event& event) {
+void PageSwitcherHorizontal::ButtonPressed(views::Button* sender,
+                                           const ui::Event& event) {
   for (int i = 0; i < buttons_->child_count(); ++i) {
     if (sender == static_cast<views::Button*>(buttons_->child_at(i))) {
       model_->SelectPage(i, true /* animate */);
@@ -240,7 +236,7 @@
   }
 }
 
-void PageSwitcher::TotalPagesChanged() {
+void PageSwitcherHorizontal::TotalPagesChanged() {
   buttons_->RemoveAllChildViews(true);
   for (int i = 0; i < model_->total_pages(); ++i) {
     PageSwitcherButton* button = new PageSwitcherButton(this);
@@ -251,17 +247,17 @@
   Layout();
 }
 
-void PageSwitcher::SelectedPageChanged(int old_selected, int new_selected) {
+void PageSwitcherHorizontal::SelectedPageChanged(int old_selected,
+                                                 int new_selected) {
   if (old_selected >= 0 && old_selected < buttons_->child_count())
     GetButtonByIndex(buttons_, old_selected)->SetSelectedRange(0);
   if (new_selected >= 0 && new_selected < buttons_->child_count())
     GetButtonByIndex(buttons_, new_selected)->SetSelectedRange(1);
 }
 
-void PageSwitcher::TransitionStarted() {
-}
+void PageSwitcherHorizontal::TransitionStarted() {}
 
-void PageSwitcher::TransitionChanged() {
+void PageSwitcherHorizontal::TransitionChanged() {
   const int current_page = model_->selected_page();
   const int target_page = model_->transition().target_page;
 
diff --git a/ui/app_list/views/page_switcher_horizontal.h b/ui/app_list/views/page_switcher_horizontal.h
new file mode 100644
index 0000000..c7f46a30
--- /dev/null
+++ b/ui/app_list/views/page_switcher_horizontal.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 UI_APP_LIST_VIEWS_PAGE_SWITCHER_HORIZONTAL_H_
+#define UI_APP_LIST_VIEWS_PAGE_SWITCHER_HORIZONTAL_H_
+
+#include "base/macros.h"
+#include "ui/app_list/pagination_model_observer.h"
+#include "ui/app_list/views/page_switcher.h"
+#include "ui/views/controls/button/button.h"
+
+namespace app_list {
+
+class PaginationModel;
+
+// PageSwitcher represents its underlying PaginationModel with a button strip.
+// Each page in the PageinationModel has a button in the strip and when the
+// button is clicked, the corresponding page becomes selected.
+class PageSwitcherHorizontal : public PageSwitcher,
+                               public views::ButtonListener,
+                               public PaginationModelObserver {
+ public:
+  explicit PageSwitcherHorizontal(PaginationModel* model);
+  ~PageSwitcherHorizontal() override;
+
+  // Overridden from PageSwitcher:
+  int GetPageForPoint(const gfx::Point& point) const override;
+  void UpdateUIForDragPoint(const gfx::Point& point) override;
+
+  // Overridden from views::View:
+  gfx::Size CalculatePreferredSize() const override;
+  void Layout() override;
+
+ private:
+  void CalculateButtonWidthAndSpacing(int contents_width);
+
+  // Overridden from views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // Overridden from PaginationModelObserver:
+  void TotalPagesChanged() override;
+  void SelectedPageChanged(int old_selected, int new_selected) override;
+  void TransitionStarted() override;
+  void TransitionChanged() override;
+
+  PaginationModel* model_;  // Owned by AppsGridView.
+  views::View* buttons_;    // Owned by views hierarchy.
+
+  DISALLOW_COPY_AND_ASSIGN(PageSwitcherHorizontal);
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_PAGE_SWITCHER_HORIZONTAL_H_
diff --git a/ui/app_list/views/page_switcher.cc b/ui/app_list/views/page_switcher_vertical.cc
similarity index 68%
copy from ui/app_list/views/page_switcher.cc
copy to ui/app_list/views/page_switcher_vertical.cc
index a2c427f..3155bf7d 100644
--- a/ui/app_list/views/page_switcher.cc
+++ b/ui/app_list/views/page_switcher_vertical.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 "ui/app_list/views/page_switcher.h"
+#include "ui/app_list/views/page_switcher_vertical.h"
 
 #include <algorithm>
 
@@ -21,13 +21,13 @@
 
 namespace {
 
-const int kPreferredHeight = 58;
+const int kPreferredWidth = 58;
 
 const int kMaxButtonSpacing = 18;
 const int kMinButtonSpacing = 4;
-const int kMaxButtonWidth = 68;
-const int kMinButtonWidth = 28;
-const int kButtonHeight = 6;
+const int kMaxButtonHeight = 68;
+const int kMinButtonHeight = 28;
+const int kButtonWidth = 6;
 const int kButtonCornerRadius = 2;
 const int kButtonStripPadding = 20;
 
@@ -35,9 +35,8 @@
  public:
   explicit PageSwitcherButton(views::ButtonListener* listener)
       : views::CustomButton(listener),
-        button_width_(kMaxButtonWidth),
-        selected_range_(0) {
-  }
+        button_height_(kMaxButtonHeight),
+        selected_range_(0) {}
   ~PageSwitcherButton() override {}
 
   void SetSelectedRange(double selected_range) {
@@ -48,11 +47,11 @@
     SchedulePaint();
   }
 
-  void set_button_width(int button_width) { button_width_ = button_width; }
+  void set_button_height(int button_height) { button_height_ = button_height; }
 
   // Overridden from views::View:
   gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(button_width_, kButtonHeight);
+    return gfx::Size(kButtonWidth, button_height_);
   }
 
   void PaintButtonContents(gfx::Canvas* canvas) override {
@@ -77,7 +76,7 @@
   // Paints a button that has two rounded corner at bottom.
   void PaintButton(gfx::Canvas* canvas, SkColor base_color) {
     gfx::Rect rect(GetContentsBounds());
-    rect.ClampToCenteredSize(gfx::Size(button_width_, kButtonHeight));
+    rect.ClampToCenteredSize(gfx::Size(kButtonWidth, button_height_));
 
     SkPath path;
     path.addRoundRect(gfx::RectToSkRect(rect),
@@ -90,19 +89,19 @@
     flags.setColor(base_color);
     canvas->DrawPath(path, flags);
 
-    int selected_start_x = 0;
-    int selected_width = 0;
+    int selected_start_y = 0;
+    int selected_height = 0;
     if (selected_range_ > 0) {
-      selected_width = selected_range_ * rect.width();
+      selected_height = selected_range_ * rect.height();
     } else if (selected_range_ < 0) {
-      selected_width =  -selected_range_ * rect.width();
-      selected_start_x = rect.right() - selected_width;
+      selected_height = -selected_range_ * rect.height();
+      selected_start_y = rect.bottom() - selected_height;
     }
 
-    if (selected_width) {
+    if (selected_height) {
       gfx::Rect selected_rect(rect);
-      selected_rect.set_x(selected_start_x);
-      selected_rect.set_width(selected_width);
+      selected_rect.set_y(selected_start_y);
+      selected_rect.set_height(selected_height);
 
       SkPath selected_path;
       selected_path.addRoundRect(gfx::RectToSkRect(selected_rect),
@@ -113,11 +112,11 @@
     }
   }
 
-  int button_width_;
+  int button_height_;
 
   // [-1, 1] range that represents the portion of the button that should be
-  // painted with kSelectedColor. Positive range starts from left side and
-  // negative range starts from the right side.
+  // painted with kSelectedColor. Positive range starts from top side and
+  // negative range starts from the bottom side.
   double selected_range_;
 
   DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton);
@@ -130,9 +129,8 @@
 
 }  // namespace
 
-PageSwitcher::PageSwitcher(PaginationModel* model)
-    : model_(model),
-      buttons_(new views::View) {
+PageSwitcherVertical::PageSwitcherVertical(PaginationModel* model)
+    : model_(model), buttons_(new views::View) {
   AddChildView(buttons_);
 
   TotalPagesChanged();
@@ -140,11 +138,11 @@
   model_->AddObserver(this);
 }
 
-PageSwitcher::~PageSwitcher() {
+PageSwitcherVertical::~PageSwitcherVertical() {
   model_->RemoveObserver(this);
 }
 
-int PageSwitcher::GetPageForPoint(const gfx::Point& point) const {
+int PageSwitcherVertical::GetPageForPoint(const gfx::Point& point) const {
   if (!buttons_->bounds().Contains(point))
     return -1;
 
@@ -160,7 +158,7 @@
   return -1;
 }
 
-void PageSwitcher::UpdateUIForDragPoint(const gfx::Point& point) {
+void PageSwitcherVertical::UpdateUIForDragPoint(const gfx::Point& point) {
   int page = GetPageForPoint(point);
 
   const int button_count = buttons_->child_count();
@@ -178,60 +176,59 @@
   }
 }
 
-gfx::Size PageSwitcher::CalculatePreferredSize() const {
-  // Always return a size with correct height so that container resize is not
+gfx::Size PageSwitcherVertical::CalculatePreferredSize() const {
+  // Always return a size with correct width so that container resize is not
   // needed when more pages are added.
-  return gfx::Size(buttons_->GetPreferredSize().width(),
-                   kPreferredHeight);
+  return gfx::Size(kPreferredWidth, buttons_->GetPreferredSize().height());
 }
 
-void PageSwitcher::Layout() {
+void PageSwitcherVertical::Layout() {
   gfx::Rect rect(GetContentsBounds());
 
-  CalculateButtonWidthAndSpacing(rect.width());
+  CalculateButtonHeightAndSpacing(rect.height());
 
-  // Makes |buttons_| horizontally center and vertically fill.
+  // Makes |buttons_| vertically center and horizontally fill.
   gfx::Size buttons_size(buttons_->GetPreferredSize());
-  gfx::Rect buttons_bounds(rect.CenterPoint().x() - buttons_size.width() / 2,
-                           rect.y(),
-                           buttons_size.width(),
-                           rect.height());
+  gfx::Rect buttons_bounds(rect.x(),
+                           rect.CenterPoint().y() - buttons_size.height() / 2,
+                           rect.width(), buttons_size.height());
   buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds));
 }
 
-void PageSwitcher::CalculateButtonWidthAndSpacing(int contents_width) {
+void PageSwitcherVertical::CalculateButtonHeightAndSpacing(
+    int contents_height) {
   const int button_count = buttons_->child_count();
   if (!button_count)
     return;
 
-  contents_width -= 2 * kButtonStripPadding;
+  contents_height -= 2 * kButtonStripPadding;
 
-  int button_width = kMinButtonWidth;
+  int button_height = kMinButtonHeight;
   int button_spacing = kMinButtonSpacing;
   if (button_count > 1) {
-    button_spacing = (contents_width - button_width * button_count) /
-        (button_count - 1);
+    button_spacing =
+        (contents_height - button_height * button_count) / (button_count - 1);
     button_spacing = std::min(kMaxButtonSpacing,
                               std::max(kMinButtonSpacing, button_spacing));
   }
 
-  button_width = (contents_width - (button_count - 1) * button_spacing) /
-      button_count;
-  button_width = std::min(kMaxButtonWidth,
-                          std::max(kMinButtonWidth, button_width));
+  button_height =
+      (contents_height - (button_count - 1) * button_spacing) / button_count;
+  button_height =
+      std::min(kMaxButtonHeight, std::max(kMinButtonHeight, button_height));
 
   buttons_->SetLayoutManager(new views::BoxLayout(
-      views::BoxLayout::kHorizontal, gfx::Insets(0, kButtonStripPadding),
+      views::BoxLayout::kVertical, gfx::Insets(kButtonStripPadding, 0),
       button_spacing));
   for (int i = 0; i < button_count; ++i) {
     PageSwitcherButton* button =
         static_cast<PageSwitcherButton*>(buttons_->child_at(i));
-    button->set_button_width(button_width);
+    button->set_button_height(button_height);
   }
 }
 
-void PageSwitcher::ButtonPressed(views::Button* sender,
-                                 const ui::Event& event) {
+void PageSwitcherVertical::ButtonPressed(views::Button* sender,
+                                         const ui::Event& event) {
   for (int i = 0; i < buttons_->child_count(); ++i) {
     if (sender == static_cast<views::Button*>(buttons_->child_at(i))) {
       model_->SelectPage(i, true /* animate */);
@@ -240,7 +237,7 @@
   }
 }
 
-void PageSwitcher::TotalPagesChanged() {
+void PageSwitcherVertical::TotalPagesChanged() {
   buttons_->RemoveAllChildViews(true);
   for (int i = 0; i < model_->total_pages(); ++i) {
     PageSwitcherButton* button = new PageSwitcherButton(this);
@@ -251,17 +248,17 @@
   Layout();
 }
 
-void PageSwitcher::SelectedPageChanged(int old_selected, int new_selected) {
+void PageSwitcherVertical::SelectedPageChanged(int old_selected,
+                                               int new_selected) {
   if (old_selected >= 0 && old_selected < buttons_->child_count())
     GetButtonByIndex(buttons_, old_selected)->SetSelectedRange(0);
   if (new_selected >= 0 && new_selected < buttons_->child_count())
     GetButtonByIndex(buttons_, new_selected)->SetSelectedRange(1);
 }
 
-void PageSwitcher::TransitionStarted() {
-}
+void PageSwitcherVertical::TransitionStarted() {}
 
-void PageSwitcher::TransitionChanged() {
+void PageSwitcherVertical::TransitionChanged() {
   const int current_page = model_->selected_page();
   const int target_page = model_->transition().target_page;
 
diff --git a/ui/app_list/views/page_switcher_vertical.h b/ui/app_list/views/page_switcher_vertical.h
new file mode 100644
index 0000000..0f2198ad
--- /dev/null
+++ b/ui/app_list/views/page_switcher_vertical.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 UI_APP_LIST_VIEWS_PAGE_SWITCHER_VERTICAL_H_
+#define UI_APP_LIST_VIEWS_PAGE_SWITCHER_VERTICAL_H_
+
+#include "base/macros.h"
+#include "ui/app_list/pagination_model_observer.h"
+#include "ui/app_list/views/page_switcher.h"
+#include "ui/views/controls/button/button.h"
+
+namespace app_list {
+
+class PaginationModel;
+
+// PageSwitcher represents its underlying PaginationModel with a button strip.
+// Each page in the PageinationModel has a button in the strip and when the
+// button is clicked, the corresponding page becomes selected.
+class PageSwitcherVertical : public PageSwitcher,
+                             public views::ButtonListener,
+                             public PaginationModelObserver {
+ public:
+  explicit PageSwitcherVertical(PaginationModel* model);
+  ~PageSwitcherVertical() override;
+
+  // Overridden from PageSwitcher:
+  int GetPageForPoint(const gfx::Point& point) const override;
+  void UpdateUIForDragPoint(const gfx::Point& point) override;
+
+  // Overridden from views::View:
+  gfx::Size CalculatePreferredSize() const override;
+  void Layout() override;
+
+ private:
+  void CalculateButtonHeightAndSpacing(int contents_height);
+
+  // Overridden from views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // Overridden from PaginationModelObserver:
+  void TotalPagesChanged() override;
+  void SelectedPageChanged(int old_selected, int new_selected) override;
+  void TransitionStarted() override;
+  void TransitionChanged() override;
+
+  PaginationModel* model_;  // Owned by AppsGridView.
+  views::View* buttons_;    // Owned by views hierarchy.
+
+  DISALLOW_COPY_AND_ASSIGN(PageSwitcherVertical);
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_PAGE_SWITCHER_VERTICAL_H_
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index db92a679..c5899f2aa0 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -17,6 +17,7 @@
 #include "ui/app_list/resources/grit/app_list_resources.h"
 #include "ui/app_list/search_box_model.h"
 #include "ui/app_list/speech_ui_model.h"
+#include "ui/app_list/vector_icons.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view_delegate.h"
@@ -26,6 +27,7 @@
 #include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/shadow_value.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/background.h"
@@ -42,16 +44,24 @@
 
 namespace {
 
-const int kPadding = 16;
-const int kInnerPadding = 24;
-const int kPreferredWidth = 360;
-const int kPreferredWidthFullscreen = 544;
-const int kPreferredHeight = 48;
+constexpr int kPadding = 16;
+constexpr int kInnerPadding = 24;
+constexpr int kPreferredWidth = 360;
+constexpr int kPreferredWidthFullscreen = 544;
+constexpr int kPreferredHeight = 48;
 
-const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
+constexpr SkColor kHintTextColor = SkColorSetARGBMacro(0xFF, 0xA0, 0xA0, 0xA0);
 
-const int kBackgroundBorderCornerRadius = 2;
-const int kBackgroundBorderCornerRadiusFullscreen = 20;
+constexpr int kBackgroundBorderCornerRadius = 2;
+constexpr int kBackgroundBorderCornerRadiusFullscreen = 20;
+
+constexpr int kGoogleIconSize = 24;
+constexpr int kMicIconSize = 24;
+
+// Default color used when wallpaper customized color is not available for
+// searchbox, #000 at 87% opacity.
+constexpr SkColor kDefaultSearchboxColor =
+    SkColorSetARGBMacro(0xDE, 0x00, 0x00, 0x00);
 
 // A background that paints a solid white rounded rect with a thin grey border.
 class SearchBoxBackground : public views::Background {
@@ -70,7 +80,7 @@
 
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
-    flags.setColor(kSearchBoxBackground);
+    flags.setColor(kSearchBoxBackgroundDefault);
     canvas->DrawRoundRect(bounds, background_border_corner_radius_, flags);
   }
 
@@ -129,12 +139,13 @@
                              AppListView* app_list_view)
     : delegate_(delegate),
       view_delegate_(view_delegate),
-      model_(NULL),
+      model_(nullptr),
       content_container_(new views::View),
-      back_button_(NULL),
-      speech_button_(NULL),
+      google_icon_(nullptr),
+      back_button_(nullptr),
+      speech_button_(nullptr),
       search_box_(new views::Textfield),
-      contents_view_(NULL),
+      contents_view_(nullptr),
       app_list_view_(app_list_view),
       focused_view_(FOCUS_SEARCH_BOX),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
@@ -146,14 +157,6 @@
   AddChildView(content_container_);
 
   SetShadow(GetShadowForZHeight(2));
-  back_button_ = new SearchBoxImageButton(this);
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  back_button_->SetImage(views::ImageButton::STATE_NORMAL,
-                         rb.GetImageSkiaNamed(IDR_APP_LIST_FOLDER_BACK_NORMAL));
-  back_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
-                                  views::ImageButton::ALIGN_MIDDLE);
-  SetBackButtonLabel(false);
-  content_container_->AddChildView(back_button_);
   content_container_->SetBackground(base::MakeUnique<SearchBoxBackground>());
 
   views::BoxLayout* layout = new views::BoxLayout(
@@ -166,11 +169,30 @@
 
   search_box_->SetBorder(views::NullBorder());
   search_box_->SetTextColor(kSearchTextColor);
-  search_box_->SetBackgroundColor(kSearchBoxBackground);
-  search_box_->set_placeholder_text_color(kHintTextColor);
+  search_box_->SetBackgroundColor(kSearchBoxBackgroundDefault);
   search_box_->set_controller(this);
   search_box_->SetTextInputType(ui::TEXT_INPUT_TYPE_SEARCH);
   search_box_->SetTextInputFlags(ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF);
+  if (is_fullscreen_app_list_enabled_) {
+    google_icon_ = new views::ImageView();
+    google_icon_->SetImage(gfx::CreateVectorIcon(
+        kIcGoogleBlackIcon, kGoogleIconSize, kDefaultSearchboxColor));
+    content_container_->AddChildView(google_icon_);
+
+    search_box_->set_placeholder_text_color(kDefaultSearchboxColor);
+  } else {
+    back_button_ = new SearchBoxImageButton(this);
+    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+    back_button_->SetImage(
+        views::ImageButton::STATE_NORMAL,
+        rb.GetImageSkiaNamed(IDR_APP_LIST_FOLDER_BACK_NORMAL));
+    back_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
+                                    views::ImageButton::ALIGN_MIDDLE);
+    SetBackButtonLabel(false);
+    content_container_->AddChildView(back_button_);
+
+    search_box_->set_placeholder_text_color(kHintTextColor);
+  }
   content_container_->AddChildView(search_box_);
   layout->SetFlexForView(search_box_, 1);
 
@@ -418,14 +440,26 @@
     }
 
     speech_button_->SetAccessibleName(speech_button_prop->accessible_name);
+    if (is_fullscreen_app_list_enabled_) {
+      speech_button_->SetImage(
+          views::Button::STATE_NORMAL,
+          gfx::CreateVectorIcon(kIcMicBlackIcon, kMicIconSize,
+                                kDefaultSearchboxColor));
+    }
+    // TODO(warx): consider removing on_tooltip as it is not accessible due to
+    // the overlap of speech UI.
     if (view_delegate_->GetSpeechUI()->state() ==
         SPEECH_RECOGNITION_HOTWORD_LISTENING) {
-      speech_button_->SetImage(
-          views::Button::STATE_NORMAL, &speech_button_prop->on_icon);
+      if (!is_fullscreen_app_list_enabled_) {
+        speech_button_->SetImage(views::Button::STATE_NORMAL,
+                                 &speech_button_prop->on_icon);
+      }
       speech_button_->SetTooltipText(speech_button_prop->on_tooltip);
     } else {
-      speech_button_->SetImage(
-          views::Button::STATE_NORMAL, &speech_button_prop->off_icon);
+      if (!is_fullscreen_app_list_enabled_) {
+        speech_button_->SetImage(views::Button::STATE_NORMAL,
+                                 &speech_button_prop->off_icon);
+      }
       speech_button_->SetTooltipText(speech_button_prop->off_tooltip);
     }
   } else {
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index 3643e68..b03fdc6 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -16,6 +16,7 @@
 #include "ui/views/view.h"
 
 namespace views {
+class ImageView;
 class Textfield;
 }  // namespace views
 
@@ -115,7 +116,8 @@
   AppListViewDelegate* view_delegate_;  // Not owned.
   AppListModel* model_;  // Owned by the profile-keyed service.
 
-  views::View* content_container_;     // Owned by views hierarchy.
+  views::View* content_container_;       // Owned by views hierarchy.
+  views::ImageView* google_icon_;        // Owned by views hierarchy.
   SearchBoxImageButton* back_button_;    // Owned by views hierarchy.
   SearchBoxImageButton* speech_button_;  // Owned by views hierarchy.
   views::Textfield* search_box_;  // Owned by views hierarchy.
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
index 16413e1..3138f47 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
@@ -25,6 +25,12 @@
         font-weight: 500;
       }
 
+      paper-button[disabled] {
+        background: transparent;
+        color: inherit;
+        opacity: 0.7;
+      }
+
       #number-selected {
         flex: 1;
       }
@@ -51,7 +57,8 @@
       <paper-button on-tap="onClearSelectionTap_">
         [[cancelLabel]]
       </paper-button>
-      <paper-button id="delete" on-tap="onDeleteTap_">
+      <paper-button id="delete" on-tap="onDeleteTap_"
+          disabled="[[deleteDisabled]]">
         [[deleteLabel]]
       </paper-button>
     </div>
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.js b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.js
index 832b81d8..205b1a8 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.js
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.js
@@ -17,6 +17,8 @@
     cancelLabel: String,
 
     selectionLabel: String,
+
+    deleteDisabled: Boolean,
   },
 
   /** @return {PaperButtonElement} */
diff --git a/ui/webui/resources/js/i18n_behavior.js b/ui/webui/resources/js/i18n_behavior.js
index 931f8c8..94704db 100644
--- a/ui/webui/resources/js/i18n_behavior.js
+++ b/ui/webui/resources/js/i18n_behavior.js
@@ -39,6 +39,7 @@
   /**
    * Returns a translated string where $1 to $9 are replaced by the given
    * values. Also sanitizes the output to filter out dangerous HTML/JS.
+   * Use with Polymer bindings that are *not* inner-h-t-m-l.
    * @param {string} id The ID of the string to translate.
    * @param {...string} var_args Values to replace the placeholders $1 to $9
    *     in the string.
@@ -46,22 +47,20 @@
    */
   i18n: function(id, var_args) {
     var rawString = this.i18nRaw_.apply(this, arguments);
-    var htmlStr =
-        parseHtmlSubset('<b>' + rawString + '</b>').firstChild.innerHTML;
-    // TODO(dschuyler): use textContent rather than innerHTML; remove replace().
-    return htmlStr.replace(/&nbsp;/g, '\u00a0');
+    return parseHtmlSubset('<b>' + rawString + '</b>').firstChild.textContent;
   },
 
   /**
    * Similar to 'i18n', returns a translated, sanitized, substituted string.
    * It receives the string ID and a dictionary containing the substitutions
-   * as well as optional additional allowed tags and attributes.
+   * as well as optional additional allowed tags and attributes. Use with
+   * Polymer bindings that are inner-h-t-m-l, for example.
    * @param {string} id The ID of the string to translate.
-   * @param {{substitutions: (Array<string>|undefined),
-   *          attrs: (Object<function(Node, string):boolean>|undefined),
-   *          tags: (Array<string>|undefined)}} opts
+   * @param {I18nAdvancedOpts=} opts
+   * @return {string}
    */
   i18nAdvanced: function(id, opts) {
+    opts = opts || {};
     var args = [id].concat(opts.substitutions || []);
     var rawString = this.i18nRaw_.apply(this, args);
     return parseHtmlSubset('<b>' + rawString + '</b>', opts.tags, opts.attrs)
@@ -91,8 +90,8 @@
   },
 
   /**
-   * Call this when UI strings may have changed. This will send an update to any
-   * data bindings to i18nDynamic(locale, ...).
+   * Call this when UI strings may have changed. This will send an update to
+   * any data bindings to i18nDynamic(locale, ...).
    */
   i18nUpdateLocale: function() {
     this.locale = loadTimeData.getString('language');
@@ -100,14 +99,22 @@
 };
 
 /**
+ * @typedef {{
+ *   substitutions: (Array<string>|undefined),
+ *   attrs: (Object<function(Node, string):boolean>|undefined),
+ *   tags: (Array<string>|undefined),
+ * }}
+ */
+var I18nAdvancedOpts;
+
+/**
  * TODO(stevenjb): Replace with an interface. b/24294625
  * @typedef {{
- *   i18n: function(string, ...string): string}},
- *   i18nAdvanced: function({
- *     substitutions: (Array<string>|undefined),
- *     attrs: (Object<function(Node, string):boolean>|undefined),
- *     tags: (Array<string>|undefined)}, opts),
- *   i18nExists: function(string)
+ *   i18n: function(string, ...string): string,
+ *   i18nAdvanced: function(string, I18nAdvancedOpts=): string,
+ *   i18nDynamic: function(string, string, ...string): string,
+ *   i18nExists: function(string),
+ *   i18nUpdateLocale: function()
  * }}
  */
 I18nBehavior.Proto;