diff --git a/.gitignore b/.gitignore
index f9e53d7..b3dbcaa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,7 @@
 Thumbs.db
 v8.log
 vs-chromium-project.txt
+/.clangd-index/
 # Settings directories for eclipse
 /.externalToolBuilders/
 /.settings/
diff --git a/.gn b/.gn
index b358758..635ee41 100644
--- a/.gn
+++ b/.gn
@@ -440,7 +440,6 @@
   "//third_party/isimpledom/*",
   "//third_party/javax_inject/*",
   "//third_party/jinja2/*",
-  "//third_party/jmake/*",
   "//third_party/jsoncpp/*",
   "//third_party/jsr-305/*",
   "//third_party/jstemplate/*",
diff --git a/.vpython b/.vpython
index 712ddf1..0677a171 100644
--- a/.vpython
+++ b/.vpython
@@ -232,7 +232,7 @@
 
 wheel <
   name: "infra/python/wheels/pytest-py2_py3"
-  version: "version:3.5.0"
+  version: "version:3.6.2"
 >
 
 wheel <
@@ -257,7 +257,7 @@
 
 wheel <
   name: "infra/python/wheels/pluggy-py2_py3"
-  version: "version:0.6.0"
+  version: "version:0.7.1"
 >
 
 wheel <
@@ -278,3 +278,13 @@
   name: "infra/python/wheels/colorama-py2_py3"
   version: "version:0.4.1"
 >
+
+wheel: <
+  name: "infra/python/wheels/atomicwrites-py2_py3"
+  version: "version:1.3.0"
+>
+
+wheel: <
+  name: "infra/python/wheels/pathlib2-py2_py3"
+  version: "version:2.3.3"
+>
diff --git a/BUILD.gn b/BUILD.gn
index 99b74571..b708c27a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -724,6 +724,7 @@
       (is_win && (use_drfuzz || use_libfuzzer)) || (use_libfuzzer && is_mac)) {
     deps += [
       "//testing/libfuzzer/fuzzers",
+      "//third_party/grpc:fuzzers",
       "//third_party/icu/fuzzers",
       "//third_party/qcms:fuzzers",
       "//third_party/zlib/contrib/tests/fuzzers",
diff --git a/DEPS b/DEPS
index 9aa2909c..4beb18da 100644
--- a/DEPS
+++ b/DEPS
@@ -126,7 +126,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c74512c7ff9ed10113cfba8804d21b10427f820b',
+  'skia_revision': 'bf0bcfecda685fc313ce136fec4379841684c8bc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -138,15 +138,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '3d544fffe7ff9284b21e3a15f638297c827bbc21',
+  'angle_revision': 'ffd399789203e28ea9a30e7f31c002fb5d38aa7b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '8f79d388784c4870b87c3e87753d5e054af07011',
+  'swiftshader_revision': 'e4ef5f77dab4f3ff5fbb9387bf83c216b92b4d55',
   # 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': 'e19ff219c47881212dff8be4a168dce2f60fdb13',
+  'pdfium_revision': 'ce88174adfcfe75bf8760851943a612769f239c8',
   # 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.
@@ -189,7 +189,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'e8ab7db4d4da917c3abb111de4143e867bebfe21',
+  'catapult_revision': 'f627a76e8dac96282b0a9f76eeda8c7db70cc030',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -237,7 +237,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '5994ae2a045015004cce24802dc47c33736486ea',
+  'spv_tools_revision': '76730a46a1d81bdf46f25f33684e04b429ff1718',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -253,7 +253,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'f20f5b94939e90e33392dd5b1de01a6cc93eb510',
+  'dawn_revision': '938811eef933dfacb16a464968f6dda038750705',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -334,7 +334,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/autofill/captured_sites',
-        'version': 'iTTirmwddgzWZrhbBLyYSUn-PFZNRIm5NjY9eYBgk0cC',
+        'version': 'jYvTM_KjFi4v_BTqCfkAxM1XJ4PkMDSi31FgEDf0z9sC',
       }
     ],
     'condition': 'checkout_chromium_autofill_test_dependencies',
@@ -737,7 +737,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ad6d56748d98a9c9d927564d330e92eb79bc15cf',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'efb8d2fcbd4f0e8d3f1249d5ef41e52eb5d445c4',
       'condition': 'checkout_linux',
   },
 
@@ -762,7 +762,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '177cd13347a9137c42e882b9eac2eab404c587f8',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b53fdae2f2dded2d6c43e4e68a43a5a73217aabf',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1101,7 +1101,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '74142cf4ffe76f8ed19ce734efc3a8f0a1c4e384',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '8ecd6eea833595872a4a63cd34274fd2e5266f7b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1316,7 +1316,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e1834d8c6d381affad98f96c06b3470bd5b9039b',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5d614691a03a3aa45e448bd2b2dc9d2ab3d15c29',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_gl_functor.h b/android_webview/browser/aw_gl_functor.h
index b54d5134..17d2294 100644
--- a/android_webview/browser/aw_gl_functor.h
+++ b/android_webview/browser/aw_gl_functor.h
@@ -15,7 +15,7 @@
 
 class AwGLFunctor {
  public:
-  AwGLFunctor(const JavaObjectWeakGlobalRef& java_ref);
+  explicit AwGLFunctor(const JavaObjectWeakGlobalRef& java_ref);
   ~AwGLFunctor();
 
   void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
diff --git a/android_webview/docs/OWNERS b/android_webview/docs/OWNERS
new file mode 100644
index 0000000..9e5c10f
--- /dev/null
+++ b/android_webview/docs/OWNERS
@@ -0,0 +1,5 @@
+# Feel free to send CLs to any Android WebView team member and ask for an OWNERS
+# stamp, either from an android_webview/ top-level owner or someone listed in
+# this file.
+
+ntfschr@chromium.org
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java b/android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java
index 63bda3cd..5fbf7e57 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java
@@ -14,7 +14,7 @@
 
 /**
  * Simple Java abstraction and wrapper for the native DrawGLFunctor flow.
- * An instance of this class can be constructed, bound to a single view context (i.e. AwContennts)
+ * An instance of this class can be constructed, bound to a single view context (i.e. AwContents)
  * and then drawn and detached from the view tree any number of times (using requestDrawGL and
  * detach respectively).
  */
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/TelemetryActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/TelemetryActivity.java
index a29e525..7128202 100644
--- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/TelemetryActivity.java
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/TelemetryActivity.java
@@ -16,26 +16,44 @@
  * This activity is designed for Telemetry testing of WebView.
  */
 public class TelemetryActivity extends Activity {
+    static final String START_UP_TRACE_TAG_NAME = "WebViewStartUpTraceTag";
     static final String DEFAULT_START_UP_TRACE_TAG = "WebViewStartupInterval";
+
+    static final String LOAD_URL_TRACE_TAG_NAME = "WebViewLoadUrlTraceTag";
     static final String DEFAULT_LOAD_URL_TRACE_TAG = "WebViewBlankUrlLoadInterval";
+
+    static final String START_UP_AND_LOAD_URL_TRACE_TAG_NAME = "WebViewStartUpAndLoadUrlTraceTag";
     static final String DEFAULT_START_UP_AND_LOAD_URL_TRACE_TAG =
             "WebViewStartupAndLoadBlankUrlInterval";
 
+    static final String DUMMY_TRACE_TAG_NAME = "WebViewDummyTraceTag";
+    static final String DEFAULT_DUMMY_TRACE_TAG = "WebViewDummyInterval";
+
+    private Intent mIntent;
+
+    private String getTraceTag(String tagName, String tagDefault) {
+        String tag = mIntent.getStringExtra(tagName);
+        return tag == null ? tagDefault : tag;
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mIntent = getIntent();
+
         getWindow().setTitle(
                 getResources().getString(R.string.title_activity_telemetry));
 
-        Intent intent = getIntent();
-        final String startUpTraceTag = intent.getStringExtra("WebViewStartUpTraceTag");
-        final String loadUrlTraceTag = intent.getStringExtra("WebViewLoadUrlTraceTag");
-        final String startUpAndLoadUrlTraceTag =
-                intent.getStringExtra("WebViewStartUpAndLoadUrlTraceTag");
+        final String startUpTraceTag =
+                getTraceTag(START_UP_TRACE_TAG_NAME, DEFAULT_START_UP_TRACE_TAG);
+        final String loadUrlTraceTag =
+                getTraceTag(LOAD_URL_TRACE_TAG_NAME, DEFAULT_LOAD_URL_TRACE_TAG);
+        final String startUpAndLoadUrlTraceTag = getTraceTag(
+                START_UP_AND_LOAD_URL_TRACE_TAG_NAME, DEFAULT_START_UP_AND_LOAD_URL_TRACE_TAG);
+        final String dummyTraceTag = getTraceTag(DUMMY_TRACE_TAG_NAME, DEFAULT_DUMMY_TRACE_TAG);
 
-        Trace.beginSection(startUpTraceTag == null ? DEFAULT_START_UP_AND_LOAD_URL_TRACE_TAG
-                                                   : startUpAndLoadUrlTraceTag);
-        Trace.beginSection(startUpTraceTag == null ? DEFAULT_START_UP_TRACE_TAG : startUpTraceTag);
+        Trace.beginSection(startUpAndLoadUrlTraceTag);
+        Trace.beginSection(startUpTraceTag);
         WebView webView = new WebView(this);
         setContentView(webView);
         Trace.endSection();
@@ -47,7 +65,7 @@
         settings.setLoadWithOverviewMode(true);
         settings.setDomStorageEnabled(true);
         settings.setMediaPlaybackRequiresUserGesture(false);
-        String userAgentString = intent.getStringExtra("userAgent");
+        String userAgentString = mIntent.getStringExtra("userAgent");
         if (userAgentString != null) {
             settings.setUserAgentString(userAgentString);
         }
@@ -62,12 +80,31 @@
             @Override
             public void onPageFinished(WebView view, String url) {
                 super.onPageFinished(view, url);
+                // dummyTraceTag was ended by code in Android intended to end
+                // activityStart before onPageFinished was called, so the time
+                // reported as the activityStart will be longer than actual.
+                // The actual duration of activityStart will be from the
+                // beginning of the activityStart to the end of the dummyTraceTag
+
+                // Ends loadUrlTraceTag
                 Trace.endSection();
+
+                // Ends startUpAndLoadUrlTraceTag
+                Trace.endSection();
+
+                // Ends activityStart
                 Trace.endSection();
             }
         });
 
-        Trace.beginSection(loadUrlTraceTag == null ? DEFAULT_LOAD_URL_TRACE_TAG : loadUrlTraceTag);
+        Trace.beginSection(loadUrlTraceTag);
+
         webView.loadUrl("about:blank");
+
+        // TODO(aluo): Use async tracing to avoid having to do this
+        // dummyTraceTag is needed here to prevent code in Android intended to
+        // end activityStart from ending loadUrlTraceTag prematurely,
+        // see crbug/919221
+        Trace.beginSection(dummyTraceTag);
     }
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 1ae7c99..5b54f596 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -70,8 +70,10 @@
     "wm/mru_window_tracker.h",
     "wm/overview/overview_controller.h",
     "wm/splitview/split_view_controller.h",
+    "wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h",
     "wm/tablet_mode/tablet_mode_controller.h",
     "wm/tablet_mode/tablet_mode_observer.h",
+    "wm/tablet_mode/tablet_mode_window_drag_controller.h",
     "wm/tablet_mode/tablet_mode_window_drag_delegate.h",
     "wm/window_finder.h",
     "wm/window_positioner.h",
@@ -1148,14 +1150,11 @@
     "wm/tablet_mode/tablet_mode_backdrop_delegate_impl.cc",
     "wm/tablet_mode/tablet_mode_backdrop_delegate_impl.h",
     "wm/tablet_mode/tablet_mode_browser_window_drag_delegate.cc",
-    "wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h",
     "wm/tablet_mode/tablet_mode_controller.cc",
     "wm/tablet_mode/tablet_mode_event_handler.cc",
     "wm/tablet_mode/tablet_mode_event_handler.h",
     "wm/tablet_mode/tablet_mode_window_drag_controller.cc",
-    "wm/tablet_mode/tablet_mode_window_drag_controller.h",
     "wm/tablet_mode/tablet_mode_window_drag_delegate.cc",
-    "wm/tablet_mode/tablet_mode_window_drag_delegate.h",
     "wm/tablet_mode/tablet_mode_window_drag_metrics.cc",
     "wm/tablet_mode/tablet_mode_window_drag_metrics.h",
     "wm/tablet_mode/tablet_mode_window_manager.cc",
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 7464c0db..d801aa7 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -68,6 +68,27 @@
     Shell::Get()->assistant_controller()->ui_controller()->CloseUi(exit_point);
 }
 
+// Minimize all windows that aren't the app list in reverse order to preserve
+// the mru ordering.
+// Returns false if no window is minimized.
+bool MinimizeAllWindows() {
+  bool handled = false;
+  aura::Window* app_list_container =
+      Shell::Get()->GetPrimaryRootWindow()->GetChildById(
+          kShellWindowId_AppListTabletModeContainer);
+  aura::Window::Windows windows =
+      Shell::Get()->mru_window_tracker()->BuildWindowForCycleList();
+  std::reverse(windows.begin(), windows.end());
+  for (auto* window : windows) {
+    if (!app_list_container->Contains(window) &&
+        !wm::GetWindowState(window)->IsMinimized()) {
+      wm::GetWindowState(window)->Minimize();
+      handled = true;
+    }
+  }
+  return handled;
+}
+
 }  // namespace
 
 AppListControllerImpl::AppListControllerImpl()
@@ -671,7 +692,9 @@
       if (!assistant::util::IsEmbeddedUiEntryPoint(entry_point.value()))
         break;
 
-      if (!IsVisible()) {
+      if (IsTabletMode()) {
+        MinimizeAllWindows();
+      } else if (!IsVisible()) {
         Show(GetDisplayIdToShowAppListOn(), app_list::kAssistantEntryPoint,
              base::TimeTicks());
       }
@@ -688,10 +711,16 @@
       if (!IsShowingEmbeddedAssistantUI())
         break;
 
-      if (exit_point == AssistantExitPoint::kBackInLauncher)
+      if (exit_point == AssistantExitPoint::kBackInLauncher) {
         presenter_.ShowEmbeddedAssistantUI(false);
-      else
+      } else if (!IsTabletMode()) {
         DismissAppList();
+      }
+      if (IsTabletMode()) {
+        presenter_.GetView()->app_list_main_view()->ResetForShow();
+        presenter_.GetView()->SetState(
+            app_list::AppListViewState::FULLSCREEN_ALL_APPS);
+      }
       break;
   }
 }
@@ -725,23 +754,8 @@
     }
   }
 
-  if (!handled) {
-    // Minimize all windows that aren't the app list in reverse order to
-    // preserve the mru ordering.
-    aura::Window* app_list_container =
-        Shell::Get()->GetPrimaryRootWindow()->GetChildById(
-            kShellWindowId_AppListTabletModeContainer);
-    aura::Window::Windows windows =
-        Shell::Get()->mru_window_tracker()->BuildWindowForCycleList();
-    std::reverse(windows.begin(), windows.end());
-    for (auto* window : windows) {
-      if (!app_list_container->Contains(window) &&
-          !wm::GetWindowState(window)->IsMinimized()) {
-        wm::GetWindowState(window)->Minimize();
-        handled = true;
-      }
-    }
-  }
+  if (!handled)
+    handled = MinimizeAllWindows();
 
   // Perform the "back" action for the app list.
   if (!handled)
@@ -784,7 +798,13 @@
 // Methods of |client_|:
 
 void AppListControllerImpl::StartAssistant() {
-  if (!IsTabletMode() && !app_list_features::IsEmbeddedAssistantUIEnabled())
+  if (app_list_features::IsEmbeddedAssistantUIEnabled()) {
+    ash::Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
+        ash::AssistantEntryPoint::kLauncherSearchBoxMic);
+    return;
+  }
+
+  if (!IsTabletMode())
     DismissAppList();
 
   ash::Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
@@ -1005,6 +1025,9 @@
 void AppListControllerImpl::NotifyHomeLauncherAnimationComplete(
     bool shown,
     int64_t display_id) {
+  CloseAssistantUi(shown ? AssistantExitPoint::kLauncherOpen
+                         : AssistantExitPoint::kLauncherClose);
+
   for (auto& observer : observers_)
     observer.OnHomeLauncherAnimationComplete(shown, display_id);
 }
diff --git a/ash/app_list/test/run_all_unittests.cc b/ash/app_list/test/run_all_unittests.cc
index f38da10..dd509a39 100644
--- a/ash/app_list/test/run_all_unittests.cc
+++ b/ash/app_list/test/run_all_unittests.cc
@@ -15,6 +15,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/env.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_paths.h"
 #include "ui/gl/test/gl_surface_test_support.h"
 
@@ -35,7 +36,6 @@
 
     base::TestSuite::Initialize();
     gl::GLSurfaceTestSupport::InitializeOneOff();
-    env_ = aura::Env::CreateInstance();
     ui::RegisterPathProvider();
 
     base::FilePath ui_test_pak_path;
@@ -44,6 +44,9 @@
 
     base::DiscardableMemoryAllocator::SetInstance(
         &discardable_memory_allocator_);
+    env_ = aura::Env::CreateInstance(features::IsSingleProcessMash()
+                                         ? aura::Env::Mode::MUS
+                                         : aura::Env::Mode::LOCAL);
   }
 
   void Shutdown() override {
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index c0372451..8352b3f 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -708,8 +708,10 @@
   views::ImageButton* assistant = assistant_button();
   assistant->SetImage(
       views::ImageButton::STATE_NORMAL,
-      gfx::CreateVectorIcon(ash::kAssistantIcon, kAssistantIconSize,
-                            search_box_color()));
+      gfx::CreateVectorIcon(app_list_features::IsEmbeddedAssistantUIEnabled()
+                                ? ash::kAssistantMicIcon
+                                : ash::kAssistantIcon,
+                            kAssistantIconSize, search_box_color()));
   assistant->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_APP_LIST_START_ASSISTANT));
 }
diff --git a/ash/app_list/views/search_result_answer_card_view.cc b/ash/app_list/views/search_result_answer_card_view.cc
index 8f619f0..709cd4a 100644
--- a/ash/app_list/views/search_result_answer_card_view.cc
+++ b/ash/app_list/views/search_result_answer_card_view.cc
@@ -28,6 +28,7 @@
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/window.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/canvas.h"
 #include "ui/views/background.h"
 #include "ui/views/layout/fill_layout.h"
@@ -121,6 +122,8 @@
     params->background_color = SK_ColorTRANSPARENT;
     contents_ = std::make_unique<content::NavigableContents>(
         contents_factory_.get(), std::move(params));
+    if (features::IsUsingWindowService())
+      contents_->ForceUseWindowService();
     contents_->AddObserver(this);
   }
 
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index 1fd07a2..55834e8 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -618,10 +618,13 @@
   DCHECK_EQ(AssistantVisibility::kVisible,
             assistant_controller_->ui_controller()->model()->visibility());
 
-  const bool launch_with_mic_open =
+  bool launch_with_mic_open =
       Shell::Get()->voice_interaction_controller()->launch_with_mic_open();
 
   switch (entry_point) {
+    case AssistantEntryPoint::kLauncherSearchBoxMic:
+      launch_with_mic_open = true;
+      FALLTHROUGH;
     case AssistantEntryPoint::kHotkey:
     case AssistantEntryPoint::kLauncherSearchBox:
     case AssistantEntryPoint::kLongPressLauncher: {
diff --git a/ash/assistant/assistant_view_delegate_impl.cc b/ash/assistant/assistant_view_delegate_impl.cc
index 518bef1c4..3d9fa1f 100644
--- a/ash/assistant/assistant_view_delegate_impl.cc
+++ b/ash/assistant/assistant_view_delegate_impl.cc
@@ -125,6 +125,13 @@
   assistant_controller_->DownloadImage(url, std::move(callback));
 }
 
+mojom::ConsentStatus AssistantViewDelegateImpl::GetConsentStatus() const {
+  return Shell::Get()
+      ->voice_interaction_controller()
+      ->consent_status()
+      .value_or(mojom::ConsentStatus::kUnknown);
+}
+
 ::wm::CursorManager* AssistantViewDelegateImpl::GetCursorManager() {
   return Shell::Get()->cursor_manager();
 }
@@ -168,12 +175,4 @@
     observer.OnDeepLinkReceived(type, params);
 }
 
-bool AssistantViewDelegateImpl::VoiceInteractionControllerSetupCompleted()
-    const {
-  return Shell::Get()
-      ->voice_interaction_controller()
-      ->setup_completed()
-      .value_or(false);
-}
-
 }  // namespace ash
diff --git a/ash/assistant/assistant_view_delegate_impl.h b/ash/assistant/assistant_view_delegate_impl.h
index 253943a..0ca5c071 100644
--- a/ash/assistant/assistant_view_delegate_impl.h
+++ b/ash/assistant/assistant_view_delegate_impl.h
@@ -54,6 +54,7 @@
   void DownloadImage(
       const GURL& url,
       mojom::AssistantImageDownloader::DownloadCallback callback) override;
+  mojom::ConsentStatus GetConsentStatus() const override;
   ::wm::CursorManager* GetCursorManager() override;
   void GetNavigableContentsFactoryForView(
       content::mojom::NavigableContentsFactoryRequest request) override;
@@ -63,7 +64,6 @@
                                    int notification_button_index) override;
   void OnSuggestionChipPressed(const AssistantSuggestion* suggestion) override;
   void OpenUrlFromView(const GURL& url) override;
-  bool VoiceInteractionControllerSetupCompleted() const override;
 
  private:
   AssistantController* const assistant_controller_;
diff --git a/ash/assistant/model/assistant_ui_element.cc b/ash/assistant/model/assistant_ui_element.cc
index b9762b59..1d7554b 100644
--- a/ash/assistant/model/assistant_ui_element.cc
+++ b/ash/assistant/model/assistant_ui_element.cc
@@ -6,6 +6,7 @@
 
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "base/base64.h"
+#include "ui/base/ui_base_features.h"
 
 namespace ash {
 
@@ -65,6 +66,8 @@
 
   contents_ = std::make_unique<content::NavigableContents>(
       contents_factory_, std::move(contents_params));
+  if (features::IsUsingWindowService())
+    contents_->ForceUseWindowService();
 
   // Observe |contents_| so that we are notified when loading is complete.
   contents_->AddObserver(this);
diff --git a/ash/assistant/model/assistant_ui_model.h b/ash/assistant/model/assistant_ui_model.h
index ee5bf68d..2e4b61c 100644
--- a/ash/assistant/model/assistant_ui_model.h
+++ b/ash/assistant/model/assistant_ui_model.h
@@ -27,8 +27,9 @@
   kSetup = 6,
   kStylus = 7,
   kLauncherSearchResult = 8,
+  kLauncherSearchBoxMic = 9,
   // Special enumerator value used by histogram macros.
-  kMaxValue = kLauncherSearchResult
+  kMaxValue = kLauncherSearchBoxMic
 };
 
 // Enumeration of Assistant exit points. These values are persisted to logs.
diff --git a/ash/assistant/ui/assistant_view_delegate.h b/ash/assistant/ui/assistant_view_delegate.h
index e6ebaae..6a2a72c9 100644
--- a/ash/assistant/ui/assistant_view_delegate.h
+++ b/ash/assistant/ui/assistant_view_delegate.h
@@ -123,6 +123,9 @@
       const GURL& url,
       mojom::AssistantImageDownloader::DownloadCallback callback) = 0;
 
+  // Returns the status of the user's consent.
+  virtual mojom::ConsentStatus GetConsentStatus() const = 0;
+
   // Returns the cursor_manager.
   virtual ::wm::CursorManager* GetCursorManager() = 0;
 
@@ -147,9 +150,6 @@
   // Opens the specified |url| in a new browser tab. Special handling is applied
   // to deep links which may cause deviation from this behavior.
   virtual void OpenUrlFromView(const GURL& url) = 0;
-
-  // Returns true if voice interaction controller setup completed.
-  virtual bool VoiceInteractionControllerSetupCompleted() const = 0;
 };
 
 }  // namespace ash
diff --git a/ash/assistant/ui/assistant_web_view.cc b/ash/assistant/ui/assistant_web_view.cc
index 143883a..b3a7316 100644
--- a/ash/assistant/ui/assistant_web_view.cc
+++ b/ash/assistant/ui/assistant_web_view.cc
@@ -15,6 +15,7 @@
 #include "base/callback.h"
 #include "services/content/public/cpp/navigable_contents_view.h"
 #include "ui/aura/window.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -186,6 +187,8 @@
 
   contents_ = std::make_unique<content::NavigableContents>(
       contents_factory_.get(), std::move(contents_params));
+  if (features::IsUsingWindowService())
+    contents_->ForceUseWindowService();
 
   // We observe |contents_| so that we can handle events from the underlying
   // web contents.
diff --git a/ash/assistant/ui/main_stage/assistant_footer_view.cc b/ash/assistant/ui/main_stage/assistant_footer_view.cc
index cd9ed13..be265a15 100644
--- a/ash/assistant/ui/main_stage/assistant_footer_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_footer_view.cc
@@ -68,47 +68,52 @@
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   // Initial view state is based on user consent state.
-  const bool setup_completed =
-      delegate_->VoiceInteractionControllerSetupCompleted();
+  const bool consent_given = delegate_->GetConsentStatus() ==
+                             mojom::ConsentStatus::kActivityControlAccepted;
+
   // Suggestion container.
   suggestion_container_ = new SuggestionContainerView(delegate_);
-  suggestion_container_->set_can_process_events_within_subtree(setup_completed);
+  suggestion_container_->set_can_process_events_within_subtree(consent_given);
 
   // Suggestion container will be animated on its own layer.
   suggestion_container_->SetPaintToLayer();
   suggestion_container_->layer()->SetFillsBoundsOpaquely(false);
-  suggestion_container_->layer()->SetOpacity(setup_completed ? 1.f : 0.f);
-  suggestion_container_->SetVisible(setup_completed);
+  suggestion_container_->layer()->SetOpacity(consent_given ? 1.f : 0.f);
+  suggestion_container_->SetVisible(consent_given);
 
   AddChildView(suggestion_container_);
 
   // Opt in view.
   opt_in_view_ = new AssistantOptInView();
-  opt_in_view_->set_can_process_events_within_subtree(!setup_completed);
+  opt_in_view_->set_can_process_events_within_subtree(!consent_given);
   opt_in_view_->set_delegate(delegate_->GetOptInDelegate());
 
   // Opt in view will be animated on its own layer.
   opt_in_view_->SetPaintToLayer();
   opt_in_view_->layer()->SetFillsBoundsOpaquely(false);
-  opt_in_view_->layer()->SetOpacity(setup_completed ? 0.f : 1.f);
-  opt_in_view_->SetVisible(!setup_completed);
+  opt_in_view_->layer()->SetOpacity(consent_given ? 0.f : 1.f);
+  opt_in_view_->SetVisible(!consent_given);
 
   AddChildView(opt_in_view_);
 }
 
-void AssistantFooterView::OnVoiceInteractionSetupCompleted(bool completed) {
+void AssistantFooterView::OnVoiceInteractionConsentStatusUpdated(
+    mojom::ConsentStatus consent_status) {
   using assistant::util::CreateLayerAnimationSequence;
   using assistant::util::CreateOpacityElement;
   using assistant::util::StartLayerAnimationSequence;
 
+  const bool consent_given =
+      consent_status == mojom::ConsentStatus::kActivityControlAccepted;
+
   // When the consent state changes, we need to hide/show the appropriate views.
   views::View* hide_view =
-      completed ? static_cast<views::View*>(opt_in_view_)
-                : static_cast<views::View*>(suggestion_container_);
+      consent_given ? static_cast<views::View*>(opt_in_view_)
+                    : static_cast<views::View*>(suggestion_container_);
 
   views::View* show_view =
-      completed ? static_cast<views::View*>(suggestion_container_)
-                : static_cast<views::View*>(opt_in_view_);
+      consent_given ? static_cast<views::View*>(suggestion_container_)
+                    : static_cast<views::View*>(opt_in_view_);
 
   // Reset visibility to enable animation.
   hide_view->SetVisible(true);
@@ -146,14 +151,14 @@
 
 bool AssistantFooterView::OnAnimationEnded(
     const ui::CallbackLayerAnimationObserver& observer) {
-  const bool setup_completed =
-      delegate_->VoiceInteractionControllerSetupCompleted();
+  const bool consent_given = delegate_->GetConsentStatus() ==
+                             mojom::ConsentStatus::kActivityControlAccepted;
 
   // Only the view relevant to our consent state should process events.
-  suggestion_container_->set_can_process_events_within_subtree(setup_completed);
-  suggestion_container_->SetVisible(setup_completed);
-  opt_in_view_->set_can_process_events_within_subtree(!setup_completed);
-  opt_in_view_->SetVisible(!setup_completed);
+  suggestion_container_->set_can_process_events_within_subtree(consent_given);
+  suggestion_container_->SetVisible(consent_given);
+  opt_in_view_->set_can_process_events_within_subtree(!consent_given);
+  opt_in_view_->SetVisible(!consent_given);
 
   // Return false to prevent the observer from destroying itself.
   return false;
diff --git a/ash/assistant/ui/main_stage/assistant_footer_view.h b/ash/assistant/ui/main_stage/assistant_footer_view.h
index a7be0a45..29d174d 100644
--- a/ash/assistant/ui/main_stage/assistant_footer_view.h
+++ b/ash/assistant/ui/main_stage/assistant_footer_view.h
@@ -37,7 +37,8 @@
   int GetHeightForWidth(int width) const override;
 
   // mojom::VoiceInteractionObserver:
-  void OnVoiceInteractionSetupCompleted(bool completed) override;
+  void OnVoiceInteractionConsentStatusUpdated(
+      mojom::ConsentStatus consent_status) override;
 
  private:
   void InitLayout();
diff --git a/ash/assistant/util/assistant_util.cc b/ash/assistant/util/assistant_util.cc
index c151b646..280e954 100644
--- a/ash/assistant/util/assistant_util.cc
+++ b/ash/assistant/util/assistant_util.cc
@@ -25,6 +25,7 @@
          entry_point == AssistantEntryPoint::kHotkey ||
          entry_point == AssistantEntryPoint::kHotword ||
          entry_point == AssistantEntryPoint::kLauncherSearchBox ||
+         entry_point == AssistantEntryPoint::kLauncherSearchBoxMic ||
          entry_point == AssistantEntryPoint::kLauncherSearchResult ||
          entry_point == AssistantEntryPoint::kLongPressLauncher ||
          entry_point == AssistantEntryPoint::kUnspecified;
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index dd870ec2..eb2a2f9 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -778,7 +778,7 @@
 
 ui::EventDispatchDetails WindowTreeHostManager::DispatchKeyEventPostIME(
     ui::KeyEvent* event,
-    base::OnceCallback<void(bool)> ack_callback) {
+    DispatchKeyEventPostIMECallback callback) {
   aura::Window* root_window = nullptr;
   if (event->target()) {
     root_window = static_cast<aura::Window*>(event->target())->GetRootWindow();
@@ -791,8 +791,8 @@
     root_window = active_window ? active_window->GetRootWindow()
                                 : Shell::GetPrimaryRootWindow();
   }
-  return root_window->GetHost()->DispatchKeyEventPostIME(
-      event, std::move(ack_callback));
+  return root_window->GetHost()->DispatchKeyEventPostIME(event,
+                                                         std::move(callback));
 }
 
 AshWindowTreeHost* WindowTreeHostManager::AddWindowTreeHostForDisplay(
diff --git a/ash/display/window_tree_host_manager.h b/ash/display/window_tree_host_manager.h
index e140cad..0d282c1 100644
--- a/ash/display/window_tree_host_manager.h
+++ b/ash/display/window_tree_host_manager.h
@@ -176,7 +176,7 @@
   // ui::internal::InputMethodDelegate overrides:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* event,
-      base::OnceCallback<void(bool)> ack_callback) override;
+      DispatchKeyEventPostIMECallback callback) override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(WindowTreeHostManagerTest, BoundsUpdated);
diff --git a/ash/public/cpp/assistant/assistant_state_base.h b/ash/public/cpp/assistant/assistant_state_base.h
index b3033743..1c80fa0e 100644
--- a/ash/public/cpp/assistant/assistant_state_base.h
+++ b/ash/public/cpp/assistant/assistant_state_base.h
@@ -31,8 +31,8 @@
     return settings_enabled_;
   }
 
-  const base::Optional<bool>& setup_completed() const {
-    return setup_completed_;
+  const base::Optional<mojom::ConsentStatus>& consent_status() const {
+    return consent_status_;
   }
 
   const base::Optional<bool>& context_enabled() const {
@@ -59,8 +59,8 @@
   // Whether voice interaction is enabled in system settings.
   base::Optional<bool> settings_enabled_;
 
-  // Whether voice interaction setup flow has completed.
-  base::Optional<bool> setup_completed_;
+  // The status of the user's consent.
+  base::Optional<mojom::ConsentStatus> consent_status_;
 
   // Whether screen context is enabled.
   base::Optional<bool> context_enabled_;
diff --git a/ash/public/cpp/assistant/assistant_state_proxy.cc b/ash/public/cpp/assistant/assistant_state_proxy.cc
index d241cd83..a41e69e 100644
--- a/ash/public/cpp/assistant/assistant_state_proxy.cc
+++ b/ash/public/cpp/assistant/assistant_state_proxy.cc
@@ -35,8 +35,8 @@
     observer->OnVoiceInteractionContextEnabled(context_enabled_.value());
   if (hotword_enabled_.has_value())
     observer->OnVoiceInteractionHotwordEnabled(hotword_enabled_.value());
-  if (setup_completed_.has_value())
-    observer->OnVoiceInteractionSetupCompleted(setup_completed_.value());
+  if (consent_status_.has_value())
+    observer->OnVoiceInteractionConsentStatusUpdated(consent_status_.value());
   if (hotword_always_on_.has_value())
     observer->OnVoiceInteractionHotwordAlwaysOn(hotword_always_on_.value());
   if (allowed_state_.has_value())
@@ -77,10 +77,11 @@
     observer.OnVoiceInteractionHotwordEnabled(hotword_enabled_.value());
 }
 
-void AssistantStateProxy::OnVoiceInteractionSetupCompleted(bool completed) {
-  setup_completed_ = completed;
+void AssistantStateProxy::OnVoiceInteractionConsentStatusUpdated(
+    mojom::ConsentStatus consent_status) {
+  consent_status_ = consent_status;
   for (auto& observer : observers_)
-    observer.OnVoiceInteractionSetupCompleted(setup_completed_.value());
+    observer.OnVoiceInteractionConsentStatusUpdated(consent_status_.value());
 }
 
 void AssistantStateProxy::OnVoiceInteractionHotwordAlwaysOn(bool always_on) {
diff --git a/ash/public/cpp/assistant/assistant_state_proxy.h b/ash/public/cpp/assistant/assistant_state_proxy.h
index 1c18337..51b8ff1c 100644
--- a/ash/public/cpp/assistant/assistant_state_proxy.h
+++ b/ash/public/cpp/assistant/assistant_state_proxy.h
@@ -40,20 +40,21 @@
  private:
   // mojom::VoiceInteractionObserver:
   void OnVoiceInteractionStatusChanged(
-      ash::mojom::VoiceInteractionState state) override;
+      mojom::VoiceInteractionState state) override;
   void OnVoiceInteractionSettingsEnabled(bool enabled) override;
   void OnVoiceInteractionContextEnabled(bool enabled) override;
   void OnVoiceInteractionHotwordEnabled(bool enabled) override;
-  void OnVoiceInteractionSetupCompleted(bool completed) override;
+  void OnVoiceInteractionConsentStatusUpdated(
+      mojom::ConsentStatus consent_status) override;
   void OnVoiceInteractionHotwordAlwaysOn(bool always_on) override;
   void OnAssistantFeatureAllowedChanged(
-      ash::mojom::AssistantAllowedState state) override;
+      mojom::AssistantAllowedState state) override;
   void OnLocaleChanged(const std::string& locale) override;
 
   base::ObserverList<DefaultVoiceInteractionObserver> observers_;
 
-  ash::mojom::VoiceInteractionControllerPtr voice_interaction_controller_;
-  mojo::Binding<ash::mojom::VoiceInteractionObserver>
+  mojom::VoiceInteractionControllerPtr voice_interaction_controller_;
+  mojo::Binding<mojom::VoiceInteractionObserver>
       voice_interaction_observer_binding_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantStateProxy);
diff --git a/ash/public/cpp/assistant/default_voice_interaction_observer.h b/ash/public/cpp/assistant/default_voice_interaction_observer.h
index daa9827a..b70430f 100644
--- a/ash/public/cpp/assistant/default_voice_interaction_observer.h
+++ b/ash/public/cpp/assistant/default_voice_interaction_observer.h
@@ -28,7 +28,8 @@
   void OnVoiceInteractionSettingsEnabled(bool enabled) override {}
   void OnVoiceInteractionContextEnabled(bool enabled) override {}
   void OnVoiceInteractionHotwordEnabled(bool enabled) override {}
-  void OnVoiceInteractionSetupCompleted(bool completed) override {}
+  void OnVoiceInteractionConsentStatusUpdated(
+      ash::mojom::ConsentStatus consent_status) override {}
   void OnVoiceInteractionHotwordAlwaysOn(bool always_on) override {}
   void OnAssistantFeatureAllowedChanged(
       ash::mojom::AssistantAllowedState state) override {}
diff --git a/ash/public/cpp/vector_icons/BUILD.gn b/ash/public/cpp/vector_icons/BUILD.gn
index addac85..350c537 100644
--- a/ash/public/cpp/vector_icons/BUILD.gn
+++ b/ash/public/cpp/vector_icons/BUILD.gn
@@ -10,6 +10,7 @@
 
   icons = [
     "assistant.icon",
+    "assistant_mic.icon",
     "auto_launch_managed_guest_session.icon",
     "notification_assistant.icon",
     "notification_captive_portal.icon",
diff --git a/ash/public/cpp/vector_icons/assistant_mic.icon b/ash/public/cpp/vector_icons/assistant_mic.icon
new file mode 100644
index 0000000..54f522a4
--- /dev/null
+++ b/ash/public/cpp/vector_icons/assistant_mic.icon
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 24,
+PATH_COLOR_ARGB, 0xFF, 0x42, 0x85, 0xF4,
+MOVE_TO, 12, 15,
+R_ARC_TO, 3, 3, 0, 0, 0, 3, -3,
+V_LINE_TO, 5,
+R_ARC_TO, 3, 3, 0, 0, 0, -6, 0,
+R_V_LINE_TO, 7,
+R_ARC_TO, 3, 3, 0, 0, 0, 3, 3,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0x34, 0xA8, 0x53,
+MOVE_TO, 11, 18.92f,
+R_H_LINE_TO, 2,
+V_LINE_TO, 22,
+R_H_LINE_TO, -2,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0xFA, 0xBB, 0x05,
+MOVE_TO, 7, 12,
+H_LINE_TO, 5,
+R_ARC_TO, 7, 7, 0, 0, 0, 2, 4.95f,
+R_LINE_TO, 1.41f, -1.41f,
+ARC_TO, 5, 5, 0, 0, 1, 7, 12,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0xE9, 0x42, 0x35,
+MOVE_TO, 12, 17,
+R_ARC_TO, 5, 5, 0, 0, 1, -3.54f, -1.47f,
+R_LINE_TO, -1.41f, 1.42f,
+ARC_TO, 7, 7, 0, 0, 0, 19, 12,
+R_H_LINE_TO, -2,
+R_ARC_TO, 5, 5, 0, 0, 1, -5, 5,
+CLOSE
diff --git a/ash/public/interfaces/voice_interaction_controller.mojom b/ash/public/interfaces/voice_interaction_controller.mojom
index 04589e3..79727c4 100644
--- a/ash/public/interfaces/voice_interaction_controller.mojom
+++ b/ash/public/interfaces/voice_interaction_controller.mojom
@@ -65,8 +65,8 @@
   // Called when hotword is set to always on/only with power source.
   OnVoiceInteractionHotwordAlwaysOn(bool always_on);
 
-  // Called when voice interaction setup flow completed.
-  OnVoiceInteractionSetupCompleted(bool completed);
+  // Called when consent status is updated.
+  OnVoiceInteractionConsentStatusUpdated(ConsentStatus consent_status);
 
   // Called when assistant feature allowed state has changed.
   OnAssistantFeatureAllowedChanged(AssistantAllowedState state);
@@ -97,8 +97,8 @@
   // Called when the hotword is set to always on/only with power source.
   NotifyHotwordAlwaysOn(bool enabled);
 
-  // Called when the voice interaction setup complete status is changed.
-  NotifySetupCompleted(bool completed);
+  // Called when the consent status is obtained from the server.
+  NotifyConsentStatus(ConsentStatus consent_status);
 
   // Notify if voice interaction feature is allowed or not. e.g. not allowed
   // if disabled by policy.
@@ -116,3 +116,20 @@
   // Add an observer.
   AddObserver(VoiceInteractionObserver observer);
 };
+
+// The status of the user's consent. The enum values cannot be changed because
+// they are persisted on disk.
+enum ConsentStatus {
+  // The status is unknown.
+  kUnknown = 0,
+
+  // The user accepted activity control access.
+  kActivityControlAccepted = 1,
+
+  // The user is not authorized to give consent.
+  kUnauthorized = 2,
+
+  // The user's consent information is not found. This is typically the case
+  // when consent from the user has never been requested.
+  kNotFound = 3,
+};
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index 8f6c17e..bdda4de 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -60,6 +60,9 @@
 
 }  // namespace
 
+// static
+const char AppListButton::kViewClassName[] = "ash/AppListButton";
+
 AppListButton::AppListButton(ShelfView* shelf_view, Shelf* shelf)
     : ShelfControlButton(shelf_view), shelf_(shelf) {
   DCHECK(shelf_);
@@ -178,6 +181,10 @@
   }
 }
 
+const char* AppListButton::GetClassName() const {
+  return kViewClassName;
+}
+
 std::unique_ptr<views::InkDropRipple> AppListButton::CreateInkDropRipple()
     const {
   const int app_list_button_radius = ShelfConstants::control_border_radius();
@@ -295,7 +302,8 @@
   SchedulePaint();
 }
 
-void AppListButton::OnVoiceInteractionSetupCompleted(bool completed) {
+void AppListButton::OnVoiceInteractionConsentStatusUpdated(
+    mojom::ConsentStatus consent_status) {
   SchedulePaint();
 }
 
@@ -321,11 +329,14 @@
   VoiceInteractionController* controller =
       Shell::Get()->voice_interaction_controller();
   bool settings_enabled = controller->settings_enabled().value_or(false);
-  bool setup_completed = controller->setup_completed().value_or(false);
+
+  const bool consent_given = controller->consent_status() ==
+                             mojom::ConsentStatus::kActivityControlAccepted;
+
   bool is_feature_allowed =
       controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED;
   if (assistant_overlay_ && is_feature_allowed &&
-      (settings_enabled || !setup_completed)) {
+      (settings_enabled || !consent_given)) {
     return true;
   }
   return false;
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
index ff97f08..c92b86c 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/app_list_button.h
@@ -34,6 +34,8 @@
                                  public TabletModeObserver,
                                  public DefaultVoiceInteractionObserver {
  public:
+  static const char kViewClassName[];
+
   AppListButton(ShelfView* shelf_view, Shelf* shelf);
   ~AppListButton() override;
 
@@ -44,6 +46,7 @@
 
   // views::Button:
   void OnGestureEvent(ui::GestureEvent* event) override;
+  const char* GetClassName() const override;
 
  protected:
   // views::Button:
@@ -58,7 +61,8 @@
   void OnVoiceInteractionStatusChanged(
       mojom::VoiceInteractionState state) override;
   void OnVoiceInteractionSettingsEnabled(bool enabled) override;
-  void OnVoiceInteractionSetupCompleted(bool completed) override;
+  void OnVoiceInteractionConsentStatusUpdated(
+      mojom::ConsentStatus consent_status) override;
 
   // SessionObserver:
   void OnActiveUserSessionChanged(const AccountId& account_id) override;
diff --git a/ash/shelf/back_button.cc b/ash/shelf/back_button.cc
index 31e6522..4c1ba90 100644
--- a/ash/shelf/back_button.cc
+++ b/ash/shelf/back_button.cc
@@ -27,6 +27,9 @@
 
 namespace ash {
 
+// static
+const char BackButton::kViewClassName[] = "ash/BackButton";
+
 BackButton::BackButton(ShelfView* shelf_view) : ShelfControlButton(shelf_view) {
   SetAccessibleName(l10n_util::GetStringUTF16(IDS_ASH_SHELF_BACK_BUTTON_TITLE));
 }
@@ -60,6 +63,10 @@
                        GetCenterPoint().y() - img.height() / 2);
 }
 
+const char* BackButton::GetClassName() const {
+  return kViewClassName;
+}
+
 void BackButton::GenerateAndSendBackEvent(
     const ui::EventType& original_event_type) {
   ui::EventType new_event_type;
diff --git a/ash/shelf/back_button.h b/ash/shelf/back_button.h
index c07f2b0..272426f 100644
--- a/ash/shelf/back_button.h
+++ b/ash/shelf/back_button.h
@@ -18,6 +18,8 @@
 // in/out of the icon matches the movement of ShelfView's items.
 class ASH_EXPORT BackButton : public ShelfControlButton {
  public:
+  static const char kViewClassName[];
+
   explicit BackButton(ShelfView* shelf_view);
   ~BackButton() override;
 
@@ -27,6 +29,7 @@
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
+  const char* GetClassName() const override;
 
  private:
   // Generate and send a VKEY_BROWSER_BACK key event when the back button
diff --git a/ash/shelf/overflow_button.cc b/ash/shelf/overflow_button.cc
index 306b41c..2a4378b 100644
--- a/ash/shelf/overflow_button.cc
+++ b/ash/shelf/overflow_button.cc
@@ -48,4 +48,8 @@
   shelf_view()->ButtonPressed(this, event, nullptr);
 }
 
+const char* OverflowButton::GetClassName() const {
+  return "ash/OverflowButton";
+}
+
 }  // namespace ash
diff --git a/ash/shelf/overflow_button.h b/ash/shelf/overflow_button.h
index f3624345..bf9b6ed 100644
--- a/ash/shelf/overflow_button.h
+++ b/ash/shelf/overflow_button.h
@@ -34,6 +34,7 @@
   // views::Button:
   bool ShouldEnterPushedState(const ui::Event& event) override;
   void NotifyClick(const ui::Event& event) override;
+  const char* GetClassName() const override;
 
   const gfx::ImageSkia horizontal_dots_image_;
   views::ImageView* horizontal_dots_image_view_;
diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc
index 0c8527a..abd273dd 100644
--- a/ash/shelf/shelf_button.cc
+++ b/ash/shelf/shelf_button.cc
@@ -93,4 +93,8 @@
   return std::move(ink_drop);
 }
 
+const char* ShelfButton::GetClassName() const {
+  return "ash/ShelfButton";
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_button.h b/ash/shelf/shelf_button.h
index fd1963b..38da03c 100644
--- a/ash/shelf/shelf_button.h
+++ b/ash/shelf/shelf_button.h
@@ -34,6 +34,7 @@
   void NotifyClick(const ui::Event& event) override;
   bool ShouldEnterPushedState(const ui::Event& event) override;
   std::unique_ptr<views::InkDrop> CreateInkDrop() override;
+  const char* GetClassName() const override;
 
  private:
   // The shelf view hosting this button.
diff --git a/ash/shelf/shelf_control_button.cc b/ash/shelf/shelf_control_button.cc
index 0597dfa..0143fb8 100644
--- a/ash/shelf/shelf_control_button.cc
+++ b/ash/shelf/shelf_control_button.cc
@@ -47,6 +47,10 @@
       size(), GetCenterPoint(), ShelfConstants::control_border_radius());
 }
 
+const char* ShelfControlButton::GetClassName() const {
+  return "ash/ShelfControlButton";
+}
+
 gfx::Rect ShelfControlButton::CalculateButtonBounds() const {
   ShelfAlignment alignment = shelf_->alignment();
   gfx::Rect content_bounds = GetContentsBounds();
diff --git a/ash/shelf/shelf_control_button.h b/ash/shelf/shelf_control_button.h
index 93c27cd..ed1055c 100644
--- a/ash/shelf/shelf_control_button.h
+++ b/ash/shelf/shelf_control_button.h
@@ -31,6 +31,7 @@
   // views::Button:
   std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
+  const char* GetClassName() const override;
 
   void PaintBackground(gfx::Canvas* canvas, const gfx::Rect& bounds);
   void PaintButtonContents(gfx::Canvas* canvas) override;
diff --git a/ash/voice_interaction/voice_interaction_controller.cc b/ash/voice_interaction/voice_interaction_controller.cc
index 19183fb..7ad9ada 100644
--- a/ash/voice_interaction/voice_interaction_controller.cc
+++ b/ash/voice_interaction/voice_interaction_controller.cc
@@ -83,16 +83,17 @@
     observer.OnVoiceInteractionHotwordAlwaysOn(always_on);
 }
 
-void VoiceInteractionController::NotifySetupCompleted(bool completed) {
-  if (setup_completed_.has_value() && setup_completed_.value() == completed)
+void VoiceInteractionController::NotifyConsentStatus(
+    mojom::ConsentStatus consent_status) {
+  if (consent_status_.has_value() && consent_status_.value() == consent_status)
     return;
 
-  setup_completed_ = completed;
-  observers_.ForAllPtrs([completed](auto* observer) {
-    observer->OnVoiceInteractionSetupCompleted(completed);
+  consent_status_ = consent_status;
+  observers_.ForAllPtrs([consent_status](auto* observer) {
+    observer->OnVoiceInteractionConsentStatusUpdated(consent_status);
   });
   for (auto& observer : local_observers_)
-    observer.OnVoiceInteractionSetupCompleted(completed);
+    observer.OnVoiceInteractionConsentStatusUpdated(consent_status);
 }
 
 void VoiceInteractionController::NotifyFeatureAllowed(
@@ -156,8 +157,8 @@
     observer->OnVoiceInteractionContextEnabled(context_enabled_.value());
   if (hotword_enabled_.has_value())
     observer->OnVoiceInteractionHotwordEnabled(hotword_enabled_.value());
-  if (setup_completed_.has_value())
-    observer->OnVoiceInteractionSetupCompleted(setup_completed_.value());
+  if (consent_status_.has_value())
+    observer->OnVoiceInteractionConsentStatusUpdated(consent_status_.value());
   if (hotword_always_on_.has_value())
     observer->OnVoiceInteractionHotwordAlwaysOn(hotword_always_on_.value());
   if (allowed_state_.has_value())
diff --git a/ash/voice_interaction/voice_interaction_controller.h b/ash/voice_interaction/voice_interaction_controller.h
index c6ac68d..0b31a87 100644
--- a/ash/voice_interaction/voice_interaction_controller.h
+++ b/ash/voice_interaction/voice_interaction_controller.h
@@ -32,7 +32,7 @@
   void NotifyContextEnabled(bool enabled) override;
   void NotifyHotwordEnabled(bool enabled) override;
   void NotifyHotwordAlwaysOn(bool always_on) override;
-  void NotifySetupCompleted(bool completed) override;
+  void NotifyConsentStatus(mojom::ConsentStatus consent_status) override;
   void NotifyFeatureAllowed(mojom::AssistantAllowedState state) override;
   void NotifyNotificationEnabled(bool enabled) override;
   void NotifyLocaleChanged(const std::string& locale) override;
diff --git a/ash/voice_interaction/voice_interaction_controller_unittest.cc b/ash/voice_interaction/voice_interaction_controller_unittest.cc
index fd33af05..e9da9ced 100644
--- a/ash/voice_interaction/voice_interaction_controller_unittest.cc
+++ b/ash/voice_interaction/voice_interaction_controller_unittest.cc
@@ -37,8 +37,9 @@
   void OnVoiceInteractionHotwordEnabled(bool enabled) override {
     hotword_enabled_ = enabled;
   }
-  void OnVoiceInteractionSetupCompleted(bool completed) override {
-    setup_completed_ = completed;
+  void OnVoiceInteractionConsentStatusUpdated(
+      mojom::ConsentStatus consent_status) override {
+    consent_status_ = consent_status;
   }
   void OnAssistantFeatureAllowedChanged(
       mojom::AssistantAllowedState state) override {}
@@ -51,7 +52,7 @@
   bool context_enabled() const { return context_enabled_; }
   bool hotword_always_on() const { return hotword_always_on_; }
   bool hotword_enabled() const { return hotword_enabled_; }
-  bool setup_completed() const { return setup_completed_; }
+  mojom::ConsentStatus consent_status() const { return consent_status_; }
 
   void SetVoiceInteractionController(VoiceInteractionController* controller) {
     mojom::VoiceInteractionObserverPtr ptr;
@@ -65,7 +66,7 @@
   bool context_enabled_ = false;
   bool hotword_always_on_ = false;
   bool hotword_enabled_ = false;
-  bool setup_completed_ = false;
+  mojom::ConsentStatus consent_status_ = mojom::ConsentStatus::kUnknown;
 
   mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
 
@@ -147,13 +148,16 @@
   EXPECT_TRUE(observer()->hotword_enabled());
 }
 
-TEST_F(VoiceInteractionControllerTest, NotifySetupCompleted) {
-  controller()->NotifySetupCompleted(true);
+TEST_F(VoiceInteractionControllerTest, NotifyConsentStatus) {
+  controller()->NotifyConsentStatus(
+      mojom::ConsentStatus::kActivityControlAccepted);
   controller()->FlushForTesting();
   // The cached state should be updated.
-  EXPECT_TRUE(controller()->setup_completed());
+  EXPECT_TRUE(controller()->consent_status() ==
+              mojom::ConsentStatus::kActivityControlAccepted);
   // The observers should be notified.
-  EXPECT_TRUE(observer()->setup_completed());
+  EXPECT_TRUE(observer()->consent_status() ==
+              mojom::ConsentStatus::kActivityControlAccepted);
 }
 
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 3d3e10e..18597d0 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -1918,8 +1918,12 @@
   std::unique_ptr<WindowResizer> StartDrag(aura::Window* dragged_window,
                                            aura::Window* source_window) {
     SetIsInTabDragging(dragged_window, /*is_dragging=*/true, source_window);
-    return CreateResizerForTest(dragged_window,
-                                dragged_window->bounds().origin(), HTCAPTION);
+    std::unique_ptr<WindowResizer> resizer = CreateResizerForTest(
+        dragged_window, dragged_window->bounds().origin(), HTCAPTION);
+    GetBrowserWindowDragController(resizer.get())
+        ->drag_delegate_for_testing()
+        ->set_drag_start_deadline_for_testing(base::Time::Now());
+    return resizer;
   }
 
   // Drags the window to |end_position|.
@@ -1987,17 +1991,20 @@
     }
   }
 
-  IndicatorState GetIndicatorState(WindowResizer* resizer) {
+  TabletModeWindowDragController* GetBrowserWindowDragController(
+      WindowResizer* resizer) {
     WindowResizer* real_window_resizer;
     // TODO(xdai): This piece of codes seems knowing too much impl details about
     // WindowResizer. Revisit the logic here later to see if there is anything
     // we can do to simplify the logic and hide impl details.
     real_window_resizer = static_cast<DragWindowResizer*>(resizer)
                               ->next_window_resizer_for_testing();
-    TabletModeWindowDragController* browser_controller =
-        static_cast<TabletModeWindowDragController*>(real_window_resizer);
+    return static_cast<TabletModeWindowDragController*>(real_window_resizer);
+  }
 
-    return browser_controller->drag_delegate_for_testing()
+  IndicatorState GetIndicatorState(WindowResizer* resizer) {
+    return GetBrowserWindowDragController(resizer)
+        ->drag_delegate_for_testing()
         ->split_view_drag_indicators_for_testing()
         ->current_indicator_state();
   }
@@ -3660,6 +3667,8 @@
                                     ::wm::WINDOW_MOVE_SOURCE_TOUCH);
     controller_ = std::make_unique<TabletModeWindowDragController>(
         window_state, std::make_unique<TabletModeBrowserWindowDragDelegate>());
+    controller_->drag_delegate_for_testing()
+        ->set_drag_start_deadline_for_testing(base::Time::Now());
     controller_->Drag(location, 0);
   }
 
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
index dd2733b..c30a15a 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -40,6 +40,10 @@
 // tablet mode.
 constexpr float kIndicatorsThresholdRatio = 0.1;
 
+// Duration of a drag that it will be considered as an intended drag.
+constexpr base::TimeDelta kIsWindowMovedTimeoutMs =
+    base::TimeDelta::FromMilliseconds(300);
+
 // Returns the overview session if overview mode is active, otherwise returns
 // nullptr.
 OverviewSession* GetOverviewSession() {
@@ -97,6 +101,7 @@
 
   dragged_window_ = dragged_window;
   initial_location_in_screen_ = location_in_screen;
+  drag_start_deadline_ = base::Time::Now() + kIsWindowMovedTimeoutMs;
 
   PrepareWindowDrag(location_in_screen);
 
@@ -177,16 +182,7 @@
     const gfx::Point& location_in_screen,
     UpdateDraggedWindowType type,
     const gfx::Rect& target_bounds) {
-  if (!did_move_) {
-    const gfx::Rect work_area_bounds =
-        display::Screen::GetScreen()
-            ->GetDisplayNearestWindow(dragged_window_)
-            .work_area();
-    if (location_in_screen.y() >=
-        GetIndicatorsVerticalThreshold(work_area_bounds)) {
-      did_move_ = true;
-    }
-  }
+  UpdateIsWindowConsideredMoved(location_in_screen.y());
 
   if (type == UpdateDraggedWindowType::UPDATE_BOUNDS) {
     // UPDATE_BOUNDS is used when dragging tab(s) out of a browser window.
@@ -266,7 +262,7 @@
 
   occlusion_excluder_.reset();
   dragged_window_ = nullptr;
-  did_move_ = false;
+  is_window_considered_moved_ = false;
 }
 
 void TabletModeWindowDragDelegate::FlingOrSwipe(ui::GestureEvent* event) {
@@ -299,16 +295,10 @@
   if (split_view_controller_->IsSplitViewModeActive())
     return IndicatorState::kNone;
 
-  // If the event location hasn't passed the indicator vertical threshold, do
-  // not show the drag indicators.
-  const gfx::Rect work_area_bounds =
-      display::Screen::GetScreen()
-          ->GetDisplayNearestWindow(dragged_window_)
-          .work_area();
-  if (!did_move_ && location_in_screen.y() <
-                        GetIndicatorsVerticalThreshold(work_area_bounds)) {
+  // Do not show the drag indicators if the window hasn't been considered as
+  // moved.
+  if (!is_window_considered_moved_)
     return IndicatorState::kNone;
-  }
 
   // No top drag indicator if in portrait screen orientation.
   if (IsCurrentScreenOrientationLandscape())
@@ -352,16 +342,13 @@
     }
   }
 
-  // Otherwise, the user has to drag pass the indicator vertical threshold to
-  // snap the window.
+  // Do not snap the window if it hasn't be considered as moved.
+  if (!is_window_considered_moved_)
+    return SplitViewController::NONE;
+
   gfx::Rect work_area_bounds = display::Screen::GetScreen()
                                    ->GetDisplayNearestWindow(dragged_window_)
                                    .work_area();
-  if (!did_move_ && location_in_screen.y() <
-                        GetIndicatorsVerticalThreshold(work_area_bounds)) {
-    return SplitViewController::NONE;
-  }
-
   // Check to see if the current event location |location_in_screen|is within
   // the drag indicators bounds.
   if (is_landscape) {
@@ -504,4 +491,21 @@
   return event->details().velocity_y() > kFlingToOverviewThreshold;
 }
 
+void TabletModeWindowDragDelegate::UpdateIsWindowConsideredMoved(
+    int y_location_in_screen) {
+  if (is_window_considered_moved_)
+    return;
+
+  if (base::Time::Now() < drag_start_deadline_)
+    return;
+
+  DCHECK(dragged_window_);
+  const gfx::Rect work_area_bounds =
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(dragged_window_)
+          .work_area();
+  is_window_considered_moved_ =
+      y_location_in_screen >= GetIndicatorsVerticalThreshold(work_area_bounds);
+}
+
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h
index 4d9880c..239e44ac 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h
@@ -79,6 +79,10 @@
     return split_view_drag_indicators_.get();
   }
 
+  void set_drag_start_deadline_for_testing(base::Time time) {
+    drag_start_deadline_ = time;
+  }
+
  protected:
   // These four methods are used by its child class to do its special handling
   // before/during/after dragging.
@@ -115,6 +119,10 @@
   // Returns true if fling event should drop the window into overview grid.
   bool ShouldFlingIntoOverview(const ui::GestureEvent* event) const;
 
+  // Updates |is_window_considered_moved_| on current time and
+  // |y_location_in_screen|.
+  void UpdateIsWindowConsideredMoved(int y_location_in_screen);
+
   SplitViewController* const split_view_controller_;
 
   // A widget to display the drag indicators and preview window.
@@ -137,11 +145,15 @@
   // desired window transform during dragging.
   gfx::Rect bounds_of_selected_drop_target_;
 
-  // Flag to indicate whether a window is considered as moved. A window needs to
-  // be dragged vertically a small amount of distance to be considered as moved.
-  // The drag indicators will only show up after the window has been moved. Once
-  // the window is moved, it will stay as 'moved'.
-  bool did_move_ = false;
+  // True if the |dragged_window_| has been considered as moved. Only after it
+  // has been dragged longer than kIsWindowMovedTimeoutMs on time and further
+  // than GetIndicatorsVerticalThreshold on distance, it can be considered as
+  // moved. Only change its window state or show the drag indicators if it has
+  // been 'moved'. Once it has been 'moved', it will stay as 'moved'.
+  bool is_window_considered_moved_ = false;
+
+  // Drag need to last later than the deadline here to be considered as 'moved'.
+  base::Time drag_start_deadline_;
 
   base::Optional<aura::WindowOcclusionTracker::ScopedExclude>
       occlusion_excluder_;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 0125e5f..3dea978 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1139,8 +1139,8 @@
     if (is_linux) {
       sources += [
         "base_paths_posix.cc",
-        "debug/elf_reader_linux.cc",
-        "debug/elf_reader_linux.h",
+        "debug/elf_reader.cc",
+        "debug/elf_reader.h",
       ]
     }
   }
@@ -1342,8 +1342,8 @@
     # Android uses some Linux sources, put those back.
     set_sources_assignment_filter([])
     sources += [
-      "debug/elf_reader_linux.cc",
-      "debug/elf_reader_linux.h",
+      "debug/elf_reader.cc",
+      "debug/elf_reader.h",
       "debug/proc_maps_linux.cc",
       "debug/proc_maps_linux.h",
       "files/file_path_watcher_linux.cc",
@@ -1401,6 +1401,8 @@
       "base_paths_fuchsia.cc",
       "base_paths_fuchsia.h",
       "debug/debugger_posix.cc",
+      "debug/elf_reader.cc",
+      "debug/elf_reader.h",
       "debug/stack_trace_fuchsia.cc",
       "file_descriptor_posix.h",
       "files/dir_reader_posix.h",
@@ -2330,7 +2332,6 @@
     "debug/alias_unittest.cc",
     "debug/crash_logging_unittest.cc",
     "debug/debugger_unittest.cc",
-    "debug/elf_reader_linux_unittest.cc",
     "debug/leak_tracker_unittest.cc",
     "debug/proc_maps_linux_unittest.cc",
     "debug/stack_trace_unittest.cc",
@@ -2759,6 +2760,8 @@
       sources += [ "nix/xdg_util_unittest.cc" ]
     }
 
+    sources += [ "debug/elf_reader_unittest.cc" ]
+
     deps += [ "//base/test:malloc_wrapper" ]
     defines += [
       # This library is used by ElfReaderTest to test reading elf files.
@@ -2782,6 +2785,7 @@
 
   if (is_fuchsia) {
     sources += [
+      "debug/elf_reader_unittest.cc",
       "files/dir_reader_posix_unittest.cc",
       "files/file_descriptor_watcher_posix_unittest.cc",
       "fuchsia/filtered_service_directory_unittest.cc",
@@ -2815,7 +2819,7 @@
     deps += [ "//testing/android/native_test:native_test_native_code" ]
     set_sources_assignment_filter([])
     sources += [
-      "debug/elf_reader_linux_unittest.cc",
+      "debug/elf_reader_unittest.cc",
       "debug/proc_maps_linux_unittest.cc",
       "trace_event/trace_event_android_unittest.cc",
     ]
diff --git a/base/debug/elf_reader.cc b/base/debug/elf_reader.cc
new file mode 100644
index 0000000..a13c846
--- /dev/null
+++ b/base/debug/elf_reader.cc
@@ -0,0 +1,166 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/elf_reader.h"
+
+#include <arpa/inet.h>
+#include <elf.h>
+
+#include "base/bits.h"
+#include "base/containers/span.h"
+#include "base/sha1.h"
+#include "base/strings/safe_sprintf.h"
+
+// NOTE: This code may be used in crash handling code, so the implementation
+// must avoid dynamic memory allocation or using data structures which rely on
+// dynamic allocation.
+
+namespace base {
+namespace debug {
+namespace {
+
+#if __SIZEOF_POINTER__ == 4
+using Ehdr = Elf32_Ehdr;
+using Dyn = Elf32_Dyn;
+using Half = Elf32_Half;
+using Nhdr = Elf32_Nhdr;
+using Word = Elf32_Word;
+#else
+using Ehdr = Elf64_Ehdr;
+using Dyn = Elf64_Dyn;
+using Half = Elf64_Half;
+using Nhdr = Elf64_Nhdr;
+using Word = Elf64_Word;
+#endif
+
+constexpr char kGnuNoteName[] = "GNU";
+
+// Returns a pointer to the header of the ELF binary mapped into memory,
+// or a null pointer if the header is invalid.
+const Ehdr* GetElfHeader(const void* elf_mapped_base) {
+  const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
+  if (strncmp(elf_base, ELFMAG, SELFMAG) != 0)
+    return nullptr;
+
+  const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
+  return elf_header;
+}
+
+}  // namespace
+
+span<const Phdr> GetElfProgramHeaders(const void* elf_mapped_base) {
+  // NOTE: Function should use async signal safe calls only.
+
+  const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
+  const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
+  if (!elf_header)
+    return {};
+
+  return span<const Phdr>(
+      reinterpret_cast<const Phdr*>(elf_base + elf_header->e_phoff),
+      elf_header->e_phnum);
+}
+
+size_t ReadElfBuildId(const void* elf_mapped_base,
+                      bool uppercase,
+                      ElfBuildIdBuffer build_id) {
+  // NOTE: Function should use async signal safe calls only.
+
+  const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
+  const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
+  if (!elf_header)
+    return 0;
+
+  for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
+    if (header.p_type != PT_NOTE)
+      continue;
+
+    // Look for a NT_GNU_BUILD_ID note with name == "GNU".
+    const void* section_end = elf_base + header.p_offset + header.p_memsz;
+    const Nhdr* current_note =
+        reinterpret_cast<const Nhdr*>(elf_base + header.p_offset);
+    bool found = false;
+    while (current_note < section_end) {
+      if (current_note->n_type == NT_GNU_BUILD_ID) {
+        const char* note_name =
+            reinterpret_cast<const char*>(current_note) + sizeof(Nhdr);
+        if (current_note->n_namesz == 4 &&
+            strncmp(note_name, kGnuNoteName, 4) == 0) {
+          found = true;
+          break;
+        }
+      }
+
+      current_note = reinterpret_cast<const Nhdr*>(
+          reinterpret_cast<const char*>(current_note) + sizeof(Nhdr) +
+          bits::Align(current_note->n_namesz, 4) +
+          bits::Align(current_note->n_descsz, 4));
+    }
+    if (!found)
+      continue;
+
+    // Validate that the serialized build ID will fit inside |build_id|.
+    size_t note_size = current_note->n_descsz;
+    if (current_note >= section_end ||
+        (note_size * 2) > kMaxBuildIdStringLength) {
+      continue;
+    }
+
+    // Write out the build ID as a null-terminated hex string.
+    const uint8_t* build_id_raw =
+        reinterpret_cast<const uint8_t*>(current_note) + sizeof(Nhdr) +
+        bits::Align(current_note->n_namesz, 4);
+    size_t i = 0;
+    for (i = 0; i < current_note->n_descsz; ++i) {
+      strings::SafeSNPrintf(&build_id[i * 2], 3, (uppercase ? "%02X" : "%02x"),
+                            build_id_raw[i]);
+    }
+    build_id[i * 2] = '\0';
+
+    // Return the length of the string.
+    return i * 2;
+  }
+
+  return 0;
+}
+
+Optional<StringPiece> ReadElfLibraryName(const void* elf_mapped_base) {
+  // NOTE: Function should use async signal safe calls only.
+
+  const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
+  const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
+  if (!elf_header)
+    return {};
+
+  for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
+    if (header.p_type != PT_DYNAMIC)
+      continue;
+
+    // Read through the ELF dynamic sections to find the string table and
+    // SONAME offsets, which are used to compute the offset of the library
+    // name string.
+    const Dyn* dynamic_start =
+        reinterpret_cast<const Dyn*>(elf_base + header.p_offset);
+    const Dyn* dynamic_end = reinterpret_cast<const Dyn*>(
+        elf_base + header.p_offset + header.p_memsz);
+    Word soname_strtab_offset = 0;
+    const char* strtab_addr = 0;
+    for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end;
+         ++dynamic_iter) {
+      if (dynamic_iter->d_tag == DT_STRTAB) {
+        strtab_addr =
+            dynamic_iter->d_un.d_ptr + reinterpret_cast<const char*>(elf_base);
+      } else if (dynamic_iter->d_tag == DT_SONAME) {
+        soname_strtab_offset = dynamic_iter->d_un.d_val;
+      }
+    }
+    if (soname_strtab_offset && strtab_addr)
+      return StringPiece(strtab_addr + soname_strtab_offset);
+  }
+
+  return nullopt;
+}
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/debug/elf_reader.h b/base/debug/elf_reader.h
new file mode 100644
index 0000000..8e0f48e6
--- /dev/null
+++ b/base/debug/elf_reader.h
@@ -0,0 +1,50 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DEBUG_ELF_READER_H_
+#define BASE_DEBUG_ELF_READER_H_
+
+#include <elf.h>
+
+#include "base/base_export.h"
+#include "base/containers/span.h"
+#include "base/optional.h"
+#include "base/sha1.h"
+#include "base/strings/string_piece.h"
+
+// Functions for querying metadata from ELF binaries.
+// All functions require that the file be fully memory mapped.
+
+#if __SIZEOF_POINTER__ == 4
+using Phdr = Elf32_Phdr;
+#else
+using Phdr = Elf64_Phdr;
+#endif
+
+namespace base {
+namespace debug {
+
+// Hex-encodes the build ID from the ELF binary located at |elf_base|.
+// Returns the length of the build ID in bytes, or zero if the build ID couldn't
+// be read.
+// When |uppercase| is |true|, the output string is written using uppercase hex
+// characters. Otherwise, the output is lowercased.
+constexpr size_t kMaxBuildIdStringLength = kSHA1Length * 2;
+using ElfBuildIdBuffer = char[kMaxBuildIdStringLength + 1];
+size_t BASE_EXPORT ReadElfBuildId(const void* elf_base,
+                                  bool uppercase,
+                                  ElfBuildIdBuffer build_id);
+
+// Returns the library name from the ELF file mapped at |elf_base|.
+// Returns an empty result if the name could not be read.
+Optional<StringPiece> BASE_EXPORT ReadElfLibraryName(const void* elf_base);
+
+// Returns a span of ELF program headers for the ELF file mapped at
+// |elf_base|, or an empty span if the header couldn't be read.
+span<const Phdr> BASE_EXPORT GetElfProgramHeaders(const void* elf_base);
+
+}  // namespace debug
+}  // namespace base
+
+#endif  // BASE_DEBUG_ELF_READER_H_
diff --git a/base/debug/elf_reader_linux.cc b/base/debug/elf_reader_linux.cc
deleted file mode 100644
index cdf8193..0000000
--- a/base/debug/elf_reader_linux.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/debug/elf_reader_linux.h"
-
-#include <arpa/inet.h>
-#include <elf.h>
-
-#include <vector>
-
-#include "base/bits.h"
-#include "base/containers/span.h"
-#include "base/sha1.h"
-#include "base/strings/stringprintf.h"
-
-namespace base {
-namespace debug {
-
-namespace {
-
-#if __SIZEOF_POINTER__ == 4
-using Ehdr = Elf32_Ehdr;
-using Dyn = Elf32_Dyn;
-using Half = Elf32_Half;
-using Nhdr = Elf32_Nhdr;
-using Phdr = Elf32_Phdr;
-using Word = Elf32_Word;
-#else
-using Ehdr = Elf64_Ehdr;
-using Dyn = Elf64_Dyn;
-using Half = Elf64_Half;
-using Nhdr = Elf64_Nhdr;
-using Phdr = Elf64_Phdr;
-using Word = Elf64_Word;
-#endif
-
-using ElfSegment = span<const char>;
-
-Optional<std::string> ElfSegmentBuildIDNoteAsString(const ElfSegment& segment) {
-  const void* section_end = segment.data() + segment.size_bytes();
-  const Nhdr* note_header = reinterpret_cast<const Nhdr*>(segment.data());
-  while (note_header < section_end) {
-    if (note_header->n_type == NT_GNU_BUILD_ID)
-      break;
-    note_header = reinterpret_cast<const Nhdr*>(
-        reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
-        bits::Align(note_header->n_namesz, 4) +
-        bits::Align(note_header->n_descsz, 4));
-  }
-
-  if (note_header >= section_end || note_header->n_descsz != kSHA1Length)
-    return nullopt;
-
-  const uint8_t* guid = reinterpret_cast<const uint8_t*>(note_header) +
-                        sizeof(Nhdr) + bits::Align(note_header->n_namesz, 4);
-
-  uint32_t dword = htonl(*reinterpret_cast<const int32_t*>(guid));
-  uint16_t word1 = htons(*reinterpret_cast<const int16_t*>(guid + 4));
-  uint16_t word2 = htons(*reinterpret_cast<const int16_t*>(guid + 6));
-  std::string identifier;
-  identifier.reserve(kSHA1Length * 2);  // as hex string
-  SStringPrintf(&identifier, "%08X%04X%04X", dword, word1, word2);
-  for (size_t i = 8; i < note_header->n_descsz; ++i)
-    StringAppendF(&identifier, "%02X", guid[i]);
-
-  return identifier;
-}
-
-std::vector<ElfSegment> FindElfSegments(const void* elf_mapped_base,
-                                        uint32_t segment_type) {
-  const char* elf_base = reinterpret_cast<const char*>(elf_mapped_base);
-  if (strncmp(elf_base, ELFMAG, SELFMAG) != 0)
-    return std::vector<ElfSegment>();
-
-  const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
-  const Phdr* phdrs =
-      reinterpret_cast<const Phdr*>(elf_base + elf_header->e_phoff);
-  std::vector<ElfSegment> segments;
-  for (Half i = 0; i < elf_header->e_phnum; ++i) {
-    if (phdrs[i].p_type == segment_type)
-      segments.push_back({elf_base + phdrs[i].p_offset, phdrs[i].p_filesz});
-  }
-  return segments;
-}
-
-}  // namespace
-
-Optional<std::string> ReadElfBuildId(const void* elf_base) {
-  // Elf program headers can have multiple PT_NOTE arrays.
-  std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_NOTE);
-  if (segs.empty())
-    return nullopt;
-  Optional<std::string> id;
-  for (const ElfSegment& seg : segs) {
-    id = ElfSegmentBuildIDNoteAsString(seg);
-    if (id)
-      return id;
-  }
-
-  return nullopt;
-}
-
-Optional<std::string> ReadElfLibraryName(const void* elf_base) {
-  std::vector<ElfSegment> segs = FindElfSegments(elf_base, PT_DYNAMIC);
-  if (segs.empty())
-    return nullopt;
-  DCHECK_EQ(1u, segs.size());
-
-  const ElfSegment& dynamic_seg = segs.front();
-  const Dyn* dynamic_start = reinterpret_cast<const Dyn*>(dynamic_seg.data());
-  const Dyn* dynamic_end = reinterpret_cast<const Dyn*>(
-      dynamic_seg.data() + dynamic_seg.size_bytes());
-  Optional<std::string> soname;
-  Word soname_strtab_offset = 0;
-  const char* strtab_addr = 0;
-  for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end;
-       ++dynamic_iter) {
-    if (dynamic_iter->d_tag == DT_STRTAB) {
-      strtab_addr =
-          dynamic_iter->d_un.d_ptr + reinterpret_cast<const char*>(elf_base);
-    } else if (dynamic_iter->d_tag == DT_SONAME) {
-      soname_strtab_offset = dynamic_iter->d_un.d_val;
-    }
-  }
-  if (soname_strtab_offset && strtab_addr)
-    return std::string(strtab_addr + soname_strtab_offset);
-  return nullopt;
-}
-
-}  // namespace debug
-}  // namespace base
diff --git a/base/debug/elf_reader_linux.h b/base/debug/elf_reader_linux.h
deleted file mode 100644
index 4086dfb..0000000
--- a/base/debug/elf_reader_linux.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_DEBUG_ELF_READER_LINUX_H_
-#define BASE_DEBUG_ELF_READER_LINUX_H_
-
-#include <string>
-
-#include "base/base_export.h"
-#include "base/optional.h"
-
-namespace base {
-namespace debug {
-
-// Returns the ELF section .note.gnu.build-id from the ELF file mapped at
-// |elf_base|, if present. The caller must ensure that the file is fully mapped
-// in memory.
-Optional<std::string> BASE_EXPORT ReadElfBuildId(const void* elf_base);
-
-// Returns the library name from the ELF file mapped at |elf_base|, if present.
-// The caller must ensure that the file is fully mapped in memory.
-Optional<std::string> BASE_EXPORT ReadElfLibraryName(const void* elf_base);
-
-}  // namespace debug
-}  // namespace base
-
-#endif  // BASE_DEBUG_ELF_READER_LINUX_H_
diff --git a/base/debug/elf_reader_linux_unittest.cc b/base/debug/elf_reader_unittest.cc
similarity index 60%
rename from base/debug/elf_reader_linux_unittest.cc
rename to base/debug/elf_reader_unittest.cc
index 2c15299..38b14cb 100644
--- a/base/debug/elf_reader_linux_unittest.cc
+++ b/base/debug/elf_reader_unittest.cc
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/debug/elf_reader_linux.h"
+#include "base/debug/elf_reader.h"
 
 #include <dlfcn.h>
 
+#include <string>
+
 #include "base/files/memory_mapped_file.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
@@ -16,22 +18,44 @@
 namespace base {
 namespace debug {
 
-// The linker flag --build-id is passed only on official builds. Clang does not
-// enable it by default and we do not have build id section in non-official
+// The linker flag --build-id is passed only on official builds and Fuchsia
 // builds.
-#if defined(OFFICIAL_BUILD)
-TEST(ElfReaderTest, ReadElfBuildId) {
-  Optional<std::string> build_id = ReadElfBuildId(&__executable_start);
-  ASSERT_TRUE(build_id);
-  const size_t kGuidBytes = 20;
-  EXPECT_EQ(2 * kGuidBytes, build_id.value().size());
-  for (char c : *build_id) {
+#if defined(OFFICIAL_BUILD) || defined(OS_FUCHSIA)
+
+#if defined(OS_FUCHSIA)
+constexpr size_t kExpectedBuildIdStringLength = 16;  // 64-bit int in hex.
+#else
+constexpr size_t kExpectedBuildIdStringLength = 40;  // SHA1 hash in hex.
+#endif
+
+TEST(ElfReaderTest, ReadElfBuildIdUppercase) {
+  ElfBuildIdBuffer build_id;
+  size_t build_id_size = ReadElfBuildId(&__executable_start, true, build_id);
+  ASSERT_NE(build_id_size, 0u);
+
+  EXPECT_EQ(kExpectedBuildIdStringLength, build_id_size);
+  for (size_t i = 0; i < build_id_size; ++i) {
+    char c = build_id[i];
     EXPECT_TRUE(IsHexDigit(c));
     EXPECT_FALSE(IsAsciiLower(c));
   }
 }
-#endif
 
+TEST(ElfReaderTest, ReadElfBuildIdLowercase) {
+  ElfBuildIdBuffer build_id;
+  size_t build_id_size = ReadElfBuildId(&__executable_start, false, build_id);
+  ASSERT_NE(build_id_size, 0u);
+
+  EXPECT_EQ(kExpectedBuildIdStringLength, build_id_size);
+  for (size_t i = 0; i < kExpectedBuildIdStringLength; ++i) {
+    char c = build_id[i];
+    EXPECT_TRUE(IsHexDigit(c));
+    EXPECT_TRUE(!IsAsciiAlpha(c) || IsAsciiLower(c));
+  }
+}
+#endif  // defined(OFFICIAL_BUILD) || defined(OS_FUCHSIA)
+
+#if !defined(OS_FUCHSIA)
 TEST(ElfReaderTest, ReadElfLibraryName) {
 #if defined(OS_ANDROID)
   // On Android the library loader memory maps the full so file.
@@ -65,6 +89,7 @@
       << "Library name " << *name << " doesn't contain expected "
       << kLibraryName;
 }
+#endif  // !defined(OS_FUCHSIA)
 
 }  // namespace debug
 }  // namespace base
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 36a97fe..a5701c7 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -49,7 +49,7 @@
 #endif
 
 #if defined(OS_ANDROID)
-#include "base/debug/elf_reader_linux.h"
+#include "base/debug/elf_reader.h"
 
 // The linker assigns the virtual address of the start of current library to
 // this symbol.
@@ -1510,11 +1510,12 @@
   AddMetadataEventWhileLocked(current_thread_id, "chrome_library_address",
                               "start_address",
                               base::StringPrintf("%p", &__executable_start));
-  base::Optional<std::string> buildid =
-      base::debug::ReadElfBuildId(&__executable_start);
-  if (buildid) {
+  base::debug::ElfBuildIdBuffer build_id;
+  size_t build_id_length =
+      base::debug::ReadElfBuildId(&__executable_start, true, build_id);
+  if (build_id_length > 0) {
     AddMetadataEventWhileLocked(current_thread_id, "chrome_library_module",
-                                "id", buildid.value());
+                                "id", build_id);
   }
 #endif
 
diff --git a/build/android/bytecode/BUILD.gn b/build/android/bytecode/BUILD.gn
index 5aa1ae36..1584bec 100644
--- a/build/android/bytecode/BUILD.gn
+++ b/build/android/bytecode/BUILD.gn
@@ -14,8 +14,9 @@
     "java/org/chromium/bytecode/ClassPathValidator.java",
     "java/org/chromium/bytecode/CustomClassLoaderClassWriter.java",
     "java/org/chromium/bytecode/CustomResourcesClassAdapter.java",
-    "java/org/chromium/bytecode/TypeUtils.java",
+    "java/org/chromium/bytecode/SplitCompatClassAdapter.java",
     "java/org/chromium/bytecode/ThreadAssertionClassAdapter.java",
+    "java/org/chromium/bytecode/TypeUtils.java",
   ]
   main_class = "org.chromium.bytecode.ByteCodeProcessor"
   deps = [
diff --git a/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java b/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java
index 2b03b8ff..11474216 100644
--- a/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java
+++ b/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java
@@ -61,6 +61,7 @@
     private static ClassLoader sDirectClassPathClassLoader;
     private static ClassLoader sFullClassPathClassLoader;
     private static Set<String> sFullClassPathJarPaths;
+    private static Set<String> sSplitCompatClassNames;
     private static ClassPathValidator sValidator;
 
     private static class EntryDataPair {
@@ -129,6 +130,10 @@
             chain = new CustomResourcesClassAdapter(
                     chain, reader.getClassName(), reader.getSuperName(), sFullClassPathClassLoader);
         }
+        if (!sSplitCompatClassNames.isEmpty()) {
+            chain = new SplitCompatClassAdapter(
+                    chain, sSplitCompatClassNames, sFullClassPathClassLoader);
+        }
         reader.accept(chain, 0);
         byte[] patchedByteCode = writer.toByteArray();
         return EntryDataPair.create(entry.getName(), patchedByteCode);
@@ -235,6 +240,13 @@
         currIndex += directJarsLength;
         sDirectClassPathClassLoader = loadJars(directClassPathJarPaths);
 
+        // Load list of class names that need to be fixed.
+        int splitCompatClassNamesLength = Integer.parseInt(args[currIndex++]);
+        sSplitCompatClassNames = new HashSet<>();
+        sSplitCompatClassNames.addAll(Arrays.asList(
+                Arrays.copyOfRange(args, currIndex, currIndex + splitCompatClassNamesLength)));
+        currIndex += splitCompatClassNamesLength;
+
         // Load all jars that are on the classpath for the input jar for analyzing class hierarchy.
         sFullClassPathJarPaths = new HashSet<>();
         sFullClassPathJarPaths.clear();
diff --git a/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java b/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java
new file mode 100644
index 0000000..8d6ae69
--- /dev/null
+++ b/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java
@@ -0,0 +1,149 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.bytecode;
+
+import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.RETURN;
+
+import static org.chromium.bytecode.TypeUtils.CONTEXT;
+import static org.chromium.bytecode.TypeUtils.VOID;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Set;
+
+/**
+ * A ClassVisitor for injecting ModuleInstaller.initActivity(activity) method call
+ * into Activity's attachBaseContext() method. The goal is to eventually invoke
+ * SplitCompat.install() method if running with the binary that has bundle support
+ * enabled. This needs to happen for activities that were not built with SplitCompat
+ * support.
+ */
+class SplitCompatClassAdapter extends ClassVisitor {
+    private static final String ANDROID_APP_ACTIVITY_CLASS_NAME = "android/app/Activity";
+    private static final String ATTACH_BASE_CONTEXT_METHOD_NAME = "attachBaseContext";
+    private static final String ATTACH_BASE_CONTEXT_DESCRIPTOR =
+            TypeUtils.getMethodDescriptor(VOID, CONTEXT);
+
+    private static final String MODULE_INSTALLER_CLASS_NAME =
+            "org/chromium/components/module_installer/ModuleInstaller";
+    private static final String INIT_ACTIVITY_METHOD_NAME = "initActivity";
+    private static final String INIT_ACTIVITY_DESCRIPTOR =
+            TypeUtils.getMethodDescriptor(VOID, CONTEXT);
+
+    private boolean mShouldTransform;
+
+    private Set<String> mClassNames;
+
+    private ClassLoader mClassLoader;
+
+    /**
+     * Creates instance of SplitCompatClassAdapter.
+     *
+     * @param visitor
+     * @param classNames Names of classes into which the attachBaseContext method will be
+     *         injected. Currently, we'll only consider classes for bytecode rewriting only if
+     *         they inherit directly from android.app.Activity & not already contain
+     *         attachBaseContext method.
+     * @param classLoader
+     */
+    SplitCompatClassAdapter(ClassVisitor visitor, Set<String> classNames, ClassLoader classLoader) {
+        super(Opcodes.ASM5, visitor);
+
+        mShouldTransform = false;
+        mClassNames = classNames;
+        mClassLoader = classLoader;
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName,
+            String[] interfaces) {
+        super.visit(version, access, name, signature, superName, interfaces);
+
+        if (mClassNames.contains(name)) {
+            if (!isSubclassOfActivity(name)) {
+                throw new RuntimeException(name
+                        + " should be transformed but does not inherit from android.app.Activity");
+            }
+
+            mShouldTransform = true;
+        }
+    }
+
+    @Override
+    public MethodVisitor visitMethod(
+            int access, String name, String descriptor, String signature, String[] exceptions) {
+        // Check if current method matches attachBaseContext & we're supposed to emit code - if so,
+        // fail.
+        if (mShouldTransform && name.equals(ATTACH_BASE_CONTEXT_METHOD_NAME)) {
+            throw new RuntimeException(ATTACH_BASE_CONTEXT_METHOD_NAME + " method already exists");
+        }
+
+        return super.visitMethod(access, name, descriptor, signature, exceptions);
+    }
+
+    @Override
+    public void visitEnd() {
+        if (mShouldTransform) {
+            // If we reached this place, it means we're rewriting a class that inherits from
+            // Activity and there was no exception thrown due to existence of attachBaseContext
+            // method - emit code.
+            emitAttachBaseContext();
+        }
+
+        super.visitEnd();
+    }
+
+    /**
+     * Generates:
+     *
+     * <pre>
+     * protected void attachBaseContext(Context base) {
+     *     super.attachBaseContext(base);
+     *     ModuleInstaller.initActivity(this);
+     * }
+     * </pre>
+     */
+    private void emitAttachBaseContext() {
+        MethodVisitor mv = super.visitMethod(ACC_PROTECTED, ATTACH_BASE_CONTEXT_METHOD_NAME,
+                ATTACH_BASE_CONTEXT_DESCRIPTOR, null, null);
+        mv.visitCode();
+        mv.visitVarInsn(ALOAD, 0); // load "this" on stack
+        mv.visitVarInsn(ALOAD, 1); // load first method parameter on stack (Context)
+        mv.visitMethodInsn(INVOKESPECIAL, ANDROID_APP_ACTIVITY_CLASS_NAME,
+                ATTACH_BASE_CONTEXT_METHOD_NAME,
+                ATTACH_BASE_CONTEXT_DESCRIPTOR); // invoke super's attach base context
+        mv.visitVarInsn(ALOAD, 0); // load "this" on stack
+        mv.visitMethodInsn(INVOKESTATIC, MODULE_INSTALLER_CLASS_NAME, INIT_ACTIVITY_METHOD_NAME,
+                INIT_ACTIVITY_DESCRIPTOR);
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(2, 2); // max stack size - 2, max locals - 2
+        mv.visitEnd();
+    }
+
+    /**
+     * Checks whether passed in class inherits from android.app.Activity.
+     * @param name Name of the class to be checked.
+     * @return true if class inherits from android.app.Activity, false otherwise.
+     */
+    private boolean isSubclassOfActivity(String name) {
+        Class<?> activityClass = loadClass(ANDROID_APP_ACTIVITY_CLASS_NAME);
+        Class<?> candidateClass = loadClass(name);
+        return activityClass.isAssignableFrom(candidateClass);
+    }
+
+    private Class<?> loadClass(String className) {
+        try {
+            return mClassLoader.loadClass(className.replace('/', '.'));
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/build/android/gyp/bytecode_processor.py b/build/android/gyp/bytecode_processor.py
index 76775d3..020b52f 100755
--- a/build/android/gyp/bytecode_processor.py
+++ b/build/android/gyp/bytecode_processor.py
@@ -36,6 +36,11 @@
   _AddSwitch(parser, '--enable-assert')
   _AddSwitch(parser, '--enable-thread-annotations')
   _AddSwitch(parser, '--enable-check-class-path')
+  parser.add_argument(
+      '--split-compat-class-names',
+      action='append',
+      default=[],
+      help='Names of classes that need to be made SplitCompat-enabled.')
   args = parser.parse_args(argv)
 
   sdk_jars = build_utils.ParseGnList(args.sdk_classpath_jars)
@@ -48,6 +53,9 @@
   for a in args.extra_jars:
     extra_classpath_jars.extend(build_utils.ParseGnList(a))
 
+  split_compat_class_names = build_utils.ParseGnList(
+      args.split_compat_class_names)
+
   if args.verbose:
     verbose = '--verbose'
   else:
@@ -58,7 +66,9 @@
       args.enable_assert, args.enable_custom_resources,
       args.enable_thread_annotations, args.enable_check_class_path,
       str(len(sdk_jars))
-  ] + sdk_jars + [str(len(direct_jars))] + direct_jars + extra_classpath_jars)
+  ] + sdk_jars + [str(len(direct_jars))] + direct_jars + [
+      str(len(split_compat_class_names))
+  ] + split_compat_class_names + extra_classpath_jars)
   subprocess.check_call(cmd)
 
 
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py
index 20ffbaa..9c9e7d38 100755
--- a/build/android/gyp/javac.py
+++ b/build/android/gyp/javac.py
@@ -183,22 +183,6 @@
     shutil.copystat(jar_path, path)
 
 
-def _ConvertToJMakeArgs(javac_cmd, pdb_path):
-  new_args = ['bin/jmake', '-pdb', pdb_path, '-jcexec', javac_cmd[0]]
-  if md5_check.PRINT_EXPLANATIONS:
-    new_args.append('-Xtiming')
-
-  do_not_prefix = ('-classpath', '-bootclasspath')
-  skip_next = False
-  for arg in javac_cmd[1:]:
-    if not skip_next and arg not in do_not_prefix:
-      arg = '-C' + arg
-    new_args.append(arg)
-    skip_next = arg in do_not_prefix
-
-  return new_args
-
-
 def _ParsePackageAndClassNames(java_file):
   package_name = ''
   class_names = []
@@ -257,7 +241,7 @@
     assert not chromium_code or len(class_names) == 1, (
         'Chromium java files must only have one class: {}'.format(source))
     if chromium_code:
-      # This check is necessary to ensure JMake works.
+      # This check is not necessary but nice to check this somewhere.
       _CheckPathMatchesClassName(java_file, package_name, class_names[0])
 
 
@@ -307,11 +291,8 @@
   logging.info('Completed jar file: %s', jar_path)
 
 
-def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
-                classpath):
+def _OnStaleMd5(options, javac_cmd, java_files, classpath):
   logging.info('Starting _OnStaleMd5')
-  # Don't bother enabling incremental compilation for non-chromium code.
-  incremental = options.incremental and options.chromium_code
 
   # Compiles with Error Prone take twice as long to run as pure javac. Thus GN
   # rules run both in parallel, with Error Prone only used for checks.
@@ -323,42 +304,12 @@
     classes_dir = os.path.join(temp_dir, 'classes')
     os.makedirs(classes_dir)
 
-    changed_paths = None
-    # jmake can handle deleted files, but it's a rare case and it would
-    # complicate this script's logic.
-    if incremental and changes.AddedOrModifiedOnly():
-      changed_paths = set(changes.IterChangedPaths())
-      # Do a full compile if classpath has changed.
-      # jmake doesn't seem to do this on its own... Might be that ijars mess up
-      # its change-detection logic.
-      if any(p in changed_paths for p in classpath_inputs):
-        changed_paths = None
-
-    if options.incremental:
-      pdb_path = options.jar_path + '.pdb'
-
-    if incremental:
-      # jmake is a compiler wrapper that figures out the minimal set of .java
-      # files that need to be rebuilt given a set of .java files that have
-      # changed.
-      # jmake determines what files are stale based on timestamps between .java
-      # and .class files. Since we use .jars, .srcjars, and md5 checks,
-      # timestamp info isn't accurate for this purpose. Rather than use jmake's
-      # programatic interface (like we eventually should), we ensure that all
-      # .class files are newer than their .java files, and convey to jmake which
-      # sources are stale by having their .class files be missing entirely
-      # (by not extracting them).
-      javac_cmd = _ConvertToJMakeArgs(javac_cmd, pdb_path)
-
     if save_outputs:
       generated_java_dir = options.generated_dir
     else:
       generated_java_dir = os.path.join(temp_dir, 'gen')
 
-    # Incremental means not all files will be extracted, so don't bother
-    # clearing out stale generated files.
-    if not incremental:
-      shutil.rmtree(generated_java_dir, True)
+    shutil.rmtree(generated_java_dir, True)
 
     srcjar_files = {}
     if srcjars:
@@ -366,12 +317,8 @@
       build_utils.MakeDirectory(generated_java_dir)
       jar_srcs = []
       for srcjar in options.java_srcjars:
-        if changed_paths:
-          changed_paths.update(os.path.join(generated_java_dir, f)
-                               for f in changes.IterChangedSubpaths(srcjar))
         extracted_files = build_utils.ExtractAll(
-            srcjar, no_clobber=not incremental, path=generated_java_dir,
-            pattern='*.java')
+            srcjar, no_clobber=True, path=generated_java_dir, pattern='*.java')
         for path in extracted_files:
           # We want the path inside the srcjar so the viewer can have a tree
           # structure.
@@ -380,28 +327,8 @@
         jar_srcs.extend(extracted_files)
       logging.info('Done extracting srcjars')
       java_files.extend(jar_srcs)
-      if changed_paths:
-        # Set the mtime of all sources to 0 since we use the absence of .class
-        # files to tell jmake which files are stale.
-        for path in jar_srcs:
-          os.utime(path, (0, 0))
 
     if java_files:
-      if changed_paths:
-        changed_java_files = [p for p in java_files if p in changed_paths]
-        if os.path.exists(options.jar_path):
-          _ExtractClassFiles(options.jar_path, classes_dir, changed_java_files)
-        # Add the extracted files to the classpath. This is required because
-        # when compiling only a subset of files, classes that haven't changed
-        # need to be findable.
-        classpath.append(classes_dir)
-
-      # Can happen when a target goes from having no sources, to having sources.
-      # It's created by the call to build_utils.Touch() below.
-      if incremental:
-        if os.path.exists(pdb_path) and not os.path.getsize(pdb_path):
-          os.unlink(pdb_path)
-
       # Don't include the output directory in the initial set of args since it
       # being in a temp dir makes it unstable (breaks md5 stamping).
       cmd = javac_cmd + ['-d', classes_dir]
@@ -416,33 +343,14 @@
         f.write(' '.join(java_files))
       cmd += ['@' + java_files_rsp_path]
 
-      # JMake prints out some diagnostic logs that we want to ignore.
-      # This assumes that all compiler output goes through stderr.
-      stdout_filter = lambda s: ''
-      if md5_check.PRINT_EXPLANATIONS:
-        stdout_filter = None
-
       logging.debug('Build command %s', cmd)
-      attempt_build = lambda: build_utils.CheckOutput(
+      build_utils.CheckOutput(
           cmd,
           print_stdout=options.chromium_code,
-          stdout_filter=stdout_filter,
           stderr_filter=ProcessJavacOutput)
-      try:
-        attempt_build()
-      except build_utils.CalledProcessError as e:
-        # Work-around for a bug in jmake (http://crbug.com/551449).
-        if ('project database corrupted' not in e.output
-            and 'jmake: internal Java exception' not in e.output):
-          raise
-        logging.error(
-            'Applying work-around for jmake project database corrupted '
-            '(http://crbug.com/551449).')
-        os.unlink(pdb_path)
-        attempt_build()
       logging.info('Finished build command')
 
-    if options.incremental or save_outputs:
+    if save_outputs:
       # Creating the jar file takes the longest, start it first on a separate
       # process to unblock the rest of the post-processing steps.
       jar_file_worker = multiprocessing.Process(
@@ -460,10 +368,6 @@
     else:
       build_utils.Touch(options.jar_path + '.info')
 
-    if options.incremental and (not java_files or not incremental):
-      # Make sure output exists.
-      build_utils.Touch(pdb_path)
-
     if jar_file_worker:
       jar_file_worker.join()
     logging.info('Completed all steps in _OnStaleMd5')
@@ -500,11 +404,6 @@
       action='append',
       help='Classpath to use when no annotation processors are present.')
   parser.add_option(
-      '--incremental',
-      action='store_true',
-      help='Whether to re-use .class files rather than recompiling them '
-           '(when possible).')
-  parser.add_option(
       '--processors',
       action='append',
       help='GN list of annotation processor main classes.')
@@ -668,6 +567,7 @@
 
   classpath_inputs = (options.bootclasspath + options.interface_classpath +
                       options.processorpath)
+
   # GN already knows of java_files, so listing them just make things worse when
   # they change.
   depfile_deps = [javac_path] + classpath_inputs + options.java_srcjars
@@ -677,25 +577,16 @@
       options.jar_path,
       options.jar_path + '.info',
   ]
-  if options.incremental:
-    output_paths.append(options.jar_path + '.pdb')
-
-  # An escape hatch to be able to check if incremental compiles are causing
-  # problems.
-  force = int(os.environ.get('DISABLE_INCREMENTAL_JAVAC', 0))
 
   # List python deps in input_strings rather than input_paths since the contents
   # of them does not change what gets written to the depsfile.
   build_utils.CallAndWriteDepfileIfStale(
-      lambda changes: _OnStaleMd5(changes, options, javac_cmd, java_files,
-                                  classpath_inputs, classpath),
+      lambda: _OnStaleMd5(options, javac_cmd, java_files, classpath),
       options,
       depfile_deps=depfile_deps,
       input_paths=input_paths,
       input_strings=javac_cmd + classpath,
       output_paths=output_paths,
-      force=force,
-      pass_changes=True,
       add_pydeps=False)
   logging.info('Script complete: %s', __file__)
 
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 7050374..77b9b770 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -288,6 +288,9 @@
 For some Java related types, a list of extra `.jar` files to use at build time
 but not at runtime.
 
+* `deps_info['extra_classpath_interface_jars']:
+The interface jars corresponding to extra_classpath_jars.
+
 ## <a name="target_java_binary">Target type `java_binary`</a>:
 
 This type corresponds to a Java binary, which is nothing more than a
@@ -1219,6 +1222,7 @@
       # These are .jars to add to javac classpath but not to runtime classpath.
       extra_jars = build_utils.ParseGnList(options.extra_classpath_jars)
       deps_info['extra_classpath_jars'] = extra_jars
+      deps_info['extra_classpath_interface_jars'] = extra_jars
 
   if is_java_target:
     # The classpath used to compile this target when annotation processors are
@@ -1240,8 +1244,10 @@
     for dep in group_deps:
       javac_classpath.extend(dep.get('extra_classpath_jars', []))
       javac_full_classpath.extend(dep.get('extra_classpath_jars', []))
-      javac_interface_classpath.extend(dep.get('extra_classpath_jars', []))
-      javac_full_interface_classpath.extend(dep.get('extra_classpath_jars', []))
+      javac_interface_classpath.extend(
+          dep.get('extra_classpath_interface_jars', []))
+      javac_full_interface_classpath.extend(
+          dep.get('extra_classpath_interface_jars', []))
 
     # Deps to add to the compile-time classpath (but not the runtime classpath).
     # TODO(agrieve): Might be less confusing to fold these into bootclasspath.
@@ -1249,24 +1255,39 @@
                   for c in classpath_deps.Direct('java_library')]
     extra_jars = [c['jar_path']
                   for c in classpath_deps.Direct('java_library')]
+    interface_extra_jars = [
+        c['interface_jar_path'] for c in classpath_deps.Direct('java_library')
+    ]
 
+    # These are jars specified by input_jars_paths that almost never change.
+    # Just add them directly to all the *extra_jars.
     if options.extra_classpath_jars:
       # These are .jars to add to javac classpath but not to runtime classpath.
       javac_extra_jars.extend(
           build_utils.ParseGnList(options.extra_classpath_jars))
       extra_jars.extend(build_utils.ParseGnList(options.extra_classpath_jars))
+      interface_extra_jars.extend(
+          build_utils.ParseGnList(options.extra_classpath_jars))
 
     if extra_jars:
       deps_info['extra_classpath_jars'] = extra_jars
 
+    if interface_extra_jars:
+      deps_info['extra_classpath_interface_jars'] = interface_extra_jars
+
     javac_extra_jars = [p for p in javac_extra_jars if p not in javac_classpath]
     javac_classpath.extend(javac_extra_jars)
-    javac_interface_classpath.extend(javac_extra_jars)
-    javac_full_interface_classpath.extend(
-        p for p in javac_extra_jars if p not in javac_full_classpath)
     javac_full_classpath.extend(
         p for p in javac_extra_jars if p not in javac_full_classpath)
 
+    interface_extra_jars = [
+        p for p in interface_extra_jars if p not in javac_interface_classpath
+    ]
+    javac_interface_classpath.extend(interface_extra_jars)
+    javac_full_interface_classpath.extend(
+        p for p in interface_extra_jars
+        if p not in javac_full_interface_classpath)
+
   if is_java_target or options.type == 'android_app_bundle':
     # The classpath to use to run this target (or as an input to ProGuard).
     java_full_classpath = []
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 57d7e73..b4a8870 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -107,8 +107,6 @@
     <ignore regexp="tools/android/audio_focus_grabber/java/res/drawable-xxxhdpi/notification_icon.png"/>
   </issue>
   <issue id="IconDensities">
-    <!-- The large assets below only include a few densities to reduce APK size. -->
-    <ignore regexp="data_reduction_illustration.png"/>
     <!-- This is intentional to save on WebAPKs' size. -->
     <ignore regexp="chrome/android/webapk/shell_apk/res/drawable-*"/>
     <!-- crbug.com/457918 is tracking missing assets -->
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index f3001ac..59f6585 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -201,9 +201,6 @@
     # Required for Android M+ due to SELinux policies (stronger sandboxing).
     disable_incremental_isolated_processes = false
 
-    # Speeds up incremental compiles by compiling only changed files.
-    enable_incremental_javac = false
-
     # Build incremental targets whenever possible.
     # Ex. with this arg set to true, the chrome_public_apk target result in
     # chrome_public_apk_incremental being built.
@@ -252,11 +249,9 @@
     use_hashed_jni_names = !is_java_debug
   }
 
-  # Neither of these should ever be used for release builds since they are
-  # somewhat experimental and dx --incremental is known to not produce
-  # byte-for-byte identical output.
+  # This should not be used for release builds since dx --incremental is known
+  # to not produce byte-for-byte identical output.
   assert(!(enable_incremental_dx && !is_java_debug))
-  assert(!(enable_incremental_javac && !is_java_debug))
 
   # Path to where selected build variables are written to.
   android_build_vars = "$root_build_dir/build_vars.txt"
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index d7782ab..3b5b6dd 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1486,8 +1486,10 @@
 
     _desugar = defined(invoker.supports_android) && invoker.supports_android
     _emma_instrument = invoker.emma_instrument
+    _enable_split_compat = defined(invoker.split_compat_class_names)
     _enable_bytecode_rewriter =
-        _enable_assert || _enable_custom_resources || _enable_thread_annotations
+        _enable_assert || _enable_custom_resources ||
+        _enable_thread_annotations || _enable_split_compat
     _is_prebuilt = defined(invoker.is_prebuilt) && invoker.is_prebuilt
     _enable_bytecode_checks = !defined(invoker.enable_bytecode_checks) ||
                               invoker.enable_bytecode_checks
@@ -1570,6 +1572,10 @@
         if (_enable_bytecode_checks) {
           args += [ "--enable-check-class-path" ]
         }
+        if (_enable_split_compat) {
+          args += [ "--split-compat-class-names" ] +
+                  invoker.split_compat_class_names
+        }
         args += [
           "--direct-classpath-jars",
           "@FileArg($_rebased_build_config:javac:classpath)",
@@ -2756,10 +2762,6 @@
   #  additional_jar_files: Optional list of files to copy into the resulting
   #    .jar file (by default, only .class files are put there). Each entry
   #    has the 'srcPath:dstPath' format.
-  #  enable_incremental_javac_override: Optional. If provided, determines
-  #    whether incremental javac compilation (based on jmake) is enabled.
-  #    Otherwise, decision is based on the global enable_incremental_javac
-  #    build arg variable.
   #  enable_errorprone: Optional. If True, use the errorprone compiler to
   #    check for error-prone constructs in the language. If not provided,
   #    whether this is enabled depends on chromium_code and the global
@@ -2797,14 +2799,6 @@
       _additional_jar_files = invoker.additional_jar_files
     }
 
-    if (defined(invoker.enable_incremental_javac_override)) {
-      # Use invoker-specified override.
-      _enable_incremental_javac = invoker.enable_incremental_javac_override
-    } else {
-      # Default to build arg if not overridden.
-      _enable_incremental_javac = enable_incremental_javac
-    }
-
     _srcjar_deps = []
     if (defined(invoker.srcjar_deps)) {
       _srcjar_deps += invoker.srcjar_deps
@@ -2861,12 +2855,6 @@
       if (defined(invoker.srcjar_filearg)) {
         args += [ "--java-srcjars=${invoker.srcjar_filearg}" ]
       }
-      if (_enable_incremental_javac) {
-        args += [ "--incremental" ]
-        deps += [ "//third_party/jmake($default_toolchain)" ]
-        inputs += [ "$root_build_dir/bin/jmake" ]
-        outputs += [ "${invoker.javac_jar_path}.pdb" ]
-      }
       if (invoker.requires_android) {
         args += [ "--bootclasspath=@FileArg($_rebased_build_config:android:sdk_interface_jars)" ]
       }
@@ -3401,7 +3389,6 @@
       _compile_java_forward_variables = [
         "additional_jar_files",
         "apk_name",
-        "enable_incremental_javac_override",
         "processor_args_javac",
         "provider_configurations",
         "javac_args",
@@ -3493,6 +3480,7 @@
                                    "enable_bytecode_rewriter",
                                    "jar_excluded_patterns",
                                    "jar_included_patterns",
+                                   "split_compat_class_names",
                                  ])
           is_prebuilt = _is_prebuilt
           supports_android = _supports_android
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index bff3882..77596b9 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1383,8 +1383,6 @@
   #
   #   chromium_code: If true, extra analysis warning/errors will be enabled.
   #   enable_errorprone: If true, enables the errorprone compiler.
-  #   enable_incremental_javac_override: Overrides the global
-  #     enable_incremental_javac.
   #
   #   jar_excluded_patterns: List of patterns of .class files to exclude.
   #   jar_included_patterns: List of patterns of .class files to include.
@@ -3587,6 +3585,10 @@
   #   extract_native_libraries: Whether to extract .so files found in the .aar.
   #       If the file contains .so, either extract_native_libraries or
   #       ignore_native_libraries must be set.
+  #   split_compat_class_names: Names of the classes that will have their
+  #       bytecode rewritten to inject the call to SplitCompat.install().
+  #       Used to make dependencies compatible with SplitCompat to immediately
+  #       access resources brought in by the modules.
   #   create_srcjar: If false, does not create an R.java file.
   #   TODO(jbudorick@): remove this arguments after crbug.com/522043 is fixed.
   #   requires_android: Whether this target can only be used for compiling
@@ -3763,6 +3765,7 @@
                                  "jar_included_patterns",
                                  "proguard_configs",
                                  "requires_android",
+                                 "split_compat_class_names",
                                  "testonly",
                                ])
         if (!defined(deps)) {
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index 063b964..b0aaabb 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -1988,7 +1988,11 @@
       "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
     ]
 
-    bundle_deps = [
+    bundle_deps = []
+    if (defined(invoker.bundle_deps)) {
+      bundle_deps += invoker.bundle_deps
+    }
+    bundle_deps += [
       ":$_info_plist_bundle",
       ":$_pkginfo_bundle",
       ":$_xctest_bundle",
@@ -2044,6 +2048,7 @@
   ios_xcuitest_test_runner_bundle(_xcuitest_runner_target) {
     output_name = _xcuitest_runner_output
     xctest_bundle = _xcuitest_module_target + "_bundle"
+    forward_variables_from(invoker, [ "bundle_deps" ])
   }
 }
 
diff --git a/build/config/linux/gtk/BUILD.gn b/build/config/linux/gtk/BUILD.gn
index 6624d13..d78f740 100644
--- a/build/config/linux/gtk/BUILD.gn
+++ b/build/config/linux/gtk/BUILD.gn
@@ -32,7 +32,7 @@
     "//remoting/host/it2me:common",
     "//remoting/host/it2me:remote_assistance_host",
     "//remoting/host:common",
-    "//remoting/host/file_transfer:common",
+    "//remoting/host/file_transfer",
     "//remoting/host:remoting_me2me_host_static",
     "//remoting/test:it2me_standalone_host_main",
     "//webrtc/examples:peerconnection_client",
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index f8c9a30..43c6efe 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-81b54a7e2b2fff7f5dad246801c3c4b34486abdf
\ No newline at end of file
+a2ea5f5d6d67ba13062c98cf312f0887aea91cb3
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index eec3a7a4..d6437e4 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-f80285fb56fc829316c85929d34f5fbe9b15b4a9
\ No newline at end of file
+8f16de2e93bdcb2442be4935500102658c60a330
\ No newline at end of file
diff --git a/build/toolchain/goma.gni b/build/toolchain/goma.gni
index 0e1d815..2fbf572 100644
--- a/build/toolchain/goma.gni
+++ b/build/toolchain/goma.gni
@@ -27,6 +27,3 @@
     }
   }
 }
-
-assert(!(is_win && host_os != "win") || !use_goma,
-       "goma does not yet work in win cross builds, b/64390790")
diff --git a/build/util/lastchange.py b/build/util/lastchange.py
index bf38785..81c7431 100755
--- a/build/util/lastchange.py
+++ b/build/util/lastchange.py
@@ -7,23 +7,20 @@
 lastchange.py -- Chromium revision fetching utility.
 """
 
-import argparse
-import collections
+import re
 import logging
+import argparse
 import os
 import subprocess
 import sys
 
-VersionInfo = collections.namedtuple("VersionInfo",
-                                     ("revision_id", "revision", "timestamp"))
+class VersionInfo(object):
+  def __init__(self, revision_id, full_revision_string, timestamp):
+    self.revision_id = revision_id
+    self.revision = full_revision_string
+    self.timestamp = timestamp
 
-class GitError(Exception):
-  pass
 
-# This function exists for compatibility with logic outside this
-# repository that uses this file as a library.
-# TODO(eliribble) remove this function after it has been ported into
-# the repositories that depend on it
 def RunGitCommand(directory, command):
   """
   Launches git subcommand.
@@ -52,95 +49,53 @@
     return None
 
 
-def _RunGitCommand(directory, command):
-  """Launches git subcommand.
-
-  Returns:
-    The stripped stdout of the git command.
-  Raises:
-    GitError on failure, including a nonzero return code.
-  """
-  command = ['git'] + command
-  # Force shell usage under cygwin. This is a workaround for
-  # mysterious loss of cwd while invoking cygwin's git.
-  # We can't just pass shell=True to Popen, as under win32 this will
-  # cause CMD to be used, while we explicitly want a cygwin shell.
-  if sys.platform == 'cygwin':
-    command = ['sh', '-c', ' '.join(command)]
-  try:
-    logging.info("Executing '%s' in %s", ' '.join(command), directory)
-    proc = subprocess.Popen(command,
-                            stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE,
-                            cwd=directory,
-                            shell=(sys.platform=='win32'))
-    stdout, stderr = proc.communicate()
-    stdout = stdout.strip()
-    logging.debug("returncode: %d", proc.returncode)
-    logging.debug("stdout: %s", stdout)
-    logging.debug("stderr: %s", stderr)
-    if proc.returncode != 0 or not stdout:
-      raise GitError((
-          "Git command 'git {}' in {} failed: "
-          "rc={}, stdout='{}' stderr='{}'").format(
-          " ".join(command), directory, proc.returncode, stdout, stderr))
-    return stdout
-  except OSError as e:
-    raise GitError("Git command 'git {}' in {} failed: {}".format(
-        " ".join(command), directory, e))
-
-
-def GetMergeBase(directory, ref):
-  """
-  Return the merge-base of HEAD and ref.
-
-  Args:
-    directory: The directory containing the .git directory.
-    ref: The ref to use to find the merge base.
-  Returns:
-    The git commit SHA of the merge-base as a string.
-  """
-  logging.debug("Calculating merge base between HEAD and %s in %s",
-                ref, directory)
-  command = ['merge-base', 'HEAD', ref]
-  return _RunGitCommand(directory, command)
-
-
-def FetchGitRevision(directory, commit_filter, start_commit="HEAD"):
+def FetchGitRevision(directory, git_log_filter):
   """
   Fetch the Git hash (and Cr-Commit-Position if any) for a given directory.
 
+  Errors are swallowed.
+
   Args:
-    directory: The directory containing the .git directory.
-    commit_filter: A filter to supply to grep to filter commits
-    start_commit: A commit identifier. The result of this function
-      will be limited to only consider commits before the provided
-      commit.
+    git_log_filter: a string to be used for filtering git log result.
+
   Returns:
-    A VersionInfo object. On error all values will be 0.
+    A VersionInfo object or None on error.
   """
-  hash_ = ''
-
+  hsh = ''
   git_args = ['log', '-1', '--format=%H %ct']
-  if commit_filter is not None:
-    git_args.append('--grep=' + commit_filter)
+  if git_log_filter is not None:
+    git_args.append('--grep=' + git_log_filter)
+  proc = RunGitCommand(directory, git_args)
+  if proc:
+    output = proc.communicate()[0].strip()
+    if proc.returncode == 0 and output:
+      hsh, ct = output.split()
+    else:
+      logging.error('Git error: rc=%d, output=%r' %
+                    (proc.returncode, output))
+  if not hsh:
+    return None
+  pos = ''
+  proc = RunGitCommand(directory, ['cat-file', 'commit', hsh])
+  if proc:
+    output = proc.communicate()[0]
+    if proc.returncode == 0 and output:
+      for line in reversed(output.splitlines()):
+        if line.startswith('Cr-Commit-Position:'):
+          pos = line.rsplit()[-1].strip()
+          break
+  return VersionInfo(hsh, '%s-%s' % (hsh, pos), int(ct))
 
-  git_args.append(start_commit)
 
-  output = _RunGitCommand(directory, git_args)
-  hash_, commit_timestamp = output.split()
-  if not hash_:
-    return VersionInfo('0', '0', 0)
-
-  revision = hash_
-  output = _RunGitCommand(directory, ['cat-file', 'commit', hash_])
-  for line in reversed(output.splitlines()):
-    if line.startswith('Cr-Commit-Position:'):
-      pos = line.rsplit()[-1].strip()
-      logging.debug("Found Cr-Commit-Position '%s'", pos)
-      revision = "{}-{}".format(hash_, pos)
-      break
-  return VersionInfo(hash_, revision, int(commit_timestamp))
+def FetchVersionInfo(directory=None, git_log_filter=None):
+  """
+  Returns the last change (as a VersionInfo object)
+  from some appropriate revision control system.
+  """
+  version_info = FetchGitRevision(directory, git_log_filter)
+  if not version_info:
+    version_info = VersionInfo('0', '0', 0)
+  return version_info
 
 
 def GetHeaderGuard(path):
@@ -181,17 +136,6 @@
   return header_contents
 
 
-def GetGitTopDirectory(source_dir):
-  """Get the top git directory - the directory that contains the .git directory.
-
-  Args:
-    source_dir: The directory to search.
-  Returns:
-    The output of "git rev-parse --show-toplevel" as a string
-  """
-  return _RunGitCommand(source_dir, ['rev-parse', '--show-toplevel'])
-
-
 def WriteIfChanged(file_name, contents):
   """
   Writes the specified contents to the specified file_name
@@ -216,23 +160,20 @@
 
   parser = argparse.ArgumentParser(usage="lastchange.py [options]")
   parser.add_argument("-m", "--version-macro",
-                    help=("Name of C #define when using --header. Defaults to "
-                          "LAST_CHANGE."))
+                    help="Name of C #define when using --header. Defaults to " +
+                    "LAST_CHANGE.",
+                    default="LAST_CHANGE")
   parser.add_argument("-o", "--output", metavar="FILE",
-                    help=("Write last change to FILE. "
-                          "Can be combined with --header to write both files."))
+                    help="Write last change to FILE. " +
+                    "Can be combined with --header to write both files.")
   parser.add_argument("--header", metavar="FILE",
                     help=("Write last change to FILE as a C/C++ header. "
                           "Can be combined with --output to write both files."))
-  parser.add_argument("--merge-base-ref",
-                    default=None,
-                    help=("Only consider changes since the merge "
-                          "base between HEAD and the provided ref"))
   parser.add_argument("--revision-id-only", action='store_true',
                     help=("Output the revision as a VCS revision ID only (in "
                           "Git, a 40-character commit hash, excluding the "
                           "Cr-Commit-Position)."))
-  parser.add_argument("--print-only", action="store_true",
+  parser.add_argument("--print-only", action='store_true',
                     help=("Just print the revision string. Overrides any "
                           "file-output-related options."))
   parser.add_argument("-s", "--source-dir", metavar="DIR",
@@ -242,14 +183,13 @@
                           "matches the supplied filter regex. Defaults to "
                           "'^Change-Id:' to suppress local commits."),
                     default='^Change-Id:')
-
   args, extras = parser.parse_known_args(argv[1:])
 
   logging.basicConfig(level=logging.WARNING)
 
   out_file = args.output
   header = args.header
-  commit_filter=args.filter
+  git_log_filter=args.filter
 
   while len(extras) and out_file is None:
     if out_file is None:
@@ -259,37 +199,19 @@
     parser.print_help()
     sys.exit(2)
 
-  source_dir = args.source_dir or os.path.dirname(os.path.abspath(__file__))
-  try:
-    git_top_dir = GetGitTopDirectory(source_dir)
-  except GitError as e:
-    logging.error("Failed to get git top directory from '%s': %s",
-                  source_dir, e)
-    return 2
-
-  if args.merge_base_ref:
-    try:
-      merge_base_sha = GetMergeBase(git_top_dir, args.merge_base_ref)
-    except GitError as e:
-      logging.error("You requested a --merge-base-ref value of '%s' but no "
-                    "merge base could be found between it and HEAD. Git "
-                    "reports: %s", args.merge_base_ref, e)
-      return 3
+  if args.source_dir:
+    src_dir = args.source_dir
   else:
-    merge_base_sha = 'HEAD'
+    src_dir = os.path.dirname(os.path.abspath(__file__))
 
-  try:
-    version_info = FetchGitRevision(git_top_dir, commit_filter, merge_base_sha)
-  except GitError as e:
-    logging.error("Failed to get version info: %s")
-    return 1
-
+  version_info = FetchVersionInfo(directory=src_dir,
+                                  git_log_filter=git_log_filter)
   revision_string = version_info.revision
   if args.revision_id_only:
     revision_string = version_info.revision_id
 
   if args.print_only:
-    print(revision_string)
+    print revision_string
   else:
     contents = "LASTCHANGE=%s\n" % revision_string
     if not out_file and not args.header:
diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn
index 1d17b3f..b6af43e 100644
--- a/buildtools/third_party/libc++/BUILD.gn
+++ b/buildtools/third_party/libc++/BUILD.gn
@@ -123,7 +123,7 @@
       ]
     }
   }
-  if (is_asan || is_tsan || is_msan) {
+  if (!is_mac && (is_asan || is_tsan || is_msan)) {
     # In {a,t,m}san configurations, operator new and operator delete will be
     # provided by the sanitizer runtime library.  Since libc++ defines these
     # symbols with weak linkage, and the *san runtime uses strong linkage, it
diff --git a/cc/layers/scrollbar_layer_impl_base.cc b/cc/layers/scrollbar_layer_impl_base.cc
index 24a855e2f..768048fe 100644
--- a/cc/layers/scrollbar_layer_impl_base.cc
+++ b/cc/layers/scrollbar_layer_impl_base.cc
@@ -87,6 +87,12 @@
   const auto* scroll_node =
       property_trees->scroll_tree.FindNodeFromElementId(scroll_element_id_);
   DCHECK(scroll_node);
+  // TODO(bokan): Looks like we sometimes get here without a ScrollNode. It
+  // should be safe to just return false here (we don't use scroll_element_id_
+  // anywhere else) so we can merge the fix. Once merged, will investigate the
+  // underlying cause. https://crbug.com/924068.
+  if (!scroll_node)
+    return false;
 
   if (orientation() == ScrollbarOrientation::HORIZONTAL) {
     if (!scroll_node->user_scrollable_horizontal)
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc
index 7611dd0..afe7d8e 100644
--- a/cc/layers/surface_layer.cc
+++ b/cc/layers/surface_layer.cc
@@ -40,13 +40,14 @@
       deadline_policy.use_existing_deadline()) {
     return;
   }
-
-  TRACE_EVENT_WITH_FLOW2(
-      TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
-      "LocalSurfaceId.Embed.Flow",
-      TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()),
-      TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
-      "SetSurfaceId", "surface_id", surface_id.ToString());
+  if (surface_id.local_surface_id().is_valid()) {
+    TRACE_EVENT_WITH_FLOW2(
+        TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+        "LocalSurfaceId.Embed.Flow",
+        TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()),
+        TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+        "SetSurfaceId", "surface_id", surface_id.ToString());
+  }
 
   if (layer_tree_host() && surface_range_.IsValid())
     layer_tree_host()->RemoveSurfaceRange(surface_range_);
diff --git a/cc/layers/surface_layer_impl.cc b/cc/layers/surface_layer_impl.cc
index 3b411aa7..36c4dc3 100644
--- a/cc/layers/surface_layer_impl.cc
+++ b/cc/layers/surface_layer_impl.cc
@@ -43,7 +43,8 @@
     return;
   }
 
-  if (surface_range_.end() != surface_range.end()) {
+  if (surface_range_.end() != surface_range.end() &&
+      surface_range.end().local_surface_id().is_valid()) {
     TRACE_EVENT_WITH_FLOW2(
         TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
         "LocalSurfaceId.Embed.Flow",
@@ -54,7 +55,8 @@
   }
 
   if (surface_range.start() &&
-      surface_range_.start() != surface_range.start()) {
+      surface_range_.start() != surface_range.start() &&
+      surface_range.start()->local_surface_id().is_valid()) {
     TRACE_EVENT_WITH_FLOW2(
         TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
         "LocalSurfaceId.Submission.Flow",
diff --git a/cc/paint/draw_image.cc b/cc/paint/draw_image.cc
index 984ccd5c..f7d5cbe 100644
--- a/cc/paint/draw_image.cc
+++ b/cc/paint/draw_image.cc
@@ -28,6 +28,14 @@
       scale_(SkSize::Make(1.f, 1.f)),
       matrix_is_decomposable_(true) {}
 
+DrawImage::DrawImage(PaintImage image)
+    : paint_image_(std::move(image)),
+      src_rect_(
+          SkIRect::MakeXYWH(0, 0, paint_image_.width(), paint_image_.height())),
+      filter_quality_(kNone_SkFilterQuality),
+      scale_(SkSize::Make(1.f, 1.f)),
+      matrix_is_decomposable_(true) {}
+
 DrawImage::DrawImage(PaintImage image,
                      const SkIRect& src_rect,
                      SkFilterQuality filter_quality,
diff --git a/cc/paint/draw_image.h b/cc/paint/draw_image.h
index 615b25e..6365379 100644
--- a/cc/paint/draw_image.h
+++ b/cc/paint/draw_image.h
@@ -24,6 +24,7 @@
 class CC_PAINT_EXPORT DrawImage {
  public:
   DrawImage();
+  explicit DrawImage(PaintImage image);
   DrawImage(PaintImage image,
             const SkIRect& src_rect,
             SkFilterQuality filter_quality,
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 1a6d63a7..654bdee 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -1205,8 +1205,22 @@
                                   const PaintFlags* flags,
                                   SkCanvas* canvas,
                                   const PlaybackParams& params) {
+  // TODO(crbug.com/931704): make sure to support the case where paint worklet
+  // generated images are used in other raster work such as canvas2d.
   SkPaint paint = flags ? flags->ToSkPaint() : SkPaint();
 
+  if (op->image.IsPaintWorklet()) {
+    DCHECK(params.image_provider);
+    ImageProvider::ScopedResult result =
+        params.image_provider->GetRasterContent(DrawImage(op->image));
+
+    DCHECK(IsScaleAdjustmentIdentity(op->scale_adjustment));
+    SkAutoCanvasRestore save_restore(canvas, true);
+    canvas->translate(op->left, op->top);
+    result.paint_record()->Playback(canvas, params);
+    return;
+  }
+
   if (!params.image_provider) {
     const bool needs_scale = !IsScaleAdjustmentIdentity(op->scale_adjustment);
     SkAutoCanvasRestore save_restore(canvas, needs_scale);
@@ -1245,6 +1259,7 @@
   canvas->drawImage(decoded_image.image().get(), op->left, op->top, &paint);
 }
 
+// TODO(xidachen): ensure paint worklet generated images are correctly handled.
 void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op,
                                       const PaintFlags* flags,
                                       SkCanvas* canvas,
@@ -2393,8 +2408,6 @@
   PlaybackParams new_params(params.image_provider, canvas->getTotalMatrix(),
                             params.custom_callback,
                             params.did_draw_op_callback);
-  // TODO(xidachen): retrieve the PaintRecord stored in PaintWorkletImageCache,
-  // from the PaintWorkletImageProvider in the params.
   for (PlaybackFoldingIterator iter(this, offsets); iter; ++iter) {
     const PaintOp* op = *iter;
 
diff --git a/cc/paint/paint_op_buffer_serializer.cc b/cc/paint/paint_op_buffer_serializer.cc
index f7f7f4f..fe4028b 100644
--- a/cc/paint/paint_op_buffer_serializer.cc
+++ b/cc/paint/paint_op_buffer_serializer.cc
@@ -84,9 +84,9 @@
           context_supports_distance_field_text),
       text_blob_canvas_(kMaxExtent,
                         kMaxExtent,
-                        SkMatrix::I(),
                         ComputeSurfaceProps(can_use_lcd_text),
                         strike_server,
+                        nullptr,  // colorspace
                         MakeCanvasSettings(context_supports_distance_field_text,
                                            max_texture_size,
                                            max_texture_bytes)) {
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 22a3bbbc..e6204aaf 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -20,6 +20,7 @@
 #include "cc/test/paint_op_helper.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_options_provider.h"
+#include "cc/test/test_paint_worklet_input.h"
 #include "cc/test/test_skcanvas.h"
 #include "cc/test/transfer_cache_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -429,6 +430,15 @@
   EXPECT_TRUE(buffer.HasDiscardableImages());
 }
 
+TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImage) {
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f));
+  PaintOpBuffer buffer;
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0), nullptr);
+  EXPECT_TRUE(buffer.HasDiscardableImages());
+}
+
 TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) {
   PaintOpBuffer buffer;
   PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
@@ -2711,9 +2721,16 @@
 
   ~MockImageProvider() override = default;
 
+  void DoNothing() {}
+
   ImageProvider::ScopedResult GetRasterContent(
       const DrawImage& draw_image) override {
-    DCHECK(!draw_image.paint_image().IsPaintWorklet());
+    if (draw_image.paint_image().IsPaintWorklet()) {
+      auto callback =
+          base::BindOnce(&MockImageProvider::DoNothing, base::Unretained(this));
+      return ScopedResult(record_, std::move(callback));
+    }
+
     if (fail_all_decodes_)
       return ImageProvider::ScopedResult();
 
@@ -2726,12 +2743,15 @@
                                          quality_[i], true));
   }
 
+  void SetRecord(PaintRecord* record) { record_ = record; }
+
  private:
   std::vector<SkSize> src_rect_offset_;
   std::vector<SkSize> scale_;
   std::vector<SkFilterQuality> quality_;
   size_t index_ = 0;
   bool fail_all_decodes_ = false;
+  PaintRecord* record_;
 };
 
 TEST(PaintOpBufferTest, SkipsOpsOutsideClip) {
@@ -2827,6 +2847,78 @@
   return true;
 }
 
+TEST(PaintOpBufferTest, RasterPaintWorkletImage1) {
+  PaintOpBuffer paint_worklet_buffer;
+  PaintFlags noop_flags;
+  SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 100, 100);
+  paint_worklet_buffer.push<TranslateOp>(8.0f, 8.0f);
+  paint_worklet_buffer.push<SaveLayerOp>(&savelayer_rect, &noop_flags);
+  PaintFlags draw_flags;
+  draw_flags.setColor(0u);
+  SkRect rect = SkRect::MakeXYWH(0, 0, 100, 100);
+  paint_worklet_buffer.push<DrawRectOp>(rect, draw_flags);
+
+  MockImageProvider provider;
+  provider.SetRecord(&paint_worklet_buffer);
+
+  PaintOpBuffer blink_buffer;
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  blink_buffer.push<DrawImageOp>(image, 0.0f, 0.0f, nullptr);
+
+  testing::StrictMock<MockCanvas> canvas;
+  testing::Sequence s;
+
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(SkMatrix::MakeTrans(8.0f, 8.0f)));
+  EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
+  EXPECT_CALL(canvas, OnDrawRectWithColor(0u));
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+
+  blink_buffer.Playback(&canvas, PlaybackParams(&provider));
+}
+
+TEST(PaintOpBufferTest, RasterPaintWorkletImage2) {
+  PaintOpBuffer paint_worklet_buffer;
+  PaintFlags noop_flags;
+  SkRect savelayer_rect = SkRect::MakeXYWH(0, 0, 10, 10);
+  paint_worklet_buffer.push<SaveLayerOp>(&savelayer_rect, &noop_flags);
+  PaintFlags draw_flags;
+  draw_flags.setFilterQuality(kLow_SkFilterQuality);
+  PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
+  paint_worklet_buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, &draw_flags);
+
+  std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()};
+  std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)};
+  std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality};
+  MockImageProvider provider(src_rect_offset, scale_adjustment, quality);
+  provider.SetRecord(&paint_worklet_buffer);
+
+  PaintOpBuffer blink_buffer;
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  blink_buffer.push<DrawImageOp>(image, 5.0f, 7.0f, nullptr);
+
+  testing::StrictMock<MockCanvas> canvas;
+  testing::Sequence s;
+
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(SkMatrix::MakeTrans(5.0f, 7.0f)));
+  EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
+  EXPECT_CALL(canvas, willSave()).InSequence(s);
+  EXPECT_CALL(canvas, didConcat(MatchesInvScale(scale_adjustment[0])));
+  EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f,
+                                  MatchesQuality(quality[0])));
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+  EXPECT_CALL(canvas, willRestore()).InSequence(s);
+
+  blink_buffer.Playback(&canvas, PlaybackParams(&provider));
+}
+
 TEST(PaintOpBufferTest, ReplacesImagesFromProvider) {
   std::vector<SkSize> src_rect_offset = {
       SkSize::MakeEmpty(), SkSize::Make(2.0f, 2.0f), SkSize::Make(3.0f, 3.0f)};
diff --git a/cc/tiles/paint_worklet_image_cache.cc b/cc/tiles/paint_worklet_image_cache.cc
index 9565fb3..f9d9d42 100644
--- a/cc/tiles/paint_worklet_image_cache.cc
+++ b/cc/tiles/paint_worklet_image_cache.cc
@@ -49,6 +49,8 @@
 // Do check the cache first. If there is already a cache entry for this input,
 // then there is no need to call the Paint() function.
 void PaintWorkletImageCache::PaintImageInTask(const PaintImage& paint_image) {
+  // TODO(xidachen): ensure that the canvas operations in the PaintRecord
+  // matches the PaintGeneratedImage::Draw.
   sk_sp<PaintRecord> record = painter_->Paint();
   records_[paint_image.paint_worklet_input()] =
       PaintWorkletImageCacheValue(std::move(record), 0);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 1f18015..a1c0c47 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -147,8 +147,8 @@
   enum MainOrder : int {
     MAIN_START = 1,
     MAIN_LAYOUT,
-    MAIN_COMMIT_COMPLETE,
     MAIN_DID_BEGIN_FRAME,
+    MAIN_COMMIT_COMPLETE,
     MAIN_END,
   };
 
diff --git a/cc/trees/layer_tree_mutator.h b/cc/trees/layer_tree_mutator.h
index c3d6fbe..696b95c 100644
--- a/cc/trees/layer_tree_mutator.h
+++ b/cc/trees/layer_tree_mutator.h
@@ -18,6 +18,17 @@
 
 namespace cc {
 
+enum class MutateQueuingStrategy {
+  kDrop,            // Discard request if busy.
+  kQueueAndReplace  // Queue request if busy replacing previously queued
+                    // request.
+};
+
+enum class MutateStatus {
+  kCompleted,  // Mutation cycle successfully ran to completion.
+  kCanceled    // Mutation cycle dropped from the input queue.
+};
+
 struct CC_EXPORT WorkletAnimationId {
   // Uniquely identifies the animation worklet with which this animation is
   // associated.
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index e0683e5..7218721 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -236,6 +236,8 @@
   skip_commit |= defer_main_frame_update_ || defer_commits_;
 
   if (skip_commit) {
+    current_pipeline_stage_ = NO_PIPELINE_STAGE;
+    layer_tree_host_->DidBeginMainFrame();
     TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferCommit_InsideBeginMainFrame",
                          TRACE_EVENT_SCOPE_THREAD);
     layer_tree_host_->RecordEndOfFrameMetrics(begin_main_frame_start_time);
@@ -246,10 +248,8 @@
                                   CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT,
                                   begin_main_frame_start_time,
                                   base::Passed(&empty_swap_promises)));
-    current_pipeline_stage_ = NO_PIPELINE_STAGE;
     // We intentionally don't report CommitComplete() here since it was aborted
     // prematurely and we're waiting to do another commit in the future.
-    layer_tree_host_->DidBeginMainFrame();
     // When we stop deferring commits, we should resume any previously requested
     // pipeline stages.
     deferred_final_pipeline_stage_ = final_pipeline_stage_;
@@ -284,6 +284,8 @@
 
   current_pipeline_stage_ = COMMIT_PIPELINE_STAGE;
   if (final_pipeline_stage_ < COMMIT_PIPELINE_STAGE) {
+    current_pipeline_stage_ = NO_PIPELINE_STAGE;
+    layer_tree_host_->DidBeginMainFrame();
     TRACE_EVENT_INSTANT0("cc", "EarlyOut_NoUpdates", TRACE_EVENT_SCOPE_THREAD);
     layer_tree_host_->RecordEndOfFrameMetrics(begin_main_frame_start_time);
     std::vector<std::unique_ptr<SwapPromise>> swap_promises =
@@ -298,9 +300,7 @@
     // Although the commit is internally aborted, this is because it has been
     // detected to be a no-op.  From the perspective of an embedder, this commit
     // went through, and input should no longer be throttled, etc.
-    current_pipeline_stage_ = NO_PIPELINE_STAGE;
     layer_tree_host_->CommitComplete();
-    layer_tree_host_->DidBeginMainFrame();
     return;
   }
 
@@ -314,6 +314,9 @@
   layer_tree_host_->QueueSwapPromise(
       std::make_unique<LatencyInfoSwapPromise>(new_latency_info));
 
+  current_pipeline_stage_ = NO_PIPELINE_STAGE;
+  layer_tree_host_->DidBeginMainFrame();
+
   // Notify the impl thread that the main thread is ready to commit. This will
   // begin the commit process, which is blocking from the main thread's
   // point of view, but asynchronously performed on the impl thread,
@@ -336,9 +339,7 @@
     completion.Wait();
   }
 
-  current_pipeline_stage_ = NO_PIPELINE_STAGE;
   layer_tree_host_->CommitComplete();
-  layer_tree_host_->DidBeginMainFrame();
 }
 
 void ProxyMain::DidPresentCompositorFrame(
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 874079d..056cfe70 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -231,8 +231,8 @@
       << "Activation is expected to have synchronously occurred by now.";
 
   DebugScopedSetMainThread main(task_runner_provider_);
-  layer_tree_host_->CommitComplete();
   layer_tree_host_->DidBeginMainFrame();
+  layer_tree_host_->CommitComplete();
 
   next_frame_is_newly_committed_frame_ = true;
 }
diff --git a/chrome/android/java/res/drawable-mdpi/data_reduction_illustration.png b/chrome/android/java/res/drawable-mdpi/data_reduction_illustration.png
deleted file mode 100644
index 1e04158..0000000
--- a/chrome/android/java/res/drawable-mdpi/data_reduction_illustration.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/data_reduction_illustration.png b/chrome/android/java/res/drawable-xxhdpi/data_reduction_illustration.png
deleted file mode 100644
index 5261aef7..0000000
--- a/chrome/android/java/res/drawable-xxhdpi/data_reduction_illustration.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable/data_reduction_illustration.xml b/chrome/android/java/res/drawable/data_reduction_illustration.xml
new file mode 100644
index 0000000..ec8e23f6
--- /dev/null
+++ b/chrome/android/java/res/drawable/data_reduction_illustration.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!-- VectorRaster warning is ignored because we intentionally want the width to
+     be greater than 200. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    tools:ignore="VectorRaster"
+    android:width="232dp"
+    android:height="107dp"
+    android:viewportWidth="232"
+    android:viewportHeight="107">
+  <path
+      android:pathData="M156.91,3.733l18.155,31.59l-90.126,-1.508l12.968,-31.662z"
+      android:strokeWidth="1"
+      android:fillColor="#F1F3F4"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M227.729,52.338L225.928,52.338C224.703,52.338 223.694,51.333 223.694,50.113L223.694,44.369C223.694,43.149 224.703,42.144 225.928,42.144L227.729,42.144C228.954,42.144 229.962,43.149 229.962,44.369L229.962,50.113C230.034,51.333 229.026,52.338 227.729,52.338Z"
+      android:strokeWidth="1"
+      android:fillColor="#E1741F"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M115.125,1.149h2.089v39.056h-2.089z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M49.638,81.128L205.612,81.128C217.283,81.128 226.72,71.723 226.72,60.092L226.72,37.836C226.72,35.467 224.775,33.528 222.398,33.528L32.419,33.528C30.186,33.528 28.313,35.323 28.313,37.621L28.313,59.949C28.313,71.651 37.895,81.128 49.638,81.128Z"
+      android:strokeWidth="1"
+      android:fillColor="#F9BB2D"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M74.764,106.115a25.924,22.906 104.033,1 0,12.573 -50.301a25.924,22.906 104.033,1 0,-12.573 50.301z"
+      android:strokeWidth="1"
+      android:fillColor="#3C4043"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M78.334,91.832a11.203,9.94 104.033,1 0,5.433 -21.737a11.203,9.94 104.033,1 0,-5.433 21.737z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M175.629,106.111a25.924,22.906 104.033,1 0,12.573 -50.301a25.924,22.906 104.033,1 0,-12.573 50.301z"
+      android:strokeWidth="1"
+      android:fillColor="#3C4043"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M179.199,91.828a11.203,9.94 104.033,1 0,5.433 -21.737a11.203,9.94 104.033,1 0,-5.433 21.737z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M227.801,77.682L223.118,77.682C221.101,77.682 219.444,76.031 219.444,74.021L219.444,71.149C219.444,69.138 221.101,67.487 223.118,67.487L227.801,67.487C229.818,67.487 231.475,69.138 231.475,71.149L231.475,74.021C231.475,76.031 229.818,77.682 227.801,77.682Z"
+      android:strokeWidth="1"
+      android:fillColor="#BDC1C6"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M38.975,77.682L26.728,77.682C24.567,77.682 22.766,75.887 22.766,73.733L22.766,71.364C22.766,69.21 24.567,67.415 26.728,67.415L38.903,67.415C41.065,67.415 42.866,69.21 42.866,71.364L42.866,73.733C42.938,75.959 41.137,77.682 38.975,77.682Z"
+      android:strokeWidth="1"
+      android:fillColor="#BDC1C6"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M79.752,12.851L14.697,12.851C12.752,12.851 11.239,11.272 11.239,9.405L11.239,9.405C11.239,7.467 12.824,5.959 14.697,5.959L79.752,5.959C81.697,5.959 83.21,7.538 83.21,9.405L83.21,9.405C83.21,11.272 81.697,12.851 79.752,12.851Z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M12.608,30.872L3.962,30.872C2.017,30.872 0.504,29.292 0.504,27.426L0.504,27.426C0.504,25.487 2.089,23.979 3.962,23.979L12.608,23.979C14.553,23.979 16.066,25.559 16.066,27.426L16.066,27.426C16.138,29.292 14.553,30.872 12.608,30.872Z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M37.967,48.821L12.103,48.821C10.158,48.821 8.645,47.241 8.645,45.374L8.645,45.374C8.645,43.436 10.23,41.928 12.103,41.928L37.967,41.928C39.912,41.928 41.425,43.508 41.425,45.374L41.425,45.374C41.497,47.313 39.912,48.821 37.967,48.821Z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M98.267,48.821L52.664,48.821C50.718,48.821 49.206,47.241 49.206,45.374L49.206,45.374C49.206,43.436 50.791,41.928 52.664,41.928L98.267,41.928C100.212,41.928 101.725,43.508 101.725,45.374L101.725,45.374C101.725,47.313 100.212,48.821 98.267,48.821Z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M47.765,35.897C47.188,35.897 46.9,35.395 47.188,34.964L72.115,3.949C74.133,1.436 77.158,-0 80.328,-0L157.198,-0C158.207,-0 159,0.862 159,1.867L159,2.154C159,3.159 158.207,4.021 157.198,4.021L105.039,4.021C102.374,4.021 99.996,5.528 98.771,7.897L84.939,34.892C84.651,35.467 83.93,35.897 83.138,35.897L47.765,35.897L47.765,35.897Z"
+      android:strokeWidth="1"
+      android:fillColor="#3C4043"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M96.394,30.872L26.584,30.872C24.639,30.872 23.126,29.292 23.126,27.426L23.126,27.426C23.126,25.487 24.711,23.979 26.584,23.979L96.394,23.979C98.339,23.979 99.852,25.559 99.852,27.426L99.852,27.426C99.924,29.292 98.339,30.872 96.394,30.872Z"
+      android:strokeWidth="1"
+      android:fillColor="#DADCE0"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+</vector>
diff --git a/chrome/android/java/res/layout/bottom_toolbar.xml b/chrome/android/java/res/layout/bottom_toolbar.xml
index a8e3ee1..cf50148 100644
--- a/chrome/android/java/res/layout/bottom_toolbar.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar.xml
@@ -15,7 +15,6 @@
       android:layout_width="match_parent"
       android:layout_height="@dimen/bottom_toolbar_height_with_shadow"
       android:inflatedId="@+id/bottom_toolbar_tab_switcher_mode"
-      android:layout="@layout/bottom_toolbar_tab_switcher"
-      android:layout_gravity="bottom" />
+      android:layout="@layout/bottom_toolbar_tab_switcher" />
 
 </FrameLayout>
diff --git a/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml b/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
index d541959..80f8993 100644
--- a/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
@@ -3,11 +3,12 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<FrameLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/bottom_toolbar_tab_switcher"
+    android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:visibility="gone"
@@ -29,8 +30,7 @@
         android:layout_height="@dimen/bottom_toolbar_height"
         android:background="@color/modern_primary_color"
         android:paddingStart="@dimen/bottom_toolbar_start_padding"
-        android:paddingEnd="@dimen/bottom_toolbar_end_padding"
-        android:layout_marginTop="@dimen/toolbar_shadow_height" >
+        android:paddingEnd="@dimen/bottom_toolbar_end_padding" >
 
       <org.chromium.chrome.browser.toolbar.bottom.CloseAllTabsButton
           android:id="@+id/close_all_tabs_button"
@@ -56,4 +56,13 @@
 
     </LinearLayout>
 
-</FrameLayout>
+    <ImageView
+        android:id="@+id/bottom_toolbar_bottom_shadow"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/toolbar_shadow_height"
+        android:src="@drawable/modern_toolbar_shadow"
+        android:scaleType="fitXY"
+        android:visibility="gone"
+        tools:ignore="ContentDescription" />
+
+</LinearLayout>
diff --git a/chrome/android/java/res/layout/fre_data_reduction_proxy.xml b/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
index a8a61d57..7629239 100644
--- a/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
+++ b/chrome/android/java/res/layout/fre_data_reduction_proxy.xml
@@ -6,6 +6,7 @@
 -->
 <org.chromium.chrome.browser.firstrun.FirstRunView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -27,7 +28,7 @@
                 android:id="@+id/title"
                 android:text="@string/data_reduction_promo_title"
                 style="@style/FreTitle"/>
-            
+
             <!-- The orientation of this view is changed dynamically to give a nicer layout when in
             landscape mode on devices with small screens. -->
             <LinearLayout
@@ -43,7 +44,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="@dimen/fre_image_height"
                     tools:ignore="ContentDescription"
-                    android:src="@drawable/data_reduction_illustration" />
+                    app:srcCompat="@drawable/data_reduction_illustration" />
 
                 <LinearLayout
                     android:id="@+id/fre_content_wrapper"
@@ -89,4 +90,4 @@
         android:paddingEnd="@dimen/fre_button_padding"
         android:text="@string/next"
         style="@style/FilledButton.Flat" />
-</org.chromium.chrome.browser.firstrun.FirstRunView>
\ No newline at end of file
+</org.chromium.chrome.browser.firstrun.FirstRunView>
diff --git a/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml b/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
index f2bbbe1..16abb25 100644
--- a/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
+++ b/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
@@ -6,6 +6,7 @@
 -->
 <org.chromium.chrome.browser.firstrun.FirstRunView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -43,7 +44,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="@dimen/fre_image_height"
                     tools:ignore="ContentDescription"
-                    android:src="@drawable/data_reduction_illustration" />
+                    app:srcCompat="@drawable/data_reduction_illustration" />
 
                 <LinearLayout
                     android:id="@+id/fre_content_wrapper"
diff --git a/chrome/android/java/res/layout/omnibox_answer_suggestion.xml b/chrome/android/java/res/layout/omnibox_answer_suggestion.xml
index 1929e04..537fb5ba 100644
--- a/chrome/android/java/res/layout/omnibox_answer_suggestion.xml
+++ b/chrome/android/java/res/layout/omnibox_answer_suggestion.xml
@@ -17,6 +17,7 @@
         android:id="@+id/omnibox_answer"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
+        android:paddingVertical="10dp"
         android:layout_height="wrap_content"
         android:layout_toStartOf="@+id/omnibox_answer_refine_icon"
         android:layout_width="0dp">
@@ -25,7 +26,7 @@
             android:contentDescription="@null"
             android:id="@+id/omnibox_answer_icon"
             android:layout_centerVertical="true"
-            android:layout_height="wrap_content"
+            android:layout_height="36dp"
             android:layout_marginEnd="10dp"
             android:layout_marginStart="@dimen/omnibox_answer_suggestion_icon_margin_start"
             android:layout_width="36dp"
diff --git a/chrome/android/java/res/layout/tab_grid_card_item.xml b/chrome/android/java/res/layout/tab_grid_card_item.xml
index fe1a2e6..74847a2 100644
--- a/chrome/android/java/res/layout/tab_grid_card_item.xml
+++ b/chrome/android/java/res/layout/tab_grid_card_item.xml
@@ -2,58 +2,58 @@
 <!-- Copyright 2019 The Chromium Authors. All rights reserved.
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
-<!-- TODO(crbug/928388): Spec issues with this layout: Handle elevation for KitKat. Touch target
-     for close button is too small. Text height needs to be verified for XL text while we are using
-     FrameLayout as parent -->
+<!-- TODO(crbug/928388): Handle elevation for KitKat.-->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="200dp"
-    android:background="@drawable/tab_grid_card_background"
-    android:elevation="4dp"
-    android:layout_margin="8dp">
-        <ImageView
-            android:id="@+id/tab_favicon"
-            android:layout_width="@dimen/tab_grid_favicon_size"
-            android:layout_height="@dimen/tab_grid_favicon_size"
-            android:padding="8dp"
-            android:importantForAccessibility="no"
-            android:src="@drawable/ic_omnibox_page"/>
-
-        <TextView
-            android:id="@+id/tab_title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:minHeight="32dp"
-            android:layout_marginEnd="32dp"
-            android:layout_marginStart="32dp"
-            android:ellipsize="end"
-            android:maxLines="1"
-            android:paddingBottom="6dp"
-            android:paddingTop="6dp"
-            android:textAppearance="@style/TextAppearance.BlackTitle2"/>
-        <ImageView
-            android:id="@+id/close_button"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:scaleType="center"
-            android:layout_gravity="end"
-            android:contentDescription="@string/accessibility_tabstrip_btn_close_tab"
-            android:src="@drawable/btn_delete_24dp"/>
-
-        <org.chromium.ui.widget.RoundedCornerImageView
-            android:id="@+id/tab_thumbnail"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_marginTop="32dp"
-            android:gravity="center_horizontal"
-            android:scaleType="centerCrop"
-            android:importantForAccessibility="no"
-            android:src="@color/thumbnail_placeholder_on_primary_bg"
-            app:cornerRadiusBottomStart="@dimen/default_card_corner_radius"
-            app:cornerRadiusBottomEnd="@dimen/default_card_corner_radius"/>
-
-        <View
-            style="@style/HorizontalDivider"
-            android:layout_marginTop="32dp"/>
+    android:layout_height="216dp">
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:elevation="4dp"
+        android:background="@drawable/tab_grid_card_background"
+        android:layout_margin="8dp">
+            <ImageView
+                android:id="@+id/tab_favicon"
+                android:layout_width="@dimen/tab_grid_favicon_size"
+                android:layout_height="@dimen/tab_grid_favicon_size"
+                android:padding="8dp"
+                android:importantForAccessibility="no"
+                android:src="@drawable/ic_omnibox_page"/>
+            <TextView
+                android:id="@+id/tab_title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/tab_favicon"
+                android:layout_marginEnd="32dp"
+                android:minHeight="32dp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:paddingBottom="6dp"
+                android:paddingTop="6dp"
+                android:textAppearance="@style/TextAppearance.BlackTitle2"/>
+            <org.chromium.ui.widget.RoundedCornerImageView
+                android:id="@+id/tab_thumbnail"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_below="@id/tab_title"
+                android:gravity="center_horizontal"
+                android:scaleType="centerCrop"
+                android:importantForAccessibility="no"
+                android:src="@color/thumbnail_placeholder_on_primary_bg"
+                app:cornerRadiusBottomStart="@dimen/default_card_corner_radius"
+                app:cornerRadiusBottomEnd="@dimen/default_card_corner_radius"/>
+            <View
+                style="@style/HorizontalDivider"
+                android:layout_below="@id/tab_title"/>
+    </RelativeLayout>
+    <ImageView
+        android:id="@+id/close_button"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:elevation="4dp"
+        android:scaleType="center"
+        android:layout_gravity="end"
+        android:contentDescription="@string/accessibility_tabstrip_btn_close_tab"
+        android:src="@drawable/btn_delete_24dp"/>
 </FrameLayout>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index eaa47a0..1ec0790 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -593,4 +593,5 @@
     <dimen name="autofill_assistant_details_image_size">48dp</dimen>
 
     <dimen name="tab_grid_favicon_size">32dp</dimen>
+    <dimen name="tab_list_selected_inset">5dp</dimen>
 </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 7eb573c2..be80ee5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -322,7 +322,7 @@
     private ActivityTabProvider mActivityTabProvider = new ActivityTabProvider();
 
     /** A means of providing the theme color to different features. */
-    private final TabThemeColorProvider mTabThemeColorProvider = new TabThemeColorProvider();
+    private TabThemeColorProvider mTabThemeColorProvider;
 
     /** Whether or not the activity is in started state. */
     private boolean mStarted;
@@ -673,6 +673,7 @@
 
         mTabModelSelector = createTabModelSelector();
         mActivityTabProvider.setTabModelSelector(mTabModelSelector);
+        mTabThemeColorProvider = new TabThemeColorProvider(this);
         mTabThemeColorProvider.setActivityTabProvider(mActivityTabProvider);
 
         if (mTabModelSelector == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 018fb8b..72f6513 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -187,6 +187,7 @@
     public static final String CCT_REPORT_PARALLEL_REQUEST_STATUS =
             "CCTReportParallelRequestStatus";
     public static final String CHROME_DUET = "ChromeDuet";
+    public static final String CHROME_DUET_ADAPTIVE = "ChromeDuetAdaptive";
     public static final String CHROME_SMART_SELECTION = "ChromeSmartSelection";
     public static final String CLEAR_OLD_BROWSING_DATA = "ClearOldBrowsingData";
     public static final String CLIPBOARD_CONTENT_SETTING = "ClipboardContentSetting";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index af0e418..dc7b0d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -739,8 +739,6 @@
     public void onStopWithNative() {
         super.onStopWithNative();
 
-        if (getActivityTab() != null) getActivityTab().setIsAllowedToReturnToExternalApp(false);
-
         mTabModelSelectorImpl.saveState();
         mActivityStopMetrics.onStopWithNative(this);
 
@@ -1363,21 +1361,8 @@
          */
         private void openNewTab(String url, String referer, String headers,
                 String externalAppId, Intent intent, boolean forceNewTab) {
-            boolean isAllowedToReturnToExternalApp = IntentUtils.safeGetBooleanExtra(
-                    intent, LaunchIntentDispatcher.EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT, true);
-
             // Create a new tab.
-            Tab newTab =
-                    launchIntent(url, referer, headers, externalAppId, forceNewTab, intent);
-            if (newTab != null) {
-                newTab.setIsAllowedToReturnToExternalApp(isAllowedToReturnToExternalApp);
-            } else {
-                // TODO(twellington): This should only happen for NTPs created in Chrome Home.  See
-                //                    if we should be caching setIsAllowedToReturnToExternalApp
-                //                    in those cases.
-                assert NewTabPage.isNTPUrl(url);
-                assert getBottomSheet() != null;
-            }
+            launchIntent(url, referer, headers, externalAppId, forceNewTab, intent);
             logMobileReceivedExternalIntent(externalAppId, intent);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index 50e7f90..624f0db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -69,12 +69,6 @@
     public static final String EXTRA_LAUNCH_MODE =
             "com.google.android.apps.chrome.EXTRA_LAUNCH_MODE";
 
-    /**
-     * Whether or not the toolbar should indicate that a tab was spawned by another Activity.
-     */
-    public static final String EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT =
-            "org.chromium.chrome.browser.document.IS_ALLOWED_TO_RETURN_TO_PARENT";
-
     private static final String TAG = "ActivitiyDispatcher";
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TabThemeColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/TabThemeColorProvider.java
index 219a7bd..b757a50 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/TabThemeColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/TabThemeColorProvider.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser;
 
+import android.content.Context;
+
 import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabThemeColorHelper;
@@ -13,7 +15,9 @@
     /** The {@link sActivityTabTabObserver} used to know when the active tab color changed. */
     private ActivityTabTabObserver mActivityTabTabObserver;
 
-    public TabThemeColorProvider() {}
+    public TabThemeColorProvider(Context context) {
+        super(context);
+    }
 
     /**
      * @param provider A means of getting the activity's tab.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ThemeColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/ThemeColorProvider.java
index 26732b9..5b2efad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ThemeColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ThemeColorProvider.java
@@ -8,7 +8,6 @@
 import android.content.res.ColorStateList;
 import android.support.v7.content.res.AppCompatResources;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.util.ColorUtils;
@@ -57,10 +56,12 @@
     /** List of {@link TintObserver}s. These are used to broadcast events to listeners. */
     private final ObserverList<TintObserver> mTintObservers;
 
-    public ThemeColorProvider() {
+    /**
+     * @param context The {@link Context} that is used to retrieve color related resources.
+     */
+    public ThemeColorProvider(Context context) {
         mThemeColorObservers = new ObserverList<ThemeColorObserver>();
         mTintObservers = new ObserverList<TintObserver>();
-        final Context context = ContextUtils.getApplicationContext();
         mLightModeTint = AppCompatResources.getColorStateList(context, R.color.light_mode_tint);
         mDarkModeTint = AppCompatResources.getColorStateList(context, R.color.dark_mode_tint);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java
index aa65d15..1394442 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java
@@ -16,7 +16,6 @@
 
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.IntentHandler;
-import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.offlinepages.OfflinePageOrigin;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -63,7 +62,6 @@
         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl));
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setPackage(mActivity.getPackageName());
-        intent.putExtra(LaunchIntentDispatcher.EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT, false);
         intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true);
         intent.putExtra(Browser.EXTRA_APPLICATION_ID, mActivity.getPackageName());
         IntentHandler.addTrustedIntentExtras(intent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index b38b0978..714fd0200 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -940,7 +940,6 @@
         if (TextUtils.isEmpty(url)) url = mIntentDataProvider.getUrlToLoad();
         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(LaunchIntentDispatcher.EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT, false);
 
         boolean willChromeHandleIntent =
                 getIntentDataProvider().isOpenedByChrome() || getIntentDataProvider().isIncognito();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
index 3d71630..cef14d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
@@ -27,6 +27,7 @@
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.browserservices.PostMessageHandler;
 import org.chromium.chrome.browser.customtabs.CloseButtonNavigator;
+import org.chromium.chrome.browser.customtabs.CloseButtonNavigator.PageCriteria;
 import org.chromium.chrome.browser.customtabs.CustomTabBottomBarDelegate;
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.CustomTabTopBarDelegate;
@@ -154,6 +155,8 @@
             new DynamicModuleNavigationEventObserver();
     private final DynamicModulePageLoadObserver mPageLoadObserver;
 
+    private final PageCriteria mPageCriteria;
+
     @Inject
     public DynamicModuleCoordinator(CustomTabIntentDataProvider intentDataProvider,
                                     CloseButtonNavigator closeButtonNavigator,
@@ -184,8 +187,8 @@
         mBottomBarDelegate = bottomBarDelegate;
         mFullscreenManager = fullscreenManager;
 
-        closeButtonNavigator.setLandingPageCriteria(url ->
-                (isModuleLoading() || isModuleLoaded()) && isModuleManagedUrl(url));
+        mPageCriteria = url -> (isModuleLoading() || isModuleLoaded()) && isModuleManagedUrl(url);
+        closeButtonNavigator.setLandingPageCriteria(mPageCriteria);
 
         activityLifecycleDispatcher.register(this);
     }
@@ -385,6 +388,10 @@
         return mModuleCallback != null;
     }
 
+    /* package */ boolean hasModuleFailedToLoad() {
+        return mActivityDelegate == null;
+    }
+
     private boolean isModuleManagedUrl(String url) {
         if (TextUtils.isEmpty(url)) {
             return false;
@@ -445,9 +452,9 @@
                 mIntentDataProvider.getSession());
     }
 
-    private View getProgressBarAnchorView(boolean isModuleManagedUrl) {
+    private View getProgressBarAnchorView(boolean showTopBar) {
         View anchorView = null;
-        if (isModuleManagedUrl) {
+        if (showTopBar) {
             View topBarContentView = mTopBarDelegate.get().getTopBarContentView();
             if (topBarContentView != null && topBarContentView.getVisibility() == View.VISIBLE) {
                 anchorView = topBarContentView;
@@ -459,19 +466,19 @@
     }
 
     private void maybeCustomizeCctHeader(String url) {
-        if (!isModuleLoaded() && !isModuleLoading()) return;
+        if (!isModuleLoaded() && !isModuleLoading() && !hasModuleFailedToLoad()) return;
 
-        boolean isModuleManagedUrl = isModuleManagedUrl(url);
-        mTopBarDelegate.get().showTopBarIfNecessary(isModuleManagedUrl);
+        boolean showTopBar = mPageCriteria.matches(url);
+        mTopBarDelegate.get().showTopBarIfNecessary(showTopBar);
         if (shouldHideCctHeaderOnModuleManagedUrls()) {
             mActivity.getToolbarManager().setToolbarVisibility(
-                    isModuleManagedUrl ? View.GONE : mDefaultToolbarVisibility);
+                    showTopBar ? View.GONE : mDefaultToolbarVisibility);
             mActivity.getToolbarManager().setToolbarShadowVisibility(
-                    isModuleManagedUrl ? View.GONE : mDefaultToolbarShadowVisibility);
+                    showTopBar ? View.GONE : mDefaultToolbarShadowVisibility);
             mFullscreenManager.get().setTopControlsHeight(
-                    isModuleManagedUrl ? getTopBarHeight() : mDefaultTopControlContainerHeight);
+                    showTopBar ? getTopBarHeight() : mDefaultTopControlContainerHeight);
             mActivity.getToolbarManager().setProgressBarAnchorView(
-                    getProgressBarAnchorView(isModuleManagedUrl));
+                    getProgressBarAnchorView(showTopBar));
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java
index 01a9f3f5..37f154d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java
@@ -19,7 +19,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.components.sync.ModelType;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -216,7 +216,7 @@
             reportStatus(STATUS_SYNC_NOT_INITIALIZED);
         } else if (!syncService.getActiveDataTypes().contains(ModelType.TYPED_URLS)) {
             reportStatus(STATUS_SYNC_NOT_SYNCING_URLS);
-        } else if (!syncService.getPassphraseType().equals(PassphraseType.KEYSTORE_PASSPHRASE)) {
+        } else if (syncService.getPassphraseType() != Passphrase.Type.KEYSTORE) {
             reportStatus(STATUS_SYNC_NOT_KEYSTORE_PASSPHRASE);
         } else {
             reportStatus(STATUS_SYNC_OTHER);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
index b3b64e55..dd14ba3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
@@ -64,12 +64,14 @@
 
     @Override
     public int getViewTypeId() {
-        return OmniboxSuggestionUiType.DEFAULT;
+        return mEnableNewAnswerLayout ? OmniboxSuggestionUiType.ANSWER_SUGGESTION
+                                      : OmniboxSuggestionUiType.DEFAULT;
     }
 
     @Override
     public PropertyModel createModelForSuggestion(OmniboxSuggestion suggestion) {
-        return new PropertyModel(SuggestionViewProperties.ALL_KEYS);
+        return mEnableNewAnswerLayout ? new PropertyModel(AnswerSuggestionViewProperties.ALL_KEYS)
+                                      : new PropertyModel(SuggestionViewProperties.ALL_KEYS);
     }
 
     @Override
@@ -78,7 +80,11 @@
         SuggestionViewDelegate delegate =
                 mSuggestionHost.createSuggestionViewDelegate(suggestion, position);
 
-        setStateForClassicSuggestion(model, suggestion.getAnswer(), delegate);
+        if (mEnableNewAnswerLayout) {
+            setStateForNewSuggestion(model, suggestion.getAnswer(), delegate);
+        } else {
+            setStateForClassicSuggestion(model, suggestion.getAnswer(), delegate);
+        }
     }
 
     @Override
@@ -117,7 +123,12 @@
                         for (int i = 0; i < models.size(); i++) {
                             PropertyModel model = models.get(i);
                             if (!mSuggestionHost.isActiveModel(model)) continue;
-                            model.set(SuggestionViewProperties.ANSWER_IMAGE, bitmap);
+
+                            if (mEnableNewAnswerLayout) {
+                                model.set(AnswerSuggestionViewProperties.ANSWER_IMAGE, bitmap);
+                            } else {
+                                model.set(SuggestionViewProperties.ANSWER_IMAGE, bitmap);
+                            }
                             didUpdate = true;
                         }
                         if (didUpdate) mSuggestionHost.notifyPropertyModelsChanged();
@@ -132,8 +143,6 @@
             PropertyModel model, SuggestionAnswer answer, SuggestionViewDelegate delegate) {
         SuggestionAnswer.ImageLine firstLine = answer.getFirstLine();
         SuggestionAnswer.ImageLine secondLine = answer.getSecondLine();
-        int numAnswerLines = parseNumAnswerLines(secondLine.getTextFields());
-        if (numAnswerLines == -1) numAnswerLines = 1;
 
         model.set(SuggestionViewProperties.IS_ANSWER, true);
 
@@ -170,8 +179,6 @@
             PropertyModel model, SuggestionAnswer answer, SuggestionViewDelegate delegate) {
         SuggestionAnswer.ImageLine firstLine = answer.getFirstLine();
         SuggestionAnswer.ImageLine secondLine = answer.getSecondLine();
-        int numAnswerLines = parseNumAnswerLines(secondLine.getTextFields());
-        if (numAnswerLines == -1) numAnswerLines = 1;
 
         AnswerText[] details = AnswerTextNewLayout.from(mContext, answer);
 
@@ -220,13 +227,4 @@
 
         model.set(AnswerSuggestionViewProperties.ANSWER_ICON_TYPE, icon);
     }
-
-    private static int parseNumAnswerLines(List<SuggestionAnswer.TextField> textFields) {
-        for (int i = 0; i < textFields.size(); i++) {
-            if (textFields.get(i).hasNumLines()) {
-                return Math.min(3, textFields.get(i).getNumLines());
-            }
-        }
-        return -1;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java
index 4d27562..1ad3822 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java
@@ -6,13 +6,19 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.DrawableRes;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.text.Spannable;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
 
@@ -21,9 +27,11 @@
  */
 public class AnswerSuggestionView extends RelativeLayout {
     private SuggestionViewDelegate mSuggestionDelegate;
+    private View mAnswerView;
     private TextView mTextView1;
     private TextView mTextView2;
-    private ImageView mAnswerIcon;
+    private ImageView mAnswerIconView;
+    private ImageView mRefineView;
 
     /** Creates new instance of AnswerSuggestionView. */
     public AnswerSuggestionView(Context context, AttributeSet attributes) {
@@ -35,19 +43,57 @@
         super.onFinishInflate();
         mTextView1 = findViewById(R.id.omnibox_answer_line_1);
         mTextView2 = findViewById(R.id.omnibox_answer_line_2);
-        mAnswerIcon = findViewById(R.id.omnibox_answer_icon);
+        mAnswerIconView = findViewById(R.id.omnibox_answer_icon);
+        mAnswerView = findViewById(R.id.omnibox_answer);
+        mRefineView = findViewById(R.id.omnibox_answer_refine_icon);
+    }
+
+    @Override
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        if (selected && !isInTouchMode()) {
+            postDelegateAction(() -> mSuggestionDelegate.onSetUrlToSuggestion());
+        }
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        // Whenever the suggestion dropdown is touched, we dispatch onGestureDown which is
+        // used to let autocomplete controller know that it should stop updating suggestions.
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mSuggestionDelegate.onGestureDown();
+                break;
+            case MotionEvent.ACTION_UP:
+                mSuggestionDelegate.onGestureUp(ev.getEventTime());
+                break;
+        }
+        return super.dispatchTouchEvent(ev);
     }
 
     /** Specify delegate receiving click/refine events. */
     void setDelegate(SuggestionViewDelegate delegate) {
         mSuggestionDelegate = delegate;
+        mAnswerView.setOnClickListener(
+                (View v) -> postDelegateAction(() -> mSuggestionDelegate.onSelection()));
+        mAnswerView.setOnLongClickListener((View v) -> {
+            postDelegateAction(() -> mSuggestionDelegate.onLongPress());
+            return true;
+        });
+        mRefineView.setOnClickListener(
+                (View v) -> postDelegateAction(() -> mSuggestionDelegate.onRefineSuggestion()));
     }
 
     /**
      * Toggles theme.
      * @param useDarkColors specifies whether UI should use dark theme.
      */
-    void setUseDarkColors(boolean useDarkColors) {}
+    void setUseDarkColors(boolean useDarkColors) {
+        Drawable drawable = mRefineView.getDrawable();
+        DrawableCompat.setTint(drawable,
+                ApiCompatibilityUtils.getColor(getContext().getResources(),
+                        useDarkColors ? R.color.dark_mode_tint : R.color.light_mode_tint));
+    }
 
     /**
      * Specifies text content of the first text line.
@@ -69,13 +115,25 @@
      * Specifies image bitmap to be displayed as an answer icon.
      * @param bitmap Decoded image to be displayed as an answer icon.
      */
-    void setIconBitmap(Bitmap bitmap) {}
+    void setIconBitmap(Bitmap bitmap) {
+        BitmapDrawable drawable = new BitmapDrawable(bitmap);
+        mAnswerIconView.setImageDrawable(drawable);
+    }
 
     /**
      * Specifies fallback image to be presented if no image is available.
      * @param res Drawable resource ID to be used in place of answer image.
      */
     void setFallbackIconRes(@DrawableRes int res) {
-        mAnswerIcon.setImageResource(res);
+        mAnswerIconView.setImageResource(res);
+    }
+
+    /**
+     * Post delegate action to main thread. Invoked only if delegate is specified.
+     * @param action Delegate action to invoke on the UI thread.
+     */
+    private void postDelegateAction(Runnable action) {
+        if (mSuggestionDelegate == null) return;
+        if (!post(action)) action.run();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManageSyncPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManageSyncPreferences.java
index 3205e22..9007801f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManageSyncPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ManageSyncPreferences.java
@@ -38,7 +38,7 @@
 import org.chromium.chrome.browser.sync.ui.PassphraseTypeDialogFragment;
 import org.chromium.components.signin.ChromeSigninController;
 import org.chromium.components.sync.ModelType;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.util.HashSet;
@@ -365,7 +365,7 @@
 
     /** Callback for PassphraseTypeDialogFragment.Listener */
     @Override
-    public void onPassphraseTypeSelected(PassphraseType type) {
+    public void onPassphraseTypeSelected(@Passphrase.Type int type) {
         if (!mProfileSyncService.isEngineInitialized()) {
             // If the engine was shut down since the dialog was opened, do nothing.
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java
index f80768e..7d6089d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java
@@ -51,7 +51,7 @@
     @Override
     protected DialogParams getDialogParams() {
         PromoDialog.DialogParams params = new PromoDialog.DialogParams();
-        params.drawableResource = R.drawable.data_reduction_illustration;
+        params.vectorDrawableResource = R.drawable.data_reduction_illustration;
         params.headerStringResource =
                 DataReductionBrandingResourceProvider.getDataSaverBrandedString(
                         R.string.data_reduction_promo_title);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
index 8995813..9e88137 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -13,7 +13,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.components.sync.ModelType;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 
 import java.util.HashSet;
 import java.util.List;
@@ -170,10 +170,13 @@
      * This method should only be used if you want to know the raw value. For checking whether
      * we should ask the user for a passphrase, use isPassphraseRequiredForDecryption().
      */
-    public PassphraseType getPassphraseType() {
+    public @Passphrase.Type int getPassphraseType() {
         assert isEngineInitialized();
         int passphraseType = nativeGetPassphraseType(mNativeProfileSyncServiceAndroid);
-        return PassphraseType.fromInternalValue(passphraseType);
+        if (passphraseType < 0 || passphraseType >= Passphrase.Type.NUM_ENTRIES) {
+            throw new IllegalArgumentException();
+        }
+        return passphraseType;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
index 3f088af..099d6bac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
@@ -24,7 +24,7 @@
 import org.chromium.components.signin.ChromeSigninController;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.components.sync.ModelType;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 import org.chromium.components.sync.StopSource;
 
 /**
@@ -221,8 +221,7 @@
     public boolean isSyncingUrlsWithKeystorePassphrase() {
         return mProfileSyncService.isEngineInitialized()
                 && mProfileSyncService.getPreferredDataTypes().contains(ModelType.TYPED_URLS)
-                && mProfileSyncService.getPassphraseType().equals(
-                           PassphraseType.KEYSTORE_PASSPHRASE);
+                && mProfileSyncService.getPassphraseType() == Passphrase.Type.KEYSTORE;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
index 339d40b..3d3d62d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
@@ -29,6 +29,7 @@
 import org.chromium.chrome.browser.sync.GoogleServiceAuthError.State;
 import org.chromium.chrome.browser.sync.ui.PassphraseActivity;
 import org.chromium.components.sync.AndroidSyncSettings;
+import org.chromium.components.sync.Passphrase;
 
 /**
  * {@link SyncNotificationController} provides functionality for displaying Android notifications
@@ -68,12 +69,12 @@
                 return;
             }
             switch (mProfileSyncService.getPassphraseType()) {
-                case IMPLICIT_PASSPHRASE: // Falling through intentionally.
-                case FROZEN_IMPLICIT_PASSPHRASE: // Falling through intentionally.
-                case CUSTOM_PASSPHRASE:
+                case Passphrase.Type.IMPLICIT: // Falling through intentionally.
+                case Passphrase.Type.FROZEN_IMPLICIT: // Falling through intentionally.
+                case Passphrase.Type.CUSTOM:
                     showSyncNotification(R.string.sync_need_passphrase, createPasswordIntent());
                     break;
-                case KEYSTORE_PASSPHRASE: // Falling through intentionally.
+                case Passphrase.Type.KEYSTORE: // Falling through intentionally.
                 default:
                     mNotificationManager.cancel(NotificationConstants.NOTIFICATION_ID_SYNC);
                     return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
index dab49ce..247e639 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
@@ -39,7 +39,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.util.IntentUtils;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
 
@@ -190,21 +190,22 @@
     private SpannableString getPromptText() {
         ProfileSyncService pss = ProfileSyncService.get();
         String accountName = pss.getCurrentSignedInAccountText() + "\n\n";
-        PassphraseType passphraseType = pss.getPassphraseType();
+        @Passphrase.Type
+        int passphraseType = pss.getPassphraseType();
         if (pss.hasExplicitPassphraseTime()) {
             String syncPassphraseHelpContext =
                     getString(R.string.help_context_change_sync_passphrase);
             switch (passphraseType) {
-                case FROZEN_IMPLICIT_PASSPHRASE:
+                case Passphrase.Type.FROZEN_IMPLICIT:
                     return applyInProductHelpSpan(
                             accountName + pss.getSyncEnterGooglePassphraseBodyWithDateText(),
                             syncPassphraseHelpContext);
-                case CUSTOM_PASSPHRASE:
+                case Passphrase.Type.CUSTOM:
                     return applyInProductHelpSpan(
                             accountName + pss.getSyncEnterCustomPassphraseBodyWithDateText(),
                             syncPassphraseHelpContext);
-                case IMPLICIT_PASSPHRASE: // Falling through intentionally.
-                case KEYSTORE_PASSPHRASE: // Falling through intentionally.
+                case Passphrase.Type.IMPLICIT: // Falling through intentionally.
+                case Passphrase.Type.KEYSTORE: // Falling through intentionally.
                 default:
                     Log.w(TAG, "Found incorrect passphrase type " + passphraseType
                                     + ". Falling back to default string.");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
index 1cdf8df0..a1de874 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
@@ -30,16 +30,14 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeStringConstants;
 import org.chromium.chrome.browser.util.IntentUtils;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
 import org.chromium.ui.widget.TextViewWithClickableSpans;
 
 import java.text.DateFormat;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Dialog to ask the user select what type of password to use for encryption.
@@ -48,9 +46,9 @@
         DialogInterface.OnClickListener, OnItemClickListener {
     private static final String TAG = "PassphraseTypeDialogFragment";
 
-    public interface Listener { void onPassphraseTypeSelected(PassphraseType type); }
+    public interface Listener { void onPassphraseTypeSelected(@Passphrase.Type int type); }
 
-    private String[] getDisplayNames(List<PassphraseType> passphraseTypes) {
+    private String[] getDisplayNames(List<Integer /* @Passphrase.Type */> passphraseTypes) {
         String[] displayNames = new String[passphraseTypes.size()];
         for (int i = 0; i < displayNames.length; i++) {
             displayNames[i] = textForPassphraseType(passphraseTypes.get(i));
@@ -58,25 +56,25 @@
         return displayNames;
     }
 
-    private String textForPassphraseType(PassphraseType type) {
+    private String textForPassphraseType(@Passphrase.Type int type) {
         switch (type) {
-            case IMPLICIT_PASSPHRASE:  // Intentional fall through.
-            case KEYSTORE_PASSPHRASE:
+            case Passphrase.Type.IMPLICIT: // Intentional fall through.
+            case Passphrase.Type.KEYSTORE:
                 return getString(R.string.sync_passphrase_type_keystore);
-            case FROZEN_IMPLICIT_PASSPHRASE:
+            case Passphrase.Type.FROZEN_IMPLICIT:
                 String passphraseDate = getPassphraseDateStringFromArguments();
                 String frozenPassphraseString = getString(R.string.sync_passphrase_type_frozen);
                 return String.format(frozenPassphraseString, passphraseDate);
-            case CUSTOM_PASSPHRASE:
+            case Passphrase.Type.CUSTOM:
                 return getString(R.string.sync_passphrase_type_custom);
             default:
                 return "";
         }
     }
 
-    private Adapter createAdapter(PassphraseType currentType) {
-        List<PassphraseType> passphraseTypes =
-                new ArrayList<PassphraseType>(currentType.getVisibleTypes());
+    private Adapter createAdapter(@Passphrase.Type int currentType) {
+        List<Integer /* @Passphrase.Type */> passphraseTypes =
+                Passphrase.getVisibleTypes(currentType);
         return new Adapter(passphraseTypes, getDisplayNames(passphraseTypes));
     }
 
@@ -85,14 +83,14 @@
      */
     @VisibleForTesting
     public class Adapter extends ArrayAdapter<String> {
-
-        private final List<PassphraseType> mPassphraseTypes;
+        private final List<Integer /* @PassphraseType */> mPassphraseTypes;
 
         /**
          * Do not call this constructor directly. Instead use
          * {@link PassphraseTypeDialogFragment#createAdapter}.
          */
-        private Adapter(List<PassphraseType> passphraseTypes, String[] displayStrings) {
+        private Adapter(
+                List<Integer /* @Passphrase.Type */> passphraseTypes, String[] displayStrings) {
             super(getActivity(), R.layout.passphrase_type_item, displayStrings);
             mPassphraseTypes = passphraseTypes;
         }
@@ -104,24 +102,26 @@
 
         @Override
         public long getItemId(int position) {
-            return getType(position).internalValue();
+            return getType(position);
         }
 
-        public PassphraseType getType(int position) {
+        public @Passphrase.Type int getType(int position) {
             return mPassphraseTypes.get(position);
         }
 
-        public int getPositionForType(PassphraseType type) {
+        public int getPositionForType(@Passphrase.Type int type) {
             return mPassphraseTypes.indexOf(type);
         }
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             CheckedTextView view = (CheckedTextView) super.getView(position, convertView, parent);
-            PassphraseType positionType = getType(position);
-            PassphraseType currentType = getCurrentTypeFromArguments();
-            Set<PassphraseType> allowedTypes =
-                    currentType.getAllowedTypes(getIsEncryptEverythingAllowedFromArguments());
+            @Passphrase.Type
+            int positionType = getType(position);
+            @Passphrase.Type
+            int currentType = getCurrentTypeFromArguments();
+            List<Integer /* @Passphrase.Type */> allowedTypes = Passphrase.getAllowedTypes(
+                    currentType, getIsEncryptEverythingAllowedFromArguments());
 
             // Set the item to checked it if it is the currently selected encryption type.
             view.setChecked(positionType == currentType);
@@ -132,7 +132,7 @@
     }
 
     /**
-     * This argument should contain a single value of type {@link PassphraseType}.
+     * This argument should contain a single value of type {@link Passphrase.Type}.
      */
     private static final String ARG_CURRENT_TYPE = "arg_current_type";
 
@@ -141,11 +141,11 @@
     private static final String ARG_IS_ENCRYPT_EVERYTHING_ALLOWED =
             "arg_is_encrypt_everything_allowed";
 
-    public static PassphraseTypeDialogFragment create(
-            PassphraseType currentType, long passphraseTime, boolean isEncryptEverythingAllowed) {
+    public static PassphraseTypeDialogFragment create(@Passphrase.Type int currentType,
+            long passphraseTime, boolean isEncryptEverythingAllowed) {
         PassphraseTypeDialogFragment dialog = new PassphraseTypeDialogFragment();
         Bundle args = new Bundle();
-        args.putParcelable(ARG_CURRENT_TYPE, currentType);
+        args.putInt(ARG_CURRENT_TYPE, currentType);
         args.putLong(ARG_PASSPHRASE_TIME, passphraseTime);
         args.putBoolean(ARG_IS_ENCRYPT_EVERYTHING_ALLOWED, isEncryptEverythingAllowed);
         dialog.setArguments(args);
@@ -160,11 +160,12 @@
         // Configure the passphrase type list
         ListView list = (ListView) v.findViewById(R.id.passphrase_types);
 
-        PassphraseType currentType = getCurrentTypeFromArguments();
+        @Passphrase.Type
+        int currentType = getCurrentTypeFromArguments();
 
         // Configure the hint to reset the passphrase settings
         // Only show this hint if encryption has been set to use sync passphrase
-        if (currentType == PassphraseType.CUSTOM_PASSPHRASE) {
+        if (currentType == Passphrase.Type.CUSTOM) {
             TextViewWithClickableSpans instructionsView =
                     new TextViewWithClickableSpans(getActivity());
             instructionsView.setPadding(0,
@@ -217,13 +218,15 @@
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long typeId) {
-        PassphraseType currentType = getCurrentTypeFromArguments();
-        // We know this conversion from long to int is safe, because it represents very small
-        // enum values.
-        PassphraseType type = PassphraseType.fromInternalValue((int) typeId);
+        @Passphrase.Type
+        int currentType = getCurrentTypeFromArguments();
+        // We know that typeId conversion from long to int is safe, because it represents very
+        // small enum values.
+        @Passphrase.Type
+        int type = (int) typeId;
         boolean isEncryptEverythingAllowed = getIsEncryptEverythingAllowedFromArguments();
-        if (currentType.getAllowedTypes(isEncryptEverythingAllowed).contains(type)) {
-            if (typeId != currentType.internalValue()) {
+        if (Passphrase.getAllowedTypes(currentType, isEncryptEverythingAllowed).contains(type)) {
+            if (type != currentType) {
                 Listener listener = (Listener) getTargetFragment();
                 listener.onPassphraseTypeSelected(type);
             }
@@ -232,9 +235,10 @@
     }
 
     @VisibleForTesting
-    public PassphraseType getCurrentTypeFromArguments() {
-        PassphraseType currentType = getArguments().getParcelable(ARG_CURRENT_TYPE);
-        if (currentType == null) {
+    public @Passphrase.Type int getCurrentTypeFromArguments() {
+        // NUM_ENTRIES is used to find when value doesn't exist.
+        int currentType = getArguments().getInt(ARG_CURRENT_TYPE, Passphrase.Type.NUM_ENTRIES);
+        if (currentType == Passphrase.Type.NUM_ENTRIES) {
             throw new IllegalStateException("Unable to find argument with current type.");
         }
         return currentType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
index 579b6e8..8d3ac2a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
@@ -50,7 +50,7 @@
 import org.chromium.components.signin.ChromeSigninController;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.components.sync.ModelType;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 import org.chromium.components.sync.ProtocolErrorClientAction;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
@@ -470,7 +470,7 @@
      * Callback for PassphraseTypeDialogFragment.Listener
      */
     @Override
-    public void onPassphraseTypeSelected(PassphraseType type) {
+    public void onPassphraseTypeSelected(@Passphrase.Type int type) {
         if (!mProfileSyncService.isEngineInitialized()) {
             // If the engine was shut down since the dialog was opened, do nothing.
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 9e89dab..05991ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -327,9 +327,6 @@
      */
     private boolean mIsDetached;
 
-    /** Whether or not the tab closing the tab can send the user back to the app that opened it. */
-    private boolean mIsAllowedToReturnToExternalApp;
-
     /**
      * The publisher URL for pages hosted on a trusted CDN, or null otherwise.
      */
@@ -581,7 +578,6 @@
             if ((params.getTransitionType() & PageTransition.FROM_ADDRESS_BAR)
                     == PageTransition.FROM_ADDRESS_BAR) {
                 mAppAssociatedWith = null;
-                setIsAllowedToReturnToExternalApp(false);
             }
             if ("chrome://java-crash/".equals(params.getUrl())) {
                 return handleJavaCrash();
@@ -2653,20 +2649,6 @@
     }
 
     /**
-     * Set whether closing this Tab should return the user to the app that spawned Chrome.
-     */
-    public void setIsAllowedToReturnToExternalApp(boolean state) {
-        mIsAllowedToReturnToExternalApp = state;
-    }
-
-    /**
-     * @return Whether closing this Tab should return the user to the app that spawned Chrome.
-     */
-    public boolean isAllowedToReturnToExternalApp() {
-        return mIsAllowedToReturnToExternalApp;
-    }
-
-    /**
      * @return Whether or not the tab was opened by an app other than Chrome.
      */
     public boolean isCreatedForExternalApp() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
index 9699f9f..78548bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -17,7 +17,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.DefaultBrowserInfo;
 import org.chromium.chrome.browser.IntentHandler;
-import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.contextmenu.ContextMenuItemDelegate;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
@@ -243,10 +242,6 @@
             if (packageManager.queryIntentActivities(chromeIntent, 0).isEmpty()) return;
         }
 
-        // For "Open in Chrome" from the context menu in WebappActivity we want to bypass
-        // CustomTab, and this flag ensures we open in TabbedChrome.
-        chromeIntent.putExtra(LaunchIntentDispatcher.EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT, false);
-
         boolean activityStarted = false;
         if (pageUrl != null) {
             try {
@@ -274,7 +269,6 @@
         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl));
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setClass(mTab.getApplicationContext(), ChromeLauncherActivity.class);
-        intent.putExtra(LaunchIntentDispatcher.EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT, false);
         if (isIncognito) {
             intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true);
             intent.putExtra(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java
index 5bd9cb1..7121d6a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.tasks.tab_list_ui;
 
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.support.annotation.Nullable;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -37,12 +40,13 @@
             holder.closeButton.setContentDescription(holder.itemView.getResources().getString(
                     R.string.accessibility_tabstrip_btn_close_tab, title));
         } else if (TabProperties.IS_SELECTED == propertyKey) {
+            Resources res = holder.itemView.getResources();
+            Drawable drawable = new InsetDrawable(
+                    ResourcesCompat.getDrawable(res, R.drawable.selected_tab_background,
+                            holder.itemView.getContext().getTheme()),
+                    (int) res.getDimension(R.dimen.tab_list_selected_inset));
             ((FrameLayout) holder.itemView)
-                    .setForeground(item.get(TabProperties.IS_SELECTED)
-                                    ? ResourcesCompat.getDrawable(holder.itemView.getResources(),
-                                            R.drawable.selected_tab_background,
-                                            holder.itemView.getContext().getTheme())
-                                    : null);
+                    .setForeground(item.get(TabProperties.IS_SELECTED) ? drawable : null);
         } else if (TabProperties.TAB_SELECTED_LISTENER == propertyKey) {
             holder.itemView.setOnClickListener(view -> {
                 item.get(TabProperties.TAB_SELECTED_LISTENER).run(holder.getTabId());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
index 8e80a46..d1acafc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
@@ -31,15 +31,19 @@
  * The home button.
  */
 public class HomeButton extends ChromeImageButton
-        implements TintObserver, OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener {
+        implements TintObserver, OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener,
+                   HomepageManager.HomepageStateListener {
     private static final int ID_REMOVE = 0;
 
     /** A provider that notifies components when the theme color changes.*/
     private ThemeColorProvider mThemeColorProvider;
 
-    /** The {@link sActivityTabTabObserver} used to know when the active page changed. */
+    /** The {@link ActivityTabTabObserver} used to know when the active page changed. */
     private ActivityTabTabObserver mActivityTabTabObserver;
 
+    /** The {@link ActivityTabProvider} used to know if the active tab is on the NTP. */
+    private ActivityTabProvider mActivityTabProvider;
+
     public HomeButton(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -51,6 +55,8 @@
                 && !FeatureUtilities.isBottomToolbarEnabled()) {
             setOnCreateContextMenuListener(this);
         }
+
+        HomepageManager.getInstance().addListener(this);
     }
 
     public void destroy() {
@@ -58,10 +64,13 @@
             mThemeColorProvider.removeTintObserver(this);
             mThemeColorProvider = null;
         }
+
         if (mActivityTabTabObserver != null) {
             mActivityTabTabObserver.destroy();
             mActivityTabTabObserver = null;
         }
+
+        HomepageManager.getInstance().removeListener(this);
     }
 
     public void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
@@ -86,23 +95,37 @@
         return true;
     }
 
+    @Override
+    public void onHomepageStateUpdated() {
+        updateButtonEnabledState();
+    }
+
     public void setActivityTabProvider(ActivityTabProvider activityTabProvider) {
+        mActivityTabProvider = activityTabProvider;
         mActivityTabTabObserver = new ActivityTabTabObserver(activityTabProvider) {
             @Override
             public void onObservingDifferentTab(Tab tab) {
                 if (tab == null) return;
-                setEnabled(shouldEnableHome(tab.getUrl()));
+                updateButtonEnabledState();
             }
 
             @Override
             public void onUpdateUrl(Tab tab, String url) {
-                setEnabled(shouldEnableHome(url));
+                updateButtonEnabledState();
             }
         };
     }
 
-    private static boolean shouldEnableHome(String url) {
-        if (!FeatureUtilities.isBottomToolbarEnabled()) return true;
-        return !NewTabPage.isNTPUrl(url);
+    private void updateButtonEnabledState() {
+        if (FeatureUtilities.isNewTabPageButtonEnabled() || !HomepageManager.isHomepageEnabled()) {
+            setEnabled(!isActiveTabNTP());
+        } else {
+            setEnabled(true);
+        }
+    }
+
+    private boolean isActiveTabNTP() {
+        if (mActivityTabProvider == null) return false;
+        return NewTabPage.isNTPUrl(mActivityTabProvider.getActivityTab().getUrl());
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 11d30c4..1424c775 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -31,6 +31,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.TabLoadStatus;
 import org.chromium.chrome.browser.ThemeColorProvider;
@@ -976,7 +977,7 @@
                         wrapBottomToolbarClickListenerForIPH(newTabClickHandler),
                         closeTabsClickListener, mAppMenuButtonHelper, mTabModelSelector,
                         mOverviewModeBehavior, mActivity.getWindowAndroid(), mTabCountProvider,
-                        mIncognitoStateProvider);
+                        mIncognitoStateProvider, mActivity.findViewById(R.id.control_container));
 
                 // Allow the bottom toolbar to be focused in accessibility after the top toolbar.
                 ApiCompatibilityUtils.setAccessibilityTraversalBefore(
@@ -1212,6 +1213,15 @@
     public void onOrientationChange() {
         if (mActionModeController == null) return;
         mActionModeController.showControlsOnOrientationChange();
+        if (mBottomToolbarCoordinator != null
+                && ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_DUET_ADAPTIVE)) {
+            final boolean isLandscape = mActivity.getResources().getConfiguration().orientation
+                    == Configuration.ORIENTATION_LANDSCAPE;
+            mBottomToolbarCoordinator.setBottomToolbarHidden(isLandscape);
+            if (mAppMenuButtonHelper != null) {
+                mAppMenuButtonHelper.setMenuShowsFromBottom(!isLandscape);
+            }
+        }
     }
 
     /**
@@ -1681,7 +1691,6 @@
             if (previousTab != tab) {
                 if (previousTab != null) {
                     previousTab.removeObserver(mTabObserver);
-                    previousTab.setIsAllowedToReturnToExternalApp(false);
                 }
                 if (tab != null) tab.addObserver(mTabObserver);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
index 9ba378eb..3bfc3c14 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
@@ -6,6 +6,7 @@
 
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.view.ViewStub;
 
 import org.chromium.chrome.R;
@@ -59,7 +60,7 @@
 
         mTabSwitcherModeStub = root.findViewById(R.id.bottom_toolbar_tab_switcher_mode_stub);
 
-        mBottomToolbarThemeColorProvider = new BottomToolbarThemeColorProvider();
+        mBottomToolbarThemeColorProvider = new BottomToolbarThemeColorProvider(root.getContext());
     }
 
     /**
@@ -82,13 +83,14 @@
      * @param tabCountProvider Updates the tab count number in the tab switcher button and in the
      *                         incognito toggle tab layout.
      * @param incognitoStateProvider Notifies components when incognito mode is entered or exited.
+     * @param topToolbarRoot The root {@link ViewGroup} of the top toolbar.
      */
     public void initializeWithNative(ResourceManager resourceManager, LayoutManager layoutManager,
             OnClickListener tabSwitcherListener, OnClickListener newTabClickListener,
             OnClickListener closeTabsClickListener, AppMenuButtonHelper menuButtonHelper,
             TabModelSelector tabModelSelector, OverviewModeBehavior overviewModeBehavior,
             WindowAndroid windowAndroid, TabCountProvider tabCountProvider,
-            IncognitoStateProvider incognitoStateProvider) {
+            IncognitoStateProvider incognitoStateProvider, ViewGroup topToolbarRoot) {
         mBottomToolbarThemeColorProvider.setIncognitoStateProvider(incognitoStateProvider);
         mBottomToolbarThemeColorProvider.setOverviewModeBehavior(overviewModeBehavior);
 
@@ -96,9 +98,18 @@
                 tabSwitcherListener, menuButtonHelper, overviewModeBehavior, windowAndroid,
                 tabCountProvider, mBottomToolbarThemeColorProvider, tabModelSelector);
         mTabSwitcherModeCoordinator = new TabSwitcherBottomToolbarCoordinator(mTabSwitcherModeStub,
-                incognitoStateProvider, mBottomToolbarThemeColorProvider, newTabClickListener,
-                closeTabsClickListener, menuButtonHelper, tabModelSelector, overviewModeBehavior,
-                tabCountProvider);
+                topToolbarRoot, incognitoStateProvider, mBottomToolbarThemeColorProvider,
+                newTabClickListener, closeTabsClickListener, menuButtonHelper, tabModelSelector,
+                overviewModeBehavior, tabCountProvider);
+    }
+
+    /**
+     * @param shouldHide Whether the bottom toolbar should be hidden.
+     */
+    public void setBottomToolbarHidden(boolean shouldHide) {
+        if (mTabSwitcherModeCoordinator != null) {
+            mTabSwitcherModeCoordinator.showToolbarOnTop(shouldHide);
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarThemeColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarThemeColorProvider.java
index 69cdb44..9f7c29c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarThemeColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarThemeColorProvider.java
@@ -7,7 +7,6 @@
 import android.content.Context;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ThemeColorProvider;
@@ -45,8 +44,8 @@
     /** Whether app is in overview mode. */
     private boolean mIsOverviewVisible;
 
-    public BottomToolbarThemeColorProvider() {
-        final Context context = ContextUtils.getApplicationContext();
+    public BottomToolbarThemeColorProvider(Context context) {
+        super(context);
         mLightPrimaryColor = ApiCompatibilityUtils.getColor(
                 context.getResources(), R.color.modern_primary_color);
         mDarkPrimaryColor = ApiCompatibilityUtils.getColor(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
index e2e221f..7cc9487 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
@@ -6,6 +6,7 @@
 
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.view.ViewStub;
 
 import org.chromium.chrome.R;
@@ -38,6 +39,7 @@
     /**
      * Build the coordinator that manages the tab switcher bottom toolbar.
      * @param stub The tab switcher bottom toolbar {@link ViewStub} to inflate.
+     * @param topToolbarRoot The root {@link ViewGroup} of the top toolbar.
      * @param incognitoStateProvider Notifies components when incognito mode is entered or exited.
      * @param themeColorProvider Notifies components when the theme color changes.
      * @param newTabClickListener An {@link OnClickListener} that is triggered when the
@@ -52,7 +54,7 @@
      * @param tabCountProvider Updates the tab count number in the tab switcher button and in the
      *                         incognito toggle tab layout.
      */
-    public TabSwitcherBottomToolbarCoordinator(ViewStub stub,
+    public TabSwitcherBottomToolbarCoordinator(ViewStub stub, ViewGroup topToolbarRoot,
             IncognitoStateProvider incognitoStateProvider, ThemeColorProvider themeColorProvider,
             OnClickListener newTabClickListener, OnClickListener closeTabsClickListener,
             AppMenuButtonHelper menuButtonHelper, TabModelSelector tabModelSelector,
@@ -61,7 +63,9 @@
 
         TabSwitcherBottomToolbarModel model = new TabSwitcherBottomToolbarModel();
 
-        PropertyModelChangeProcessor.create(model, root, new TabSwitcherBottomToolbarViewBinder());
+        PropertyModelChangeProcessor.create(model, root,
+                new TabSwitcherBottomToolbarViewBinder(
+                        topToolbarRoot, (ViewGroup) root.getParent()));
 
         mMediator = new TabSwitcherBottomToolbarMediator(
                 model, themeColorProvider, overviewModeBehavior);
@@ -84,6 +88,13 @@
     }
 
     /**
+     * @param showOnTop Whether to show the tab switcher bottom toolbar on the top of the screen.
+     */
+    public void showToolbarOnTop(boolean showOnTop) {
+        mMediator.showToolbarOnTop(showOnTop);
+    }
+
+    /**
      * Clean up any state when the bottom toolbar is destroyed.
      */
     public void destroy() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java
index aa8769f4..93d5b407 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java
@@ -43,6 +43,13 @@
     }
 
     /**
+     * @param showOnTop Whether to show the tab switcher bottom toolbar on the top of the screen.
+     */
+    void showToolbarOnTop(boolean showOnTop) {
+        mModel.set(TabSwitcherBottomToolbarModel.SHOW_ON_TOP, showOnTop);
+    }
+
+    /**
      * Clean up anything that needs to be when the tab switcher bottom toolbar is destroyed.
      */
     void destroy() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
index 3eb5413..03a43a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
@@ -17,8 +17,11 @@
     /** Whether the tab switcher bottom toolbar is visible */
     public static final WritableBooleanPropertyKey IS_VISIBLE = new WritableBooleanPropertyKey();
 
+    /** Whether the tab switcher bottom toolbar shows on top of the screen. */
+    public static final WritableBooleanPropertyKey SHOW_ON_TOP = new WritableBooleanPropertyKey();
+
     /** Default constructor. */
     public TabSwitcherBottomToolbarModel() {
-        super(PRIMARY_COLOR, IS_VISIBLE);
+        super(PRIMARY_COLOR, IS_VISIBLE, SHOW_ON_TOP);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
index ee635ca9..7fe4bbe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
@@ -5,8 +5,10 @@
 package org.chromium.chrome.browser.toolbar.bottom;
 
 import android.view.View;
+import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
+import org.chromium.ui.UiUtils;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -18,11 +20,17 @@
 public class TabSwitcherBottomToolbarViewBinder
         implements PropertyModelChangeProcessor
                            .ViewBinder<TabSwitcherBottomToolbarModel, View, PropertyKey> {
+    private final ViewGroup mTopRoot;
+    private final ViewGroup mBottomRoot;
+
     /**
      * Build a binder that handles interaction between the model and the tab switcher bottom toolbar
      * view.
      */
-    public TabSwitcherBottomToolbarViewBinder() {}
+    public TabSwitcherBottomToolbarViewBinder(ViewGroup topRoot, ViewGroup bottomRoot) {
+        mTopRoot = topRoot;
+        mBottomRoot = bottomRoot;
+    }
 
     @Override
     public final void bind(
@@ -33,8 +41,20 @@
         } else if (TabSwitcherBottomToolbarModel.PRIMARY_COLOR == propertyKey) {
             view.findViewById(R.id.bottom_toolbar_buttons)
                     .setBackgroundColor(model.get(TabSwitcherBottomToolbarModel.PRIMARY_COLOR));
+        } else if (TabSwitcherBottomToolbarModel.SHOW_ON_TOP == propertyKey) {
+            final boolean showOnTop = model.get(TabSwitcherBottomToolbarModel.SHOW_ON_TOP);
+            view.findViewById(R.id.bottom_toolbar_top_shadow)
+                    .setVisibility(showOnTop ? View.GONE : View.VISIBLE);
+            view.findViewById(R.id.bottom_toolbar_bottom_shadow)
+                    .setVisibility(showOnTop ? View.VISIBLE : View.GONE);
+            reparentView(view, showOnTop ? mTopRoot : mBottomRoot);
         } else {
             assert false : "Unhandled property detected in TabSwitcherBottomToolbarViewBinder!";
         }
     }
+
+    private static void reparentView(View v, ViewGroup newParent) {
+        UiUtils.removeViewFromParent(v);
+        newParent.addView(v);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
index 3e75871..9ed6b4c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
@@ -193,7 +193,8 @@
         // onFinishNativeInitialization() call happens before therefore if we instantiate
         // DynamicModuleCoordinator now, the module will not be loaded.
         DynamicModuleCoordinator coordinator = getModuleCoordinator();
-        assertFalse(coordinator.isModuleLoading() || coordinator.isModuleLoaded());
+        assertFalse(coordinator.isModuleLoading() || coordinator.isModuleLoaded()
+                || coordinator.hasModuleFailedToLoad());
 
         // We shouldn't be able to open a channel or post messages yet.
         assertFalse(coordinator.requestPostMessageChannel(FAKE_ORIGIN_URI));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleUITest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleUITest.java
index 015b870..ba93c53 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleUITest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleUITest.java
@@ -318,7 +318,7 @@
     @Test
     @SmallTest
     @Features.EnableFeatures(ChromeFeatureList.CCT_MODULE)
-    public void testSetTopBarContentView_moduleLoadingFailed_noTopBar() throws Exception {
+    public void testSetTopBarContentView_moduleLoadingFailed_cctHeaderVisible() throws Exception {
         Intent intent = new IntentBuilder(mTestPage).setModuleFailToLoadComponentName().build();
 
         mActivityRule.startCustomTabActivityWithIntent(intent);
@@ -327,9 +327,13 @@
         runOnUiThreadBlocking(() -> {
             View anyView = new View(getActivity());
             getModuleCoordinator().setTopBarContentView(anyView);
+            ViewGroup topBar = getActivity().findViewById(R.id.topbar);
+            Assert.assertNotNull(topBar);
+            Assert.assertThat(anyView.getParent(), equalTo(topBar));
+            assertEquals(View.GONE, anyView.getVisibility());
         });
 
-        assertNoTopBar();
+        assertCCTHeaderIsVisible();
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
index 523d1916..5c577071 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
@@ -45,7 +45,7 @@
 import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.components.sync.ModelType;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 import org.chromium.components.sync.protocol.AutofillWalletSpecifics;
 import org.chromium.components.sync.protocol.EntitySpecifics;
 import org.chromium.components.sync.protocol.SyncEntity;
@@ -521,10 +521,8 @@
 
         final PassphraseTypeDialogFragment typeFragment = getPassphraseTypeDialogFragment();
         mSyncTestRule.stopSync();
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            typeFragment.onItemClick(
-                    null, null, 0, PassphraseType.CUSTOM_PASSPHRASE.internalValue());
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> { typeFragment.onItemClick(null, null, 0, Passphrase.Type.CUSTOM); });
         // No crash means we passed.
     }
 
@@ -588,7 +586,7 @@
         SyncTestUtil.waitForSyncActive();
         final SyncCustomizationFragment fragment = startSyncCustomizationFragment();
         ThreadUtils.runOnUiThreadBlocking(
-                () -> fragment.onPassphraseTypeSelected(PassphraseType.CUSTOM_PASSPHRASE));
+                () -> fragment.onPassphraseTypeSelected(Passphrase.Type.CUSTOM));
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         PassphraseCreationDialogFragment pcdf = getPassphraseCreationDialogFragment();
         AlertDialog dialog = (AlertDialog) pcdf.getDialog();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java
index fc5879b..191956d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java
@@ -22,7 +22,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.sync.SyncTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.components.sync.PassphraseType;
+import org.chromium.components.sync.Passphrase;
 
 /**
  * Tests to make sure that PassphraseTypeDialogFragment presents the correct options.
@@ -41,10 +41,10 @@
     private static final boolean UNCHECKED = false;
 
     private static class TypeOptions {
-        public final PassphraseType type;
+        public final @Passphrase.Type int type;
         public final boolean isEnabled;
         public final boolean isChecked;
-        public TypeOptions(PassphraseType type, boolean isEnabled, boolean isChecked) {
+        public TypeOptions(@Passphrase.Type int type, boolean isEnabled, boolean isChecked) {
             this.type = type;
             this.isEnabled = isEnabled;
             this.isChecked = isChecked;
@@ -57,20 +57,20 @@
     @SmallTest
     @Feature({"Sync"})
     public void testKeystoreEncryptionOptions() throws Exception {
-        createFragment(PassphraseType.KEYSTORE_PASSPHRASE, true);
+        createFragment(Passphrase.Type.KEYSTORE, true);
         assertPassphraseTypeOptions(false,
-                new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, ENABLED, UNCHECKED),
-                new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, ENABLED, CHECKED));
+                new TypeOptions(Passphrase.Type.CUSTOM, ENABLED, UNCHECKED),
+                new TypeOptions(Passphrase.Type.KEYSTORE, ENABLED, CHECKED));
     }
 
     @Test
     @SmallTest
     @Feature({"Sync"})
     public void testCustomEncryptionOptions() throws Exception {
-        createFragment(PassphraseType.CUSTOM_PASSPHRASE, true);
+        createFragment(Passphrase.Type.CUSTOM, true);
         assertPassphraseTypeOptions(true,
-                new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, DISABLED, CHECKED),
-                new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, DISABLED, UNCHECKED));
+                new TypeOptions(Passphrase.Type.CUSTOM, DISABLED, CHECKED),
+                new TypeOptions(Passphrase.Type.KEYSTORE, DISABLED, UNCHECKED));
     }
 
     /*
@@ -80,43 +80,43 @@
     @Test
     @FlakyTest(message = "crbug.com/588050")
     public void testFrozenImplicitEncryptionOptions() throws Exception {
-        createFragment(PassphraseType.FROZEN_IMPLICIT_PASSPHRASE, true);
+        createFragment(Passphrase.Type.FROZEN_IMPLICIT, true);
         assertPassphraseTypeOptions(true,
-                new TypeOptions(PassphraseType.FROZEN_IMPLICIT_PASSPHRASE, DISABLED, CHECKED),
-                new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, DISABLED, UNCHECKED));
+                new TypeOptions(Passphrase.Type.FROZEN_IMPLICIT, DISABLED, CHECKED),
+                new TypeOptions(Passphrase.Type.KEYSTORE, DISABLED, UNCHECKED));
     }
 
     @Test
     @SmallTest
     @Feature({"Sync"})
     public void testImplicitEncryptionOptions() throws Exception {
-        createFragment(PassphraseType.IMPLICIT_PASSPHRASE, true);
+        createFragment(Passphrase.Type.IMPLICIT, true);
         assertPassphraseTypeOptions(false,
-                new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, ENABLED, UNCHECKED),
-                new TypeOptions(PassphraseType.IMPLICIT_PASSPHRASE, ENABLED, CHECKED));
+                new TypeOptions(Passphrase.Type.CUSTOM, ENABLED, UNCHECKED),
+                new TypeOptions(Passphrase.Type.IMPLICIT, ENABLED, CHECKED));
     }
 
     @Test
     @SmallTest
     @Feature({"Sync"})
     public void testKeystoreEncryptionOptionsEncryptEverythingDisallowed() throws Exception {
-        createFragment(PassphraseType.KEYSTORE_PASSPHRASE, false);
+        createFragment(Passphrase.Type.KEYSTORE, false);
         assertPassphraseTypeOptions(false,
-                new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, DISABLED, UNCHECKED),
-                new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, ENABLED, CHECKED));
+                new TypeOptions(Passphrase.Type.CUSTOM, DISABLED, UNCHECKED),
+                new TypeOptions(Passphrase.Type.KEYSTORE, ENABLED, CHECKED));
     }
 
     @Test
     @SmallTest
     @Feature({"Sync"})
     public void testImplicitEncryptionOptionsEncryptEverythingDisallowed() throws Exception {
-        createFragment(PassphraseType.IMPLICIT_PASSPHRASE, false);
+        createFragment(Passphrase.Type.IMPLICIT, false);
         assertPassphraseTypeOptions(false,
-                new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, DISABLED, UNCHECKED),
-                new TypeOptions(PassphraseType.IMPLICIT_PASSPHRASE, ENABLED, CHECKED));
+                new TypeOptions(Passphrase.Type.CUSTOM, DISABLED, UNCHECKED),
+                new TypeOptions(Passphrase.Type.IMPLICIT, ENABLED, CHECKED));
     }
 
-    public void createFragment(PassphraseType type, boolean isEncryptEverythingAllowed) {
+    public void createFragment(@Passphrase.Type int type, boolean isEncryptEverythingAllowed) {
         mTypeFragment = PassphraseTypeDialogFragment.create(type, 0, isEncryptEverythingAllowed);
         mTypeFragment.show(mSyncTestRule.getActivity().getFragmentManager(), TAG);
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java
index ac67229b..5da01e14 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/PromoDialogTest.java
@@ -121,7 +121,7 @@
     public void testBasic_Visibility() throws Exception {
         // Create a full dialog.
         DialogParams dialogParams = new DialogParams();
-        dialogParams.drawableResource = R.drawable.data_reduction_illustration;
+        dialogParams.vectorDrawableResource = R.drawable.data_reduction_illustration;
         dialogParams.headerStringResource = R.string.data_reduction_promo_title;
         dialogParams.subheaderStringResource = R.string.data_reduction_promo_summary;
         dialogParams.primaryButtonStringResource = R.string.ok;
@@ -150,7 +150,7 @@
         View footer = promoDialogLayout.findViewById(R.id.footer);
 
         // Any controls not specified by the DialogParams won't exist.
-        checkControlVisibility(illustration, dialogParams.drawableResource != 0);
+        checkControlVisibility(illustration, dialogParams.vectorDrawableResource != 0);
         checkControlVisibility(header, dialogParams.headerStringResource != 0);
         checkControlVisibility(subheader, dialogParams.subheaderStringResource != 0);
         checkControlVisibility(primary, dialogParams.primaryButtonStringResource != 0);
@@ -171,7 +171,7 @@
     @SmallTest
     public void testBasic_Orientation() throws Exception {
         DialogParams dialogParams = new DialogParams();
-        dialogParams.drawableResource = R.drawable.data_reduction_illustration;
+        dialogParams.vectorDrawableResource = R.drawable.data_reduction_illustration;
         dialogParams.headerStringResource = R.string.data_reduction_promo_title;
         dialogParams.subheaderStringResource = R.string.data_reduction_promo_summary;
         dialogParams.primaryButtonStringResource = R.string.ok;
@@ -249,7 +249,7 @@
     public void testBasic_HeaderBehavior_WithIllustration() throws Exception {
         // With an illustration, the header View is part of the scrollable content.
         DialogParams dialogParams = new DialogParams();
-        dialogParams.drawableResource = R.drawable.data_reduction_illustration;
+        dialogParams.drawableResource = R.drawable.data_reduction_main_menu_chart;
         dialogParams.headerStringResource = R.string.data_reduction_promo_title;
         dialogParams.primaryButtonStringResource = R.string.data_reduction_enable_button;
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 35fc5b68..fd6a571 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3703,7 +3703,7 @@
         </message>
 
         <message name="IDS_EXTENSION_PROMPT_WARNING_CONTENT_SETTINGS" desc="Permission string for access to content settings.">
-          Change your settings that control websites' access to features such as cookies, JavaScript, plugins, geolocation, microphone, camera etc.
+          Change your settings that control websites' access to features such as cookies, JavaScript, plugins, geolocation, microphone, camera, etc.
         </message>
         <message name="IDS_EXTENSION_PROMPT_WARNING_PRIVACY" desc="Permission string for access to privacy settings.">
           Change your privacy-related settings
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5fc7de7..c112b5b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -652,6 +652,10 @@
     "lookalikes/lookalike_url_controller_client.h",
     "lookalikes/lookalike_url_interstitial_page.cc",
     "lookalikes/lookalike_url_interstitial_page.h",
+    "lookalikes/lookalike_url_navigation_throttle.cc",
+    "lookalikes/lookalike_url_navigation_throttle.h",
+    "lookalikes/lookalike_url_service.cc",
+    "lookalikes/lookalike_url_service.h",
     "mac/bluetooth_utility.h",
     "mac/bluetooth_utility.mm",
     "mac/dock.h",
@@ -2068,7 +2072,6 @@
     "//ui/accessibility",
     "//ui/base",
     "//ui/base:ui_data_pack",
-    "//ui/base/idle",
     "//ui/base/ime",
     "//ui/events:events_base",
     "//ui/gfx",
@@ -2841,10 +2844,6 @@
       "lifetime/browser_close_manager.h",
       "lifetime/termination_notification.cc",
       "lifetime/termination_notification.h",
-      "lookalikes/lookalike_url_navigation_observer.cc",
-      "lookalikes/lookalike_url_navigation_observer.h",
-      "lookalikes/lookalike_url_service.cc",
-      "lookalikes/lookalike_url_service.h",
       "media/capture_access_handler_base.cc",
       "media/capture_access_handler_base.h",
       "media/unified_autoplay_config.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 6c9ccd2..c23579d 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -266,8 +266,4 @@
     # The following is used to build FakeSigninManager instances for testing.
     "+components/signin/core/browser/fake_signin_manager.h",
   ],
-  # To share values of UMA enums between product code and tests.
-  "translate_manager_browsertest.cc": [
-    "+services/network/initiator_lock_compatibility.h",
-  ],
 }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0fe1aba..af610d04 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2264,6 +2264,9 @@
     {"force-update-menu-type", flag_descriptions::kUpdateMenuTypeName,
      flag_descriptions::kUpdateMenuTypeDescription, kOsAndroid,
      MULTI_VALUE_TYPE(kForceUpdateMenuTypeChoices)},
+    {"enable-inline-update-flow", flag_descriptions::kInlineUpdateFlowName,
+     flag_descriptions::kInlineUpdateFlowDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kInlineUpdateFlow)},
     {"update-menu-item-custom-summary",
      flag_descriptions::kUpdateMenuItemCustomSummaryName,
      flag_descriptions::kUpdateMenuItemCustomSummaryDescription, kOsAndroid,
@@ -3628,12 +3631,6 @@
      flag_descriptions::kExperimentalProductivityFeaturesDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kExperimentalProductivityFeatures)},
 
-#if defined(OS_ANDROID)
-    {"enable-display-cutout-api", flag_descriptions::kDisplayCutoutAPIName,
-     flag_descriptions::kDisplayCutoutAPIDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kDisplayCutoutAPI)},
-#endif  // OS_ANDROID
-
 #if defined(USE_AURA)
     {"touchpad-overscroll-history-navigation",
      flag_descriptions::kTouchpadOverscrollHistoryNavigationName,
@@ -4207,6 +4204,11 @@
          password_manager::features::kManualPasswordGenerationAndroid)},
 #endif  // defined(OS_ANDROID)
 
+    {"autofill-show-full-disclosure-label",
+     flag_descriptions::kAutofillShowFullDisclosureLabelName,
+     flag_descriptions::kAutofillShowFullDisclosureLabelDescription, kOsAll,
+     FEATURE_VALUE_TYPE(autofill::features::kAutofillShowFullDisclosureLabel)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index cc0a6b0c..dc351700 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -108,6 +108,7 @@
     &kCCTReportParallelRequestStatus,
     &kCCTResourcePrefetch,
     &kChromeDuetFeature,
+    &kChromeDuetAdaptive,
     &kChromeSmartSelection,
     &kCommandLineOnNonRooted,
     &kContentSuggestionsScrollToLoad,
@@ -290,6 +291,9 @@
 const base::Feature kChromeDuetFeature{"ChromeDuet",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kChromeDuetAdaptive{"ChromeDuetAdaptive",
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kChromeSmartSelection{"ChromeSmartSelection",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index ad395a3..93796ca 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -38,6 +38,7 @@
 extern const base::Feature kCCTReportParallelRequestStatus;
 extern const base::Feature kCCTResourcePrefetch;
 extern const base::Feature kChromeDuetFeature;
+extern const base::Feature kChromeDuetAdaptive;
 extern const base::Feature kChromeSmartSelection;
 extern const base::Feature kCommandLineOnNonRooted;
 extern const base::Feature kContentSuggestionsScrollToLoad;
diff --git a/chrome/browser/android/download/local_media_data_source_factory.cc b/chrome/browser/android/download/local_media_data_source_factory.cc
index 201874d..90081573 100644
--- a/chrome/browser/android/download/local_media_data_source_factory.cc
+++ b/chrome/browser/android/download/local_media_data_source_factory.cc
@@ -6,12 +6,14 @@
 
 #include <vector>
 
+#include "base/android/content_uri_utils.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop_current.h"
+#include "build/build_config.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
 namespace {
@@ -20,6 +22,15 @@
     SafeMediaMetadataParser::MediaDataSourceFactory::MediaDataCallback;
 using ReadFileCallback = base::OnceCallback<void(bool, std::vector<char>)>;
 
+// Helper method to post |cb| on the |main_task_runner| with read result.
+void OnReadComplete(scoped_refptr<base::SequencedTaskRunner> main_task_runner,
+                    ReadFileCallback cb,
+                    bool success,
+                    std::vector<char> data) {
+  main_task_runner->PostTask(
+      FROM_HERE, base::BindOnce(std::move(cb), success, std::move(data)));
+}
+
 // Reads a chunk of the file on a file thread, and reply the data or error to
 // main thread.
 void ReadFile(const base::FilePath& file_path,
@@ -27,30 +38,38 @@
               int64_t length,
               scoped_refptr<base::SequencedTaskRunner> main_task_runner,
               ReadFileCallback cb) {
-  base::File file(file_path,
-                  base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
+  base::File file;
+#if defined(OS_ANDROID)
+  if (file_path.IsContentUri()) {
+    file = base::OpenContentUriForRead(file_path);
+    if (!file.IsValid()) {
+      OnReadComplete(main_task_runner, std::move(cb), false /*success*/,
+                     std::vector<char>());
+      return;
+    }
+  }
+#endif  // defined(OS_ANDROID)
+  if (!file.IsValid())
+    file = base::File(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
   if (!file.IsValid()) {
-    main_task_runner->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(cb), false /*success*/, std::vector<char>()));
+    OnReadComplete(main_task_runner, std::move(cb), false /*success*/,
+                   std::vector<char>());
     return;
   }
 
   auto buffer = std::vector<char>(length);
   int bytes_read = file.Read(position, buffer.data(), length);
   if (bytes_read == -1) {
-    main_task_runner->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(cb), false /*success*/, std::vector<char>()));
+    OnReadComplete(main_task_runner, std::move(cb), false /*success*/,
+                   std::vector<char>());
     return;
   }
   DCHECK_GE(bytes_read, 0);
   if (bytes_read < length)
     buffer.resize(bytes_read);
 
-  main_task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(cb), true /*success*/, std::move(buffer)));
+  OnReadComplete(main_task_runner, std::move(cb), true /*success*/,
+                 std::move(buffer));
 }
 
 // Read media file incrementally and send data to the utility process to parse
diff --git a/chrome/browser/android/vr/vr_shell.cc b/chrome/browser/android/vr/vr_shell.cc
index da74cfc1..001e6701 100644
--- a/chrome/browser/android/vr/vr_shell.cc
+++ b/chrome/browser/android/vr/vr_shell.cc
@@ -149,9 +149,7 @@
                  int display_height_pixels,
                  bool pause_content,
                  bool low_density)
-    : web_vr_autopresentation_expected_(
-          ui_initial_state.web_vr_autopresentation_expected),
-      delegate_provider_(delegate),
+    : delegate_provider_(delegate),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       reprojected_rendering_(reprojected_rendering),
       display_size_meters_(display_width_meters, display_height_meters),
@@ -181,12 +179,6 @@
 
   gl_thread_->Start();
 
-  if (ui_initial_state.in_web_vr ||
-      ui_initial_state.web_vr_autopresentation_expected) {
-    UMA_HISTOGRAM_BOOLEAN("VRAutopresentedWebVR",
-                          ui_initial_state.web_vr_autopresentation_expected);
-  }
-
   can_load_new_assets_ = AssetsLoader::GetInstance()->ComponentReady();
   if (!can_load_new_assets_) {
     waiting_for_assets_component_timer_.Start(
@@ -259,8 +251,7 @@
   if (web_contents_ && !SessionMetricsHelper::FromWebContents(web_contents_)) {
     SessionMetricsHelper::CreateForWebContents(
         web_contents_,
-        webvr_mode_ ? Mode::kWebXrVrPresentation : Mode::kVrBrowsingRegular,
-        web_vr_autopresentation_expected_);
+        webvr_mode_ ? Mode::kWebXrVrPresentation : Mode::kVrBrowsingRegular);
   }
 }
 
@@ -542,11 +533,11 @@
   CreatePageInfo();
   ui_->SetWebVrMode(enabled);
 
-  if (!webvr_mode_ && !web_vr_autopresentation_expected_) {
-    AssetsLoader::GetInstance()->GetMetricsHelper()->OnEnter(Mode::kVrBrowsing);
-  } else {
+  if (webvr_mode_) {
     AssetsLoader::GetInstance()->GetMetricsHelper()->OnEnter(
         Mode::kWebXrVrPresentation);
+  } else {
+    AssetsLoader::GetInstance()->GetMetricsHelper()->OnEnter(Mode::kVrBrowsing);
   }
 }
 
diff --git a/chrome/browser/android/vr/vr_shell.h b/chrome/browser/android/vr/vr_shell.h
index 0e1f68f..9c7965b 100644
--- a/chrome/browser/android/vr/vr_shell.h
+++ b/chrome/browser/android/vr/vr_shell.h
@@ -333,7 +333,6 @@
   std::unique_ptr<PageInfo> CreatePageInfo();
 
   bool webvr_mode_ = false;
-  bool web_vr_autopresentation_expected_ = false;
 
   content::WebContents* web_contents_ = nullptr;
   bool web_contents_is_native_page_ = false;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 9f61d79e..783a2969 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -60,6 +60,7 @@
 #include "chrome/browser/language/translate_frame_binder.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/loader/chrome_navigation_data.h"
+#include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h"
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "chrome/browser/media/router/presentation/receiver_presentation_service_delegate_impl.h"
@@ -4205,6 +4206,14 @@
   }
 #endif
 
+#if !defined(OS_ANDROID)
+  std::unique_ptr<content::NavigationThrottle>
+      lookalike_url_navigation_throttle =
+          LookalikeUrlNavigationThrottle::MaybeCreateNavigationThrottle(handle);
+  if (lookalike_url_navigation_throttle)
+    throttles.push_back(std::move(lookalike_url_navigation_throttle));
+#endif
+
   std::unique_ptr<content::NavigationThrottle> pdf_iframe_throttle =
       PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle);
   if (pdf_iframe_throttle)
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index bf0e175..dc1a26d 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -227,7 +227,6 @@
     "//ui/accessibility",
     "//ui/aura",
     "//ui/base",
-    "//ui/base/idle",
     "//ui/chromeos",
     "//ui/chromeos/events",
     "//ui/compositor",
@@ -580,6 +579,8 @@
     "arc/voice_interaction/voice_interaction_controller_client.h",
     "arc/wallpaper/arc_wallpaper_service.cc",
     "arc/wallpaper/arc_wallpaper_service.h",
+    "assistant/assistant_util.cc",
+    "assistant/assistant_util.h",
     "attestation/attestation_ca_client.cc",
     "attestation/attestation_ca_client.h",
     "attestation/attestation_policy_observer.cc",
@@ -2210,6 +2211,7 @@
     "arc/voice_interaction/fake_voice_interaction_controller.cc",
     "arc/voice_interaction/voice_interaction_controller_client_unittest.cc",
     "arc/wallpaper/arc_wallpaper_service_unittest.cc",
+    "assistant/assistant_util_unittest.cc",
     "attestation/attestation_ca_client_unittest.cc",
     "attestation/attestation_policy_observer_unittest.cc",
     "attestation/enrollment_policy_observer_unittest.cc",
diff --git a/chrome/browser/chromeos/accessibility/accessibility_panel.cc b/chrome/browser/chromeos/accessibility/accessibility_panel.cc
index 37502bc..83b7f4e2 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_panel.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_panel.cc
@@ -127,6 +127,7 @@
 }
 
 bool AccessibilityPanel::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   // Eat all requests as context menus are disallowed.
   return true;
diff --git a/chrome/browser/chromeos/accessibility/accessibility_panel.h b/chrome/browser/chromeos/accessibility/accessibility_panel.h
index 4cfcdcf..6f39b0d 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_panel.h
+++ b/chrome/browser/chromeos/accessibility/accessibility_panel.h
@@ -54,7 +54,8 @@
   class AccessibilityPanelWebContentsObserver;
 
   // content::WebContentsDelegate:
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
 
   // Indirectly invoked by the component extension.
   void DidFirstVisuallyNonEmptyPaint();
diff --git a/chrome/browser/chromeos/arc/arc_util.cc b/chrome/browser/chromeos/arc/arc_util.cc
index 536ab84..338ae5a2 100644
--- a/chrome/browser/chromeos/arc/arc_util.cc
+++ b/chrome/browser/chromeos/arc/arc_util.cc
@@ -41,12 +41,10 @@
 #include "components/arc/arc_features.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_util.h"
-#include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
-#include "third_party/icu/source/common/unicode/locid.h"
 
 namespace arc {
 
@@ -583,48 +581,6 @@
                      std::move(callback)));
 }
 
-ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
-    const Profile* profile) {
-  if (!chromeos::switches::IsAssistantEnabled()) {
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_FLAG;
-  }
-
-  if (!chromeos::ProfileHelper::IsPrimaryProfile(profile))
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER;
-
-  if (profile->IsOffTheRecord())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
-
-  if (profile->IsLegacySupervised())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER;
-
-  if (chromeos::DemoSession::IsDeviceInDemoMode())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_DEMO_MODE;
-
-  if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount())
-    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION;
-
-  const std::string kAllowedLocales[] = {ULOC_US, ULOC_UK, ULOC_CANADA,
-                                         ULOC_CANADA_FRENCH};
-
-  const PrefService* prefs = profile->GetPrefs();
-  std::string pref_locale =
-      prefs->GetString(language::prefs::kApplicationLocale);
-  // Also accept runtime locale which maybe an approximation of user's pref
-  // locale.
-  const std::string kRuntimeLocale = icu::Locale::getDefault().getName();
-  if (!pref_locale.empty()) {
-    base::ReplaceChars(pref_locale, "-", "_", &pref_locale);
-    bool disallowed = !base::ContainsValue(kAllowedLocales, pref_locale) &&
-                      !base::ContainsValue(kAllowedLocales, kRuntimeLocale);
-
-    if (disallowed)
-      return ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE;
-  }
-
-  return ash::mojom::AssistantAllowedState::ALLOWED;
-}
-
 ArcSupervisionTransition GetSupervisionTransition(const Profile* profile) {
   DCHECK(profile);
   DCHECK(profile->GetPrefs());
diff --git a/chrome/browser/chromeos/arc/arc_util.h b/chrome/browser/chromeos/arc/arc_util.h
index fe4b1cb..c913e34 100644
--- a/chrome/browser/chromeos/arc/arc_util.h
+++ b/chrome/browser/chromeos/arc/arc_util.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/callback_forward.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "components/arc/arc_supervision_transition.h"
@@ -160,10 +159,6 @@
     const base::FilePath& profile_path,
     base::OnceClosure callback);
 
-// Returns whether Google Assistant feature is allowed for given |profile|.
-ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
-    const Profile* profile);
-
 // Returns the supervision transition status as stored in profile prefs.
 ArcSupervisionTransition GetSupervisionTransition(const Profile* profile);
 
diff --git a/chrome/browser/chromeos/arc/arc_util_unittest.cc b/chrome/browser/chromeos/arc/arc_util_unittest.cc
index 3be5cae..42f53e4 100644
--- a/chrome/browser/chromeos/arc/arc_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_util_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/test/icu_test_util.h"
 #include "base/test/scoped_command_line.h"
 #include "base/test/scoped_feature_list.h"
-#include "base/test/scoped_locale.h"
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
@@ -37,7 +36,6 @@
 #include "chromeos/dbus/fake_oobe_configuration_client.h"
 #include "components/account_id/account_id.h"
 #include "components/arc/arc_prefs.h"
-#include "components/language/core/browser/pref_names.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
@@ -50,7 +48,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/icu/source/common/unicode/locid.h"
 
 namespace arc {
 namespace util {
@@ -173,7 +170,6 @@
 
   void SetUp() override {
     command_line_ = std::make_unique<base::test::ScopedCommandLine>();
-    feature_list_.InitAndEnableFeature(chromeos::switches::kAssistantFeature);
 
     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
     profile_manager_ = std::make_unique<TestingProfileManager>(
@@ -510,63 +506,6 @@
   EXPECT_FALSE(IsArcPlayStoreEnabledPreferenceManagedForProfile(profile()));
 }
 
-TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_SecondaryUser) {
-  ScopedLogIn login2(
-      GetFakeUserManager(),
-      AccountId::FromUserEmailGaiaId("user2@gmail.com", "0123456789"));
-  ScopedLogIn login(GetFakeUserManager(),
-                    AccountId::FromUserEmailGaiaId(
-                        profile()->GetProfileUserName(), kTestGaiaId));
-
-  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER,
-            IsAssistantAllowedForProfile(profile()));
-}
-
-TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_SupervisedUser) {
-  ScopedLogIn login(GetFakeUserManager(),
-                    AccountId::FromUserEmailGaiaId(
-                        profile()->GetProfileUserName(), kTestGaiaId));
-  profile()->SetSupervisedUserId("foo");
-  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER,
-            IsAssistantAllowedForProfile(profile()));
-}
-
-TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_Locale) {
-  profile()->GetTestingPrefService()->SetString(
-      language::prefs::kApplicationLocale, "he");
-  UErrorCode error_code = U_ZERO_ERROR;
-  const icu::Locale& old_locale = icu::Locale::getDefault();
-  icu::Locale::setDefault(icu::Locale("he"), error_code);
-  ScopedLogIn login(GetFakeUserManager(),
-                    AccountId::FromUserEmailGaiaId(
-                        profile()->GetProfileUserName(), kTestGaiaId));
-
-  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE,
-            IsAssistantAllowedForProfile(profile()));
-  icu::Locale::setDefault(old_locale, error_code);
-}
-
-TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_DemoMode) {
-  chromeos::DemoSession::SetDemoConfigForTesting(
-      chromeos::DemoSession::DemoModeConfig::kOnline);
-  ScopedLogIn login(GetFakeUserManager(),
-                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
-                    user_manager::USER_TYPE_PUBLIC_ACCOUNT);
-  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_DEMO_MODE,
-            IsAssistantAllowedForProfile(profile()));
-
-  chromeos::DemoSession::SetDemoConfigForTesting(
-      chromeos::DemoSession::DemoModeConfig::kNone);
-}
-
-TEST_F(ChromeArcUtilTest, IsAssistantAllowedForProfile_PublicSession) {
-  ScopedLogIn login(GetFakeUserManager(),
-                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
-                    user_manager::USER_TYPE_PUBLIC_ACCOUNT);
-  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION,
-            IsAssistantAllowedForProfile(profile()));
-}
-
 // Test the AreArcAllOptInPreferencesIgnorableForProfile() function.
 TEST_F(ChromeArcUtilTest, AreArcAllOptInPreferencesIgnorableForProfile) {
   base::CommandLine::ForCurrentProcess()->InitFromArgv(
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
index 5340715..6f1f6e0 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
@@ -343,8 +343,7 @@
 
   if (state_ != State::kDisabled) {
     DLOG(WARNING) << "Cannot start tracing, it is already enabled.";
-    if (callback)
-      std::move(callback).Run(false /*success*/);
+    std::move(callback).Run(false /*success*/);
     return;
   }
   state_ = State::kStarting;
@@ -391,8 +390,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(State::kStarting, state_);
   state_ = success ? State::kEnabled : State::kDisabled;
-  if (callback)
-    std::move(callback).Run(success);
+  std::move(callback).Run(success);
 }
 
 void ArcTracingBridge::StopAndFlush(TraceDataCallback callback) {
@@ -463,9 +461,10 @@
 
 void ArcTracingBridge::ArcTracingAgent::StartTracing(
     const std::string& config,
-    base::TimeTicks coordinator_time) {
+    base::TimeTicks coordinator_time,
+    Agent::StartTracingCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  bridge_->StartTracing(config, SuccessCallback());
+  bridge_->StartTracing(config, std::move(callback));
 }
 
 void ArcTracingBridge::ArcTracingAgent::StopAndFlush(
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h
index e77f8a4..dc321c2 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h
@@ -78,7 +78,8 @@
 
     // tracing::mojom::Agent.
     void StartTracing(const std::string& config,
-                      base::TimeTicks coordinator_time) override;
+                      base::TimeTicks coordinator_time,
+                      Agent::StartTracingCallback callback) override;
     void StopAndFlush(tracing::mojom::RecorderPtr recorder) override;
 
     void OnTraceData(const std::string& data);
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
index 295b6c4..1280a28 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
@@ -39,8 +39,9 @@
   voice_interaction_hotword_enabled_ = enabled;
 }
 
-void FakeVoiceInteractionController::NotifySetupCompleted(bool completed) {
-  voice_interaction_setup_completed_ = completed;
+void FakeVoiceInteractionController::NotifyConsentStatus(
+    ash::mojom::ConsentStatus consent_status) {
+  consent_status_ = consent_status;
 }
 
 void FakeVoiceInteractionController::NotifyFeatureAllowed(
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
index a14b993..36a4228 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
@@ -26,7 +26,7 @@
   void NotifyContextEnabled(bool enabled) override;
   void NotifyHotwordAlwaysOn(bool enabled) override;
   void NotifyHotwordEnabled(bool enabled) override;
-  void NotifySetupCompleted(bool completed) override;
+  void NotifyConsentStatus(ash::mojom::ConsentStatus consent_status) override;
   void NotifyFeatureAllowed(ash::mojom::AssistantAllowedState state) override;
   void NotifyNotificationEnabled(bool enabled) override;
   void NotifyLocaleChanged(const std::string& locale) override;
@@ -45,8 +45,8 @@
   bool voice_interaction_hotword_enabled() const {
     return voice_interaction_hotword_enabled_;
   }
-  bool voice_interaction_setup_completed() const {
-    return voice_interaction_setup_completed_;
+  ash::mojom::ConsentStatus voice_interaction_consent_status() const {
+    return consent_status_;
   }
   ash::mojom::AssistantAllowedState assistant_allowed_state() const {
     return assistant_allowed_state_;
@@ -64,7 +64,8 @@
   bool voice_interaction_context_enabled_ = false;
   bool voice_interaction_hotword_always_on_ = false;
   bool voice_interaction_hotword_enabled_ = false;
-  bool voice_interaction_setup_completed_ = false;
+  ash::mojom::ConsentStatus consent_status_ =
+      ash::mojom::ConsentStatus::kUnknown;
   bool voice_interaction_notification_enabled_ = false;
   std::string locale_;
   ash::mojom::AssistantAllowedState assistant_allowed_state_ =
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
index 834b6b0..8d4b196 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
@@ -11,9 +11,10 @@
 #include "ash/public/interfaces/constants.mojom.h"
 #include "base/bind.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_util.h"
@@ -116,18 +117,17 @@
   voice_interaction_controller_->NotifyHotwordAlwaysOn(always_on);
 }
 
-void VoiceInteractionControllerClient::NotifySetupCompleted() {
+void VoiceInteractionControllerClient::NotifyConsentStatus() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
-  bool completed =
-      prefs->GetBoolean(prefs::kVoiceInteractionActivityControlAccepted);
-  voice_interaction_controller_->NotifySetupCompleted(completed);
+  voice_interaction_controller_->NotifyConsentStatus(
+      assistant::prefs::GetConsentStatus(prefs));
 }
 
 void VoiceInteractionControllerClient::NotifyFeatureAllowed() {
   DCHECK(profile_);
   ash::mojom::AssistantAllowedState state =
-      IsAssistantAllowedForProfile(profile_);
+      assistant::IsAssistantAllowedForProfile(profile_);
   voice_interaction_controller_->NotifyFeatureAllowed(state);
 }
 
@@ -182,9 +182,9 @@
   pref_change_registrar_->Init(prefs);
 
   pref_change_registrar_->Add(
-      prefs::kVoiceInteractionActivityControlAccepted,
+      assistant::prefs::kAssistantConsentStatus,
       base::BindRepeating(
-          &VoiceInteractionControllerClient::NotifySetupCompleted,
+          &VoiceInteractionControllerClient::NotifyConsentStatus,
           base::Unretained(this)));
   pref_change_registrar_->Add(
       language::prefs::kApplicationLocale,
@@ -222,7 +222,7 @@
           &VoiceInteractionControllerClient::NotifyLaunchWithMicOpen,
           base::Unretained(this)));
 
-  NotifySetupCompleted();
+  NotifyConsentStatus();
   NotifySettingsEnabled();
   NotifyContextEnabled();
   NotifyLocaleChanged();
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
index 008ce13..7e64737 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
@@ -56,7 +56,7 @@
   void NotifyContextEnabled();
   void NotifyHotwordEnabled();
   void NotifyHotwordAlwaysOn();
-  void NotifySetupCompleted();
+  void NotifyConsentStatus();
   void NotifyFeatureAllowed();
   void NotifyNotificationEnabled();
   void NotifyLocaleChanged();
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
index 5d428d1..5e658eb 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/arc_prefs.h"
@@ -138,15 +139,17 @@
       false,
       voice_interaction_controller()->voice_interaction_notification_enabled());
 
-  ASSERT_EQ(false,
-            prefs->GetBoolean(prefs::kVoiceInteractionActivityControlAccepted));
-  prefs->SetBoolean(prefs::kVoiceInteractionActivityControlAccepted, true);
-  ASSERT_EQ(true,
-            prefs->GetBoolean(prefs::kVoiceInteractionActivityControlAccepted));
+  ASSERT_EQ(static_cast<int>(ash::mojom::ConsentStatus::kUnknown),
+            prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
+  prefs->SetInteger(
+      assistant::prefs::kAssistantConsentStatus,
+      static_cast<int>(ash::mojom::ConsentStatus::kActivityControlAccepted));
+  ASSERT_EQ(
+      static_cast<int>(ash::mojom::ConsentStatus::kActivityControlAccepted),
+      prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
   voice_interaction_controller_client()->FlushMojoForTesting();
-  EXPECT_EQ(
-      true,
-      voice_interaction_controller()->voice_interaction_setup_completed());
+  EXPECT_EQ(ash::mojom::ConsentStatus::kActivityControlAccepted,
+            voice_interaction_controller()->voice_interaction_consent_status());
 
   ASSERT_EQ("", prefs->GetString(language::prefs::kApplicationLocale));
   prefs->SetString(language::prefs::kApplicationLocale, "en-CA");
diff --git a/chrome/browser/chromeos/assistant/OWNERS b/chrome/browser/chromeos/assistant/OWNERS
new file mode 100644
index 0000000..1f575b1
--- /dev/null
+++ b/chrome/browser/chromeos/assistant/OWNERS
@@ -0,0 +1,3 @@
+file://chromeos/assistant/OWNERS
+
+# COMPONENTS: UI>Shell>Assistant
diff --git a/chrome/browser/chromeos/assistant/assistant_util.cc b/chrome/browser/chromeos/assistant/assistant_util.cc
new file mode 100644
index 0000000..25ad631
--- /dev/null
+++ b/chrome/browser/chromeos/assistant/assistant_util.cc
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
+
+#include <string>
+
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/constants/chromeos_switches.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+
+namespace assistant {
+
+ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
+    const Profile* profile) {
+  if (!chromeos::switches::IsAssistantEnabled()) {
+    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_FLAG;
+  }
+
+  if (!chromeos::ProfileHelper::IsPrimaryProfile(profile))
+    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER;
+
+  if (profile->IsOffTheRecord())
+    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
+
+  if (profile->IsLegacySupervised())
+    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER;
+
+  if (chromeos::DemoSession::IsDeviceInDemoMode())
+    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_DEMO_MODE;
+
+  if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount())
+    return ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION;
+
+  const std::string kAllowedLocales[] = {ULOC_US, ULOC_UK, ULOC_CANADA,
+                                         ULOC_CANADA_FRENCH};
+
+  const PrefService* prefs = profile->GetPrefs();
+  std::string pref_locale =
+      prefs->GetString(language::prefs::kApplicationLocale);
+  // Also accept runtime locale which maybe an approximation of user's pref
+  // locale.
+  const std::string kRuntimeLocale = icu::Locale::getDefault().getName();
+  if (!pref_locale.empty()) {
+    base::ReplaceChars(pref_locale, "-", "_", &pref_locale);
+    bool disallowed = !base::ContainsValue(kAllowedLocales, pref_locale) &&
+                      !base::ContainsValue(kAllowedLocales, kRuntimeLocale);
+
+    if (disallowed)
+      return ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE;
+  }
+
+  return ash::mojom::AssistantAllowedState::ALLOWED;
+}
+
+}  // namespace assistant
diff --git a/chrome/browser/chromeos/assistant/assistant_util.h b/chrome/browser/chromeos/assistant/assistant_util.h
new file mode 100644
index 0000000..8ddacb1
--- /dev/null
+++ b/chrome/browser/chromeos/assistant/assistant_util.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ASSISTANT_ASSISTANT_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_ASSISTANT_ASSISTANT_UTIL_H_
+
+#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
+
+class Profile;
+
+namespace assistant {
+
+// Returns whether Google Assistant feature is allowed for given |profile|.
+ash::mojom::AssistantAllowedState IsAssistantAllowedForProfile(
+    const Profile* profile);
+
+}  // namespace assistant
+
+#endif  // CHROME_BROWSER_CHROMEOS_ASSISTANT_ASSISTANT_UTIL_H_
diff --git a/chrome/browser/chromeos/assistant/assistant_util_unittest.cc b/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
new file mode 100644
index 0000000..144544e
--- /dev/null
+++ b/chrome/browser/chromeos/assistant/assistant_util_unittest.cc
@@ -0,0 +1,237 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
+
+#include <memory>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/scoped_command_line.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/constants/chromeos_switches.h"
+#include "components/account_id/account_id.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_type.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+
+namespace assistant {
+namespace {
+
+constexpr char kTestProfileName[] = "user@gmail.com";
+constexpr char kTestGaiaId[] = "1234567890";
+
+class FakeUserManagerWithLocalState : public chromeos::FakeChromeUserManager {
+ public:
+  explicit FakeUserManagerWithLocalState(
+      TestingProfileManager* testing_profile_manager)
+      : testing_profile_manager_(testing_profile_manager),
+        test_local_state_(std::make_unique<TestingPrefServiceSimple>()) {
+    RegisterPrefs(test_local_state_->registry());
+  }
+
+  PrefService* GetLocalState() const override {
+    return test_local_state_.get();
+  }
+
+  TestingProfileManager* testing_profile_manager() {
+    return testing_profile_manager_;
+  }
+
+ private:
+  // Unowned pointer.
+  TestingProfileManager* const testing_profile_manager_;
+
+  std::unique_ptr<TestingPrefServiceSimple> test_local_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeUserManagerWithLocalState);
+};
+
+class ScopedLogIn {
+ public:
+  ScopedLogIn(
+      FakeUserManagerWithLocalState* fake_user_manager,
+      const AccountId& account_id,
+      user_manager::UserType user_type = user_manager::USER_TYPE_REGULAR)
+      : fake_user_manager_(fake_user_manager), account_id_(account_id) {
+    // Prevent access to DBus. This switch is reset in case set from test SetUp
+    // due massive usage of InitFromArgv.
+    base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
+    if (!command_line.HasSwitch(switches::kTestType))
+      command_line.AppendSwitch(switches::kTestType);
+
+    switch (user_type) {
+      case user_manager::USER_TYPE_REGULAR:  // fallthrough
+      case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
+        LogIn();
+        break;
+      case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+        LogInAsPublicAccount();
+        break;
+      case user_manager::USER_TYPE_ARC_KIOSK_APP:
+        LogInArcKioskApp();
+        break;
+      default:
+        NOTREACHED();
+    }
+    fake_user_manager_->testing_profile_manager()->SetLoggedIn(true);
+  }
+
+  ~ScopedLogIn() {
+    fake_user_manager_->testing_profile_manager()->SetLoggedIn(false);
+    LogOut();
+  }
+
+ private:
+  void LogIn() {
+    fake_user_manager_->AddUser(account_id_);
+    fake_user_manager_->LoginUser(account_id_);
+  }
+
+  void LogInAsPublicAccount() {
+    fake_user_manager_->AddPublicAccountUser(account_id_);
+    fake_user_manager_->LoginUser(account_id_);
+  }
+
+  void LogInArcKioskApp() {
+    fake_user_manager_->AddArcKioskAppUser(account_id_);
+    fake_user_manager_->LoginUser(account_id_);
+  }
+
+  void LogOut() { fake_user_manager_->RemoveUserFromList(account_id_); }
+
+  FakeUserManagerWithLocalState* fake_user_manager_;
+  const AccountId account_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedLogIn);
+};
+
+}  // namespace
+
+class ChromeAssistantUtilTest : public testing::Test {
+ public:
+  ChromeAssistantUtilTest() = default;
+  ~ChromeAssistantUtilTest() override = default;
+
+  void SetUp() override {
+    command_line_ = std::make_unique<base::test::ScopedCommandLine>();
+    feature_list_.InitAndEnableFeature(chromeos::switches::kAssistantFeature);
+
+    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+    profile_manager_ = std::make_unique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(profile_manager_->SetUp());
+
+    user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
+        std::make_unique<FakeUserManagerWithLocalState>(
+            profile_manager_.get()));
+
+    profile_ = profile_manager_->CreateTestingProfile(kTestProfileName);
+  }
+
+  void TearDown() override {
+    // Avoid retries, let the next test start safely.
+    profile_manager_->DeleteTestingProfile(kTestProfileName);
+    profile_ = nullptr;
+    user_manager_enabler_.reset();
+    profile_manager_.reset();
+    command_line_.reset();
+  }
+
+  TestingProfile* profile() { return profile_; }
+
+  FakeUserManagerWithLocalState* GetFakeUserManager() const {
+    return static_cast<FakeUserManagerWithLocalState*>(
+        user_manager::UserManager::Get());
+  }
+
+  void LogIn() {
+    const auto account_id = AccountId::FromUserEmailGaiaId(
+        profile()->GetProfileUserName(), kTestGaiaId);
+    GetFakeUserManager()->AddUser(account_id);
+    GetFakeUserManager()->LoginUser(account_id);
+  }
+
+ private:
+  std::unique_ptr<base::test::ScopedCommandLine> command_line_;
+  base::test::ScopedFeatureList feature_list_;
+  content::TestBrowserThreadBundle thread_bundle_;
+  base::ScopedTempDir data_dir_;
+  std::unique_ptr<TestingProfileManager> profile_manager_;
+  std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
+  // Owned by |profile_manager_|
+  TestingProfile* profile_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeAssistantUtilTest);
+};
+
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_SecondaryUser) {
+  ScopedLogIn login2(
+      GetFakeUserManager(),
+      AccountId::FromUserEmailGaiaId("user2@gmail.com", "0123456789"));
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmailGaiaId(
+                        profile()->GetProfileUserName(), kTestGaiaId));
+
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER,
+            IsAssistantAllowedForProfile(profile()));
+}
+
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_SupervisedUser) {
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmailGaiaId(
+                        profile()->GetProfileUserName(), kTestGaiaId));
+  profile()->SetSupervisedUserId("foo");
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_SUPERVISED_USER,
+            IsAssistantAllowedForProfile(profile()));
+}
+
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_Locale) {
+  profile()->GetTestingPrefService()->SetString(
+      language::prefs::kApplicationLocale, "he");
+  UErrorCode error_code = U_ZERO_ERROR;
+  const icu::Locale& old_locale = icu::Locale::getDefault();
+  icu::Locale::setDefault(icu::Locale("he"), error_code);
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmailGaiaId(
+                        profile()->GetProfileUserName(), kTestGaiaId));
+
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_LOCALE,
+            IsAssistantAllowedForProfile(profile()));
+  icu::Locale::setDefault(old_locale, error_code);
+}
+
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_DemoMode) {
+  chromeos::DemoSession::SetDemoConfigForTesting(
+      chromeos::DemoSession::DemoModeConfig::kOnline);
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
+                    user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_DEMO_MODE,
+            IsAssistantAllowedForProfile(profile()));
+
+  chromeos::DemoSession::SetDemoConfigForTesting(
+      chromeos::DemoSession::DemoModeConfig::kNone);
+}
+
+TEST_F(ChromeAssistantUtilTest, IsAssistantAllowedForProfile_PublicSession) {
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmail(profile()->GetProfileUserName()),
+                    user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  EXPECT_EQ(ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION,
+            IsAssistantAllowedForProfile(profile()));
+}
+
+}  // namespace assistant
diff --git a/chrome/browser/chromeos/crostini/OWNERS b/chrome/browser/chromeos/crostini/OWNERS
index 48eb7f2..3e30dd2 100644
--- a/chrome/browser/chromeos/crostini/OWNERS
+++ b/chrome/browser/chromeos/crostini/OWNERS
@@ -1,5 +1,4 @@
 benwells@chromium.org
+joelhockey@chromium.org
 nverne@chromium.org
 timloh@chromium.org
-
-per-file crostini_share_path*=joelhockey@chromium.org
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 473bb64..aa05209 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -26,6 +26,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
@@ -176,7 +177,7 @@
   if (pref_name == arc::prefs::kVoiceInteractionHotwordEnabled) {
     DCHECK(value.is_bool());
 
-    if (arc::IsAssistantAllowedForProfile(profile) !=
+    if (assistant::IsAssistantAllowedForProfile(profile) !=
         ash::mojom::AssistantAllowedState::ALLOWED) {
       return "Assistant is not available for the current user";
     }
@@ -1212,7 +1213,7 @@
   EXTENSION_FUNCTION_VALIDATE(params);
 
   Profile* profile = Profile::FromBrowserContext(browser_context());
-  if (arc::IsAssistantAllowedForProfile(profile) !=
+  if (assistant::IsAssistantAllowedForProfile(profile) !=
       ash::mojom::AssistantAllowedState::ALLOWED) {
     return RespondNow(Error("Assistant is not available for the current user"));
   }
@@ -1278,7 +1279,7 @@
   EXTENSION_FUNCTION_VALIDATE(params);
 
   Profile* profile = Profile::FromBrowserContext(browser_context());
-  if (!profile || arc::IsAssistantAllowedForProfile(profile) !=
+  if (!profile || assistant::IsAssistantAllowedForProfile(profile) !=
                       ash::mojom::AssistantAllowedState::ALLOWED) {
     return RespondNow(Error("Assistant is not available for the current user"));
   }
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index c25e9b4..1b79531 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -13,6 +13,7 @@
 #include "ash/public/interfaces/shell_test_api.test-mojom-test-utils.h"
 #include "ash/public/interfaces/shell_test_api.test-mojom.h"
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/containers/circular_deque.h"
 #include "base/files/file_path.h"
 #include "base/json/json_reader.h"
@@ -1803,7 +1804,7 @@
         extensions::AppWindowRegistry::Get(profile())->app_windows();
     ASSERT_FALSE(app_windows.empty());
     app_windows.front()->GetNativeWindow()->GetHost()->DispatchKeyEventPostIME(
-        &key_event, base::BindOnce([](bool) {}));
+        &key_event, base::NullCallback());
     *output = "mediaKeyDispatched";
     return;
   }
@@ -1827,7 +1828,7 @@
       host = app_windows.front()->GetNativeWindow()->GetHost();
     }
     ASSERT_TRUE(host);
-    host->DispatchKeyEventPostIME(&key_event, base::BindOnce([](bool) {}));
+    host->DispatchKeyEventPostIME(&key_event, base::NullCallback());
     *output = "tabKeyDispatched";
     return;
   }
diff --git a/chrome/browser/chromeos/first_run/first_run_view.cc b/chrome/browser/chromeos/first_run/first_run_view.cc
index 5533b3d0..287b521 100644
--- a/chrome/browser/chromeos/first_run/first_run_view.cc
+++ b/chrome/browser/chromeos/first_run/first_run_view.cc
@@ -60,6 +60,7 @@
 }
 
 bool FirstRunView::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   // Discards context menu.
   return true;
diff --git a/chrome/browser/chromeos/first_run/first_run_view.h b/chrome/browser/chromeos/first_run/first_run_view.h
index ff2ebf89..d8b86c2 100644
--- a/chrome/browser/chromeos/first_run/first_run_view.h
+++ b/chrome/browser/chromeos/first_run/first_run_view.h
@@ -38,7 +38,8 @@
 
  private:
   // Overriden from content::WebContentsDelegate.
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
diff --git a/chrome/browser/chromeos/hats/hats_dialog.cc b/chrome/browser/chromeos/hats/hats_dialog.cc
index f5676fb..5e35ee8 100644
--- a/chrome/browser/chromeos/hats/hats_dialog.cc
+++ b/chrome/browser/chromeos/hats/hats_dialog.cc
@@ -190,7 +190,8 @@
   return false;
 }
 
-bool HatsDialog::HandleContextMenu(const content::ContextMenuParams& params) {
+bool HatsDialog::HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                                   const content::ContextMenuParams& params) {
   // Disable context menu.
   return true;
 }
diff --git a/chrome/browser/chromeos/hats/hats_dialog.h b/chrome/browser/chromeos/hats/hats_dialog.h
index 9041cec..52e2697 100644
--- a/chrome/browser/chromeos/hats/hats_dialog.h
+++ b/chrome/browser/chromeos/hats/hats_dialog.h
@@ -42,7 +42,8 @@
   void OnCloseContents(content::WebContents* source,
                        bool* out_close_dialog) override;
   bool ShouldShowDialogTitle() const override;
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
 
   std::string html_data_;
 
diff --git a/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen.cc b/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen.cc
index d91e207..c6b8a10 100644
--- a/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen.cc
+++ b/chrome/browser/chromeos/login/screens/assistant_optin_flow_screen.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/chromeos/login/screens/assistant_optin_flow_screen.h"
 
-#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/chromeos/login/screens/assistant_optin_flow_screen_view.h"
 #include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
 #include "chrome/browser/profiles/profile.h"
@@ -40,7 +40,7 @@
 
 #if BUILDFLAG(ENABLE_CROS_ASSISTANT)
   if (chromeos::switches::IsAssistantEnabled() &&
-      arc::IsAssistantAllowedForProfile(
+      assistant::IsAssistantAllowedForProfile(
           ProfileManager::GetActiveUserProfile()) ==
           ash::mojom::AssistantAllowedState::ALLOWED) {
     view_->Show();
diff --git a/chrome/browser/chromeos/login/ui/login_web_dialog.cc b/chrome/browser/chromeos/login/ui/login_web_dialog.cc
index 5b831b6..406554f9 100644
--- a/chrome/browser/chromeos/login/ui/login_web_dialog.cc
+++ b/chrome/browser/chromeos/login/ui/login_web_dialog.cc
@@ -146,6 +146,7 @@
 }
 
 bool LoginWebDialog::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   // Disable context menu.
   return true;
diff --git a/chrome/browser/chromeos/login/ui/login_web_dialog.h b/chrome/browser/chromeos/login/ui/login_web_dialog.h
index e4b4fb5..571ba7c 100644
--- a/chrome/browser/chromeos/login/ui/login_web_dialog.h
+++ b/chrome/browser/chromeos/login/ui/login_web_dialog.h
@@ -76,7 +76,8 @@
   void OnCloseContents(content::WebContents* source,
                        bool* out_close_dialog) override;
   bool ShouldShowDialogTitle() const override;
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
   bool HandleOpenURLFromTab(content::WebContents* source,
                             const content::OpenURLParams& params,
                             content::WebContents** out_new_contents) override;
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index 1133ad36..c4f1938 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -408,6 +408,7 @@
 }
 
 bool OobeUIDialogDelegate::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   return true;
 }
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
index eae80b8..50c1319 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
@@ -110,7 +110,8 @@
   void OnCloseContents(content::WebContents* source,
                        bool* out_close_dialog) override;
   bool ShouldShowDialogTitle() const override;
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
   std::vector<ui::Accelerator> GetAccelerators() override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.cc b/chrome/browser/chromeos/login/ui/webui_login_view.cc
index 3cbeae8..0273d8e 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_view.cc
+++ b/chrome/browser/chromeos/login/ui/webui_login_view.cc
@@ -420,6 +420,7 @@
 // WebUILoginView private: -----------------------------------------------------
 
 bool WebUILoginView::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
 #ifndef NDEBUG
   // Do not show the context menu.
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.h b/chrome/browser/chromeos/login/ui/webui_login_view.h
index cc29db4..0824a24 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_view.h
+++ b/chrome/browser/chromeos/login/ui/webui_login_view.h
@@ -149,7 +149,8 @@
   void OnKeyboardVisibilityChanged(bool visible) override;
 
   // Overridden from content::WebContentsDelegate.
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
   bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
diff --git a/chrome/browser/conflicts/DEPS b/chrome/browser/conflicts/DEPS
index acd5f432..4a806f9 100644
--- a/chrome/browser/conflicts/DEPS
+++ b/chrome/browser/conflicts/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chrome/services/util_win/util_win_service.h",
   "+chrome_elf/sha1/sha1.h",
   "+chrome_elf/third_party_dlls",
 ]
diff --git a/chrome/browser/conflicts/module_inspector_win.cc b/chrome/browser/conflicts/module_inspector_win.cc
index 0adab09..d97f4e1 100644
--- a/chrome/browser/conflicts/module_inspector_win.cc
+++ b/chrome/browser/conflicts/module_inspector_win.cc
@@ -22,9 +22,6 @@
 
 namespace {
 
-constexpr base::Feature kWinOOPInspectModuleFeature = {
-    "WinOOPInspectModule", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // The maximum amount of time a stale entry is kept in the cache before it is
 // deleted.
 constexpr base::TimeDelta kMaxEntryAge = base::TimeDelta::FromDays(30);
@@ -38,21 +35,6 @@
   });
 }
 
-// Returns a bound pointer to the UtilWin service.
-chrome::mojom::UtilWinPtr ConnectToUtilWinService(
-    base::OnceClosure connection_error_handler) {
-  chrome::mojom::UtilWinPtr util_win_ptr;
-
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(chrome::mojom::kUtilWinServiceName, &util_win_ptr);
-
-  util_win_ptr.set_connection_error_handler(
-      std::move(connection_error_handler));
-
-  return util_win_ptr;
-}
-
 void ReportConnectionError(bool value) {
   base::UmaHistogramBoolean("Windows.InspectModule.ConnectionError", value);
 }
@@ -95,6 +77,9 @@
 // static
 constexpr base::Feature ModuleInspector::kEnableBackgroundModuleInspection;
 
+// static
+constexpr base::Feature ModuleInspector::kWinOOPInspectModuleFeature;
+
 ModuleInspector::ModuleInspector(
     const OnModuleInspectedCallback& on_module_inspected_callback)
     : on_module_inspected_callback_(on_module_inspected_callback),
@@ -110,6 +95,7 @@
       connection_error_retry_count_(kConnectionErrorRetryCount),
       background_inspection_enabled_(
           base::FeatureList::IsEnabled(kEnableBackgroundModuleInspection)),
+      test_connector_(nullptr),
       weak_ptr_factory_(this) {
   // Use AfterStartupTaskUtils to be notified when startup is finished.
   AfterStartupTaskUtils::PostTask(
@@ -165,6 +151,31 @@
                                 inspection_results_cache_));
 }
 
+void ModuleInspector::EnsureUtilWinServiceBound() {
+  DCHECK(base::FeatureList::IsEnabled(kWinOOPInspectModuleFeature));
+
+  // Use the |test_connector_| if set.
+  service_manager::Connector* connector = test_connector_;
+  if (!connector) {
+    // The ServiceManagerConnection can be null during shutdown.
+    content::ServiceManagerConnection* service_manager_connection =
+        content::ServiceManagerConnection::GetForProcess();
+    if (!service_manager_connection)
+      return;
+
+    connector = service_manager_connection->GetConnector();
+  }
+
+  connector->BindInterface(chrome::mojom::kUtilWinServiceName, &util_win_ptr_);
+  util_win_ptr_.set_connection_error_handler(
+      base::BindOnce(&ModuleInspector::OnUtilWinServiceConnectionError,
+                     base::Unretained(this)));
+
+  // Emit a false value to the connection error histogram to serve as a
+  // baseline.
+  ReportConnectionError(false);
+}
+
 void ModuleInspector::OnStartupFinished() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -231,31 +242,31 @@
     return;
   }
 
-  // There is a small priority inversion that happens when
-  // IncreaseInspectionPriority() is called while a module is currently being
-  // inspected.
-  //
-  // This is because all the subsequent tasks will be posted at a higher
-  // priority, but they are waiting on the current task that is currently
-  // running at a lower priority.
-  //
-  // In practice, this is not an issue because the only caller of
-  // IncreaseInspectionPriority() (chrome://conflicts) does not depend on the
-  // inspection to finish synchronously and is not blocking anything else.
   if (base::FeatureList::IsEnabled(kWinOOPInspectModuleFeature)) {
-    // Make sure the pointer is bound to the service first.
-    if (!util_win_ptr_) {
-      util_win_ptr_ = ConnectToUtilWinService(
-          base::BindOnce(&ModuleInspector::OnUtilWinServiceConnectionError,
-                         base::Unretained(this)));
-      ReportConnectionError(false);
-    }
+    EnsureUtilWinServiceBound();
+
+    // An unbound InterfacePtr at this point means the service is not available.
+    // This is only possible during shutdown. In this case, just dropping the
+    // request and stop processing new modules is fine.
+    if (!util_win_ptr_)
+      return;
 
     util_win_ptr_->InspectModule(
         module_key.module_path,
         base::BindOnce(&ModuleInspector::OnModuleNewlyInspected,
                        weak_ptr_factory_.GetWeakPtr(), module_key));
   } else {
+    // There is a small priority inversion that happens when
+    // IncreaseInspectionPriority() is called while a module is currently being
+    // inspected.
+    //
+    // This is because all the subsequent tasks on |inspection_task_runner_|
+    // will be posted at a higher priority, but they are waiting on the current
+    // task that is currently running at a lower priority.
+    //
+    // In practice, this is not an issue because the only caller of
+    // IncreaseInspectionPriority() (chrome://conflicts) does not depend on the
+    // inspection to finish synchronously and is not blocking anything else.
     base::PostTaskAndReplyWithResult(
         inspection_task_runner_.get(), FROM_HERE,
         base::BindOnce(&InspectModule, module_key.module_path),
diff --git a/chrome/browser/conflicts/module_inspector_win.h b/chrome/browser/conflicts/module_inspector_win.h
index 73637be..542c97f 100644
--- a/chrome/browser/conflicts/module_inspector_win.h
+++ b/chrome/browser/conflicts/module_inspector_win.h
@@ -24,6 +24,10 @@
 class SequencedTaskRunner;
 }
 
+namespace service_manager {
+class Connector;
+}
+
 // This class takes care of inspecting several modules (identified by their
 // ModuleInfoKey) and returning the result via the OnModuleInspectedCallback on
 // the SequencedTaskRunner where it was created.
@@ -43,6 +47,10 @@
   static constexpr base::Feature kEnableBackgroundModuleInspection = {
       "EnableBackgroundModuleInspection", base::FEATURE_ENABLED_BY_DEFAULT};
 
+  // Controls whether or not module inspection is done out of process.
+  static constexpr base::Feature kWinOOPInspectModuleFeature = {
+      "WinOOPInspectModule", base::FEATURE_DISABLED_BY_DEFAULT};
+
   using OnModuleInspectedCallback =
       base::Callback<void(const ModuleInfoKey& module_key,
                           ModuleInspectionResult inspection_result)>;
@@ -64,7 +72,15 @@
   // ModuleDatabaseObserver:
   void OnModuleDatabaseIdle() override;
 
+  void SetConnectorForTesting(service_manager::Connector* connector) {
+    test_connector_ = connector;
+  }
+
  private:
+  // Ensures the |util_win_ptr_| instance is bound to the UtilWin service. This
+  // may result in an unbounded pointer if Chrome is currently shutting down.
+  void EnsureUtilWinServiceBound();
+
   // Invoked when Chrome has finished starting up to initiate the inspection of
   // queued modules.
   void OnStartupFinished();
@@ -135,6 +151,9 @@
   // to true if IncreaseInspectionPriority() is called.
   bool background_inspection_enabled_;
 
+  // Used to connect to the UtilWin service during tests.
+  service_manager::Connector* test_connector_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Weak pointers are used to safely post the inspection result back to the
diff --git a/chrome/browser/conflicts/module_inspector_win_unittest.cc b/chrome/browser/conflicts/module_inspector_win_unittest.cc
index 59fbeaa..05c6029b 100644
--- a/chrome/browser/conflicts/module_inspector_win_unittest.cc
+++ b/chrome/browser/conflicts/module_inspector_win_unittest.cc
@@ -14,7 +14,10 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/services/util_win/public/mojom/constants.mojom.h"
+#include "chrome/services/util_win/util_win_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "services/service_manager/public/cpp/test/test_connector_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -117,3 +120,29 @@
 
   EXPECT_EQ(2u, inspected_modules().size());
 }
+
+TEST_F(ModuleInspectorTest, OOPInspectModule) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      ModuleInspector::kWinOOPInspectModuleFeature);
+
+  service_manager::TestConnectorFactory test_connector_factory_;
+  UtilWinService util_win_service(test_connector_factory_.RegisterInstance(
+      chrome::mojom::kUtilWinServiceName));
+
+  ModuleInfoKey kTestCases[] = {
+      {base::FilePath(), 0, 0},
+      {base::FilePath(), 0, 0},
+  };
+
+  ModuleInspector module_inspector(base::Bind(
+      &ModuleInspectorTest::OnModuleInspected, base::Unretained(this)));
+  module_inspector.SetConnectorForTesting(
+      test_connector_factory_.GetDefaultConnector());
+
+  for (const auto& module : kTestCases)
+    module_inspector.AddModule(module);
+
+  RunUntilIdle();
+  EXPECT_EQ(2u, inspected_modules().size());
+}
diff --git a/chrome/browser/download/notification/download_notification_browsertest.cc b/chrome/browser/download/notification/download_notification_browsertest.cc
index 1cb4575..4ed9fc8 100644
--- a/chrome/browser/download/notification/download_notification_browsertest.cc
+++ b/chrome/browser/download/notification/download_notification_browsertest.cc
@@ -7,6 +7,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
@@ -1058,11 +1059,19 @@
 
 IN_PROC_BROWSER_TEST_F(MultiProfileDownloadNotificationTest,
                        PRE_DownloadMultipleFiles) {
+  // TODO(crbug.com/933963): Flaky with network service.
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   AddAllUsers();
 }
 
 IN_PROC_BROWSER_TEST_F(MultiProfileDownloadNotificationTest,
                        DownloadMultipleFiles) {
+  // TODO(crbug.com/933963): Flaky with network service.
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
   AddAllUsers();
 
   GURL url(SlowDownloadInterceptor::kUnknownSizeUrl);
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 6b0e7408..7d1001e 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -606,12 +606,11 @@
   }
 }
 
-// Tests that the separator character '^' correctly matches the end of the url.
-// TODO(crbug.com/772260): Enable once the bug is fixed.
 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
-                       DISABLED_BlockRequests_SeparatorMatchesEndOfURL) {
+                       BlockRequests_SeparatorMatchesEndOfURL) {
   TestRule rule = CreateGenericRule();
   rule.condition->url_filter = std::string("page2.html^");
+  rule.condition->resource_types = std::vector<std::string>({"main_frame"});
 
   ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
 
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 2b804c2..f28d285 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -52,6 +52,7 @@
 #include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.h"
 #include "chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.h"
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "components/arc/arc_prefs.h"
@@ -380,8 +381,8 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
   // Google Assistant.
-  (*s_whitelist)[arc::prefs::kVoiceInteractionActivityControlAccepted] =
-      settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_whitelist)[::assistant::prefs::kAssistantConsentStatus] =
+      settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_whitelist)[arc::prefs::kVoiceInteractionEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[arc::prefs::kVoiceInteractionContextEnabled] =
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 2fb5740..e29488f 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -282,6 +282,11 @@
     "expiry_milestone": 75
   },
   {
+    "name": "autofill-show-full-disclosure-label",
+    "owners": [ "ftirelo", "tmartino" ],
+    "expiry_milestone": 77
+  },
+  {
     "name": "automatic-password-generation",
     "owners": [ "ioanap", "fhorschig" ],
     "expiry_milestone": 76
@@ -1072,11 +1077,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "enable-display-cutout-api",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "enable-downloads-location-change",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
@@ -1300,6 +1300,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "enable-inline-update-flow",
+    "owners": [ "nyquist", "dtrainor" ],
+    "expiry_milestone": 76
+  },
+  {
     "name": "enable-input-ime-api",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
@@ -3033,7 +3038,7 @@
   },
   {
     "name": "unfiltered-bluetooth-devices",
-    // "owners": [ "your-team" ],
+    "owners": [ "sonnysasaka", "qiyuh@google.com" ],
     "expiry_milestone": 76
   },
   {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e5e731c..e872ed2a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -134,6 +134,12 @@
 const char kAutofillProfileServerValidationDescription[] =
     "Allows autofill to use server side validation";
 
+const char kAutofillShowFullDisclosureLabelName[] =
+    "Autofill Show Full Disclosure Label";
+const char kAutofillShowFullDisclosureLabelDescription[] =
+    "When enabled, the Autofill dropdown's labels are displayed in the full "
+    "disclosure format.";
+
 const char kAutofillPreviewStyleExperimentName[] =
     "Autofill Preview Style Experiment";
 const char kAutofillPreviewStyleExperimentDescription[] =
@@ -2273,11 +2279,6 @@
 const char kContextualSearchUnityIntegrationDescription[] =
     "Enables integration of Tap to Search with Unified Consent.";
 
-const char kDisplayCutoutAPIName[] =
-    "Enable support for the Display Cutout API";
-const char kDisplayCutoutAPIDescription[] =
-    "Enables developers to support devices that have a display cutout.";
-
 const char kDontPrefetchLibrariesName[] = "Don't Prefetch Libraries";
 const char kDontPrefetchLibrariesDescription[] =
     "Don't prefetch libraries after loading.";
@@ -2699,6 +2700,13 @@
 const char kUpdateMenuTypeInlineUpdateInstallFailed[] =
     "Inline Update Error: Install Failed";
 
+const char kInlineUpdateFlowName[] = "Enable Google Play inline update flow";
+const char kInlineUpdateFlowDescription[] =
+    "When this flag is set, instead of taking the user to the Google Play "
+    "Store when an update is available, the user is presented with an inline "
+    "flow where they do not have to leave Chrome until the update is ready "
+    "to install.";
+
 const char kThirdPartyDoodlesName[] =
     "Enable Doodles for third-party search engines";
 const char kThirdPartyDoodlesDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b115c7d..e6ccf52 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -119,6 +119,9 @@
 extern const char kAutofillProfileServerValidationName[];
 extern const char kAutofillProfileServerValidationDescription[];
 
+extern const char kAutofillShowFullDisclosureLabelName[];
+extern const char kAutofillShowFullDisclosureLabelDescription[];
+
 extern const char kAutofillPreviewStyleExperimentName[];
 extern const char kAutofillPreviewStyleExperimentDescription[];
 
@@ -1347,9 +1350,6 @@
 extern const char kContextualSearchUnityIntegrationName[];
 extern const char kContextualSearchUnityIntegrationDescription[];
 
-extern const char kDisplayCutoutAPIName[];
-extern const char kDisplayCutoutAPIDescription[];
-
 extern const char kDontPrefetchLibrariesName[];
 extern const char kDontPrefetchLibrariesDescription[];
 
@@ -1588,6 +1588,9 @@
 extern const char kUpdateMenuTypeInlineUpdateDownloadCanceled[];
 extern const char kUpdateMenuTypeInlineUpdateInstallFailed[];
 
+extern const char kInlineUpdateFlowName[];
+extern const char kInlineUpdateFlowDescription[];
+
 extern const char kThirdPartyDoodlesName[];
 extern const char kThirdPartyDoodlesDescription[];
 
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index e16c7d4..f1ada4fc 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -231,7 +231,7 @@
     const content::ResourceRequestInfo::FrameTreeNodeIdGetter&
         frame_tree_node_id_getter,
     const GURL& url,
-    const net::HostPortPair& host_port_pair,
+    const net::IPEndPoint& remote_endpoint,
     const content::GlobalRequestID& request_id,
     int render_process_id,
     int render_frame_id,
@@ -279,7 +279,7 @@
       content::RenderFrameHost* render_frame_host_or_null =
           content::RenderFrameHost::FromID(render_process_id, render_frame_id);
       metrics_observer->OnRequestComplete(
-          url, host_port_pair, frame_tree_node_id_getter.Run(), request_id,
+          url, remote_endpoint, frame_tree_node_id_getter.Run(), request_id,
           render_frame_host_or_null, resource_type, was_cached,
           std::move(data_reduction_proxy_data), raw_body_bytes,
           original_content_length, request_creation_time, net_error,
@@ -547,19 +547,14 @@
                                                                  lofi_decider)
           : url_request->GetRawBodyBytes();
 
-  net::HostPortPair request_host_port;
+  net::IPEndPoint remote_endpoint;
   // We want to get the IP address of the response if it was returned, and the
   // last endpoint that was checked if it failed.
-  if (url_request->response_headers()) {
-    request_host_port = url_request->GetSocketAddress();
-  }
-  if (request_host_port.IsEmpty()) {
-    net::IPEndPoint request_ip_endpoint;
-    bool was_successful = url_request->GetRemoteEndpoint(&request_ip_endpoint);
-    if (was_successful) {
-      request_host_port =
-          net::HostPortPair::FromIPEndPoint(request_ip_endpoint);
-    }
+  if (url_request->response_headers())
+    remote_endpoint = url_request->GetResponseRemoteEndpoint();
+  if (!remote_endpoint.address().IsValid() &&
+      !url_request->GetTransactionRemoteEndpoint(&remote_endpoint)) {
+    remote_endpoint = net::IPEndPoint();
   }
 
   auto load_timing_info = std::make_unique<net::LoadTimingInfo>();
@@ -571,7 +566,7 @@
           &NotifyUIThreadOfRequestComplete,
           info->GetWebContentsGetterForRequest(),
           info->GetFrameTreeNodeIdGetterForRequest(), url_request->url(),
-          request_host_port, info->GetGlobalRequestID(), info->GetChildID(),
+          remote_endpoint, info->GetGlobalRequestID(), info->GetChildID(),
           info->GetRenderFrameID(), info->GetResourceType(), info->IsDownload(),
           url_request->was_cached(), std::move(data_reduction_proxy_data),
           net_error, url_request->GetTotalReceivedBytes(),
diff --git a/chrome/browser/lookalikes/lookalike_url_controller_client.cc b/chrome/browser/lookalikes/lookalike_url_controller_client.cc
index e436ab2..299b6be 100644
--- a/chrome/browser/lookalikes/lookalike_url_controller_client.cc
+++ b/chrome/browser/lookalikes/lookalike_url_controller_client.cc
@@ -16,14 +16,23 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/referrer.h"
 
+// static
+std::unique_ptr<security_interstitials::MetricsHelper>
+LookalikeUrlControllerClient::GetMetricsHelper(const GURL& url) {
+  security_interstitials::MetricsHelper::ReportDetails settings;
+  settings.metric_prefix = "lookalike";
+
+  return std::make_unique<security_interstitials::MetricsHelper>(url, settings,
+                                                                 nullptr);
+}
+
 LookalikeUrlControllerClient::LookalikeUrlControllerClient(
     content::WebContents* web_contents,
-    std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper,
     const GURL& request_url,
     const GURL& safe_url)
     : SecurityInterstitialControllerClient(
           web_contents,
-          std::move(metrics_helper),
+          GetMetricsHelper(request_url),
           Profile::FromBrowserContext(web_contents->GetBrowserContext())
               ->GetPrefs(),
           g_browser_process->GetApplicationLocale(),
diff --git a/chrome/browser/lookalikes/lookalike_url_controller_client.h b/chrome/browser/lookalikes/lookalike_url_controller_client.h
index a764230..0a37a25 100644
--- a/chrome/browser/lookalikes/lookalike_url_controller_client.h
+++ b/chrome/browser/lookalikes/lookalike_url_controller_client.h
@@ -19,9 +19,11 @@
 class LookalikeUrlControllerClient
     : public security_interstitials::SecurityInterstitialControllerClient {
  public:
+  static std::unique_ptr<security_interstitials::MetricsHelper>
+  GetMetricsHelper(const GURL& url);
+
   LookalikeUrlControllerClient(
       content::WebContents* web_contents,
-      std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper,
       const GURL& request_url,
       const GURL& safe_url);
 
diff --git a/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc b/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
index aad1c9a..232101f 100644
--- a/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
+++ b/chrome/browser/lookalikes/lookalike_url_interstitial_page.cc
@@ -9,12 +9,21 @@
 #include "components/grit/components_resources.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/core/common_string_util.h"
+#include "components/security_interstitials/core/metrics_helper.h"
 #include "components/strings/grit/components_strings.h"
+#include "content/public/browser/interstitial_page_delegate.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/net_errors.h"
 #include "ui/base/l10n/l10n_util.h"
 
+using security_interstitials::MetricsHelper;
+
+// static
+const content::InterstitialPageDelegate::TypeID
+    LookalikeUrlInterstitialPage::kTypeForTesting =
+        &LookalikeUrlInterstitialPage::kTypeForTesting;
+
 LookalikeUrlInterstitialPage::LookalikeUrlInterstitialPage(
     content::WebContents* web_contents,
     const GURL& request_url,
@@ -24,10 +33,19 @@
     : security_interstitials::SecurityInterstitialPage(
           web_contents,
           request_url,
-          std::move(controller_client)) {}
+          std::move(controller_client)) {
+  controller()->metrics_helper()->RecordUserDecision(MetricsHelper::SHOW);
+  controller()->metrics_helper()->RecordUserInteraction(
+      MetricsHelper::TOTAL_VISITS);
+}
 
 LookalikeUrlInterstitialPage::~LookalikeUrlInterstitialPage() {}
 
+content::InterstitialPageDelegate::TypeID
+LookalikeUrlInterstitialPage::GetTypeForTesting() const {
+  return LookalikeUrlInterstitialPage::kTypeForTesting;
+}
+
 bool LookalikeUrlInterstitialPage::ShouldCreateNewNavigation() const {
   return true;
 }
@@ -69,9 +87,13 @@
 
   switch (cmd) {
     case security_interstitials::CMD_DONT_PROCEED:
+      controller()->metrics_helper()->RecordUserDecision(
+          MetricsHelper::DONT_PROCEED);
       controller()->GoBack();
       break;
     case security_interstitials::CMD_PROCEED:
+      controller()->metrics_helper()->RecordUserDecision(
+          MetricsHelper::PROCEED);
       controller()->Proceed();
       break;
     case security_interstitials::CMD_DO_REPORT:
diff --git a/chrome/browser/lookalikes/lookalike_url_interstitial_page.h b/chrome/browser/lookalikes/lookalike_url_interstitial_page.h
index 0eaf47c..aeeb0b5 100644
--- a/chrome/browser/lookalikes/lookalike_url_interstitial_page.h
+++ b/chrome/browser/lookalikes/lookalike_url_interstitial_page.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "components/security_interstitials/content/security_interstitial_page.h"
+#include "content/public/browser/interstitial_page_delegate.h"
 
 class GURL;
 
@@ -21,7 +22,7 @@
     : public security_interstitials::SecurityInterstitialPage {
  public:
   // Interstitial type, used in tests.
-  static const InterstitialPageDelegate::TypeID kTypeForTesting;
+  static const content::InterstitialPageDelegate::TypeID kTypeForTesting;
 
   LookalikeUrlInterstitialPage(
       content::WebContents* web_contents,
@@ -32,6 +33,9 @@
 
   ~LookalikeUrlInterstitialPage() override;
 
+  // InterstitialPageDelegate method:
+  InterstitialPageDelegate::TypeID GetTypeForTesting() const override;
+
  protected:
   // InterstitialPageDelegate implementation:
   void CommandReceived(const std::string& command) override;
@@ -44,6 +48,8 @@
   int GetHTMLTemplateId() override;
 
  private:
+  friend class LookalikeUrlNavigationThrottleBrowserTest;
+
   // Values added to get our shared interstitial HTML to play nice.
   void PopulateStringsForSharedHTML(base::DictionaryValue* load_time_data);
 
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_observer.h b/chrome/browser/lookalikes/lookalike_url_navigation_observer.h
deleted file mode 100644
index ba011be..0000000
--- a/chrome/browser/lookalikes/lookalike_url_navigation_observer.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_OBSERVER_H_
-#define CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_OBSERVER_H_
-
-#include <string>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/engagement/site_engagement_details.mojom.h"
-#include "components/url_formatter/url_formatter.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-namespace content {
-class NavigationHandle;
-}
-
-class Profile;
-
-// Observes navigations and shows an infobar if the navigated domain name
-// is visually similar to a top domain or a domain with a site engagement score.
-class LookalikeUrlNavigationObserver
-    : public content::WebContentsObserver,
-      public content::WebContentsUserData<LookalikeUrlNavigationObserver> {
- public:
-  // Used for metrics. Multiple events can occur per navigation.
-  enum class NavigationSuggestionEvent {
-    kNone = 0,
-    kInfobarShown = 1,
-    kLinkClicked = 2,
-    kMatchTopSite = 3,
-    kMatchSiteEngagement = 4,
-    kMatchEditDistance = 5,
-
-    // Append new items to the end of the list above; do not modify or
-    // replace existing values. Comment out obsolete items.
-    kMaxValue = kMatchEditDistance,
-  };
-
-  // Used for UKM. There is only a single MatchType per navigation.
-  enum class MatchType {
-    kNone = 0,
-    kTopSite = 1,
-    kSiteEngagement = 2,
-    kEditDistance = 3,
-
-    // Append new items to the end of the list above; do not modify or replace
-    // existing values. Comment out obsolete items.
-    kMaxValue = kEditDistance,
-  };
-
-  struct DomainInfo {
-    const std::string domain_and_registry;
-    const url_formatter::IDNConversionResult idn_result;
-    const url_formatter::Skeletons skeletons;
-    DomainInfo(const std::string& arg_domain_and_registry,
-               const url_formatter::IDNConversionResult& arg_idn_result,
-               const url_formatter::Skeletons& arg_skeletons);
-    ~DomainInfo();
-    DomainInfo(const DomainInfo& other);
-  };
-
-  static const char kHistogramName[];
-
-  static void CreateForWebContents(content::WebContents* web_contents);
-
-  explicit LookalikeUrlNavigationObserver(content::WebContents* web_contents);
-  ~LookalikeUrlNavigationObserver() override;
-
-  // content::WebContentsObserver:
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-
- private:
-  friend class content::WebContentsUserData<LookalikeUrlNavigationObserver>;
-  FRIEND_TEST_ALL_PREFIXES(LookalikeUrlNavigationObserverTest,
-                           IsEditDistanceAtMostOne);
-
-  DomainInfo GetDomainInfo(const GURL& url);
-
-  // Performs top domain and engaged site checks on the navigated |url|. Uses
-  // |engaged_sites| for the engaged site checks.
-  void PerformChecks(const GURL& url,
-                     const DomainInfo& navigated_domain,
-                     const std::vector<GURL>& engaged_sites);
-
-  // Returns true if a domain is visually similar to the hostname of |url|. The
-  // matching domain can be a top domain or an engaged site. Similarity check
-  // is made using both visual skeleton and edit distance comparison. If this
-  // returns true, match details will be written into |matched_domain| and
-  // |match_type|. They cannot be nullptr.
-  bool GetMatchingDomain(const DomainInfo& navigated_domain,
-                         const std::vector<GURL>& engaged_sites,
-                         std::string* matched_domain,
-                         MatchType* match_type);
-
-  // Returns if the Levenshtein distance between |str1| and |str2| is at most 1.
-  // This has O(max(n,m)) complexity as opposed to O(n*m) of the usual edit
-  // distance computation.
-  static bool IsEditDistanceAtMostOne(const base::string16& str1,
-                                      const base::string16& str2);
-
-  // Returns the first matching top domain with an edit distance of at most one
-  // to |domain_and_registry|.
-  static std::string GetSimilarDomainFromTop500(const DomainInfo& domain_info);
-
-  Profile* profile_;
-  base::WeakPtrFactory<LookalikeUrlNavigationObserver> weak_factory_;
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-};
-
-#endif  // CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_OBSERVER_H_
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_observer_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_observer_browsertest.cc
deleted file mode 100644
index c8176a12..0000000
--- a/chrome/browser/lookalikes/lookalike_url_navigation_observer_browsertest.cc
+++ /dev/null
@@ -1,560 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/simple_test_clock.h"
-#include "chrome/browser/engagement/site_engagement_score.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/history/history_test_utils.h"
-#include "chrome/browser/infobars/infobar_observer.h"
-#include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/lookalikes/lookalike_url_navigation_observer.h"
-#include "chrome/browser/lookalikes/lookalike_url_service.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/infobars/core/infobar.h"
-#include "components/infobars/core/infobar_delegate.h"
-#include "components/ukm/test_ukm_recorder.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/test_navigation_observer.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/metrics/public/cpp/ukm_source.h"
-#include "ui/base/window_open_disposition.h"
-
-namespace {
-
-using UkmEntry = ukm::builders::LookalikeUrl_NavigationSuggestion;
-using NavigationSuggestionEvent =
-    LookalikeUrlNavigationObserver::NavigationSuggestionEvent;
-
-enum class UIEnabled { kDisabled, kEnabled };
-
-// An engagement score above MEDIUM.
-const int kHighEngagement = 20;
-
-// An engagement score below MEDIUM.
-const int kLowEngagement = 1;
-
-// The domains here should not private domains (e.g. site.test), otherwise they
-// might test the wrong thing. Also note that site5.com is in the top domain
-// list, so it shouldn't be used here.
-struct SiteEngagementTestCase {
-  const char* const navigated;
-  const char* const suggested;
-} kSiteEngagementTestCases[] = {
-    {"sité1.com", "site1.com"},
-    {"mail.www.sité1.com", "site1.com"},
-
-    // These should match since the comparison uses eTLD+1s.
-    {"sité2.com", "www.site2.com"},
-    {"mail.sité2.com", "www.site2.com"},
-
-    {"síté3.com", "sité3.com"},
-    {"mail.síté3.com", "sité3.com"},
-
-    {"síté4.com", "www.sité4.com"},
-    {"mail.síté4.com", "www.sité4.com"},
-};
-
-static std::unique_ptr<net::test_server::HttpResponse>
-NetworkErrorResponseHandler(const net::test_server::HttpRequest& request) {
-  return std::unique_ptr<net::test_server::HttpResponse>(
-      new net::test_server::RawHttpResponse("", ""));
-}
-
-}  // namespace
-
-class LookalikeUrlNavigationObserverBrowserTest
-    : public InProcessBrowserTest,
-      public testing::WithParamInterface<UIEnabled> {
- protected:
-  // Sets the absolute Site Engagement |score| for the testing origin.
-  static void SetEngagementScore(Browser* browser,
-                                 const GURL& url,
-                                 double score) {
-    SiteEngagementService::Get(browser->profile())
-        ->ResetBaseScoreForURL(url, score);
-  }
-
-  void SetUp() override {
-    if (ui_enabled()) {
-      feature_list_.InitAndEnableFeature(
-          features::kLookalikeUrlNavigationSuggestionsUI);
-    } else {
-      feature_list_.InitAndDisableFeature(
-          features::kLookalikeUrlNavigationSuggestionsUI);
-    }
-    InProcessBrowserTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-    ASSERT_TRUE(embedded_test_server()->Start());
-    test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
-
-    const base::Time kNow = base::Time::FromDoubleT(1000);
-    test_clock_.SetNow(kNow);
-
-    LookalikeUrlService* lookalike_service =
-        LookalikeUrlService::Get(browser()->profile());
-    lookalike_service->SetClockForTesting(&test_clock_);
-    lookalike_service->ClearEngagedSitesForTesting();
-  }
-
-  GURL GetURL(const char* hostname) const {
-    return embedded_test_server()->GetURL(hostname, "/title1.html");
-  }
-
-  // Checks that UKM recorded a metric for each URL in |navigated_urls|.
-  void CheckUkm(const std::vector<GURL>& navigated_urls,
-                LookalikeUrlNavigationObserver::MatchType match_type) {
-    auto entries = test_ukm_recorder()->GetEntriesByName(UkmEntry::kEntryName);
-    ASSERT_EQ(navigated_urls.size(), entries.size());
-    int entry_count = 0;
-    for (const auto* const entry : entries) {
-      test_ukm_recorder()->ExpectEntrySourceHasUrl(entry,
-                                                   navigated_urls[entry_count]);
-      test_ukm_recorder()->ExpectEntryMetric(entry, "MatchType",
-                                             static_cast<int>(match_type));
-      entry_count++;
-    }
-  }
-
-  // Checks that UKM did not record any lookalike URL metrics.
-  void CheckNoUkm() {
-    EXPECT_TRUE(
-        test_ukm_recorder()->GetEntriesByName(UkmEntry::kEntryName).empty());
-  }
-
-  void TestInfobarNotShown(Browser* browser, const GURL& navigated_url) {
-    content::WebContents* web_contents =
-        browser->tab_strip_model()->GetActiveWebContents();
-    InfoBarService* infobar_service =
-        InfoBarService::FromWebContents(web_contents);
-    {
-      content::TestNavigationObserver navigation_observer(web_contents, 1);
-      NavigateToURL(browser, navigated_url);
-      navigation_observer.Wait();
-      EXPECT_EQ(0u, infobar_service->infobar_count());
-    }
-    {
-      // Navigate to an empty page. This will happen after any
-      // LookalikeUrlService tasks, so will effectively wait for those tasks to
-      // finish.
-      content::TestNavigationObserver navigation_observer(web_contents, 1);
-      NavigateToURL(browser, GURL("about:blank"));
-      navigation_observer.Wait();
-      EXPECT_EQ(0u, infobar_service->infobar_count());
-    }
-  }
-
-  // Tests that the histogram event |expected_event| is recorded. If the UI is
-  // enabled, additinal events for infobar display and link click will also be
-  // tested.
-  void TestHistogramEventsRecordedAndInfobarVisibility(
-      Browser* browser,
-      base::HistogramTester* histograms,
-      const GURL& navigated_url,
-      const GURL& expected_suggested_url,
-      LookalikeUrlNavigationObserver::NavigationSuggestionEvent
-          expected_event) {
-    if (ui_enabled()) {
-      // If the feature is enabled, the UI will be displayed. Expect extra
-      // histogram entries for kInfobarShown and kLinkClicked events.
-      TestInfobarShown(browser, navigated_url, expected_suggested_url);
-      histograms->ExpectTotalCount(
-          LookalikeUrlNavigationObserver::kHistogramName, 3);
-      histograms->ExpectBucketCount(
-          LookalikeUrlNavigationObserver::kHistogramName,
-          LookalikeUrlNavigationObserver::NavigationSuggestionEvent::
-              kInfobarShown,
-          1);
-      histograms->ExpectBucketCount(
-          LookalikeUrlNavigationObserver::kHistogramName,
-          LookalikeUrlNavigationObserver::NavigationSuggestionEvent::
-              kLinkClicked,
-          1);
-      histograms->ExpectBucketCount(
-          LookalikeUrlNavigationObserver::kHistogramName, expected_event, 1);
-      return;
-    }
-
-    TestInfobarNotShown(browser, navigated_url);
-    histograms->ExpectTotalCount(LookalikeUrlNavigationObserver::kHistogramName,
-                                 1);
-    histograms->ExpectBucketCount(
-        LookalikeUrlNavigationObserver::kHistogramName, expected_event, 1);
-  }
-
-  ukm::TestUkmRecorder* test_ukm_recorder() { return test_ukm_recorder_.get(); }
-
-  base::SimpleTestClock* test_clock() { return &test_clock_; }
-
- private:
-  bool ui_enabled() const { return GetParam() == UIEnabled::kEnabled; }
-
-  // Simulates a link click navigation. We don't use
-  // ui_test_utils::NavigateToURL(const GURL&) because it simulates the user
-  // typing the URL, causing the site to have a site engagement score of at
-  // least LOW.
-  static void NavigateToURL(Browser* browser, const GURL& url) {
-    NavigateParams params(browser, url, ui::PAGE_TRANSITION_LINK);
-    params.initiator_origin = url::Origin::Create(GURL("about:blank"));
-    params.disposition = WindowOpenDisposition::CURRENT_TAB;
-    params.is_renderer_initiated = true;
-    ui_test_utils::NavigateToURL(&params);
-  }
-
-  // Checks that navigating to |navigated_url| results in displaying a
-  // navigation suggesting that says "Did you mean to go to
-  // |expected_suggested_url|?". Both |navigated_url| and
-  // |expected_suggested_url| can be ASCII or IDN.
-  static void TestInfobarShown(Browser* browser,
-                               const GURL& navigated_url,
-                               const GURL& expected_suggested_url) {
-    history::HistoryService* const history_service =
-        HistoryServiceFactory::GetForProfile(
-            browser->profile(), ServiceAccessType::EXPLICIT_ACCESS);
-    ui_test_utils::WaitForHistoryToLoad(history_service);
-
-    content::WebContents* web_contents =
-        browser->tab_strip_model()->GetActiveWebContents();
-    InfoBarService* infobar_service =
-        InfoBarService::FromWebContents(web_contents);
-    InfoBarObserver infobar_added_observer(
-        infobar_service, InfoBarObserver::Type::kInfoBarAdded);
-    NavigateToURL(browser, navigated_url);
-    infobar_added_observer.Wait();
-
-    infobars::InfoBar* infobar = infobar_service->infobar_at(0);
-    EXPECT_EQ(infobars::InfoBarDelegate::ALTERNATE_NAV_INFOBAR_DELEGATE,
-              infobar->delegate()->GetIdentifier());
-
-    // Clicking the link in the infobar should remove the infobar and navigate
-    // to the suggested URL.
-    InfoBarObserver infobar_removed_observer(
-        infobar_service, InfoBarObserver::Type::kInfoBarRemoved);
-    AlternateNavInfoBarDelegate* infobar_delegate =
-        static_cast<AlternateNavInfoBarDelegate*>(infobar->delegate());
-    infobar_delegate->LinkClicked(WindowOpenDisposition::CURRENT_TAB);
-    infobar_removed_observer.Wait();
-
-    EXPECT_EQ(0u, infobar_service->infobar_count());
-    EXPECT_EQ(expected_suggested_url, web_contents->GetURL());
-
-    // Clicking the link in the infobar should also remove the original URL from
-    // history.
-    ui_test_utils::HistoryEnumerator enumerator(browser->profile());
-    EXPECT_FALSE(base::ContainsValue(enumerator.urls(), navigated_url));
-  }
-
-  base::test::ScopedFeatureList feature_list_;
-  std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
-  base::SimpleTestClock test_clock_;
-};
-
-INSTANTIATE_TEST_SUITE_P(,
-                         LookalikeUrlNavigationObserverBrowserTest,
-                         ::testing::Values(UIEnabled::kDisabled,
-                                           UIEnabled::kEnabled));
-
-// Navigating to a non-IDN shouldn't show an infobar or record metrics.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       NonIdn_NoMatch) {
-  TestInfobarNotShown(browser(), GetURL("google.com"));
-  CheckNoUkm();
-}
-
-// Navigating to a domain whose visual representation does not look like a
-// top domain shouldn't show an infobar or record metrics.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       NonTopDomainIdn_NoInfobar) {
-  TestInfobarNotShown(browser(), GetURL("éxample.com"));
-  CheckNoUkm();
-}
-
-// If the user has engaged with the domain before, metrics shouldn't be recorded
-// and the infobar shouldn't be shown, even if the domain is visually similar
-// to a top domain.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       Idn_TopDomain_EngagedSite_NoMatch) {
-  const GURL url = GetURL("googlé.com");
-  SetEngagementScore(browser(), url, kHighEngagement);
-  TestInfobarNotShown(browser(), url);
-  CheckNoUkm();
-}
-
-// Navigate to a domain whose visual representation looks like a top domain.
-// This should record metrics. It should also show a "Did you mean to go to ..."
-// infobar if configured via a feature param.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       Idn_TopDomain_Match) {
-  base::HistogramTester histograms;
-
-  const GURL kNavigatedUrl = GetURL("googlé.com");
-  const GURL kExpectedSuggestedUrl = GetURL("google.com");
-  // Even if the navigated site has a low engagement score, it should be
-  // considered for lookalike suggestions.
-  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
-
-  TestHistogramEventsRecordedAndInfobarVisibility(
-      browser(), &histograms, kNavigatedUrl, kExpectedSuggestedUrl,
-      NavigationSuggestionEvent::kMatchTopSite);
-
-  CheckUkm({kNavigatedUrl},
-           LookalikeUrlNavigationObserver::MatchType::kTopSite);
-}
-
-// The navigated domain itself is a top domain or a subdomain of a top domain.
-// Should not record metrics. The top domain list doesn't contain any IDN, so
-// this only tests the case where the subdomains are IDNs.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       TopDomainIdnSubdomain_NoMatch) {
-  TestInfobarNotShown(browser(), GetURL("tést.google.com"));
-  CheckNoUkm();
-
-  // blogspot.com is a private registry, so the eTLD+1 of "tést.blogspot.com" is
-  // itself, instead of just "blogspot.com". This is different than
-  // tést.google.com whose eTLD+1 is google.com, and it should be handled
-  // correctly.
-  TestInfobarNotShown(browser(), GetURL("tést.blogspot.com"));
-  CheckNoUkm();
-}
-
-// Schemes other than HTTP and HTTPS should be ignored.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       TopDomainChromeUrl_NoMatch) {
-  TestInfobarNotShown(browser(), GURL("chrome://googlé.com"));
-  CheckNoUkm();
-}
-
-// Navigate to a domain within an edit distance of 1 to a top domain.
-// This should record metrics. It should also show a "Did you mean to go to ..."
-// infobar if configured via a feature param.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       EditDistance_TopDomain_Match) {
-  base::HistogramTester histograms;
-
-  // The skeleton of this domain, gooogle.corn, is one 1 edit away from
-  // google.corn, the skeleton of google.com.
-  const GURL kNavigatedUrl = GetURL("goooglé.com");
-  const GURL kExpectedSuggestedUrl = GetURL("google.com");
-  // Even if the navigated site has a low engagement score, it should be
-  // considered for lookalike suggestions.
-  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
-
-  TestHistogramEventsRecordedAndInfobarVisibility(
-      browser(), &histograms, kNavigatedUrl, kExpectedSuggestedUrl,
-      NavigationSuggestionEvent::kMatchEditDistance);
-
-  CheckUkm({kNavigatedUrl},
-           LookalikeUrlNavigationObserver::MatchType::kEditDistance);
-}
-
-// Tests negative examples for the edit distance.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       EditDistance_TopDomain_NoMatch) {
-  // Matches google.com.tr but only differs in registry.
-  TestInfobarNotShown(browser(), GetURL("google.com.tw"));
-  CheckNoUkm();
-
-  // Matches bing.com but is a top domain itself.
-  TestInfobarNotShown(browser(), GetURL("ning.com"));
-  CheckNoUkm();
-
-  // Matches ask.com but is too short.
-  TestInfobarNotShown(browser(), GetURL("bsk.com"));
-  CheckNoUkm();
-}
-
-// Test that the heuristics aren't triggered on net errors.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       NetError_NoMatch) {
-  // Create a test server that returns invalid responses.
-  net::EmbeddedTestServer custom_test_server;
-  custom_test_server.RegisterRequestHandler(
-      base::BindRepeating(&NetworkErrorResponseHandler));
-  ASSERT_TRUE(custom_test_server.Start());
-
-  // Matches google.com but page returns an invalid response.
-  TestInfobarNotShown(browser(),
-                      custom_test_server.GetURL("gooogle.com", "/title1.html"));
-  CheckNoUkm();
-
-  TestInfobarNotShown(browser(),
-                      custom_test_server.GetURL("googlé.com", "/title1.html"));
-  CheckNoUkm();
-
-  SetEngagementScore(browser(), GURL("http://site1.com"), kHighEngagement);
-  TestInfobarNotShown(browser(),
-                      custom_test_server.GetURL("sité1.com", "/title1.html"));
-  CheckNoUkm();
-}
-
-// Navigate to a domain whose visual representation looks like a domain with a
-// site engagement score above a certain threshold. This should record metrics.
-// It should also show a "Did you mean to go to ..." infobar if configured via
-// a feature param.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       Idn_SiteEngagement_Match) {
-  const char* const kEngagedSites[] = {
-      "http://site1.com", "http://www.site2.com", "http://sité3.com",
-      "http://www.sité4.com"};
-
-  for (const char* const kSite : kEngagedSites) {
-    SetEngagementScore(browser(), GURL(kSite), kHighEngagement);
-  }
-
-  std::vector<GURL> ukm_urls;
-  for (const auto& test_case : kSiteEngagementTestCases) {
-    base::HistogramTester histograms;
-    const GURL kNavigatedUrl = GetURL(test_case.navigated);
-    const GURL kExpectedSuggestedUrl = GetURL(test_case.suggested);
-
-    // Even if the navigated site has a low engagement score, it should be
-    // considered for lookalike suggestions.
-    SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
-    // Advance the clock to force LookalikeUrlService to fetch a new engaged
-    // site list.
-    test_clock()->Advance(base::TimeDelta::FromHours(1));
-
-    TestHistogramEventsRecordedAndInfobarVisibility(
-        browser(), &histograms, kNavigatedUrl, kExpectedSuggestedUrl,
-        NavigationSuggestionEvent::kMatchSiteEngagement);
-
-    ukm_urls.push_back(kNavigatedUrl);
-    CheckUkm(ukm_urls,
-             LookalikeUrlNavigationObserver::MatchType::kSiteEngagement);
-  }
-}
-
-// Similar to Idn_SiteEngagement_Match, but tests a single domain. Also checks
-// that the list of engaged sites in incognito and the main profile don't affect
-// each other.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       Idn_SiteEngagement_Match_Incognito) {
-  const GURL kNavigatedUrl = GetURL("sité1.com");
-  const GURL kEngagedUrl = GetURL("site1.com");
-
-  // Set high engagement scores in the main profile and low engagement scores
-  // in incognito. Main profile should record metrics, incognito shouldn't.
-  Browser* incognito = CreateIncognitoBrowser();
-  LookalikeUrlService::Get(incognito->profile())
-      ->SetClockForTesting(test_clock());
-  SetEngagementScore(browser(), kEngagedUrl, kHighEngagement);
-  SetEngagementScore(incognito, kEngagedUrl, kLowEngagement);
-
-  std::vector<GURL> ukm_urls;
-  // Main profile should record metrics because there are engaged sites.
-  {
-    base::HistogramTester histograms;
-    // Advance the clock to force LookalikeUrlService to fetch a new engaged
-    // site list.
-    test_clock()->Advance(base::TimeDelta::FromHours(1));
-    TestHistogramEventsRecordedAndInfobarVisibility(
-        browser(), &histograms, kNavigatedUrl, kEngagedUrl,
-        NavigationSuggestionEvent::kMatchSiteEngagement);
-
-    ukm_urls.push_back(kNavigatedUrl);
-    CheckUkm(ukm_urls,
-             LookalikeUrlNavigationObserver::MatchType::kSiteEngagement);
-  }
-
-  // Incognito shouldn't record metrics because there are no engaged sites.
-  {
-    base::HistogramTester histograms;
-    test_clock()->Advance(base::TimeDelta::FromHours(1));
-    TestInfobarNotShown(incognito, kNavigatedUrl);
-    histograms.ExpectTotalCount(LookalikeUrlNavigationObserver::kHistogramName,
-                                0);
-  }
-
-  // Now reverse the scores: Set low engagement in the main profile and high
-  // engagement in incognito.
-  SetEngagementScore(browser(), kEngagedUrl, kLowEngagement);
-  SetEngagementScore(incognito, kEngagedUrl, kHighEngagement);
-
-  // Incognito should start recording metrics and main profile should stop.
-  {
-    base::HistogramTester histograms;
-    test_clock()->Advance(base::TimeDelta::FromHours(1));
-
-    TestHistogramEventsRecordedAndInfobarVisibility(
-        incognito, &histograms, kNavigatedUrl, kEngagedUrl,
-        NavigationSuggestionEvent::kMatchSiteEngagement);
-    ukm_urls.push_back(kNavigatedUrl);
-    CheckUkm(ukm_urls,
-             LookalikeUrlNavigationObserver::MatchType::kSiteEngagement);
-  }
-
-  // Main profile shouldn't record metrics because there are no engaged sites.
-  {
-    base::HistogramTester histograms;
-    test_clock()->Advance(base::TimeDelta::FromHours(1));
-    TestInfobarNotShown(browser(), kNavigatedUrl);
-    histograms.ExpectTotalCount(LookalikeUrlNavigationObserver::kHistogramName,
-                                0);
-  }
-}
-
-// Test that navigations to a site with a high engagement score shouldn't
-// record metrics or show infobar.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       Idn_SiteEngagement_Match_IgnoreHighlyEngagedSite) {
-  base::HistogramTester histograms;
-  SetEngagementScore(browser(), GURL("http://site-not-in-top-domain-list.com"),
-                     kHighEngagement);
-  const GURL high_engagement_url = GetURL("síte-not-ín-top-domaín-líst.com");
-  SetEngagementScore(browser(), high_engagement_url, kHighEngagement);
-  TestInfobarNotShown(browser(), high_engagement_url);
-  histograms.ExpectTotalCount(LookalikeUrlNavigationObserver::kHistogramName,
-                              0);
-}
-
-// Test that an engaged site with a scheme other than HTTP or HTTPS should be
-// ignored.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       Idn_SiteEngagement_IgnoreChromeUrl) {
-  base::HistogramTester histograms;
-  SetEngagementScore(browser(),
-                     GURL("chrome://site-not-in-top-domain-list.com"),
-                     kHighEngagement);
-  const GURL low_engagement_url("http://síte-not-ín-top-domaín-líst.com");
-  SetEngagementScore(browser(), low_engagement_url, kLowEngagement);
-  TestInfobarNotShown(browser(), low_engagement_url);
-  histograms.ExpectTotalCount(LookalikeUrlNavigationObserver::kHistogramName,
-                              0);
-}
-
-// IDNs with a single label should be properly handled. There are two cases
-// where this might occur:
-// 1. The navigated URL is an IDN with a single label.
-// 2. One of the engaged sites is an IDN with a single label.
-// Neither of these should cause a crash.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationObserverBrowserTest,
-                       IdnWithSingleLabelShouldNotCauseACrash) {
-  base::HistogramTester histograms;
-
-  // Case 1: Navigating to an IDN with a single label shouldn't cause a crash.
-  TestInfobarNotShown(browser(), GetURL("é"));
-
-  // Case 2: An IDN with a single label with a site engagement score shouldn't
-  // cause a crash.
-  SetEngagementScore(browser(), GURL("http://tést"), kHighEngagement);
-  TestInfobarNotShown(browser(), GetURL("tést.com"));
-
-  histograms.ExpectTotalCount(LookalikeUrlNavigationObserver::kHistogramName,
-                              0);
-  CheckNoUkm();
-}
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_observer.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
similarity index 60%
rename from chrome/browser/lookalikes/lookalike_url_navigation_observer.cc
rename to chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index f89be4df..480b131 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_observer.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -1,22 +1,29 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/lookalikes/lookalike_url_navigation_observer.h"
+#include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
-#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/lookalikes/lookalike_url_allowlist.h"
+#include "chrome/browser/lookalikes/lookalike_url_controller_client.h"
+#include "chrome/browser/lookalikes/lookalike_url_interstitial_page.h"
 #include "chrome/browser/lookalikes/lookalike_url_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
 #include "chrome/common/chrome_features.h"
-#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/ukm/content/source_url_recorder.h"
-#include "components/url_formatter/idn_spoof_checker.h"
 #include "components/url_formatter/top_domains/top_domain_util.h"
 #include "content/public/browser/navigation_handle.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -27,13 +34,14 @@
 
 #include "components/url_formatter/top_domains/top500-domains-inc.cc"
 
-using MatchType = LookalikeUrlNavigationObserver::MatchType;
+using MatchType = LookalikeUrlNavigationThrottle::MatchType;
 using NavigationSuggestionEvent =
-    LookalikeUrlNavigationObserver::NavigationSuggestionEvent;
+    LookalikeUrlNavigationThrottle::NavigationSuggestionEvent;
+typedef content::NavigationThrottle::ThrottleCheckResult ThrottleCheckResult;
 
 void RecordEvent(
-    LookalikeUrlNavigationObserver::NavigationSuggestionEvent event) {
-  UMA_HISTOGRAM_ENUMERATION(LookalikeUrlNavigationObserver::kHistogramName,
+    LookalikeUrlNavigationThrottle::NavigationSuggestionEvent event) {
+  UMA_HISTOGRAM_ENUMERATION(LookalikeUrlNavigationThrottle::kHistogramName,
                             event);
 }
 
@@ -42,15 +50,16 @@
   DCHECK(!skeletons1.empty());
   DCHECK(!skeletons2.empty());
   for (const std::string& skeleton1 : skeletons1) {
-    if (base::ContainsKey(skeletons2, skeleton1))
+    if (base::ContainsKey(skeletons2, skeleton1)) {
       return true;
+    }
   }
   return false;
 }
 
 // Returns true if the domain given by |domain_info| is a top domain.
 bool IsTopDomain(
-    const LookalikeUrlNavigationObserver::DomainInfo& domain_info) {
+    const LookalikeUrlNavigationThrottle::DomainInfo& domain_info) {
   // Top domains are only accessible through their skeletons, so query the top
   // domains trie for each skeleton of this domain.
   for (const std::string& skeleton : domain_info.skeletons) {
@@ -77,8 +86,8 @@
 // |domain_and_registry| may be attempting to spoof, based on skeleton
 // comparison.
 std::string GetMatchingSiteEngagementDomain(
-    const std::vector<GURL>& engaged_sites,
-    const LookalikeUrlNavigationObserver::DomainInfo& navigated_domain) {
+    const std::set<GURL>& engaged_sites,
+    const LookalikeUrlNavigationThrottle::DomainInfo& navigated_domain) {
   DCHECK(!navigated_domain.domain_and_registry.empty());
   std::map<std::string, url_formatter::Skeletons>
       domain_and_registry_to_skeleton;
@@ -93,11 +102,13 @@
     const std::string engaged_domain_and_registry =
         GetETLDPlusOne(engaged_site);
     // eTLD+1 can be empty for private domains (e.g. http://test).
-    if (engaged_domain_and_registry.empty())
+    if (engaged_domain_and_registry.empty()) {
       continue;
+    }
 
-    if (navigated_domain.domain_and_registry == engaged_domain_and_registry)
+    if (navigated_domain.domain_and_registry == engaged_domain_and_registry) {
       return std::string();
+    }
 
     // Multiple domains can map to the same eTLD+1, avoid skeleton generation
     // when possible.
@@ -118,8 +129,9 @@
       skeletons = it->second;
     }
 
-    if (SkeletonsMatch(navigated_domain.skeletons, skeletons))
+    if (SkeletonsMatch(navigated_domain.skeletons, skeletons)) {
       return engaged_site.host();
+    }
   }
   return std::string();
 }
@@ -127,10 +139,10 @@
 }  // namespace
 
 // static
-const char LookalikeUrlNavigationObserver::kHistogramName[] =
+const char LookalikeUrlNavigationThrottle::kHistogramName[] =
     "NavigationSuggestion.Event";
 
-LookalikeUrlNavigationObserver::DomainInfo::DomainInfo(
+LookalikeUrlNavigationThrottle::DomainInfo::DomainInfo(
     const std::string& arg_domain_and_registry,
     const url_formatter::IDNConversionResult& arg_idn_result,
     const url_formatter::Skeletons& arg_skeletons)
@@ -138,56 +150,122 @@
       idn_result(arg_idn_result),
       skeletons(arg_skeletons) {}
 
-LookalikeUrlNavigationObserver::DomainInfo::~DomainInfo() = default;
+LookalikeUrlNavigationThrottle::DomainInfo::~DomainInfo() = default;
 
-LookalikeUrlNavigationObserver::DomainInfo::DomainInfo(const DomainInfo&) =
+LookalikeUrlNavigationThrottle::DomainInfo::DomainInfo(const DomainInfo&) =
     default;
 
-LookalikeUrlNavigationObserver::LookalikeUrlNavigationObserver(
-    content::WebContents* web_contents)
-    : WebContentsObserver(web_contents),
-      profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
+LookalikeUrlNavigationThrottle::LookalikeUrlNavigationThrottle(
+    content::NavigationHandle* navigation_handle)
+    : content::NavigationThrottle(navigation_handle),
+      interstitials_enabled_(base::FeatureList::IsEnabled(
+          features::kLookalikeUrlNavigationSuggestionsUI)),
+      profile_(Profile::FromBrowserContext(
+          navigation_handle->GetWebContents()->GetBrowserContext())),
       weak_factory_(this) {}
 
-LookalikeUrlNavigationObserver::~LookalikeUrlNavigationObserver() {}
+LookalikeUrlNavigationThrottle::~LookalikeUrlNavigationThrottle() {}
 
-void LookalikeUrlNavigationObserver::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
+ThrottleCheckResult LookalikeUrlNavigationThrottle::HandleThrottleRequest(
+    const GURL& url) {
+  content::NavigationHandle* handle = navigation_handle();
+  content::WebContents* web_contents = handle->GetWebContents();
+
   // Ignore subframe and same document navigations.
-  if (!navigation_handle->IsInMainFrame() ||
-      navigation_handle->IsSameDocument())
-    return;
+  if (!handle->IsInMainFrame() || handle->IsSameDocument()) {
+    return content::NavigationThrottle::PROCEED;
+  }
 
-  // If the navigation was not committed, it means either the page was a
-  // download or error 204/205, or the navigation never left the previous
-  // URL. Basically, this isn't a problem since we stayed at the existing URL.
-  // Also ignore any navigation errors.
-  if (!navigation_handle->HasCommitted() ||
-      navigation_handle->GetNetErrorCode() != net::OK)
-    return;
+  if (!url.SchemeIsHTTPOrHTTPS()) {
+    return content::NavigationThrottle::PROCEED;
+  }
 
-  const GURL url = navigation_handle->GetURL();
-  if (!url.SchemeIsHTTPOrHTTPS())
-    return;
-
-  // If the user has engaged with this site, don't show any lookalike
-  // navigation suggestions.
-  if (SiteEngagementService::Get(profile_)->IsEngagementAtLeast(
-          url, blink::mojom::EngagementLevel::MEDIUM))
-    return;
+  // If the URL is in the allowlist, don't show any warning.
+  LookalikeUrlAllowlist* allowlist =
+      LookalikeUrlAllowlist::GetOrCreateAllowlist(web_contents);
+  if (allowlist->IsDomainInList(url.host())) {
+    return content::NavigationThrottle::PROCEED;
+  }
 
   const DomainInfo navigated_domain = GetDomainInfo(url);
   if (navigated_domain.domain_and_registry.empty() ||
-      IsTopDomain(navigated_domain))
-    return;
+      IsTopDomain(navigated_domain)) {
+    return content::NavigationThrottle::PROCEED;
+  }
 
-  LookalikeUrlService::Get(profile_)->GetEngagedSites(
-      base::BindOnce(&LookalikeUrlNavigationObserver::PerformChecks,
-                     weak_factory_.GetWeakPtr(), url, navigated_domain));
+  LookalikeUrlService* service = LookalikeUrlService::Get(profile_);
+  if (service->UpdateEngagedSites(
+          base::BindOnce(&LookalikeUrlNavigationThrottle::PerformChecksDeferred,
+                         weak_factory_.GetWeakPtr(), url, navigated_domain))) {
+    // If we're not going to show an interstitial, there's no reason to delay
+    // the navigation any further.
+    if (!interstitials_enabled_) {
+      return content::NavigationThrottle::PROCEED;
+    }
+    return content::NavigationThrottle::DEFER;
+  }
+
+  return PerformChecks(url, navigated_domain, service->GetLatestEngagedSites());
 }
 
-LookalikeUrlNavigationObserver::DomainInfo
-LookalikeUrlNavigationObserver::GetDomainInfo(const GURL& url) {
+ThrottleCheckResult LookalikeUrlNavigationThrottle::WillProcessResponse() {
+  if (navigation_handle()->GetNetErrorCode() != net::OK) {
+    return content::NavigationThrottle::PROCEED;
+  }
+  return HandleThrottleRequest(navigation_handle()->GetURL());
+}
+
+ThrottleCheckResult LookalikeUrlNavigationThrottle::WillRedirectRequest() {
+  const std::vector<GURL>& chain = navigation_handle()->GetRedirectChain();
+
+  // WillRedirectRequest is called after a redirect occurs, so the end of the
+  // chain is the URL that was redirected to. We need to check the preceding URL
+  // that caused the redirection. The final URL in the chain is checked either:
+  //  - after the next redirection (when there is a longer chain), or
+  //  - by WillProcessResponse (before content is rendered).
+  if (chain.size() < 2) {
+    return content::NavigationThrottle::PROCEED;
+  }
+  return HandleThrottleRequest(chain[chain.size() - 2]);
+}
+
+const char* LookalikeUrlNavigationThrottle::GetNameForLogging() {
+  return "LookalikeUrlNavigationThrottle";
+}
+
+ThrottleCheckResult LookalikeUrlNavigationThrottle::ShowInterstitial(
+    const GURL& safe_url,
+    const GURL& url) {
+  content::NavigationHandle* handle = navigation_handle();
+  content::WebContents* web_contents = handle->GetWebContents();
+
+  auto controller = std::make_unique<LookalikeUrlControllerClient>(
+      web_contents, url, safe_url);
+
+  std::unique_ptr<LookalikeUrlInterstitialPage> blocking_page(
+      new LookalikeUrlInterstitialPage(web_contents, safe_url,
+                                       std::move(controller)));
+
+  base::Optional<std::string> error_page_contents =
+      blocking_page->GetHTMLContents();
+
+  security_interstitials::SecurityInterstitialTabHelper::AssociateBlockingPage(
+      web_contents, handle->GetNavigationId(), std::move(blocking_page));
+
+  return ThrottleCheckResult(content::NavigationThrottle::CANCEL,
+                             net::ERR_BLOCKED_BY_CLIENT, error_page_contents);
+}
+
+std::unique_ptr<LookalikeUrlNavigationThrottle>
+LookalikeUrlNavigationThrottle::MaybeCreateNavigationThrottle(
+    content::NavigationHandle* navigation_handle) {
+  // For metrics, we always insert the throttle
+  return std::make_unique<LookalikeUrlNavigationThrottle>(navigation_handle);
+}
+
+// static
+LookalikeUrlNavigationThrottle::DomainInfo
+LookalikeUrlNavigationThrottle::GetDomainInfo(const GURL& url) {
   // Perform all computations on eTLD+1.
   const std::string domain_and_registry = GetETLDPlusOne(url);
   // eTLD+1 can be empty for private domains.
@@ -203,15 +281,42 @@
   return DomainInfo(domain_and_registry, idn_result, skeletons);
 }
 
-void LookalikeUrlNavigationObserver::PerformChecks(
+void LookalikeUrlNavigationThrottle::PerformChecksDeferred(
     const GURL& url,
     const DomainInfo& navigated_domain,
-    const std::vector<GURL>& engaged_sites) {
+    const std::set<GURL>& engaged_sites) {
+  ThrottleCheckResult result = LookalikeUrlNavigationThrottle::PerformChecks(
+      url, navigated_domain, engaged_sites);
+
+  if (!interstitials_enabled_) {
+    return;
+  }
+
+  if (result.action() == content::NavigationThrottle::PROCEED) {
+    Resume();
+    return;
+  }
+
+  CancelDeferredNavigation(result);
+}
+
+ThrottleCheckResult LookalikeUrlNavigationThrottle::PerformChecks(
+    const GURL& url,
+    const DomainInfo& navigated_domain,
+    const std::set<GURL>& engaged_sites) {
   std::string matched_domain;
   MatchType match_type;
+
+  // Ensure that this URL is not already engaged. We can't use the synchronous
+  // SiteEngagementService::IsEngagementAtLeast as it has side effects. We check
+  // in PerformChecks to ensure we have up-to-date engaged_sites.
+  if (base::ContainsKey(engaged_sites, url.GetOrigin())) {
+    return content::NavigationThrottle::PROCEED;
+  }
+
   if (!GetMatchingDomain(navigated_domain, engaged_sites, &matched_domain,
                          &match_type)) {
-    return;
+    return content::NavigationThrottle::PROCEED;
   }
 
   DCHECK(!matched_domain.empty());
@@ -222,24 +327,22 @@
 
   ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
   CHECK(ukm_recorder);
-  ukm::SourceId source_id =
-      ukm::GetSourceIdForWebContentsDocument(web_contents());
+  ukm::SourceId source_id = ukm::ConvertToSourceId(
+      navigation_handle()->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
   ukm::builders::LookalikeUrl_NavigationSuggestion(source_id)
       .SetMatchType(static_cast<int>(match_type))
       .Record(ukm_recorder);
 
-  if (base::FeatureList::IsEnabled(
-          features::kLookalikeUrlNavigationSuggestionsUI)) {
-    RecordEvent(NavigationSuggestionEvent::kInfobarShown);
-    AlternateNavInfoBarDelegate::CreateForLookalikeUrlNavigation(
-        web_contents(), base::UTF8ToUTF16(matched_domain), suggested_url, url,
-        base::BindOnce(RecordEvent, NavigationSuggestionEvent::kLinkClicked));
+  if (interstitials_enabled_) {
+    return ShowInterstitial(suggested_url, url);
   }
+
+  return content::NavigationThrottle::PROCEED;
 }
 
-bool LookalikeUrlNavigationObserver::GetMatchingDomain(
+bool LookalikeUrlNavigationThrottle::GetMatchingDomain(
     const DomainInfo& navigated_domain,
-    const std::vector<GURL>& engaged_sites,
+    const std::set<GURL>& engaged_sites,
     std::string* matched_domain,
     MatchType* match_type) {
   DCHECK(!navigated_domain.domain_and_registry.empty());
@@ -286,7 +389,7 @@
 }
 
 // static
-bool LookalikeUrlNavigationObserver::IsEditDistanceAtMostOne(
+bool LookalikeUrlNavigationThrottle::IsEditDistanceAtMostOne(
     const base::string16& str1,
     const base::string16& str2) {
   if (str1.size() > str2.size() + 1 || str2.size() > str1.size() + 1) {
@@ -329,7 +432,7 @@
 }
 
 // static
-std::string LookalikeUrlNavigationObserver::GetSimilarDomainFromTop500(
+std::string LookalikeUrlNavigationThrottle::GetSimilarDomainFromTop500(
     const DomainInfo& navigated_domain) {
   if (!url_formatter::top_domains::IsEditDistanceCandidate(
           navigated_domain.domain_and_registry)) {
@@ -361,16 +464,3 @@
   }
   return std::string();
 }
-
-// static
-void LookalikeUrlNavigationObserver::CreateForWebContents(
-    content::WebContents* web_contents) {
-  DCHECK(web_contents);
-  if (!FromWebContents(web_contents)) {
-    web_contents->SetUserData(
-        UserDataKey(),
-        std::make_unique<LookalikeUrlNavigationObserver>(web_contents));
-  }
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(LookalikeUrlNavigationObserver)
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
new file mode 100644
index 0000000..4746c3c4
--- /dev/null
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
@@ -0,0 +1,126 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_THROTTLE_H_
+#define CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_THROTTLE_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/engagement/site_engagement_details.mojom.h"
+#include "components/url_formatter/url_formatter.h"
+#include "content/public/browser/navigation_throttle.h"
+
+namespace content {
+class NavigationHandle;
+}  // namespace content
+
+class Profile;
+
+// Observes navigations and shows an interstitial if the navigated domain name
+// is visually similar to a top domain or a domain with a site engagement score.
+class LookalikeUrlNavigationThrottle : public content::NavigationThrottle {
+ public:
+  // Used for metrics. Multiple events can occur per navigation.
+  enum class NavigationSuggestionEvent {
+    kNone = 0,
+    // Interstitial results recorded using security_interstitials::MetricsHelper
+    // kInfobarShown = 1,
+    // kLinkClicked = 2,
+    kMatchTopSite = 3,
+    kMatchSiteEngagement = 4,
+    kMatchEditDistance = 5,
+
+    // Append new items to the end of the list above; do not modify or
+    // replace existing values. Comment out obsolete items.
+    kMaxValue = kMatchEditDistance,
+  };
+
+  // Used for UKM. There is only a single MatchType per navigation.
+  enum class MatchType {
+    kNone = 0,
+    kTopSite = 1,
+    kSiteEngagement = 2,
+    kEditDistance = 3,
+
+    // Append new items to the end of the list above; do not modify or replace
+    // existing values. Comment out obsolete items.
+    kMaxValue = kEditDistance,
+  };
+
+  struct DomainInfo {
+    const std::string domain_and_registry;
+    const url_formatter::IDNConversionResult idn_result;
+    const url_formatter::Skeletons skeletons;
+    DomainInfo(const std::string& arg_domain_and_registry,
+               const url_formatter::IDNConversionResult& arg_idn_result,
+               const url_formatter::Skeletons& arg_skeletons);
+    ~DomainInfo();
+    DomainInfo(const DomainInfo& other);
+  };
+
+  static const char kHistogramName[];
+
+  explicit LookalikeUrlNavigationThrottle(content::NavigationHandle* handle);
+  ~LookalikeUrlNavigationThrottle() override;
+
+  // content::NavigationThrottle:
+  ThrottleCheckResult WillProcessResponse() override;
+  ThrottleCheckResult WillRedirectRequest() override;
+  const char* GetNameForLogging() override;
+
+  static std::unique_ptr<LookalikeUrlNavigationThrottle>
+  MaybeCreateNavigationThrottle(content::NavigationHandle* navigation_handle);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(LookalikeUrlNavigationThrottleTest,
+                           IsEditDistanceAtMostOne);
+
+  ThrottleCheckResult HandleThrottleRequest(const GURL& url);
+
+  static DomainInfo GetDomainInfo(const GURL& url);
+
+  // Performs synchronous top domain and engaged site checks on the navigated
+  // |url|. Uses |engaged_sites| for the engaged site checks.
+  ThrottleCheckResult PerformChecks(const GURL& url,
+                                    const DomainInfo& navigated_domain,
+                                    const std::set<GURL>& engaged_sites);
+
+  // A void-returning variant, only used with deferred throttle results.
+  void PerformChecksDeferred(const GURL& url,
+                             const DomainInfo& navigated_domain,
+                             const std::set<GURL>& engaged_sites);
+
+  // Returns true if a domain is visually similar to the hostname of |url|. The
+  // matching domain can be a top domain or an engaged site. Similarity check
+  // is made using both visual skeleton and edit distance comparison. If this
+  // returns true, match details will be written into |matched_domain| and
+  // |match_type|. They cannot be nullptr.
+  bool GetMatchingDomain(const DomainInfo& navigated_domain,
+                         const std::set<GURL>& engaged_sites,
+                         std::string* matched_domain,
+                         MatchType* match_type);
+
+  // Returns if the Levenshtein distance between |str1| and |str2| is at most 1.
+  // This has O(max(n,m)) complexity as opposed to O(n*m) of the usual edit
+  // distance computation.
+  static bool IsEditDistanceAtMostOne(const base::string16& str1,
+                                      const base::string16& str2);
+
+  // Returns the first matching top domain with an edit distance of at most one
+  // to |domain_and_registry|.
+  static std::string GetSimilarDomainFromTop500(const DomainInfo& domain_info);
+
+  ThrottleCheckResult ShowInterstitial(const GURL& safe_domain,
+                                       const GURL& url);
+
+  bool interstitials_enabled_;
+
+  Profile* profile_;
+  base::WeakPtrFactory<LookalikeUrlNavigationThrottle> weak_factory_;
+};
+
+#endif  // CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
new file mode 100644
index 0000000..8173894
--- /dev/null
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -0,0 +1,733 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "chrome/browser/engagement/site_engagement_score.h"
+#include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/history/history_test_utils.h"
+#include "chrome/browser/lookalikes/lookalike_url_interstitial_page.h"
+#include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h"
+#include "chrome/browser/lookalikes/lookalike_url_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/security_interstitials/content/security_interstitial_page.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source.h"
+#include "ui/base/window_open_disposition.h"
+
+namespace {
+
+using security_interstitials::MetricsHelper;
+using security_interstitials::SecurityInterstitialCommand;
+using UkmEntry = ukm::builders::LookalikeUrl_NavigationSuggestion;
+using NavigationSuggestionEvent =
+    LookalikeUrlNavigationThrottle::NavigationSuggestionEvent;
+
+enum class UIEnabled { kDisabled, kEnabled };
+
+// An engagement score above MEDIUM.
+const int kHighEngagement = 20;
+
+// An engagement score below MEDIUM.
+const int kLowEngagement = 1;
+
+// The UMA metric names registered by metrics_helper
+const char kInterstitialDecisionMetric[] = "interstitial.lookalike.decision";
+const char kInterstitialInteractionMetric[] =
+    "interstitial.lookalike.interaction";
+
+// The domains here should not private domains (e.g. site.test), otherwise they
+// might test the wrong thing. Also note that site5.com is in the top domain
+// list, so it shouldn't be used here.
+struct SiteEngagementTestCase {
+  const char* const navigated;
+  const char* const suggested;
+} kSiteEngagementTestCases[] = {
+    {"sité1.com", "site1.com"},
+    {"mail.www.sité1.com", "site1.com"},
+
+    // These should match since the comparison uses eTLD+1s.
+    {"sité2.com", "www.site2.com"},
+    {"mail.sité2.com", "www.site2.com"},
+
+    {"síté3.com", "sité3.com"},
+    {"mail.síté3.com", "sité3.com"},
+
+    {"síté4.com", "www.sité4.com"},
+    {"mail.síté4.com", "www.sité4.com"},
+};
+
+static std::unique_ptr<net::test_server::HttpResponse>
+NetworkErrorResponseHandler(const net::test_server::HttpRequest& request) {
+  return std::unique_ptr<net::test_server::HttpResponse>(
+      new net::test_server::RawHttpResponse("", ""));
+}
+
+security_interstitials::SecurityInterstitialPage* GetCurrentInterstitial(
+    content::WebContents* web_contents) {
+  security_interstitials::SecurityInterstitialTabHelper* helper =
+      security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
+          web_contents);
+  if (!helper) {
+    return nullptr;
+  }
+  return helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting();
+}
+
+security_interstitials::SecurityInterstitialPage::TypeID GetInterstitialType(
+    content::WebContents* web_contents) {
+  security_interstitials::SecurityInterstitialPage* page =
+      GetCurrentInterstitial(web_contents);
+  if (!page) {
+    return nullptr;
+  }
+  return page->GetTypeForTesting();
+}
+
+void SendInterstitialCommand(content::WebContents* web_contents,
+                             SecurityInterstitialCommand command) {
+  GetCurrentInterstitial(web_contents)
+      ->CommandReceived(base::NumberToString(command));
+}
+
+}  // namespace
+
+class LookalikeUrlNavigationThrottleBrowserTest
+    : public InProcessBrowserTest,
+      public testing::WithParamInterface<UIEnabled> {
+ protected:
+  // Sets the absolute Site Engagement |score| for the testing origin.
+  static void SetEngagementScore(Browser* browser,
+                                 const GURL& url,
+                                 double score) {
+    SiteEngagementService::Get(browser->profile())
+        ->ResetBaseScoreForURL(url, score);
+  }
+
+  void SetUp() override {
+    if (ui_enabled()) {
+      feature_list_.InitAndEnableFeature(
+          features::kLookalikeUrlNavigationSuggestionsUI);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          features::kLookalikeUrlNavigationSuggestionsUI);
+    }
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+    test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+
+    const base::Time kNow = base::Time::FromDoubleT(1000);
+    test_clock_.SetNow(kNow);
+
+    LookalikeUrlService* lookalike_service =
+        LookalikeUrlService::Get(browser()->profile());
+    lookalike_service->SetClockForTesting(&test_clock_);
+  }
+
+  GURL GetURL(const char* hostname) const {
+    return embedded_test_server()->GetURL(hostname, "/title1.html");
+  }
+
+  GURL GetLongRedirect(const char* via_hostname1,
+                       const char* via_hostname2,
+                       const char* dest_hostname) const {
+    GURL dest = GetURL(dest_hostname);
+    GURL mid = embedded_test_server()->GetURL(
+        via_hostname2, "/server-redirect?" + dest.spec());
+    return embedded_test_server()->GetURL(via_hostname1,
+                                          "/server-redirect?" + mid.spec());
+  }
+
+  // Checks that UKM recorded a metric for each URL in |navigated_urls|.
+  void CheckUkm(const std::vector<GURL>& navigated_urls,
+                LookalikeUrlNavigationThrottle::MatchType match_type) {
+    auto entries = test_ukm_recorder()->GetEntriesByName(UkmEntry::kEntryName);
+    ASSERT_EQ(navigated_urls.size(), entries.size());
+    int entry_count = 0;
+    for (const auto* const entry : entries) {
+      test_ukm_recorder()->ExpectEntrySourceHasUrl(entry,
+                                                   navigated_urls[entry_count]);
+      test_ukm_recorder()->ExpectEntryMetric(entry, "MatchType",
+                                             static_cast<int>(match_type));
+      entry_count++;
+    }
+  }
+
+  // Checks that UKM did not record any lookalike URL metrics.
+  void CheckNoUkm() {
+    EXPECT_TRUE(
+        test_ukm_recorder()->GetEntriesByName(UkmEntry::kEntryName).empty());
+  }
+
+  void TestInterstitialNotShown(Browser* browser, const GURL& navigated_url) {
+    content::WebContents* web_contents =
+        browser->tab_strip_model()->GetActiveWebContents();
+    {
+      content::TestNavigationObserver navigation_observer(web_contents, 1);
+      NavigateToURL(browser, navigated_url);
+      navigation_observer.Wait();
+      EXPECT_EQ(nullptr, GetCurrentInterstitial(web_contents));
+    }
+    {
+      // Navigate to an empty page. This will happen after any
+      // LookalikeUrlService tasks, so will effectively wait for those tasks to
+      // finish.
+      content::TestNavigationObserver navigation_observer(web_contents, 1);
+      NavigateToURL(browser, GURL("about:blank"));
+      navigation_observer.Wait();
+      EXPECT_EQ(nullptr, GetCurrentInterstitial(web_contents));
+    }
+  }
+
+  // Tests only that the interstitial is shown (when enabled) when the user
+  // tries to visit the provided URL.
+  void TestOnlyInterstitialShown(Browser* browser, const GURL& navigated_url) {
+    if (!ui_enabled()) {
+      return;
+    }
+
+    content::WebContents* web_contents =
+        browser->tab_strip_model()->GetActiveWebContents();
+
+    content::TestNavigationObserver navigation_observer(web_contents, 1);
+    NavigateToURL(browser, navigated_url);
+    navigation_observer.Wait();
+
+    EXPECT_EQ(LookalikeUrlInterstitialPage::kTypeForTesting,
+              GetInterstitialType(web_contents));
+  }
+
+  // Tests that the histogram event |expected_event| is recorded. If the UI is
+  // enabled, additinal events for interstitial display and link click will also
+  // be tested.
+  void TestHistogramEventsRecordedAndInterstitialShown(
+      Browser* browser,
+      base::HistogramTester* histograms,
+      const GURL& navigated_url,
+      const GURL& expected_suggested_url,
+      LookalikeUrlNavigationThrottle::NavigationSuggestionEvent
+          expected_event) {
+    if (ui_enabled()) {
+      // If the feature is enabled, the UI will be displayed.
+      TestInterstitialShown(browser, navigated_url, expected_suggested_url);
+      histograms->ExpectTotalCount(
+          LookalikeUrlNavigationThrottle::kHistogramName, 1);
+      histograms->ExpectBucketCount(
+          LookalikeUrlNavigationThrottle::kHistogramName, expected_event, 1);
+
+      histograms->ExpectTotalCount(kInterstitialDecisionMetric, 2);
+      histograms->ExpectBucketCount(kInterstitialDecisionMetric,
+                                    MetricsHelper::SHOW, 1);
+      histograms->ExpectBucketCount(kInterstitialDecisionMetric,
+                                    MetricsHelper::DONT_PROCEED, 1);
+
+      histograms->ExpectTotalCount(kInterstitialInteractionMetric, 1);
+      histograms->ExpectBucketCount(kInterstitialInteractionMetric,
+                                    MetricsHelper::TOTAL_VISITS, 1);
+      return;
+    }
+
+    TestInterstitialNotShown(browser, navigated_url);
+    histograms->ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                                 1);
+    histograms->ExpectBucketCount(
+        LookalikeUrlNavigationThrottle::kHistogramName, expected_event, 1);
+  }
+
+  // Tests that the histogram event |expected_event| is recorded. If the UI is
+  // enabled, additional events for interstitial display and dismissal will also
+  // be tested.
+  void TestHistogramEventsRecordedWhenInterstitialIgnored(
+      Browser* browser,
+      base::HistogramTester* histograms,
+      const GURL& navigated_url,
+      LookalikeUrlNavigationThrottle::NavigationSuggestionEvent
+          expected_event) {
+    if (ui_enabled()) {
+      // If the feature is enabled, the UI will be displayed.
+      DisplayThenIgnoreInterstitial(browser, navigated_url);
+      histograms->ExpectTotalCount(
+          LookalikeUrlNavigationThrottle::kHistogramName, 1);
+      histograms->ExpectBucketCount(
+          LookalikeUrlNavigationThrottle::kHistogramName, expected_event, 1);
+
+      histograms->ExpectTotalCount(kInterstitialDecisionMetric, 2);
+      histograms->ExpectBucketCount(kInterstitialDecisionMetric,
+                                    MetricsHelper::SHOW, 1);
+      histograms->ExpectBucketCount(kInterstitialDecisionMetric,
+                                    MetricsHelper::PROCEED, 1);
+
+      histograms->ExpectTotalCount(kInterstitialInteractionMetric, 1);
+      histograms->ExpectBucketCount(kInterstitialInteractionMetric,
+                                    MetricsHelper::TOTAL_VISITS, 1);
+
+      TestInterstitialNotShown(browser, navigated_url);
+
+      return;
+    }
+
+    TestInterstitialNotShown(browser, navigated_url);
+    histograms->ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                                 1);
+    histograms->ExpectBucketCount(
+        LookalikeUrlNavigationThrottle::kHistogramName, expected_event, 1);
+  }
+
+  ukm::TestUkmRecorder* test_ukm_recorder() { return test_ukm_recorder_.get(); }
+
+  base::SimpleTestClock* test_clock() { return &test_clock_; }
+
+ private:
+  bool ui_enabled() const { return GetParam() == UIEnabled::kEnabled; }
+
+  // Simulates a link click navigation. We don't use
+  // ui_test_utils::NavigateToURL(const GURL&) because it simulates the user
+  // typing the URL, causing the site to have a site engagement score of at
+  // least LOW.
+  static void NavigateToURL(Browser* browser, const GURL& url) {
+    NavigateParams params(browser, url, ui::PAGE_TRANSITION_LINK);
+    params.initiator_origin = url::Origin::Create(GURL("about:blank"));
+    params.disposition = WindowOpenDisposition::CURRENT_TAB;
+    params.is_renderer_initiated = true;
+    ui_test_utils::NavigateToURL(&params);
+  }
+
+  // Checks that navigating to |navigated_url| results in displaying an
+  // interstitial suggesting navigation to |expected_suggestion_url|.
+  // Both |navigated_url| and |expected_suggested_url| can be ASCII or IDN.
+  static void TestInterstitialShown(Browser* browser,
+                                    const GURL& navigated_url,
+                                    const GURL& expected_suggested_url) {
+    history::HistoryService* const history_service =
+        HistoryServiceFactory::GetForProfile(
+            browser->profile(), ServiceAccessType::EXPLICIT_ACCESS);
+    ui_test_utils::WaitForHistoryToLoad(history_service);
+
+    content::WebContents* web_contents =
+        browser->tab_strip_model()->GetActiveWebContents();
+
+    EXPECT_EQ(nullptr, GetCurrentInterstitial(web_contents));
+    {
+      content::TestNavigationObserver navigation_observer(web_contents, 1);
+      NavigateToURL(browser, navigated_url);
+      navigation_observer.Wait();
+    }
+
+    EXPECT_EQ(LookalikeUrlInterstitialPage::kTypeForTesting,
+              GetInterstitialType(web_contents));
+
+    // Clicking the link in the interstitial should remove the interstitial and
+    // navigate to the suggested URL.
+    {
+      content::TestNavigationObserver navigation_observer(web_contents, 1);
+      SendInterstitialCommand(web_contents,
+                              SecurityInterstitialCommand::CMD_DONT_PROCEED);
+      navigation_observer.Wait();
+    }
+
+    EXPECT_EQ(nullptr, GetCurrentInterstitial(web_contents));
+    EXPECT_EQ(expected_suggested_url, web_contents->GetURL());
+
+    // Clicking the link in the interstitial should also remove the original URL
+    // from history.
+    ui_test_utils::HistoryEnumerator enumerator(browser->profile());
+    EXPECT_FALSE(base::ContainsValue(enumerator.urls(), navigated_url));
+  }
+
+  // Checks that navigating to |navigated_url| results in displaying an
+  // interstitial, that, when ignored, proceeds to |navigated_url|.
+  // |navigated_url| can be ASCII or IDN.
+  static void DisplayThenIgnoreInterstitial(Browser* browser,
+                                            const GURL& navigated_url) {
+    history::HistoryService* const history_service =
+        HistoryServiceFactory::GetForProfile(
+            browser->profile(), ServiceAccessType::EXPLICIT_ACCESS);
+    ui_test_utils::WaitForHistoryToLoad(history_service);
+
+    content::WebContents* web_contents =
+        browser->tab_strip_model()->GetActiveWebContents();
+
+    EXPECT_EQ(nullptr, GetCurrentInterstitial(web_contents));
+    {
+      content::TestNavigationObserver navigation_observer(web_contents, 1);
+      NavigateToURL(browser, navigated_url);
+      navigation_observer.Wait();
+    }
+
+    EXPECT_EQ(LookalikeUrlInterstitialPage::kTypeForTesting,
+              GetInterstitialType(web_contents));
+
+    // Clicking the ignore button in the interstitial should remove the
+    // interstitial and navigate to the original URL.
+    {
+      content::TestNavigationObserver navigation_observer(web_contents, 1);
+      SendInterstitialCommand(web_contents,
+                              SecurityInterstitialCommand::CMD_PROCEED);
+      navigation_observer.Wait();
+    }
+
+    EXPECT_EQ(nullptr, GetCurrentInterstitial(web_contents));
+    EXPECT_EQ(navigated_url, web_contents->GetURL());
+
+    // Clicking the link should result in the original URL appearing in history.
+    ui_test_utils::HistoryEnumerator enumerator(browser->profile());
+    EXPECT_TRUE(base::ContainsValue(enumerator.urls(), navigated_url));
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
+  base::SimpleTestClock test_clock_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         LookalikeUrlNavigationThrottleBrowserTest,
+                         ::testing::Values(UIEnabled::kDisabled,
+                                           UIEnabled::kEnabled));
+
+// Navigating to a non-IDN shouldn't show an interstitial or record metrics.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       NonIdn_NoMatch) {
+  TestInterstitialNotShown(browser(), GetURL("google.com"));
+  CheckNoUkm();
+}
+
+// Navigating to a domain whose visual representation does not look like a
+// top domain shouldn't show an interstitial or record metrics.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       NonTopDomainIdn_NoInterstitial) {
+  TestInterstitialNotShown(browser(), GetURL("éxample.com"));
+  CheckNoUkm();
+}
+
+// If the user has engaged with the domain before, metrics shouldn't be recorded
+// and the interstitial shouldn't be shown, even if the domain is visually
+// similar to a top domain.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Idn_TopDomain_EngagedSite_NoMatch) {
+  const GURL url = GetURL("googlé.com");
+  SetEngagementScore(browser(), url, kHighEngagement);
+  TestInterstitialNotShown(browser(), url);
+  CheckNoUkm();
+}
+
+// Navigate to a domain whose visual representation looks like a top domain.
+// This should record metrics. It should also show a lookalike warning
+// interstitial if configured via a feature param.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Idn_TopDomain_Match) {
+  base::HistogramTester histograms;
+
+  const GURL kNavigatedUrl = GetURL("googlé.com");
+  const GURL kExpectedSuggestedUrl = GetURL("google.com");
+  // Even if the navigated site has a low engagement score, it should be
+  // considered for lookalike suggestions.
+  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+
+  TestHistogramEventsRecordedAndInterstitialShown(
+      browser(), &histograms, kNavigatedUrl, kExpectedSuggestedUrl,
+      NavigationSuggestionEvent::kMatchTopSite);
+
+  CheckUkm({kNavigatedUrl},
+           LookalikeUrlNavigationThrottle::MatchType::kTopSite);
+}
+
+// The navigated domain itself is a top domain or a subdomain of a top domain.
+// Should not record metrics. The top domain list doesn't contain any IDN, so
+// this only tests the case where the subdomains are IDNs.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       TopDomainIdnSubdomain_NoMatch) {
+  TestInterstitialNotShown(browser(), GetURL("tést.google.com"));
+  CheckNoUkm();
+
+  // blogspot.com is a private registry, so the eTLD+1 of "tést.blogspot.com" is
+  // itself, instead of just "blogspot.com". This is different than
+  // tést.google.com whose eTLD+1 is google.com, and it should be handled
+  // correctly.
+  TestInterstitialNotShown(browser(), GetURL("tést.blogspot.com"));
+  CheckNoUkm();
+}
+
+// Schemes other than HTTP and HTTPS should be ignored.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       TopDomainChromeUrl_NoMatch) {
+  TestInterstitialNotShown(browser(), GURL("chrome://googlé.com"));
+  CheckNoUkm();
+}
+
+// Navigate to a domain within an edit distance of 1 to a top domain.
+// This should record metrics. It should also show a lookalike warning
+// interstitial if configured via a feature param.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       EditDistance_TopDomain_Match) {
+  base::HistogramTester histograms;
+
+  // The skeleton of this domain, gooogle.corn, is one 1 edit away from
+  // google.corn, the skeleton of google.com.
+  const GURL kNavigatedUrl = GetURL("goooglé.com");
+  const GURL kExpectedSuggestedUrl = GetURL("google.com");
+  // Even if the navigated site has a low engagement score, it should be
+  // considered for lookalike suggestions.
+  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+
+  TestHistogramEventsRecordedAndInterstitialShown(
+      browser(), &histograms, kNavigatedUrl, kExpectedSuggestedUrl,
+      NavigationSuggestionEvent::kMatchEditDistance);
+
+  CheckUkm({kNavigatedUrl},
+           LookalikeUrlNavigationThrottle::MatchType::kEditDistance);
+}
+
+// Tests negative examples for the edit distance.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       EditDistance_TopDomain_NoMatch) {
+  // Matches google.com.tr but only differs in registry.
+  TestInterstitialNotShown(browser(), GetURL("google.com.tw"));
+  CheckNoUkm();
+
+  // Matches bing.com but is a top domain itself.
+  TestInterstitialNotShown(browser(), GetURL("ning.com"));
+  CheckNoUkm();
+
+  // Matches ask.com but is too short.
+  TestInterstitialNotShown(browser(), GetURL("bsk.com"));
+  CheckNoUkm();
+}
+
+// Test that the heuristics aren't triggered on net errors.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       NetError_NoMatch) {
+  // Create a test server that returns invalid responses.
+  net::EmbeddedTestServer custom_test_server;
+  custom_test_server.RegisterRequestHandler(
+      base::BindRepeating(&NetworkErrorResponseHandler));
+  ASSERT_TRUE(custom_test_server.Start());
+
+  // Matches google.com but page returns an invalid response.
+  TestInterstitialNotShown(
+      browser(), custom_test_server.GetURL("gooogle.com", "/title1.html"));
+  CheckNoUkm();
+
+  TestInterstitialNotShown(
+      browser(), custom_test_server.GetURL("googlé.com", "/title1.html"));
+  CheckNoUkm();
+
+  SetEngagementScore(browser(), GURL("http://site1.com"), kHighEngagement);
+  TestInterstitialNotShown(
+      browser(), custom_test_server.GetURL("sité1.com", "/title1.html"));
+  CheckNoUkm();
+}
+
+// Navigate to a domain whose visual representation looks like a domain with a
+// site engagement score above a certain threshold. This should record metrics.
+// It should also show lookalike warning interstitial if configured via
+// a feature param.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Idn_SiteEngagement_Match) {
+  const char* const kEngagedSites[] = {
+      "http://site1.com", "http://www.site2.com", "http://sité3.com",
+      "http://www.sité4.com"};
+
+  for (const char* const kSite : kEngagedSites) {
+    SetEngagementScore(browser(), GURL(kSite), kHighEngagement);
+  }
+
+  std::vector<GURL> ukm_urls;
+  for (const auto& test_case : kSiteEngagementTestCases) {
+    base::HistogramTester histograms;
+    const GURL kNavigatedUrl = GetURL(test_case.navigated);
+    const GURL kExpectedSuggestedUrl = GetURL(test_case.suggested);
+
+    // Even if the navigated site has a low engagement score, it should be
+    // considered for lookalike suggestions.
+    SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+    // Advance the clock to force LookalikeUrlService to fetch a new engaged
+    // site list.
+    test_clock()->Advance(base::TimeDelta::FromHours(1));
+
+    TestHistogramEventsRecordedAndInterstitialShown(
+        browser(), &histograms, kNavigatedUrl, kExpectedSuggestedUrl,
+        NavigationSuggestionEvent::kMatchSiteEngagement);
+
+    ukm_urls.push_back(kNavigatedUrl);
+    CheckUkm(ukm_urls,
+             LookalikeUrlNavigationThrottle::MatchType::kSiteEngagement);
+  }
+}
+
+// Similar to Idn_SiteEngagement_Match, but tests a single domain. Also checks
+// that the list of engaged sites in incognito and the main profile don't affect
+// each other.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Idn_SiteEngagement_Match_Incognito) {
+  const GURL kNavigatedUrl = GetURL("sité1.com");
+  const GURL kEngagedUrl = GetURL("site1.com");
+
+  // Set high engagement scores in the main profile and low engagement scores
+  // in incognito. Main profile should record metrics, incognito shouldn't.
+  Browser* incognito = CreateIncognitoBrowser();
+  LookalikeUrlService::Get(incognito->profile())
+      ->SetClockForTesting(test_clock());
+  SetEngagementScore(browser(), kEngagedUrl, kHighEngagement);
+  SetEngagementScore(incognito, kEngagedUrl, kLowEngagement);
+
+  std::vector<GURL> ukm_urls;
+  // Main profile should record metrics because there are engaged sites.
+  {
+    base::HistogramTester histograms;
+    // Advance the clock to force LookalikeUrlService to fetch a new engaged
+    // site list.
+    test_clock()->Advance(base::TimeDelta::FromHours(1));
+    TestHistogramEventsRecordedAndInterstitialShown(
+        browser(), &histograms, kNavigatedUrl, kEngagedUrl,
+        NavigationSuggestionEvent::kMatchSiteEngagement);
+
+    ukm_urls.push_back(kNavigatedUrl);
+    CheckUkm(ukm_urls,
+             LookalikeUrlNavigationThrottle::MatchType::kSiteEngagement);
+  }
+
+  // Incognito shouldn't record metrics because there are no engaged sites.
+  {
+    base::HistogramTester histograms;
+    test_clock()->Advance(base::TimeDelta::FromHours(1));
+    TestInterstitialNotShown(incognito, kNavigatedUrl);
+    histograms.ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                                0);
+  }
+
+  // Now reverse the scores: Set low engagement in the main profile and high
+  // engagement in incognito.
+  SetEngagementScore(browser(), kEngagedUrl, kLowEngagement);
+  SetEngagementScore(incognito, kEngagedUrl, kHighEngagement);
+
+  // Incognito should start recording metrics and main profile should stop.
+  {
+    base::HistogramTester histograms;
+    test_clock()->Advance(base::TimeDelta::FromHours(1));
+
+    TestHistogramEventsRecordedAndInterstitialShown(
+        incognito, &histograms, kNavigatedUrl, kEngagedUrl,
+        NavigationSuggestionEvent::kMatchSiteEngagement);
+    ukm_urls.push_back(kNavigatedUrl);
+    CheckUkm(ukm_urls,
+             LookalikeUrlNavigationThrottle::MatchType::kSiteEngagement);
+  }
+
+  // Main profile shouldn't record metrics because there are no engaged sites.
+  {
+    base::HistogramTester histograms;
+    test_clock()->Advance(base::TimeDelta::FromHours(1));
+    TestInterstitialNotShown(browser(), kNavigatedUrl);
+    histograms.ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                                0);
+  }
+}
+
+// Test that navigations to a site with a high engagement score shouldn't
+// record metrics or show interstitial.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Idn_SiteEngagement_Match_IgnoreHighlyEngagedSite) {
+  base::HistogramTester histograms;
+  SetEngagementScore(browser(), GURL("http://site-not-in-top-domain-list.com"),
+                     kHighEngagement);
+  const GURL high_engagement_url = GetURL("síte-not-ín-top-domaín-líst.com");
+  SetEngagementScore(browser(), high_engagement_url, kHighEngagement);
+  TestInterstitialNotShown(browser(), high_engagement_url);
+  histograms.ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                              0);
+}
+
+// Test that an engaged site with a scheme other than HTTP or HTTPS should be
+// ignored.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Idn_SiteEngagement_IgnoreChromeUrl) {
+  base::HistogramTester histograms;
+  SetEngagementScore(browser(),
+                     GURL("chrome://site-not-in-top-domain-list.com"),
+                     kHighEngagement);
+  const GURL low_engagement_url("http://síte-not-ín-top-domaín-líst.com");
+  SetEngagementScore(browser(), low_engagement_url, kLowEngagement);
+  TestInterstitialNotShown(browser(), low_engagement_url);
+  histograms.ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                              0);
+}
+
+// IDNs with a single label should be properly handled. There are two cases
+// where this might occur:
+// 1. The navigated URL is an IDN with a single label.
+// 2. One of the engaged sites is an IDN with a single label.
+// Neither of these should cause a crash.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       IdnWithSingleLabelShouldNotCauseACrash) {
+  base::HistogramTester histograms;
+
+  // Case 1: Navigating to an IDN with a single label shouldn't cause a crash.
+  TestInterstitialNotShown(browser(), GetURL("é"));
+
+  // Case 2: An IDN with a single label with a site engagement score shouldn't
+  // cause a crash.
+  SetEngagementScore(browser(), GURL("http://tést"), kHighEngagement);
+  TestInterstitialNotShown(browser(), GetURL("tést.com"));
+
+  histograms.ExpectTotalCount(LookalikeUrlNavigationThrottle::kHistogramName,
+                              0);
+  CheckNoUkm();
+}
+
+// Ensure that dismissing the interstitial works, and the result is remembered
+// in the current tab.  This should record metrics on the first visit, but not
+// the second.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Interstitial_Dismiss) {
+  base::HistogramTester histograms;
+
+  const GURL kNavigatedUrl = GetURL("goooglé.com");
+  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+
+  TestHistogramEventsRecordedWhenInterstitialIgnored(
+      browser(), &histograms, kNavigatedUrl,
+      NavigationSuggestionEvent::kMatchEditDistance);
+
+  CheckUkm({kNavigatedUrl},
+           LookalikeUrlNavigationThrottle::MatchType::kEditDistance);
+}
+
+// Navigate to lookalike domains that redirect to benign domains. Ensure that
+// we display an interstitial along the way if configured via a feature param.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
+                       Interstitial_CapturesRedirects) {
+  {
+    // Verify it works when the lookalike domain is the first in the chain
+    const GURL kNavigatedUrl =
+        GetLongRedirect("goooglé.com", "example.net", "example.com");
+    SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+    TestOnlyInterstitialShown(browser(), kNavigatedUrl);
+  }
+  {
+    // ...or when it's later in the chain
+    const GURL kNavigatedUrl =
+        GetLongRedirect("example.net", "goooglé.com", "example.com");
+    SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+    TestOnlyInterstitialShown(browser(), kNavigatedUrl);
+  }
+}
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_observer_unittest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
similarity index 89%
rename from chrome/browser/lookalikes/lookalike_url_navigation_observer_unittest.cc
rename to chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
index 4308806..32d9263a 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_observer_unittest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/lookalikes/lookalike_url_navigation_observer.h"
+#include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h"
 
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(LookalikeUrlNavigationObserverTest, IsEditDistanceAtMostOne) {
+TEST(LookalikeUrlNavigationThrottleTest, IsEditDistanceAtMostOne) {
   const struct TestCase {
     const wchar_t* domain;
     const wchar_t* top_domain;
@@ -59,7 +59,7 @@
       {L"google.com", L"goooglé.com", false},
   };
   for (const TestCase& test_case : kTestCases) {
-    bool result = LookalikeUrlNavigationObserver::IsEditDistanceAtMostOne(
+    bool result = LookalikeUrlNavigationThrottle::IsEditDistanceAtMostOne(
         base::WideToUTF16(test_case.domain),
         base::WideToUTF16(test_case.top_domain));
     EXPECT_EQ(test_case.expected, result);
diff --git a/chrome/browser/lookalikes/lookalike_url_service.cc b/chrome/browser/lookalikes/lookalike_url_service.cc
index 8acd77f..e157ebef 100644
--- a/chrome/browser/lookalikes/lookalike_url_service.cc
+++ b/chrome/browser/lookalikes/lookalike_url_service.cc
@@ -77,20 +77,20 @@
   return LookalikeUrlServiceFactory::GetForProfile(profile);
 }
 
-void LookalikeUrlService::GetEngagedSites(EngagedSitesCallback callback) {
+bool LookalikeUrlService::UpdateEngagedSites(EngagedSitesCallback callback) {
   const base::Time now = clock_->Now();
 
   if (!last_engagement_fetch_time_.is_null()) {
     const base::TimeDelta elapsed = now - last_engagement_fetch_time_;
     if (elapsed <
         base::TimeDelta::FromSeconds(kEngagedSiteUpdateIntervalInSeconds)) {
-      std::move(callback).Run(engaged_sites_);
-      return;
+      return false;
     }
   }
+
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE,
-      {base::TaskPriority::BEST_EFFORT,
+      {base::TaskPriority::USER_BLOCKING,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
       base::BindOnce(
           &SiteEngagementService::GetAllDetailsInBackground, now,
@@ -98,17 +98,17 @@
               HostContentSettingsMapFactory::GetForProfile(profile_))),
       base::BindOnce(&LookalikeUrlService::OnFetchEngagedSites,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
+  return true;
+}
+
+const std::set<GURL> LookalikeUrlService::GetLatestEngagedSites() const {
+  return engaged_sites_;
 }
 
 void LookalikeUrlService::SetClockForTesting(base::Clock* clock) {
   clock_ = clock;
 }
 
-void LookalikeUrlService::ClearEngagedSitesForTesting() {
-  engaged_sites_.clear();
-  last_engagement_fetch_time_ = clock_->Now();
-}
-
 void LookalikeUrlService::OnFetchEngagedSites(
     EngagedSitesCallback callback,
     std::vector<mojom::SiteEngagementDetails> details) {
@@ -120,7 +120,7 @@
                                       blink::mojom::EngagementLevel::MEDIUM)) {
       continue;
     }
-    engaged_sites_.push_back(detail.origin);
+    engaged_sites_.insert(detail.origin);
   }
 
   last_engagement_fetch_time_ = clock_->Now();
diff --git a/chrome/browser/lookalikes/lookalike_url_service.h b/chrome/browser/lookalikes/lookalike_url_service.h
index 6a6fcfe..ca2d29d 100644
--- a/chrome/browser/lookalikes/lookalike_url_service.h
+++ b/chrome/browser/lookalikes/lookalike_url_service.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_SERVICE_H_
 #define CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_SERVICE_H_
 
+#include <set>
 #include <vector>
 
 #include "base/callback_forward.h"
@@ -30,19 +31,22 @@
   explicit LookalikeUrlService(Profile* profile);
   ~LookalikeUrlService() override;
 
-  using EngagedSitesCallback =
-      base::OnceCallback<void(const std::vector<GURL>&)>;
+  using EngagedSitesCallback = base::OnceCallback<void(const std::set<GURL>&)>;
 
   static LookalikeUrlService* Get(Profile* profile);
 
-  // Triggers an update to engaged site list and passes the most recent result
-  // |callback|. The list is updated only after a certain amount of time passes
-  // after the last update. As a result, calling this method may or may not
-  // change the contents of engaged_sites, depending on the timing.
-  void GetEngagedSites(EngagedSitesCallback callback);
+  // Checks whether the engaged site list is recently updated, and triggers
+  // an update to the list if not. This method will not update the contents of
+  // engaged_sites nor call |callback| if an update is not required.  The method
+  // returns whether or not an update was triggered (and thus whether the
+  // callback will be called).
+  bool UpdateEngagedSites(EngagedSitesCallback callback);
+
+  // Returns the _current_ list of engaged sites, without updating them if
+  // they're out of date.
+  const std::set<GURL> GetLatestEngagedSites() const;
 
   void SetClockForTesting(base::Clock* clock);
-  void ClearEngagedSitesForTesting();
 
  private:
   void OnFetchEngagedSites(EngagedSitesCallback callback,
@@ -51,7 +55,7 @@
   Profile* profile_;
   base::Clock* clock_;
   base::Time last_engagement_fetch_time_;
-  std::vector<GURL> engaged_sites_;
+  std::set<GURL> engaged_sites_;
   base::WeakPtrFactory<LookalikeUrlService> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(LookalikeUrlService);
diff --git a/chrome/browser/media/offscreen_tab.cc b/chrome/browser/media/offscreen_tab.cc
index 69ea13f..648f3d6 100644
--- a/chrome/browser/media/offscreen_tab.cc
+++ b/chrome/browser/media/offscreen_tab.cc
@@ -245,7 +245,9 @@
   callback.Run(false);
 }
 
-bool OffscreenTab::HandleContextMenu(const content::ContextMenuParams& params) {
+bool OffscreenTab::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
+    const content::ContextMenuParams& params) {
   // Context menus should never be shown.  Do nothing, but indicate the context
   // menu was shown so that default implementation in libcontent does not
   // attempt to do so on its own.
diff --git a/chrome/browser/media/offscreen_tab.h b/chrome/browser/media/offscreen_tab.h
index 5dee093..acb56a4 100644
--- a/chrome/browser/media/offscreen_tab.h
+++ b/chrome/browser/media/offscreen_tab.h
@@ -91,7 +91,8 @@
   void CanDownload(const GURL& url,
                    const std::string& request_method,
                    const base::RepeatingCallback<void(bool)>& callback) final;
-  bool HandleContextMenu(const content::ContextMenuParams& params) final;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) final;
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) final;
diff --git a/chrome/browser/metrics/assistant_service_metrics_provider.cc b/chrome/browser/metrics/assistant_service_metrics_provider.cc
index 5df7017..758c241 100644
--- a/chrome/browser/metrics/assistant_service_metrics_provider.cc
+++ b/chrome/browser/metrics/assistant_service_metrics_provider.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/metrics/assistant_service_metrics_provider.h"
 
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/arc/arc_prefs.h"
 #include "components/prefs/pref_service.h"
@@ -15,7 +15,7 @@
 
 void AssistantServiceMetricsProvider::ProvideCurrentSessionData(
     metrics::ChromeUserMetricsExtension* uma_proto_unused) {
-  if (arc::IsAssistantAllowedForProfile(
+  if (assistant::IsAssistantAllowedForProfile(
           ProfileManager::GetActiveUserProfile()) !=
       ash::mojom::AssistantAllowedState::ALLOWED) {
     return;
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index 84aefa86..b8e0c70 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -30,6 +30,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "content/public/common/resource_load_info.mojom.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "services/network/public/cpp/features.h"
 #include "ui/base/page_transition_types.h"
@@ -319,7 +320,7 @@
     const content::mojom::CommonNetworkInfoPtr& network_info =
         resource_load_info.network_info;
     ExtraRequestCompleteInfo extra_request_complete_info(
-        resource_load_info.url, network_info->ip_port_pair.value(),
+        resource_load_info.url, network_info->remote_endpoint.value(),
         render_frame_host->GetFrameTreeNodeId(), resource_load_info.was_cached,
         resource_load_info.raw_body_bytes, original_content_length,
         std::move(data_reduction_proxy_data), resource_load_info.resource_type,
@@ -353,7 +354,7 @@
 
 void MetricsWebContentsObserver::OnRequestComplete(
     const GURL& url,
-    const net::HostPortPair& host_port_pair,
+    const net::IPEndPoint& remote_endpoint,
     int frame_tree_node_id,
     const content::GlobalRequestID& request_id,
     content::RenderFrameHost* render_frame_host_or_null,
@@ -376,7 +377,7 @@
       request_id, render_frame_host_or_null, resource_type, creation_time);
   if (tracker) {
     ExtraRequestCompleteInfo extra_request_complete_info(
-        url, host_port_pair, frame_tree_node_id, was_cached, raw_body_bytes,
+        url, remote_endpoint, frame_tree_node_id, was_cached, raw_body_bytes,
         was_cached ? 0 : original_content_length,
         std::move(data_reduction_proxy_data), resource_type, net_error,
         std::move(load_timing_info));
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index 15021396..ba06b973 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -24,6 +24,10 @@
 #include "content/public/common/resource_type.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 
+namespace net {
+class IPEndPoint;
+}  // namespace net
+
 namespace content {
 class NavigationHandle;
 class RenderFrameHost;
@@ -126,7 +130,7 @@
   // frame requests when browser-side navigation is enabled.
   void OnRequestComplete(
       const GURL& url,
-      const net::HostPortPair& host_port_pair,
+      const net::IPEndPoint& remote_endpoint,
       int frame_tree_node_id,
       const content::GlobalRequestID& request_id,
       content::RenderFrameHost* render_frame_host_or_null,
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index 9b51a90..85c286b 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -26,6 +26,7 @@
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
 #include "extensions/buildflags/buildflags.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -1467,7 +1468,7 @@
   const auto request_id = navigation_simulator->GetGlobalRequestID();
 
   observer()->OnRequestComplete(
-      main_resource_url, net::HostPortPair(), frame_tree_node_id, request_id,
+      main_resource_url, net::IPEndPoint(), frame_tree_node_id, request_id,
       web_contents()->GetMainFrame(),
       content::ResourceType::RESOURCE_TYPE_MAIN_FRAME, false, nullptr, 0, 0,
       base::TimeTicks::Now(), net::OK, nullptr);
@@ -1479,7 +1480,7 @@
   // Deliver a second main frame resource. This one should be ignored, since the
   // specified |request_id| is no longer associated with any tracked page loads.
   observer()->OnRequestComplete(
-      main_resource_url, net::HostPortPair(), frame_tree_node_id, request_id,
+      main_resource_url, net::IPEndPoint(), frame_tree_node_id, request_id,
       web_contents()->GetMainFrame(),
       content::ResourceType::RESOURCE_TYPE_MAIN_FRAME, false, nullptr, 0, 0,
       base::TimeTicks::Now(), net::OK, nullptr);
@@ -1493,7 +1494,7 @@
   web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
   GURL loaded_resource_url("http://www.other.com/");
   observer()->OnRequestComplete(
-      loaded_resource_url, net::HostPortPair(),
+      loaded_resource_url, net::IPEndPoint(),
       web_contents()->GetMainFrame()->GetFrameTreeNodeId(),
       content::GlobalRequestID(), web_contents()->GetMainFrame(),
       content::RESOURCE_TYPE_SCRIPT, false, nullptr, 0, 0,
@@ -1519,7 +1520,7 @@
       content::WebContentsTester::CreateTestWebContents(browser_context(),
                                                         nullptr));
   observer()->OnRequestComplete(
-      GURL("http://www.other.com/"), net::HostPortPair(),
+      GURL("http://www.other.com/"), net::IPEndPoint(),
       other_web_contents->GetMainFrame()->GetFrameTreeNodeId(),
       content::GlobalRequestID(), other_web_contents->GetMainFrame(),
       content::RESOURCE_TYPE_SCRIPT, false, nullptr, 0, 0,
@@ -1535,7 +1536,7 @@
   web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
   GURL loaded_resource_url("data:text/html,Hello world");
   observer()->OnRequestComplete(
-      loaded_resource_url, net::HostPortPair(),
+      loaded_resource_url, net::IPEndPoint(),
       web_contents()->GetMainFrame()->GetFrameTreeNodeId(),
       content::GlobalRequestID(), web_contents()->GetMainFrame(),
       content::RESOURCE_TYPE_SCRIPT, false, nullptr, 0, 0,
diff --git a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc
index 1240a0b1..594f0fcc 100644
--- a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/navigation_simulator.h"
+#include "net/base/ip_endpoint.h"
 #include "services/network/public/cpp/network_quality_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -194,7 +195,7 @@
   const base::TimeTicks kNow = base::TimeTicks::Now();
   load_timing_info->connect_timing.dns_start = kNow;
   page_load_metrics::ExtraRequestCompleteInfo info(
-      GURL("https://ignored.com"), net::HostPortPair(), frame_tree_node_id,
+      GURL("https://ignored.com"), net::IPEndPoint(), frame_tree_node_id,
       false, /* cached */
       10 * 1024 /* size */, 0 /* original_network_content_length */,
       nullptr
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
index 59d41aa..6911881c 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
@@ -27,6 +27,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/previews/content/previews_user_data.h"
 #include "content/public/test/web_contents_tester.h"
+#include "net/base/ip_endpoint.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
 #include "third_party/blink/public/platform/web_input_event.h"
@@ -179,7 +180,7 @@
   // Verify LoFi is tracked when a LoFi response is received.
   page_load_metrics::ExtraRequestCompleteInfo resource = {
       GURL(kResourceUrl),
-      net::HostPortPair(),
+      net::IPEndPoint(),
       -1 /* frame_tree_node_id */,
       true /*was_cached*/,
       1024 * 40 /* raw_body_bytes */,
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
index c931b48..55c569e4 100644
--- a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
@@ -8,8 +8,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/url_util.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -105,7 +104,7 @@
 }
 
 // Attempts to get the IP address of a resource request from
-// |extra_request_info.host_port_pair|, trying to get it from the URL string in
+// |extra_request_info.remote_endpoint|, trying to get it from the URL string in
 // |extra_request_info.url| if that fails.
 // Sets the values of |resource_ip| and |port| with the extracted IP address and
 // port, respectively.
@@ -116,9 +115,9 @@
     int* resource_port) {
   // If the request was successful, then the IP address should be in
   // |extra_request_info|.
-  bool ip_exists = net::ParseURLHostnameToAddress(
-      extra_request_info.host_port_pair.host(), resource_ip);
-  *resource_port = extra_request_info.host_port_pair.port();
+  bool ip_exists = extra_request_info.remote_endpoint.address().IsValid();
+  *resource_ip = extra_request_info.remote_endpoint.address();
+  *resource_port = extra_request_info.remote_endpoint.port();
 
   // If the request failed, it's possible we didn't receive the IP address,
   // possibly because domain resolution failed. As a backup, try getting the IP
@@ -307,22 +306,17 @@
     ukm::SourceId source_id) {
   // Upon page load, we want to determine whether the page loaded was a public
   // domain or private domain and generate an event describing the domain type.
-  net::HostPortPair address = navigation_handle->GetSocketAddress();
+  net::IPEndPoint remote_endpoint = navigation_handle->GetSocketAddress();
+  page_ip_address_ = remote_endpoint.address();
 
   // In cases where the page loaded does not have a socket address or was not a
   // network resource, we don't want to track the page load. Such resources will
   // fail to parse or return an empty IP address.
-  if (!net::ParseURLHostnameToAddress(address.host(), &page_ip_address_) ||
-      page_ip_address_.IsZero()) {
+  if (!page_ip_address_.IsValid()) {
     return STOP_OBSERVING;
   }
 
-  // |HostStringIsLocalhost| assumes (and doesn't verify) that any IPv6 host
-  // passed to it does not have square brackets around it, but
-  // |HostPortPair::host| retains the brackets, so we need to separately check
-  // for IPv6 localhost here.
-  if (net::HostStringIsLocalhost(address.host()) ||
-      page_ip_address_ == net::IPAddress::IPv6Localhost()) {
+  if (page_ip_address_.IsLoopback()) {
     page_domain_type_ = internal::DOMAIN_TYPE_LOCALHOST;
   } else if (!page_ip_address_.IsPubliclyRoutable()) {
     page_domain_type_ = internal::DOMAIN_TYPE_PRIVATE;
@@ -377,7 +371,7 @@
   // We can't track anything if we don't have an IP address for the resource.
   // We also don't want to track any requests to the page's IP address itself.
   if (!GetIPAndPort(extra_request_info, &resource_ip, &resource_port) ||
-      resource_ip.IsZero() || resource_ip == page_ip_address_) {
+      !resource_ip.IsValid() || resource_ip == page_ip_address_) {
     return;
   }
 
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
index 256fcbd..0dfc3116 100644
--- a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
@@ -14,8 +14,7 @@
 #include "content/public/browser/global_request_id.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/test/navigation_simulator.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source.h"
@@ -38,10 +37,10 @@
 static const PageAddressInfo
     kPublicPage = {(char*)"https://foo.com/", (char*)"216.58.195.78", 443},
     kPublicPageIPv6 = {(char*)"https://google.com/",
-                       (char*)"[2607:f8b0:4005:809::200e]", 443},
+                       (char*)"2607:f8b0:4005:809::200e", 443},
     kPrivatePage = {(char*)"http://test.local/", (char*)"192.168.10.123", 80},
     kLocalhostPage = {(char*)"http://localhost/", (char*)"127.0.0.1", 80},
-    kLocalhostPageIPv6 = {(char*)"http://[::1]/", (char*)"[::1]", 80},
+    kLocalhostPageIPv6 = {(char*)"http://[::1]/", (char*)"::1", 80},
     kPublicRequest1 = {(char*)"http://bar.com/", (char*)"100.150.200.250", 80},
     kPublicRequest2 = {(char*)"https://www.baz.com/", (char*)"192.10.20.30",
                        443},
@@ -89,12 +88,14 @@
 
   void SimulateNavigateAndCommit(const internal::PageAddressInfo& page) {
     GURL url(page.url);
-    net::HostPortPair socket_address(page.host_ip, page.port);
+    net::IPAddress address;
+    ASSERT_TRUE(address.AssignFromIPLiteral(page.host_ip));
+    net::IPEndPoint remote_endpoint(address, page.port);
 
     navigation_simulator_ =
         content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
     navigation_simulator_->Start();
-    navigation_simulator_->SetSocketAddress(socket_address);
+    navigation_simulator_->SetSocketAddress(remote_endpoint);
     navigation_simulator_->Commit();
   }
 
@@ -109,8 +110,10 @@
 
   void SimulateLoadedResource(const internal::PageAddressInfo& resource,
                               const int net_error) {
+    net::IPAddress address;
+    ASSERT_TRUE(address.AssignFromIPLiteral(resource.host_ip));
     page_load_metrics::ExtraRequestCompleteInfo request_info(
-        GURL(resource.url), net::HostPortPair(resource.host_ip, resource.port),
+        GURL(resource.url), net::IPEndPoint(address, resource.port),
         -1 /* frame_tree_node_id */, !net_error /* was_cached */,
         (net_error ? 1024 * 20 : 0) /* raw_body_bytes */,
         0 /* original_network_content_length */,
@@ -775,7 +778,7 @@
   // Load a resource that has the IP address in the URL but returned an empty
   // socket address for some reason.
   PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
-      {GURL(internal::kDiffSubnetRequest2.url), net::HostPortPair(),
+      {GURL(internal::kDiffSubnetRequest2.url), net::IPEndPoint(),
        -1 /* frame_tree_node_id */, true /* was_cached */,
        1024 * 20 /* raw_body_bytes */, 0 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
@@ -803,7 +806,7 @@
   // Load a resource that doesn't have the IP address in the URL and returned an
   // empty socket address (e.g., failed DNS resolution).
   PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
-      {GURL(internal::kPrivatePage.url), net::HostPortPair(),
+      {GURL(internal::kPrivatePage.url), net::IPEndPoint(),
        -1 /* frame_tree_node_id */, false /* was_cached */,
        0 /* raw_body_bytes */, 0 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
diff --git a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc
index cd79f4a1..ebb2a7e0 100644
--- a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc
@@ -17,6 +17,7 @@
 #include "chrome/common/page_load_metrics/page_load_timing.h"
 #include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
+#include "net/base/ip_endpoint.h"
 
 namespace data_reduction_proxy {
 
@@ -136,26 +137,26 @@
   // LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Cached request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, true /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
        1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_SCRIPT, 0,
        nullptr /* load_timing_info */},
       // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 10 /* original_network_content_length */, data->DeepCopy(),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
@@ -200,26 +201,26 @@
   // client LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Cached request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, true /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
        1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_SCRIPT, 0,
        nullptr /* load_timing_info */},
       // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 10 /* original_network_content_length */, data->DeepCopy(),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
@@ -265,26 +266,26 @@
   // server LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Cached request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, true /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
        1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_SCRIPT, 0,
        nullptr /* load_timing_info */},
       // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 10 /* original_network_content_length */, data->DeepCopy(),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
@@ -335,26 +336,26 @@
   // 1 has Server LoFi.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Cached request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, true /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
        1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_SCRIPT, 0,
        nullptr /* load_timing_info */},
       // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 10 /* original_network_content_length */, std::move(data1),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data2),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
index d8bba72d..93a5d04 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/resource_type.h"
+#include "net/base/ip_endpoint.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "url/gurl.h"
@@ -156,7 +157,7 @@
           : web_contents()->GetMainFrame();
 
   observer_->OnRequestComplete(
-      info.url, info.host_port_pair, info.frame_tree_node_id, request_id,
+      info.url, info.remote_endpoint, info.frame_tree_node_id, request_id,
       render_frame_host_or_null, info.resource_type, info.was_cached,
       info.data_reduction_proxy_data
           ? info.data_reduction_proxy_data->DeepCopy()
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
index b6700cf4..9cd408a2 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/test/web_contents_tester.h"
+#include "net/base/ip_endpoint.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 
@@ -570,13 +571,13 @@
   // Prepare 3 resources of varying size and configurations, 2 of which have
   // client LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached non-proxied request.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
@@ -619,13 +620,13 @@
   // Prepare 3 resources of varying size and configurations, 2 of which have
   // client LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached non-proxied request.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
@@ -667,13 +668,13 @@
   // Prepare 3 resources of varying size and configurations, 2 of which have
   // client LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached non-proxied request.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
@@ -711,12 +712,12 @@
   // Prepare 3 resources of varying size and configurations, 2 of which have
   // client LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
@@ -760,12 +761,12 @@
   // Prepare 3 resources of varying size and configurations, 2 of which have
   // client LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
@@ -809,12 +810,12 @@
   // Prepare 3 resources of varying size and configurations, 2 of which have
   // client LoFi set.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 /* original_network_content_length */,
        nullptr /* data_reduction_proxy_data */,
@@ -859,13 +860,13 @@
   // 1 has Server LoFi.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Uncached proxied request with .1 compression ratio.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 10 /* original_network_content_length */, std::move(data1),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .5 compression ratio.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data2),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
@@ -913,13 +914,13 @@
   // 1 has Server LoFi.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Uncached proxied request with .1 compression ratio.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 10 /* original_network_content_length */, std::move(data1),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .5 compression ratio.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data2),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
@@ -966,13 +967,13 @@
   // 1 has Server LoFi.
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Uncached proxied request with .1 compression ratio.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 10 /* original_network_content_length */, std::move(data1),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
        nullptr /* load_timing_info */},
       // Uncached proxied request with .5 compression ratio.
-      {GURL(kDefaultTestUrl), net::HostPortPair(), -1, false /*was_cached*/,
+      {GURL(kDefaultTestUrl), net::IPEndPoint(), -1, false /*was_cached*/,
        1024 * 40 /* raw_body_bytes */,
        1024 * 40 * 5 /* original_network_content_length */, std::move(data2),
        content::ResourceType::RESOURCE_TYPE_IMAGE, 0,
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
index cae25b0..3570b6ed 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/navigation_simulator.h"
+#include "net/base/ip_endpoint.h"
 #include "net/nqe/effective_connection_type.h"
 #include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -822,7 +823,7 @@
   page_load_metrics::ExtraRequestCompleteInfo resources[] = {
       // Cached request.
       {GURL(kResourceUrl),
-       net::HostPortPair(),
+       net::IPEndPoint(),
        -1 /* frame_tree_node_id */,
        true /* was_cached */,
        1024 * 20 /* raw_body_bytes */,
@@ -833,7 +834,7 @@
        {} /* load_timing_info */},
       // Uncached non-proxied request.
       {GURL(kResourceUrl),
-       net::HostPortPair(),
+       net::IPEndPoint(),
        -1 /* frame_tree_node_id */,
        false /* was_cached */,
        1024 * 40 /* raw_body_bytes */,
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
index 5f08cd3..20817fb 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
@@ -63,7 +63,7 @@
 
 ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
     const GURL& url,
-    const net::HostPortPair& host_port_pair,
+    const net::IPEndPoint& remote_endpoint,
     int frame_tree_node_id,
     bool was_cached,
     int64_t raw_body_bytes,
@@ -74,7 +74,7 @@
     int net_error,
     std::unique_ptr<net::LoadTimingInfo> load_timing_info)
     : url(url),
-      host_port_pair(host_port_pair),
+      remote_endpoint(remote_endpoint),
       frame_tree_node_id(frame_tree_node_id),
       was_cached(was_cached),
       raw_body_bytes(raw_body_bytes),
@@ -87,7 +87,7 @@
 ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
     const ExtraRequestCompleteInfo& other)
     : url(other.url),
-      host_port_pair(other.host_port_pair),
+      remote_endpoint(other.remote_endpoint),
       frame_tree_node_id(other.frame_tree_node_id),
       was_cached(other.was_cached),
       raw_body_bytes(other.raw_body_bytes),
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index fc8f72e..8fafe0d 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -16,6 +16,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "url/gurl.h"
@@ -225,7 +226,7 @@
 struct ExtraRequestCompleteInfo {
   ExtraRequestCompleteInfo(
       const GURL& url,
-      const net::HostPortPair& host_port_pair,
+      const net::IPEndPoint& remote_endpoint,
       int frame_tree_node_id,
       bool was_cached,
       int64_t raw_body_bytes,
@@ -244,7 +245,7 @@
   const GURL url;
 
   // The host (IP address) and port for the request.
-  const net::HostPortPair host_port_pair;
+  const net::IPEndPoint remote_endpoint;
 
   // The frame tree node id that initiated the request.
   const int frame_tree_node_id;
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index d193b20..3a14537 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -286,6 +286,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
+#include "chromeos/assistant/buildflags.h"
 #include "chromeos/audio/audio_devices_pref_handler_impl.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/network/fast_transition_observer.h"
@@ -297,6 +298,11 @@
 #include "components/onc/onc_pref_names.h"
 #include "components/quirks/quirks_manager.h"
 #include "extensions/browser/api/lock_screen_data/lock_screen_item_storage.h"
+
+#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
+#endif
+
 #else
 #include "chrome/browser/extensions/default_apps.h"
 #endif
@@ -394,6 +400,10 @@
 const char kNextUpdateCheck[] = "extensions.autoupdate.next_check";
 const char kLastUpdateCheck[] = "extensions.autoupdate.last_check";
 
+// Deprecated 2/2019.
+const char kVoiceInteractionActivityControlAcceptedDeprecated[] =
+    "settings.voice_interaction.activity_control.accepted";
+
 // Register prefs used only for migration (clearing or moving to a new key).
 void RegisterProfilePrefsForMigration(
     user_prefs::PrefRegistrySyncable* registry) {
@@ -423,6 +433,9 @@
                                    PrefRegistry::LOSSY_PREF);
   registry->RegisterIntegerPref(kLastUpdateCheck, 0);
   registry->RegisterIntegerPref(kNextUpdateCheck, 0);
+
+  registry->RegisterBooleanPref(
+      kVoiceInteractionActivityControlAcceptedDeprecated, false);
 }
 
 }  // namespace
@@ -760,6 +773,11 @@
   policy::AppInstallEventLogManagerWrapper::RegisterProfilePrefs(registry);
   policy::DeviceStatusCollector::RegisterProfilePrefs(registry);
   ::onc::RegisterProfilePrefs(registry);
+
+#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
+  assistant::prefs::RegisterProfilePrefs(registry);
+#endif
+
 #endif
 
 #if defined(OS_CHROMEOS) && BUILDFLAG(ENABLE_APP_LIST)
@@ -834,6 +852,29 @@
 void RegisterLoginProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
   RegisterProfilePrefs(registry);
 }
+
+#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
+
+bool MigrateVoiceInteractionActivityControlAccepted(
+    PrefService* profile_prefs) {
+  const base::Value* activity_control_accepted =
+      profile_prefs->GetUserPrefValue(
+          kVoiceInteractionActivityControlAcceptedDeprecated);
+
+  if (!activity_control_accepted)
+    return false;
+
+  ash::mojom::ConsentStatus consent_status =
+      activity_control_accepted->GetBool()
+          ? ash::mojom::ConsentStatus::kActivityControlAccepted
+          : ash::mojom::ConsentStatus::kUnknown;
+
+  assistant::prefs::SetConsentStatus(profile_prefs, consent_status);
+  return true;
+}
+
+#endif
+
 #endif
 
 // This method should be periodically pruned of year+ old migrations.
@@ -908,11 +949,21 @@
 #if defined(OS_CHROMEOS)
   // Added 12/2018.
   profile_prefs->ClearPref(prefs::kDataSaverPromptsShown);
+
+#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
+  // Added 2/2019.
+  if (MigrateVoiceInteractionActivityControlAccepted(profile_prefs)) {
+    profile_prefs->ClearPref(
+        kVoiceInteractionActivityControlAcceptedDeprecated);
+  }
+#endif
+
 #endif
 
   // Added 1/2019.
   profile_prefs->ClearPref(kLastUpdateCheck);
   profile_prefs->ClearPref(kNextUpdateCheck);
+
   syncer::MigrateSessionsToProxyTabsPrefs(profile_prefs);
   syncer::ClearObsoleteUserTypePrefs(profile_prefs);
 
diff --git a/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
index df605581..b6707a26 100644
--- a/chrome/browser/resources/chromeos/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -124,6 +124,7 @@
   "cvox2/background/base_automation_handler.js",
   "cvox2/background/braille_command_data.js",
   "cvox2/background/braille_command_handler.js",
+  "cvox2/background/color.js",
   "cvox2/background/chromevox_state.js",
   "cvox2/background/command_handler.js",
   "cvox2/background/console_tts.js",
@@ -618,6 +619,7 @@
     "braille/liblouis_test.extjs",
     "cvox2/background/automation_util_test.extjs",
     "cvox2/background/background_test.extjs",
+    "cvox2/background/color_test.extjs",
     "cvox2/background/cursors_test.extjs",
     "cvox2/background/editing_test.extjs",
     "cvox2/background/i_search_test.extjs",
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
index 08eaef21..ddee7a20 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
@@ -1005,6 +1005,15 @@
           "keyCode": [79, 66]
         }
       }
+    },
+    {
+      "command": "getRichTextDescription",
+      "sequence": {
+        "cvoxModifier": true,
+        "keys": {
+          "keyCode": [65, 70]
+        }
+      }
     }
   ]
 }
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/color.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color.js
new file mode 100644
index 0000000..312c361c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color.js
@@ -0,0 +1,237 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Provides color matching services for ChromeVox.
+ */
+
+goog.provide('Color');
+
+/**
+ * Returns a string representation of a color.
+ * @param {number|undefined} color The argb value represented as an integer.
+ * @return {string}
+ */
+Color.getColorDescription = function(color) {
+  if (!color)
+    return '';
+  // Convert to unsigned integer.
+  color = color >>> 0;
+  // The following 24 bits represent the rgb value. Filter out first 8 bits.
+  var rgb = color & 0x00ffffff;
+  var optSubs =
+      [Color.findClosestMatchingColor(rgb), Color.getOpacityPercentage(color)];
+  return Msgs.getMsg('color_description', optSubs);
+};
+
+/**
+ * Extracts the opacity of the color, which is encoded within the first 8 bits.
+ * @param {number} color An integer representation of a color.
+ * @return {number}
+ */
+Color.getOpacityPercentage = function(color) {
+  return Math.round(((color >>> 24) / 256) * 100);
+};
+
+/**
+ * Finds the most similar stored color given an rgb value.
+ * @param {number} target The rgb value as an integer.
+ * @return {string}
+ */
+Color.findClosestMatchingColor = function(target) {
+  var bestMessageId;
+  var bestDistance = Number.MAX_VALUE;
+
+  Color.ColorObjectArray.forEach(function(obj) {
+    var val = obj.value;
+    var distance = Color.findDistance(target, val);
+    if (distance < bestDistance) {
+      bestMessageId = obj.colorMessageId;
+      bestDistance = distance;
+    }
+  });
+  // Do not report color if most similar color is too inaccurate.
+  if (bestDistance > Color.DISTANCE_THRESHOLD)
+    return '';
+  return Msgs.getMsg(bestMessageId);
+};
+
+/**
+ * Calculates the distance between two 3-D points, encoded as numbers,
+ * that represent colors.
+ * The first 8 bits are unused as they have either been shifted off or are
+ * simply filled by zeros. The x component is designated by the second
+ * 8 bits. The y component is designated by the third 8 bits.
+ * The z component is designated by the last 8 bits.
+ * @param {number} firstColor
+ * @param {number} secondColor
+ * @return {number}
+ */
+Color.findDistance = function(firstColor, secondColor) {
+  // Extract x, y, and z components.
+  var firstColorX = (firstColor & 0xff0000) >> 16;
+  var firstColorY = (firstColor & 0x00ff00) >> 8;
+  var firstColorZ = (firstColor & 0x0000ff);
+  var secondColorX = (secondColor & 0xff0000) >> 16;
+  var secondColorY = (secondColor & 0x00ff00) >> 8;
+  var secondColorZ = (secondColor & 0x0000ff);
+
+  return Math.pow(secondColorX - firstColorX, 2) +
+      Math.pow(secondColorY - firstColorY, 2) +
+      Math.pow(secondColorZ - firstColorZ, 2);
+};
+
+/**
+ * The distance between black and dark grey is the threshold.
+ * @const {number}
+ */
+Color.DISTANCE_THRESHOLD = Color.findDistance(0X000000, 0X181818);
+
+/**
+ * Holds objects that contain hexadecimal RGB values of colors and their
+ * corresponding ChromeVox message IDs.
+ * @private {!Array<{colorMessageId: string, value: number}>}
+ * Obtained from url: https://www.w3schools.com/lib/w3color.js
+ */
+Color.ColorObjectArray = [
+  {'value': 0x0, 'colorMessageId': 'color_black'},
+  {'value': 0x6400, 'colorMessageId': 'color_dark_green'},
+  {'value': 0x8000, 'colorMessageId': 'color_green'},
+  {'value': 0x800080, 'colorMessageId': 'color_purple'},
+  {'value': 0xb8860b, 'colorMessageId': 'color_dark_golden_rod'},
+  {'value': 0xfffacd, 'colorMessageId': 'color_lemon_chiffon'},
+  {'value': 0xa0522d, 'colorMessageId': 'color_sienna'},
+  {'value': 0xffa500, 'colorMessageId': 'color_orange'},
+  {'value': 0x8b4513, 'colorMessageId': 'color_saddle_brown'},
+  {'value': 0xffff, 'colorMessageId': 'color_cyan'},
+  {'value': 0xadff2f, 'colorMessageId': 'color_green_yellow'},
+  {'value': 0xd2691e, 'colorMessageId': 'color_chocolate'},
+  {'value': 0x800000, 'colorMessageId': 'color_maroon'},
+  {'value': 0xdaa520, 'colorMessageId': 'color_golden_rod'},
+  {'value': 0x228b22, 'colorMessageId': 'color_forest_green'},
+  {'value': 0x6b8e23, 'colorMessageId': 'color_olive_drab'},
+  {'value': 0xfffff0, 'colorMessageId': 'color_ivory'},
+  {'value': 0xf5f5dc, 'colorMessageId': 'color_beige'},
+  {'value': 0xa52a2a, 'colorMessageId': 'color_brown'},
+  {'value': 0x9acd32, 'colorMessageId': 'color_yellow_green'},
+  {'value': 0xff4500, 'colorMessageId': 'color_orange_red'},
+  {'value': 0x556b2f, 'colorMessageId': 'color_dark_olive_green'},
+  {'value': 0x32cd32, 'colorMessageId': 'color_lime_green'},
+  {'value': 0xff00, 'colorMessageId': 'color_lime'},
+  {'value': 0xeee8aa, 'colorMessageId': 'color_pale_golden_rod'},
+  {'value': 0xff69b4, 'colorMessageId': 'color_hot_pink'},
+  {'value': 0xdc143c, 'colorMessageId': 'color_crimson'},
+  {'value': 0xb0e0e6, 'colorMessageId': 'color_powder_blue'},
+  {'value': 0x808000, 'colorMessageId': 'color_olive'},
+  {'value': 0xffffe0, 'colorMessageId': 'color_light_yellow'},
+  {'value': 0xfaf0e6, 'colorMessageId': 'color_linen'},
+  {'value': 0x8b, 'colorMessageId': 'color_dark_blue'},
+  {'value': 0xf8f8ff, 'colorMessageId': 'color_ghost_white'},
+  {'value': 0xff6347, 'colorMessageId': 'color_tomato'},
+  {'value': 0xf0e68c, 'colorMessageId': 'color_khaki'},
+  {'value': 0x2f4f4f, 'colorMessageId': 'color_dark_slate_grey'},
+  {'value': 0xff7f50, 'colorMessageId': 'color_coral'},
+  {'value': 0xf5fffa, 'colorMessageId': 'color_mint_cream'},
+  {'value': 0x8080, 'colorMessageId': 'color_teal'},
+  {'value': 0x8b008b, 'colorMessageId': 'color_dark_magenta'},
+  {'value': 0xffa07a, 'colorMessageId': 'color_light_salmon'},
+  {'value': 0x2e8b57, 'colorMessageId': 'color_sea_green'},
+  {'value': 0xff0000, 'colorMessageId': 'color_red'},
+  {'value': 0xbc8f8f, 'colorMessageId': 'color_rosy_brown'},
+  {'value': 0xcd5c5c, 'colorMessageId': 'color_indian_red'},
+  {'value': 0xd3d3d3, 'colorMessageId': 'color_light_grey'},
+  {'value': 0xf4a460, 'colorMessageId': 'color_sandy_brown'},
+  {'value': 0x90ee90, 'colorMessageId': 'color_light_green'},
+  {'value': 0xadd8e6, 'colorMessageId': 'color_light_blue'},
+  {'value': 0xff8c00, 'colorMessageId': 'color_dark_orange'},
+  {'value': 0x696969, 'colorMessageId': 'color_dim_grey'},
+  {'value': 0xffebcd, 'colorMessageId': 'color_blanched_almond'},
+  {'value': 0xbdb76b, 'colorMessageId': 'color_dark_khaki'},
+  {'value': 0xff00ff, 'colorMessageId': 'color_magenta'},
+  {'value': 0x191970, 'colorMessageId': 'color_midnight_blue'},
+  {'value': 0x3cb371, 'colorMessageId': 'color_medium_sea_green'},
+  {'value': 0xfa8072, 'colorMessageId': 'color_salmon'},
+  {'value': 0xff1493, 'colorMessageId': 'color_deep_pink'},
+  {'value': 0xe9967a, 'colorMessageId': 'color_dark_salmon'},
+  {'value': 0xcd853f, 'colorMessageId': 'color_peru'},
+  {'value': 0xff7f, 'colorMessageId': 'color_spring_green'},
+  {'value': 0x80, 'colorMessageId': 'color_navy'},
+  {'value': 0xf08080, 'colorMessageId': 'color_light_coral'},
+  {'value': 0x4b0082, 'colorMessageId': 'color_indigo'},
+  {'value': 0xffffff, 'colorMessageId': 'color_white'},
+  {'value': 0xc71585, 'colorMessageId': 'color_medium_violet_red'},
+  {'value': 0xdeb887, 'colorMessageId': 'color_burly_wood'},
+  {'value': 0xe6e6fa, 'colorMessageId': 'color_lavender'},
+  {'value': 0x483d8b, 'colorMessageId': 'color_dark_slate_blue'},
+  {'value': 0xd2b48c, 'colorMessageId': 'color_tan'},
+  {'value': 0x8fbc8f, 'colorMessageId': 'color_dark_sea_green'},
+  {'value': 0x708090, 'colorMessageId': 'color_slate_grey'},
+  {'value': 0xdb7093, 'colorMessageId': 'color_pale_violet_red'},
+  {'value': 0xfff8dc, 'colorMessageId': 'color_cornsilk'},
+  {'value': 0xafeeee, 'colorMessageId': 'color_pale_turquoise'},
+  {'value': 0x778899, 'colorMessageId': 'color_light_slate_grey'},
+  {'value': 0x98fb98, 'colorMessageId': 'color_pale_green'},
+  {'value': 0x663399, 'colorMessageId': 'color_rebecca_purple'},
+  {'value': 0xfa9a, 'colorMessageId': 'color_medium_spring_green'},
+  {'value': 0xffc0cb, 'colorMessageId': 'color_pink'},
+  {'value': 0x5f9ea0, 'colorMessageId': 'color_cadet_blue'},
+  {'value': 0x808080, 'colorMessageId': 'color_grey'},
+  {'value': 0xee82ee, 'colorMessageId': 'color_violet'},
+  {'value': 0xa9a9a9, 'colorMessageId': 'color_dark_grey'},
+  {'value': 0x20b2aa, 'colorMessageId': 'color_light_sea_green'},
+  {'value': 0x8b8b, 'colorMessageId': 'color_dark_cyan'},
+  {'value': 0xffdead, 'colorMessageId': 'color_navajo_white'},
+  {'value': 0xf0f8ff, 'colorMessageId': 'color_alice_blue'},
+  {'value': 0xfffaf0, 'colorMessageId': 'color_floral_white'},
+  {'value': 0xffe4e1, 'colorMessageId': 'color_misty_rose'},
+  {'value': 0xf5deb3, 'colorMessageId': 'color_wheat'},
+  {'value': 0x4682b4, 'colorMessageId': 'color_steel_blue'},
+  {'value': 0xffe4b5, 'colorMessageId': 'color_moccasin'},
+  {'value': 0xffdab9, 'colorMessageId': 'color_peach_puff'},
+  {'value': 0xffd700, 'colorMessageId': 'color_gold'},
+  {'value': 0xfff0f5, 'colorMessageId': 'color_lavender_blush'},
+  {'value': 0xc0c0c0, 'colorMessageId': 'color_silver'},
+  {'value': 0xffb6c1, 'colorMessageId': 'color_light_pink'},
+  {'value': 0xf0ffff, 'colorMessageId': 'color_azure'},
+  {'value': 0xffe4c4, 'colorMessageId': 'color_bisque'},
+  {'value': 0x9932cc, 'colorMessageId': 'color_dark_orchid'},
+  {'value': 0xfdf5e6, 'colorMessageId': 'color_old_lace'},
+  {'value': 0x48d1cc, 'colorMessageId': 'color_medium_turquoise'},
+  {'value': 0x6a5acd, 'colorMessageId': 'color_slate_blue'},
+  {'value': 0xcd, 'colorMessageId': 'color_medium_blue'},
+  {'value': 0x40e0d0, 'colorMessageId': 'color_turquoise'},
+  {'value': 0xced1, 'colorMessageId': 'color_dark_turquoise'},
+  {'value': 0xfafad2, 'colorMessageId': 'color_light_golden_rod_yellow'},
+  {'value': 0x9400d3, 'colorMessageId': 'color_dark_violet'},
+  {'value': 0x7fffd4, 'colorMessageId': 'color_aquamarine'},
+  {'value': 0xffefd5, 'colorMessageId': 'color_papaya_whip'},
+  {'value': 0xda70d6, 'colorMessageId': 'color_orchid'},
+  {'value': 0xfaebd7, 'colorMessageId': 'color_antique_white'},
+  {'value': 0xd8bfd8, 'colorMessageId': 'color_thistle'},
+  {'value': 0x9370db, 'colorMessageId': 'color_medium_purple'},
+  {'value': 0xdcdcdc, 'colorMessageId': 'color_gainsboro'},
+  {'value': 0xdda0dd, 'colorMessageId': 'color_plum'},
+  {'value': 0xb0c4de, 'colorMessageId': 'color_light_steel_blue'},
+  {'value': 0x8b0000, 'colorMessageId': 'color_dark_red'},
+  {'value': 0xfff5ee, 'colorMessageId': 'color_sea_shell'},
+  {'value': 0x4169e1, 'colorMessageId': 'color_royal_blue'},
+  {'value': 0x8a2be2, 'colorMessageId': 'color_blue_violet'},
+  {'value': 0x7cfc00, 'colorMessageId': 'color_lawn_green'},
+  {'value': 0xe0ffff, 'colorMessageId': 'color_light_cyan'},
+  {'value': 0xb22222, 'colorMessageId': 'color_fire_brick'},
+  {'value': 0x87ceeb, 'colorMessageId': 'color_sky_blue'},
+  {'value': 0x6495ed, 'colorMessageId': 'color_cornflower_blue'},
+  {'value': 0x7b68ee, 'colorMessageId': 'color_medium_slate_blue'},
+  {'value': 0xff, 'colorMessageId': 'color_blue'},
+  {'value': 0xf0fff0, 'colorMessageId': 'color_honeydew'},
+  {'value': 0xba55d3, 'colorMessageId': 'color_medium_orchid'},
+  {'value': 0xf5f5f5, 'colorMessageId': 'color_white_smoke'},
+  {'value': 0xffff00, 'colorMessageId': 'color_yellow'},
+  {'value': 0x87cefa, 'colorMessageId': 'color_light_sky_blue'},
+  {'value': 0xbfff, 'colorMessageId': 'color_deep_sky_blue'},
+  {'value': 0xfffafa, 'colorMessageId': 'color_snow'},
+  {'value': 0x66cdaa, 'colorMessageId': 'color_medium_aqua_marine'},
+  {'value': 0x7fff00, 'colorMessageId': 'color_chartreuse'},
+  {'value': 0x1e90ff, 'colorMessageId': 'color_dodger_blue'},
+];
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/color_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color_test.extjs
new file mode 100644
index 0000000..8eee010
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/color_test.extjs
@@ -0,0 +1,69 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Include test fixture.
+GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js']);
+
+/**
+ * Test fixture for Color.
+ * @constructor
+ * @extends {ChromeVoxE2ETest}
+ */
+function ChromeVoxColorTest() {
+  ChromeVoxNextE2ETest.call(this);
+}
+
+ChromeVoxColorTest.prototype = {
+  __proto__: ChromeVoxNextE2ETest.prototype,
+};
+
+SYNC_TEST_F('ChromeVoxColorTest', 'FindDistanceTest', function() {
+  // Hexadecimal representations of colors.
+  var red = 0xff0000;
+  var lime = 0x00ff00;
+  var blue = 0x0000ff;
+  var opaqueRed = 0xffff0000;
+  var transparentLime = 0x0000ff00;
+
+  assertEquals(Color.findDistance(red,lime), Color.findDistance(lime, blue));
+  // Opacity should not factor into this calculation.
+  assertEquals(Color.findDistance(red,lime), Color.findDistance(opaqueRed, transparentLime));
+});
+
+SYNC_TEST_F('ChromeVoxColorTest', 'FindClosestMatchingColorTest', function() {
+  var white = 0xffffff;
+  var red = 0xff0000;
+  var lime = 0x00ff00;
+  var blue = 0x0000ff;
+  var black = 0x000000;
+
+  var looksLikePink = 0xF4CCCC;
+  var looksLikeGreen = 0x38761D;
+
+  var unknownColor = 0x0C343D;
+
+  // Exact matches.
+  assertEquals('White', Color.findClosestMatchingColor(white));
+  assertEquals('Red', Color.findClosestMatchingColor(red));
+  assertEquals('Lime', Color.findClosestMatchingColor(lime));
+  assertEquals('Blue', Color.findClosestMatchingColor(blue));
+  assertEquals('Black', Color.findClosestMatchingColor(black));
+
+  // Inexact matches.
+  assertEquals('Pink', Color.findClosestMatchingColor(looksLikePink));
+  assertEquals('Forest Green', Color.findClosestMatchingColor(looksLikeGreen));
+
+  // No match.
+  assertEquals('', Color.findClosestMatchingColor(unknownColor));
+});
+
+SYNC_TEST_F('ChromeVoxColorTest', 'GetOpacityPercentageTest', function() {
+  var opaqueRed = 0xffff0000;
+  var transparentLime = 0x0000ff00;
+  var translucentBlue = 0x800000ff;
+
+  assertEquals(100, Color.getOpacityPercentage(opaqueRed));
+  assertEquals(0, Color.getOpacityPercentage(transparentLime));
+  assertEquals(50, Color.getOpacityPercentage(translucentBlue));
+});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
index ff2fed6..09b80f9 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
@@ -17,6 +17,7 @@
 goog.require('cvox.ChromeVoxKbHandler');
 goog.require('cvox.ChromeVoxPrefs');
 goog.require('cvox.CommandStore');
+goog.require('Color');
 
 goog.scope(function() {
 var AutomationEvent = chrome.automation.AutomationEvent;
@@ -868,6 +869,28 @@
             .go();
       });
       break;
+    case 'getRichTextDescription':
+      var node = ChromeVoxState.instance.currentRange.start.node;
+      var optSubs = [];
+      node.fontSize ? optSubs.push('font size: ' + node.fontSize) :
+                      optSubs.push('');
+      node.color ? optSubs.push(Color.getColorDescription(node.color)) :
+                   optSubs.push('');
+      node.bold ? optSubs.push(Msgs.getMsg('bold')) : optSubs.push('');
+      node.italic ? optSubs.push(Msgs.getMsg('italic')) : optSubs.push('');
+      node.underline ? optSubs.push(Msgs.getMsg('underline')) :
+                       optSubs.push('');
+      node.lineThrough ? optSubs.push(Msgs.getMsg('linethrough')) :
+                         optSubs.push('');
+      node.fontFamily ? optSubs.push('font family: ' + node.fontFamily) :
+                        optSubs.push('');
+
+      var richTextDescription = Msgs.getMsg('rich_text_attributes', optSubs);
+      new Output()
+          .withString(richTextDescription)
+          .withQueueMode(cvox.QueueMode.CATEGORY_FLUSH)
+          .go();
+      return false;
     default:
       return true;
   }
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
index b552849..9da4a9af 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
@@ -234,6 +234,19 @@
       root.anchorObject, root.anchorOffset, root.focusObject, root.focusOffset);
 
   this.updateIntraLineState_(this.line_);
+
+  /**
+   * @private {string|undefined}
+   */
+  this.fontFamily_;
+  /**
+   * @private {number|undefined}
+   */
+  this.fontSize_;
+  /**
+   * @private {string|undefined}
+   */
+  this.fontColor_;
 }
 
 AutomationRichEditableText.prototype = {
@@ -550,25 +563,57 @@
    */
   speakTextStyle_: function(style, opt_end) {
     var msgs = [];
-    if (style.state.linked)
-      msgs.push(opt_end ? 'link_end' : 'link_start');
-    if (style.subscript)
-      msgs.push(opt_end ? 'subscript_end' : 'subscript_start');
-    if (style.superscript)
-      msgs.push(opt_end ? 'superscript_end' : 'superscript_start');
-    if (style.bold)
-      msgs.push(opt_end ? 'bold_end' : 'bold_start');
-    if (style.italic)
-      msgs.push(opt_end ? 'italic_end' : 'italic_start');
-    if (style.underline)
-      msgs.push(opt_end ? 'underline_end' : 'underline_start');
-    if (style.lineThrough)
-      msgs.push(opt_end ? 'line_through_end' : 'line_through_start');
+    var fontFamily = style.fontFamily;
+    var fontSize = style.fontSize;
+    var fontColor = Color.getColorDescription(style.color);
+    var msg;
+
+    if (fontSize && (fontSize !== this.fontSize_)) {
+      this.fontSize_ = fontSize;
+      msg = opt_end ? 'font_size_end' : 'font_size_start';
+      msgs.push({'msg': msg, 'opt_subs': [this.fontSize_]});
+    }
+    if (fontColor && (fontColor !== this.fontColor_)) {
+      this.fontColor_ = fontColor;
+      msg = opt_end ? 'font_color_end' : 'font_color_start';
+      msgs.push({'msg': msg, 'opt_subs': [this.fontColor_]});
+    }
+    if (style.state.linked) {
+      msgs.push(opt_end ? {'msg': 'link_end'} : {'msg': 'link_start'});
+    }
+    if (style.subscript) {
+      msgs.push(
+          opt_end ? {'msg': 'subscript_end'} : {'msg': 'subscript_start'});
+    }
+    if (style.superscript) {
+      msgs.push(
+          opt_end ? {'msg': 'superscript_end'} : {'msg': 'superscript_start'});
+    }
+    if (style.bold) {
+      msgs.push(opt_end ? {'msg': 'bold_end'} : {'msg': 'bold_start'});
+    }
+    if (style.italic) {
+      msgs.push(opt_end ? {'msg': 'italic_end'} : {'msg': 'italic_start'});
+    }
+    if (style.underline) {
+      msgs.push(
+          opt_end ? {'msg': 'underline_end'} : {'msg': 'underline_start'});
+    }
+    if (style.lineThrough) {
+      msgs.push(
+          opt_end ? {'msg': 'line_through_end'} :
+                    {'msg': 'line_through_start'});
+    }
+    if (fontFamily && (fontFamily !== this.fontFamily_)) {
+      this.fontFamily_ = fontFamily;
+      msg = opt_end ? 'font_family_end' : 'font_family_start';
+      msgs.push({'msg': msg, 'opt_subs': [this.fontFamily_]});
+    }
 
     if (msgs.length) {
-      msgs.forEach(function(msg) {
+      msgs.forEach(function(obj) {
         cvox.ChromeVox.tts.speak(
-            Msgs.getMsg(msg), cvox.QueueMode.QUEUE,
+            Msgs.getMsg(obj['msg'], obj['opt_subs']), cvox.QueueMode.QUEUE,
             cvox.AbstractTts.PERSONALITY_ANNOTATION);
       });
     }
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index 0aee7614..84c9118 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -3025,12 +3025,44 @@
       <message desc="Spoken when a user navigates to the end of strike through text." name="IDS_CHROMEVOX_LINE_THROUGH_END">
         Strike through end
       </message>
+      <message desc="Spoken to describe bolded text." name="IDS_CHROMEVOX_BOLD">
+        Bold
+      </message>
+      <message desc="Spoken to describe italicized text." name="IDS_CHROMEVOX_ITALIC">
+        Italic
+      </message>
+      <message desc="Spoken to describe underlined text." name="IDS_CHROMEVOX_UNDERLINE">
+        Underline
+      </message>
+      <message desc="Spoken to describe line-through text." name="IDS_CHROMEVOX_LINETHROUGH">
+        Line through
+      </message>
       <message desc="Spoken when a user navigates to the beginning of a link's text." name="IDS_CHROMEVOX_LINK_START">
         Link start
       </message>
       <message desc="Spoken when a user navigates to the end of a link's text." name="IDS_CHROMEVOX_LINK_END">
         Link end
       </message>
+
+      <message desc="Spoken when a user navigates to the beginning of a font family's text, e.g. 'Arial start'" name="IDS_CHROMEVOX_FONT_FAMILY_START">
+        <ph name="font_family">$1</ph> start
+      </message>
+      <message desc="Spoken when a user navigates to the end of a font family's text, e.g. 'Arial end'" name="IDS_CHROMEVOX_FONT_FAMILY_END">
+        <ph name="font_family">$1</ph> end
+      </message>
+      <message desc="Spoken when a user navigates to the beginning of a font size's text, e.g. 'Size 12 start'" name="IDS_CHROMEVOX_FONT_SIZE_START">
+        Size <ph name="font_size">$1</ph> start
+      </message>
+      <message desc="Spoken when a user navigates to the end of a font size's text, e.g. 'Size 12 end'" name="IDS_CHROMEVOX_FONT_SIZE_END">
+        Size <ph name="font_size">$1</ph> end
+      </message>
+      <message desc="Spoken when a user navigates to the beginning of a font color's text, e.g. 'Red start'" name="IDS_CHROMEVOX_FONT_COLOR_START">
+        <ph name="font_color">$1</ph> start
+      </message>
+      <message desc="Spoken when a user navigates to the end of a font color's text, e.g. 'Red end'" name="IDS_CHROMEVOX_FONT_COLOR_END">
+        <ph name="font_color">$1</ph> end
+      </message>
+
       <message desc="Shown to a user when they press a braille keyboard command that requires on screen keyboard to be enabled." name="IDS_CHROMEVOX_ENABLE_VIRTUAL_KEYBOARD">
         Please enable the on screen keyboard under status tray, accessibility to use extended braille commands.
       </message>
@@ -3213,6 +3245,439 @@
         name="IDS_CHROMEVOX_OPTIONS_LANG_SWITCHING_CHECKBOX_LABEL">
         Automatically detect and switch ChromeVox language.
       </message>
+
+      <message desc="Spoken to describe all rich text attributes of a node." name="IDS_CHROMEVOX_RICH_TEXT_ATTRIBUTES">
+        Text formatting.
+        <ph name="font_size_string">$1</ph>
+        <ph name="color_string">$2</ph>
+        <ph name="bold_string">$3</ph>
+        <ph name="italic_string">$4</ph>
+        <ph name="underline_string">$5</ph>
+        <ph name="line_through_string">$6</ph>
+        <ph name="font_family_string">$7</ph>
+      </message>
+
+      <!-- Colors -->
+      <message desc="Spoken to describe color and opacity of text" name="IDS_CHROMEVOX_COLOR_DESCRIPTION">
+        <ph name="color">$1</ph>, <ph name="opacity_percentage">$2</ph>% opacity.
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BLACK">
+        Black
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_GREEN">
+        Dark Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_GREEN">
+        Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PURPLE">
+        Purple
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_GOLDEN_ROD">
+        Dark Golden Rod
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LEMON_CHIFFON">
+        Lemon Chiffon
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SIENNA">
+        Sienna
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_ORANGE">
+        Orange
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SADDLE_BROWN">
+        Saddle Brown
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CYAN">
+        Cyan
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_GREEN_YELLOW">
+        Green Yellow
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CHOCOLATE">
+        Chocolate
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MAROON">
+        Maroon
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_GOLDEN_ROD">
+        Golden Rod
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_FOREST_GREEN">
+        Forest Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_OLIVE_DRAB">
+        Olive Drab
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_IVORY">
+        Ivory
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BEIGE">
+        Beige
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BROWN">
+        Brown
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_YELLOW_GREEN">
+        Yellow Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_ORANGE_RED">
+        Orange Red
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_OLIVE_GREEN">
+        Dark Olive Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIME_GREEN">
+        Lime Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIME">
+        Lime
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PALE_GOLDEN_ROD">
+        Pale Golden Rod
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_HOT_PINK">
+        Hot Pink
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CRIMSON">
+        Crimson
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_POWDER_BLUE">
+        Powder Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_OLIVE">
+        Olive
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_YELLOW">
+        Light Yellow
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LINEN">
+        Linen
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_BLUE">
+        Dark Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_GHOST_WHITE">
+        Ghost White
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_TOMATO">
+        Tomato
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_KHAKI">
+        Khaki
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_SLATE_GREY">
+        Dark Slate Grey
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CORAL">
+        Coral
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MINT_CREAM">
+        Mint Cream
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_TEAL">
+        Teal
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_MAGENTA">
+        Dark Magenta
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_SALMON">
+        Light Salmon
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SEA_GREEN">
+        Sea Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_RED">
+        Red
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_ROSY_BROWN">
+        Rosy Brown
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_INDIAN_RED">
+        Indian Red
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_GREY">
+        Light Grey
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SANDY_BROWN">
+        Sandy Brown
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_GREEN">
+        Light Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_BLUE">
+        Light Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_ORANGE">
+        Dark Orange
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DIM_GREY">
+        Dim Grey
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BLANCHED_ALMOND">
+        Blanched Almond
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_KHAKI">
+        Dark Khaki
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MAGENTA">
+        Magenta
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MIDNIGHT_BLUE">
+        Midnight Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_SEA_GREEN">
+        Medium Sea Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SALMON">
+        Salmon
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DEEP_PINK">
+        Deep Pink
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_SALMON">
+        Dark Salmon
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PERU">
+        Peru
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SPRING_GREEN">
+        Spring Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_NAVY">
+        Navy
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_CORAL">
+        Light Coral
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_INDIGO">
+        Indigo
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_WHITE">
+        White
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_VIOLET_RED">
+        Medium Violet Red
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BURLY_WOOD">
+        Burly Wood
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LAVENDER">
+        Lavender
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_SLATE_BLUE">
+        Dark Slate Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_TAN">
+        Tan
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_SEA_GREEN">
+        Dark Sea Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SLATE_GREY">
+        Slate Grey
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PALE_VIOLET_RED">
+        Pale Violet Red
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CORNSILK">
+        Cornsilk
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PALE_TURQUOISE">
+        Pale Turquoise
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_SLATE_GREY">
+        Light Slate Grey
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PALE_GREEN">
+        Pale Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_REBECCA_PURPLE">
+        Rebecca Purple
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_SPRING_GREEN">
+        Medium Spring Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PINK">
+        Pink
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CADET_BLUE">
+        Cadet Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_GREY">
+        Grey
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_VIOLET">
+        Violet
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_GREY">
+        Dark Grey
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_SEA_GREEN">
+        Light Sea Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_CYAN">
+        Dark Cyan
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_NAVAJO_WHITE">
+        Navajo White
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_ALICE_BLUE">
+        Alice Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_FLORAL_WHITE">
+        Floral White
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MISTY_ROSE">
+        Misty Rose
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_WHEAT">
+        Wheat
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_STEEL_BLUE">
+        Steel Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MOCCASIN">
+        Moccasin
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PEACH_PUFF">
+        Peach Puff
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_GOLD">
+        Gold
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LAVENDER_BLUSH">
+        Lavender Blush
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SILVER">
+        Silver
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_PINK">
+        Light Pink
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_AZURE">
+        Azure
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BISQUE">
+        Bisque
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_ORCHID">
+        Dark Orchid
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_OLD_LACE">
+        Old Lace
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_TURQUOISE">
+        Medium Turquoise
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SLATE_BLUE">
+        Slate Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_BLUE">
+        Medium Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_TURQUOISE">
+        Turquoise
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_TURQUOISE">
+        Dark Turquoise
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_GOLDEN_ROD_YELLOW">
+        Light Golden Rod Yellow
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_VIOLET">
+        Dark Violet
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_AQUAMARINE">
+        Aquamarine
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PAPAYA_WHIP">
+        Papaya Whip
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_ORCHID">
+        Orchid
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_ANTIQUE_WHITE">
+        Antique White
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_THISTLE">
+        Thistle
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_PURPLE">
+        Medium Purple
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_GAINSBORO">
+        Gainsboro
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_PLUM">
+        Plum
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_STEEL_BLUE">
+        Light Steel Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DARK_RED">
+        Dark Red
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SEA_SHELL">
+        Sea Shell
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_ROYAL_BLUE">
+        Royal Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BLUE_VIOLET">
+        Blue Violet
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LAWN_GREEN">
+        Lawn Green
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_CYAN">
+        Light Cyan
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_FIRE_BRICK">
+        Fire Brick
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SKY_BLUE">
+        Sky Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CORNFLOWER_BLUE">
+        Cornflower Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_SLATE_BLUE">
+        Medium Slate Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_BLUE">
+        Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_HONEYDEW">
+        HoneyDew
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_ORCHID">
+        Medium Orchid
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_WHITE_SMOKE">
+        White Smoke
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_YELLOW">
+        Yellow
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_LIGHT_SKY_BLUE">
+        Light Sky Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DEEP_SKY_BLUE">
+        Deep Sky Blue
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_SNOW">
+        Snow
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_MEDIUM_AQUA_MARINE">
+        Medium Aqua Marine
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_CHARTREUSE">
+        Chartreuse
+      </message>
+      <message desc="Spoken to describe color of text" name="IDS_CHROMEVOX_COLOR_DODGER_BLUE">
+        Dodger Blue
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/resources/offline_pages/OWNERS b/chrome/browser/resources/offline_pages/OWNERS
new file mode 100644
index 0000000..f5dcbcc
--- /dev/null
+++ b/chrome/browser/resources/offline_pages/OWNERS
@@ -0,0 +1,4 @@
+file://components/offline_pages/OWNERS
+
+# TEAM: offline-dev@chromium.org
+# COMPONENT: UI>Browser>Offline
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
index b0c6432..834d691 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
@@ -231,8 +231,8 @@
       </div>
       <div slot="dialog-buttons">
         <paper-button class="cancel-button secondary-button"
-            on-click="onCancelConfiguringTap_">
-          $i18n{cancel}
+            on-click="onCloseConfiguringTap_">
+          $i18n{close}
         </paper-button>
       </div>
     </add-printer-dialog>
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
index 9a8766ca..9b97ab7 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
@@ -260,9 +260,8 @@
   },
 
   /** @private */
-  onCancelConfiguringTap_: function() {
+  onCloseConfiguringTap_: function() {
     this.close();
-    this.fire('configuring-dialog-closed');
   },
 
   close: function() {
@@ -315,7 +314,6 @@
   },
 
   listeners: {
-    'configuring-dialog-closed': 'configuringDialogClosed_',
     'open-manually-add-printer-dialog': 'openManuallyAddPrinterDialog_',
     'open-configuring-printer-dialog': 'openConfiguringPrinterDialog_',
     'open-discovery-printers-dialog': 'openDiscoveryPrintersDialog_',
@@ -444,26 +442,6 @@
   },
 
   /** @private */
-  configuringDialogClosed_: function() {
-    // If the configuring dialog is closed, we want to return whence we came.
-    //
-    // TODO(justincarlson) - This shouldn't need to be a conditional;
-    // clean up the way we switch dialogs so we don't have to supply
-    // redundant information and can just return to the previous
-    // dialog.
-    if (this.previousDialog_ == AddPrinterDialogs.DISCOVERY) {
-      this.switchDialog_(
-          this.currentDialog_, this.previousDialog_, 'showDiscoveryDialog_');
-    } else if (this.previousDialog_ == AddPrinterDialogs.MANUALLY) {
-      this.switchDialog_(
-          this.currentDialog_, this.previousDialog_, 'showManuallyAddDialog_');
-    } else if (this.previousDialog_ == AddPrinterDialogs.MANUFACTURER) {
-      this.switchDialog_(
-          this.currentDialog_, this.previousDialog_, 'showManufacturerDialog_');
-    }
-  },
-
-  /** @private */
   onNoDetectedPrinter_: function() {
     // If there is no detected printer, automatically open manually-add-printer
     // dialog only when the user opens the discovery-dialog through the
diff --git a/chrome/browser/resources/usb_internals/usb_internals.js b/chrome/browser/resources/usb_internals/usb_internals.js
index 0315f13..09fe786 100644
--- a/chrome/browser/resources/usb_internals/usb_internals.js
+++ b/chrome/browser/resources/usb_internals/usb_internals.js
@@ -14,7 +14,7 @@
 
       const pageHandler = mojom.UsbInternalsPageHandler.getProxy();
       this.usbManagerTest = new device.mojom.UsbDeviceManagerTestProxy;
-      pageHandler.bindTestInterface(this.usbManagerTest.createRequest());
+      pageHandler.bindTestInterface(this.usbManagerTest.$.createRequest());
 
       cr.ui.decorate('tabbox', cr.ui.TabBox);
       $('add-test-device-form').addEventListener('submit', (event) => {
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index 709176c..d153e66 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -38,6 +38,7 @@
 #include "content/public/common/frame_navigate_params.h"
 #include "content/public/common/resource_load_info.mojom.h"
 #include "content/public/common/url_constants.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_response_headers.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "url/gurl.h"
@@ -87,7 +88,7 @@
     url_ = navigation_handle->GetURL();
     if (navigation_handle->GetResponseHeaders())
       navigation_handle->GetResponseHeaders()->GetMimeType(&mime_type_);
-    socket_address_ = navigation_handle->GetSocketAddress();
+    remote_endpoint_ = navigation_handle->GetSocketAddress();
   }
 
   void Start() {
@@ -105,10 +106,11 @@
       DontClassifyForPhishing(NO_CLASSIFY_UNSUPPORTED_MIME_TYPE);
     }
 
-    if (csd_service_->IsPrivateIPAddress(socket_address_.host())) {
+    if (csd_service_->IsPrivateIPAddress(
+            remote_endpoint_.ToStringWithoutPort())) {
       DVLOG(1) << "Skipping phishing classification for URL: " << url_
                << " because of hosting on private IP: "
-               << socket_address_.host();
+               << remote_endpoint_.ToStringWithoutPort();
       DontClassifyForPhishing(NO_CLASSIFY_PRIVATE_IP);
       DontClassifyForMalware(NO_CLASSIFY_PRIVATE_IP);
     }
@@ -117,7 +119,7 @@
     if (!url_.SchemeIsHTTPOrHTTPS()) {
       DVLOG(1) << "Skipping phishing classification for URL: " << url_
                << " because it is not HTTP or HTTPS: "
-               << socket_address_.host();
+               << remote_endpoint_.ToStringWithoutPort();
       DontClassifyForPhishing(NO_CLASSIFY_SCHEME_NOT_SUPPORTED);
     }
 
@@ -323,7 +325,7 @@
 
   GURL url_;
   std::string mime_type_;
-  net::HostPortPair socket_address_;
+  net::IPEndPoint remote_endpoint_;
   WebContents* web_contents_;
   ClientSideDetectionService* csd_service_;
   // We keep a ref pointer here just to make sure the safe browsing
@@ -394,10 +396,11 @@
     content::ResourceType resource_type =
         navigation_handle->IsInMainFrame() ? content::RESOURCE_TYPE_MAIN_FRAME
                                            : content::RESOURCE_TYPE_SUB_FRAME;
-    UpdateIPUrlMap(navigation_handle->GetSocketAddress().host() /* ip */,
-                   navigation_handle->GetURL().spec() /* url */,
-                   navigation_handle->IsPost() ? "POST" : "GET",
-                   navigation_handle->GetReferrer().url.spec(), resource_type);
+    UpdateIPUrlMap(
+        navigation_handle->GetSocketAddress().ToStringWithoutPort() /* ip */,
+        navigation_handle->GetURL().spec() /* url */,
+        navigation_handle->IsPost() ? "POST" : "GET",
+        navigation_handle->GetReferrer().url.spec(), resource_type);
   }
 
   if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
@@ -464,11 +467,11 @@
   if (!content::IsResourceTypeFrame(resource_load_info.resource_type) &&
       browse_info_.get() && should_extract_malware_features_ &&
       resource_load_info.url.is_valid() &&
-      resource_load_info.network_info->ip_port_pair.has_value()) {
-    UpdateIPUrlMap(resource_load_info.network_info->ip_port_pair->host(),
-                   resource_load_info.url.spec(), resource_load_info.method,
-                   resource_load_info.referrer.spec(),
-                   resource_load_info.resource_type);
+      resource_load_info.network_info->remote_endpoint.has_value()) {
+    UpdateIPUrlMap(
+        resource_load_info.network_info->remote_endpoint->ToStringWithoutPort(),
+        resource_load_info.url.spec(), resource_load_info.method,
+        resource_load_info.referrer.spec(), resource_load_info.resource_type);
   }
 }
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 5b0eb7b..5570560 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -445,6 +445,8 @@
   signature_info_ = results.signature_info;
   image_headers_.reset(new ClientDownloadRequest_ImageHeaders());
   *image_headers_ = results.image_headers;
+  file_count_ = results.file_count;
+  directory_count_ = results.directory_count;
 
 #if defined(OS_MACOSX)
   if (!results.disk_image_signature.empty())
@@ -700,6 +702,8 @@
     request->set_allocated_image_headers(image_headers_.release());
   if (!archived_binaries_.empty())
     request->mutable_archived_binary()->Swap(&archived_binaries_);
+  request->set_archive_file_count(file_count_);
+  request->set_archive_directory_count(directory_count_);
   if (!request->SerializeToString(&client_download_request_data_)) {
     FinishRequest(DownloadCheckResult::UNKNOWN, REASON_INVALID_REQUEST_PROTO);
     return;
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
index af72d113..5571249 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
@@ -134,6 +134,8 @@
   bool is_extended_reporting_;
   bool is_incognito_;
   bool is_under_advanced_protection_;
+  int file_count_;
+  int directory_count_;
 
   base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_;
 
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
index be01921a..8201624 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
@@ -69,7 +69,7 @@
 
 }  // namespace
 
-FileAnalyzer::Results::Results() {}
+FileAnalyzer::Results::Results() : file_count(0), directory_count(0) {}
 FileAnalyzer::Results::~Results() {}
 FileAnalyzer::Results::Results(const FileAnalyzer::Results& other) = default;
 
@@ -208,6 +208,9 @@
     results_.type = ClientDownloadRequest::ZIPPED_EXECUTABLE;
   }
 
+  results_.file_count = archive_results.file_count;
+  results_.directory_count = archive_results.directory_count;
+
   std::move(callback_).Run(std::move(results_));
 }
 
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.h b/chrome/browser/safe_browsing/download_protection/file_analyzer.h
index a1f5abf..99684ff 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.h
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.h
@@ -72,6 +72,12 @@
         ClientDownloadRequest::DetachedCodeSignature>
         detached_code_signatures;
 #endif
+
+    // For archive files, the number of contained files.
+    int file_count;
+
+    // For archive files, the number of contained directories.
+    int directory_count;
   };
 
   explicit FileAnalyzer(
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
index 511f495..b00616c9 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
@@ -768,4 +768,65 @@
   EXPECT_TRUE(result_.archived_binaries.Get(0).digests().sha256().empty());
 }
 
+TEST_F(FileAnalyzerTest, ZipFilesGetFileCount) {
+  scoped_refptr<MockBinaryFeatureExtractor> extractor =
+      new testing::StrictMock<MockBinaryFeatureExtractor>();
+  FileAnalyzer analyzer(extractor);
+  base::RunLoop run_loop;
+
+  base::FilePath target_path(FILE_PATH_LITERAL("target.zip"));
+  base::FilePath tmp_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("tmp.crdownload"));
+
+  base::ScopedTempDir zip_source_dir;
+  ASSERT_TRUE(zip_source_dir.CreateUniqueTempDir());
+  std::string file_contents = "dummy file";
+  ASSERT_EQ(static_cast<int>(file_contents.size()),
+            base::WriteFile(
+                zip_source_dir.GetPath().Append(FILE_PATH_LITERAL("file.exe")),
+                file_contents.data(), file_contents.size()));
+  ASSERT_TRUE(zip::Zip(zip_source_dir.GetPath(), tmp_path,
+                       /* include_hidden_files= */
+                       false));
+
+  analyzer.Start(
+      target_path, tmp_path,
+      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
+                     run_loop.QuitClosure()));
+  run_loop.Run();
+
+  ASSERT_TRUE(has_result_);
+  EXPECT_EQ(1, result_.file_count);
+  EXPECT_EQ(0, result_.directory_count);
+}
+
+TEST_F(FileAnalyzerTest, ZipFilesGetDirectoryCount) {
+  scoped_refptr<MockBinaryFeatureExtractor> extractor =
+      new testing::StrictMock<MockBinaryFeatureExtractor>();
+  FileAnalyzer analyzer(extractor);
+  base::RunLoop run_loop;
+
+  base::FilePath target_path(FILE_PATH_LITERAL("target.zip"));
+  base::FilePath tmp_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("tmp.crdownload"));
+
+  base::ScopedTempDir zip_source_dir;
+  ASSERT_TRUE(zip_source_dir.CreateUniqueTempDir());
+  ASSERT_TRUE(base::CreateDirectory(
+      zip_source_dir.GetPath().Append(FILE_PATH_LITERAL("direcotry"))));
+  ASSERT_TRUE(zip::Zip(zip_source_dir.GetPath(), tmp_path,
+                       /* include_hidden_files= */
+                       false));
+
+  analyzer.Start(
+      target_path, tmp_path,
+      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
+                     run_loop.QuitClosure()));
+  run_loop.Run();
+
+  ASSERT_TRUE(has_result_);
+  EXPECT_EQ(0, result_.file_count);
+  EXPECT_EQ(1, result_.directory_count);
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
index aa76c7a6e..20f17e9 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/resource_type.h"
+#include "net/base/ip_endpoint.h"
 
 using content::WebContents;
 
@@ -227,10 +228,10 @@
 void SafeBrowsingNavigationObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   if ((navigation_handle->HasCommitted() || navigation_handle->IsDownload()) &&
-      !navigation_handle->GetSocketAddress().IsEmpty()) {
+      !navigation_handle->GetSocketAddress().address().empty()) {
     manager_->RecordHostToIpMapping(
         navigation_handle->GetURL().host(),
-        navigation_handle->GetSocketAddress().host());
+        navigation_handle->GetSocketAddress().ToStringWithoutPort());
   }
 
   if (navigation_handle_map_.find(navigation_handle) ==
diff --git a/chrome/browser/safe_browsing/threat_details_unittest.cc b/chrome/browser/safe_browsing/threat_details_unittest.cc
index dbbaada..84f3fc4 100644
--- a/chrome/browser/safe_browsing/threat_details_unittest.cc
+++ b/chrome/browser/safe_browsing/threat_details_unittest.cc
@@ -32,6 +32,7 @@
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_util.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -376,7 +377,7 @@
     network::ResourceResponseHead head;
     head.headers = new net::HttpResponseHeaders(
         net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
-    head.socket_address = net::HostPortPair("1.2.3.4", 80);
+    head.remote_endpoint = net::IPEndPoint(net::IPAddress(1, 2, 3, 4), 80);
     head.mime_type = "text/html";
     network::URLLoaderCompletionStatus status;
     status.decoded_body_length = content.size();
diff --git a/chrome/browser/sync/send_tab_to_self_sync_service_factory.cc b/chrome/browser/sync/send_tab_to_self_sync_service_factory.cc
index abe2259..12ad59b0 100644
--- a/chrome/browser/sync/send_tab_to_self_sync_service_factory.cc
+++ b/chrome/browser/sync/send_tab_to_self_sync_service_factory.cc
@@ -9,10 +9,12 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/chrome_device_id_helper.h"
 #include "chrome/browser/sync/device_info_sync_service_factory.h"
+#include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/common/channel_info.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
 #include "components/sync/device_info/device_info_sync_service.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "ui/base/device_form_factor.h"
 
 // static
@@ -33,6 +35,7 @@
           "SendTabToSelfSyncService",
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(DeviceInfoSyncServiceFactory::GetInstance());
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
 }
 
 SendTabToSelfSyncServiceFactory::~SendTabToSelfSyncServiceFactory() {}
@@ -45,8 +48,10 @@
       DeviceInfoSyncServiceFactory::GetForProfile(profile)
           ->GetLocalDeviceInfoProvider();
 
-  // TODO(jeffreycohen): use KeyedService to provide a DeviceInfo ptr.
+  syncer::OnceModelTypeStoreFactory store_factory =
+      ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory();
 
   return new send_tab_to_self::SendTabToSelfSyncService(
-      chrome::GetChannel(), local_device_info_provider);
+      chrome::GetChannel(), local_device_info_provider,
+      std::move(store_factory));
 }
diff --git a/chrome/browser/translate/translate_fake_page.cc b/chrome/browser/translate/translate_fake_page.cc
index e009e6a..de2c7a6 100644
--- a/chrome/browser/translate/translate_fake_page.cc
+++ b/chrome/browser/translate/translate_fake_page.cc
@@ -54,8 +54,6 @@
 
 // translate::mojom::Page implementation.
 void FakePageImpl::Translate(const std::string& translate_script,
-                             network::mojom::URLLoaderFactoryPtr
-                                 unused_loader_factory_for_translate_script,
                              const std::string& source_lang,
                              const std::string& target_lang,
                              TranslateCallback callback) {
diff --git a/chrome/browser/translate/translate_fake_page.h b/chrome/browser/translate/translate_fake_page.h
index c456071..bd368d22 100644
--- a/chrome/browser/translate/translate_fake_page.h
+++ b/chrome/browser/translate/translate_fake_page.h
@@ -9,12 +9,10 @@
 
 #include <memory>
 #include <set>
-#include <string>
 #include <tuple>
 #include <utility>
 #include <vector>
 
-#include "base/optional.h"
 #include "build/build_config.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
@@ -49,12 +47,10 @@
   translate::mojom::PagePtr BindToNewPagePtr();
 
   // translate::mojom::Page implementation.
-  void Translate(
-      const std::string& translate_script,
-      network::mojom::URLLoaderFactoryPtr loader_factory_for_translate_script,
-      const std::string& source_lang,
-      const std::string& target_lang,
-      TranslateCallback callback) override;
+  void Translate(const std::string& translate_script,
+                 const std::string& source_lang,
+                 const std::string& target_lang,
+                 TranslateCallback callback) override;
 
   void RevertTranslation() override;
 
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index 0926637..d44a784 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -7,12 +7,9 @@
 #include <memory>
 
 #include "base/bind.h"
-#include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/metrics/subprocess_metrics_provider.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -34,8 +31,6 @@
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
-#include "services/network/initiator_lock_compatibility.h"
-#include "services/network/public/cpp/features.h"
 #include "url/gurl.h"
 
 namespace {
@@ -988,107 +983,3 @@
   EXPECT_EQ("fr",
             restored_translate_client->GetLanguageState().current_language());
 }
-
-// Test that the translation is successful in presence of AppCache.
-// See also:
-// - https://crbug.com/910287#c24: AppCache / Translate renderer kills.
-// - https://crbug.com/925457: Translate is incompatible with
-//                             |request_initiator_site_lock|.
-IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest, AppCacheInteraction) {
-  // The real translate script used in production will try to fetch some stuff
-  // from the translate servers - the test tries to simulate a close-enough
-  // scenario below (close-enough to repro https://crbug.com/910287#c24).
-  //
-  // Unlike the real script, the request below doesn't use
-  // GetTranslateSecurityOrigin and instead fetches a resource from localhost,
-  // to simplify the test (e.g. avoid having to set up a https test server with
-  // the right certs).  Also, unlike the real script, the test below uses a
-  // random png resource (although as with the real script the resource is not
-  // protected by CORB).
-  const char kTranslateScriptTemplate[] = R"(
-      var google = {};
-      google.translate = (function() {
-        return {
-          TranslateService: function() {
-            return {
-              isAvailable : function() { return true; },
-              restore : function() { return; },
-              getDetectedLanguage : function() { return "fr"; },
-              translatePage : function(originalLang, targetLang,
-                                       onTranslateProgress) {
-                fetch($1, {mode: 'no-cors'})
-                    .then(_ => onTranslateProgress(100, true, false));
-              }
-            };
-          }
-        };
-      })();
-      cr.googleTranslate.onTranslateElementLoad();
-  )";
-  GURL fetch_url =
-      embedded_test_server()->GetURL("/downloads/image-octet-stream.png");
-  std::string translate_script =
-      content::JsReplace(kTranslateScriptTemplate, fetch_url);
-  SetTranslateScript(translate_script);
-
-  ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
-
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().original_language().empty())
-    WaitUntilLanguageDetected();
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().original_language());
-
-  // Open a new tab with a page in French and wait until AppCache controls
-  // subresource loads.
-  GURL main_url = embedded_test_server()->GetURL(
-      "/appcache/french_page_with_appcache.html");
-  ResetObserver();
-  AddTabAtIndex(0, main_url, ui::PAGE_TRANSITION_TYPED);
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  base::string16 expected_title = base::UTF8ToUTF16("AppCache mis à jour");
-  content::TitleWatcher title_watcher(web_contents, expected_title);
-  chrome_translate_client = GetChromeTranslateClient();
-  WaitUntilLanguageDetected();
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
-  ui_test_utils::NavigateToURL(browser(), main_url);
-  WaitUntilLanguageDetected();
-
-  // Translate the page through TranslateManager.
-  // In the past, this might have led to renderer kills, because of
-  // request_initiator in requests triggered by the Translate feature was
-  // different than the request_initiator_site_lock associated with the
-  // URLLoaderFactory.  See also https://crbug.com/910287#c24.
-  base::HistogramTester histograms;
-  translate::TranslateManager* manager =
-      chrome_translate_client->GetTranslateManager();
-  manager->TranslatePage(
-      chrome_translate_client->GetLanguageState().original_language(), "en",
-      true);
-
-  // Wait for NOTIFICATION_PAGE_TRANSLATED notification.
-  WaitUntilPageTranslated();
-
-  EXPECT_FALSE(chrome_translate_client->GetLanguageState().translation_error());
-  EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
-
-  // Verify that |request_initiator| seen by the network service was compatible
-  // with |request_initiator_site_lock| that was declared by the browser for the
-  // URLLoaderFactory.  See also https://crbug.com/925457.
-  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-    using LockCompatibility = network::InitiatorLockCompatibility;
-    EXPECT_EQ(
-        0,
-        histograms.GetBucketCount(
-            "NetworkService.URLLoader.RequestInitiatorOriginLockCompatibility",
-            static_cast<int>(LockCompatibility::kIncorrectLock)));
-    EXPECT_LE(
-        1,
-        histograms.GetBucketCount(
-            "NetworkService.URLLoader.RequestInitiatorOriginLockCompatibility",
-            static_cast<int>(LockCompatibility::kCompatibleLock)));
-  }
-}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0c6eace3..1506939 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3371,6 +3371,8 @@
       "ash/assistant/assistant_context_util.h",
       "ash/assistant/assistant_image_downloader.cc",
       "ash/assistant/assistant_image_downloader.h",
+      "ash/assistant/assistant_pref_util.cc",
+      "ash/assistant/assistant_pref_util.h",
       "ash/assistant/assistant_setup.cc",
       "ash/assistant/assistant_setup.h",
       "ash/assistant/device_actions.cc",
diff --git a/chrome/browser/ui/app_list/crostini/OWNERS b/chrome/browser/ui/app_list/crostini/OWNERS
index 83af899..9d9bf0c4 100644
--- a/chrome/browser/ui/app_list/crostini/OWNERS
+++ b/chrome/browser/ui/app_list/crostini/OWNERS
@@ -1,3 +1 @@
-benwells@chromium.org
-nverne@chromium.org
-timloh@chromium.org
+file://chrome/browser/chromeos/crostini/OWNERS
diff --git a/chrome/browser/ui/ash/assistant/assistant_client.cc b/chrome/browser/ui/ash/assistant/assistant_client.cc
index d523a271..a6508e2 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_client.cc
@@ -7,8 +7,8 @@
 #include <utility>
 
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
-#include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h"
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/assistant/assistant_context_util.h"
 #include "chrome/browser/ui/ash/assistant/assistant_image_downloader.h"
@@ -39,7 +39,7 @@
 }
 
 void AssistantClient::MaybeInit(Profile* profile) {
-  if (arc::IsAssistantAllowedForProfile(profile) !=
+  if (assistant::IsAssistantAllowedForProfile(profile) !=
       ash::mojom::AssistantAllowedState::ALLOWED) {
     return;
   }
diff --git a/chrome/browser/ui/ash/assistant/assistant_pref_util.cc b/chrome/browser/ui/ash/assistant/assistant_pref_util.cc
new file mode 100644
index 0000000..127a63a6c
--- /dev/null
+++ b/chrome/browser/ui/ash/assistant/assistant_pref_util.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
+
+#include <string>
+
+#include "components/arc/arc_prefs.h"
+#include "components/prefs/pref_registry_simple.h"
+
+namespace assistant {
+namespace prefs {
+
+const char kAssistantConsentStatus[] =
+    "settings.voice_interaction.activity_control.consent_status";
+
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterIntegerPref(
+      kAssistantConsentStatus,
+      static_cast<int>(ash::mojom::ConsentStatus::kUnknown));
+}
+
+ash::mojom::ConsentStatus GetConsentStatus(PrefService* pref_service) {
+  return static_cast<ash::mojom::ConsentStatus>(
+      pref_service->GetInteger(kAssistantConsentStatus));
+}
+
+void SetConsentStatus(PrefService* pref_service,
+                      ash::mojom::ConsentStatus consent_status) {
+  pref_service->SetInteger(kAssistantConsentStatus,
+                           static_cast<int>(consent_status));
+}
+
+}  // namespace prefs
+}  // namespace assistant
diff --git a/chrome/browser/ui/ash/assistant/assistant_pref_util.h b/chrome/browser/ui/ash/assistant/assistant_pref_util.h
new file mode 100644
index 0000000..77ef30d
--- /dev/null
+++ b/chrome/browser/ui/ash/assistant/assistant_pref_util.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_ASSISTANT_ASSISTANT_PREF_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_ASSISTANT_ASSISTANT_PREF_UTIL_H_
+
+#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
+#include "components/prefs/pref_service.h"
+
+class PrefRegistrySimple;
+
+namespace assistant {
+namespace prefs {
+
+extern const char kAssistantConsentStatus[];
+
+// Registers Assistant specific profile preferences.
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+// Gets the user's consent status from the given preference service.
+ash::mojom::ConsentStatus GetConsentStatus(PrefService* pref_service);
+
+// Sets the user's consent status in the given preference service.
+void SetConsentStatus(PrefService* pref_service,
+                      ash::mojom::ConsentStatus consent_status);
+
+}  // namespace prefs
+}  // namespace assistant
+
+#endif  // CHROME_BROWSER_UI_ASH_ASSISTANT_ASSISTANT_PREF_UTIL_H_
diff --git a/chrome/browser/ui/ash/assistant/assistant_setup.cc b/chrome/browser/ui/ash/assistant/assistant_setup.cc
index 68f5ed5..cee0021 100644
--- a/chrome/browser/ui/ash/assistant/assistant_setup.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_setup.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 #include "chrome/grit/generated_resources.h"
@@ -188,29 +189,25 @@
     case ConsentFlowUi::ASK_FOR_CONSENT:
       if (consent_ui.has_activity_control_ui() &&
           consent_ui.activity_control_ui().setting_zippy().size()) {
-        prefs->SetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted,
-                          false);
+        assistant::prefs::SetConsentStatus(
+            prefs, ash::mojom::ConsentStatus::kNotFound);
       } else {
-        prefs->SetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted,
-                          true);
+        assistant::prefs::SetConsentStatus(
+            prefs, ash::mojom::ConsentStatus::kActivityControlAccepted);
       }
       break;
     case ConsentFlowUi::ERROR_ACCOUNT:
-      // Show the opted out mode UI for unsupported Account as they are in opted
-      // out mode.
-      // TODO(llin): we should show a error account message in Opted out UI or
-      // in the onboarding flow.
-      prefs->SetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted,
-                        false);
+      assistant::prefs::SetConsentStatus(
+          prefs, ash::mojom::ConsentStatus::kUnauthorized);
       break;
     case ConsentFlowUi::ALREADY_CONSENTED:
-      prefs->SetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted,
-                        true);
+      assistant::prefs::SetConsentStatus(
+          prefs, ash::mojom::ConsentStatus::kActivityControlAccepted);
       break;
     case ConsentFlowUi::UNSPECIFIED:
     case ConsentFlowUi::ERROR:
-      prefs->SetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted,
-                        false);
+      assistant::prefs::SetConsentStatus(prefs,
+                                         ash::mojom::ConsentStatus::kUnknown);
       LOG(ERROR) << "Invalid activity control consent status.";
   }
 }
@@ -219,7 +216,7 @@
   auto* pref_service = ProfileManager::GetActiveUserProfile()->GetPrefs();
   DCHECK(pref_service);
   if (!pref_service->GetUserPrefValue(
-          arc::prefs::kVoiceInteractionActivityControlAccepted)) {
+          assistant::prefs::kAssistantConsentStatus)) {
     base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(&AssistantSetup::StartAssistantOptInFlow,
                                   weak_factory_.GetWeakPtr(),
diff --git a/chrome/browser/ui/ash/assistant/assistant_setup.h b/chrome/browser/ui/ash/assistant/assistant_setup.h
index 1a16670b..bc665d5 100644
--- a/chrome/browser/ui/ash/assistant/assistant_setup.h
+++ b/chrome/browser/ui/ash/assistant/assistant_setup.h
@@ -28,7 +28,7 @@
       ash::mojom::FlowType type,
       StartAssistantOptInFlowCallback callback) override;
 
-  // If prefs::kVoiceInteractionActivityControlAccepted is nullptr, means the
+  // If prefs::kVoiceInteractionConsentStatus is nullptr, means the
   // pref is not set by user. Therefore we need to start OOBE.
   void MaybeStartAssistantOptInFlow();
 
diff --git a/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc b/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc
index 03129b2..76a98bf 100644
--- a/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc
+++ b/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.cc
@@ -34,23 +34,7 @@
       base::WrapUnique(new AlternateNavInfoBarDelegate(
           Profile::FromBrowserContext(web_contents->GetBrowserContext()), text,
           std::make_unique<AutocompleteMatch>(match), match.destination_url,
-          search_url, base::OnceClosure()))));
-}
-
-// static
-void AlternateNavInfoBarDelegate::CreateForLookalikeUrlNavigation(
-    content::WebContents* web_contents,
-    const base::string16& text,
-    const GURL& destination_url,
-    const GURL& original_url,
-    base::OnceClosure link_clicked_callback) {
-  InfoBarService* infobar_service =
-      InfoBarService::FromWebContents(web_contents);
-  infobar_service->AddInfoBar(AlternateNavInfoBarDelegate::CreateInfoBar(
-      base::WrapUnique(new AlternateNavInfoBarDelegate(
-          Profile::FromBrowserContext(web_contents->GetBrowserContext()), text,
-          nullptr, destination_url, original_url,
-          std::move(link_clicked_callback)))));
+          search_url))));
 }
 
 AlternateNavInfoBarDelegate::AlternateNavInfoBarDelegate(
@@ -58,15 +42,13 @@
     const base::string16& text,
     std::unique_ptr<AutocompleteMatch> match,
     const GURL& destination_url,
-    const GURL& original_url,
-    base::OnceClosure link_clicked_callback)
+    const GURL& original_url)
     : infobars::InfoBarDelegate(),
       profile_(profile),
       text_(text),
       match_(std::move(match)),
       destination_url_(destination_url),
-      original_url_(original_url),
-      link_clicked_callback_(std::move(link_clicked_callback)) {
+      original_url_(original_url) {
   if (match_)
     DCHECK_EQ(destination_url_, match_->destination_url);
 
@@ -98,28 +80,19 @@
       HistoryServiceFactory::GetForProfile(profile_,
                                            ServiceAccessType::IMPLICIT_ACCESS);
 
-  if (match_) {
-    // If there is an autocomplete match, this is an omnibox navigation. Tell
-    // the shortcuts backend to remove the shortcut it added for the
-    // original search and instead add one reflecting this navigation.
-    scoped_refptr<ShortcutsBackend> shortcuts_backend(
-        ShortcutsBackendFactory::GetForProfile(profile_));
-    if (shortcuts_backend.get()) {  // May be NULL in incognito.
-      shortcuts_backend->DeleteShortcutsWithURL(original_url_);
-      shortcuts_backend->AddOrUpdateShortcut(text_, *match_);
-    }
-
-    // Tell the history system to remove any saved search term for the search.
-    if (history_service)
-      history_service->DeleteKeywordSearchTermForURL(original_url_);
-  } else {
-    // This is a lookalike URL navigation suggestion. Remove the current entry.
-    if (history_service)
-      history_service->DeleteURL(original_url_);
+  // Tell the shortcuts backend to remove the shortcut it added for the
+  // original search and instead add one reflecting this navigation.
+  scoped_refptr<ShortcutsBackend> shortcuts_backend(
+      ShortcutsBackendFactory::GetForProfile(profile_));
+  if (shortcuts_backend.get()) {  // May be NULL in incognito.
+    shortcuts_backend->DeleteShortcutsWithURL(original_url_);
+    shortcuts_backend->AddOrUpdateShortcut(text_, *match_);
   }
 
-  if (!link_clicked_callback_.is_null())
-    std::move(link_clicked_callback_).Run();
+  // Tell the history system to remove any saved search term for the search.
+  if (history_service)
+    history_service->DeleteKeywordSearchTermForURL(original_url_);
+
   // Pretend the user typed this URL, so that navigating to it will be the
   // default action when it's typed again in the future.
   InfoBarService::WebContentsFromInfoBar(infobar())->OpenURL(
diff --git a/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h b/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h
index 0dbd51d..10733c41 100644
--- a/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h
+++ b/chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h
@@ -35,14 +35,6 @@
                                          const AutocompleteMatch& match,
                                          const GURL& search_url);
 
-  // Creates the delegate for navigations involving lookalike URLs
-  // (e.g. googlé.com).
-  static void CreateForLookalikeUrlNavigation(
-      content::WebContents* web_contents,
-      const base::string16& text,
-      const GURL& suggested_url,
-      const GURL& original_url,
-      base::OnceClosure link_clicked_callback);
   base::string16 GetMessageTextWithOffset(size_t* link_offset) const;
   base::string16 GetLinkText() const;
   GURL GetLinkURL() const;
@@ -53,8 +45,7 @@
                               const base::string16& text,
                               std::unique_ptr<AutocompleteMatch> match,
                               const GURL& destination_url,
-                              const GURL& original_url,
-                              base::OnceClosure link_clicked_callback);
+                              const GURL& original_url);
 
   // Returns an alternate nav infobar that owns |delegate|.
   static std::unique_ptr<infobars::InfoBar> CreateInfoBar(
@@ -77,13 +68,9 @@
 
   // Original URL of the navigation. When the user clicks the suggested
   // navigation link, this will be removed from history.
-  // For search navigations this is the search URL. For lookalike URL
-  // navigations, this is the URL that visually matches a top domain or a domain
-  // with a site engagement score.
+  // For search navigations this is the search URL.
   const GURL original_url_;
 
-  base::OnceClosure link_clicked_callback_;
-
   DISALLOW_COPY_AND_ASSIGN(AlternateNavInfoBarDelegate);
 };
 
diff --git a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
index f799d88..6afcdd8 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
+++ b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
@@ -296,7 +296,8 @@
   void OnCloseContents(content::WebContents* source,
                        bool* out_close_dialog) override {}
 
-  bool HandleContextMenu(const content::ContextMenuParams& params) override {
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override {
     return true;
   }
 
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 95e9d4d0..d1c71358 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/installable/installable_manager.h"
-#include "chrome/browser/lookalikes/lookalike_url_navigation_observer.h"
 #include "chrome/browser/media/media_engagement_service.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_observer.h"
 #include "chrome/browser/metrics/oom/out_of_memory_reporter.h"
@@ -292,7 +291,6 @@
   FramebustBlockTabHelper::CreateForWebContents(web_contents);
   HungPluginTabHelper::CreateForWebContents(web_contents);
   JavaScriptDialogTabHelper::CreateForWebContents(web_contents);
-  LookalikeUrlNavigationObserver::CreateForWebContents(web_contents);
   ManagePasswordsUIController::CreateForWebContents(web_contents);
   pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(
       web_contents, std::make_unique<ChromePDFWebContentsHelperClient>());
diff --git a/chrome/browser/ui/user_manager.cc b/chrome/browser/ui/user_manager.cc
index b039fc8..cecb7be 100644
--- a/chrome/browser/ui/user_manager.cc
+++ b/chrome/browser/ui/user_manager.cc
@@ -21,6 +21,7 @@
     : guest_web_contents_(nullptr) {}
 
 bool UserManagerProfileDialog::BaseDialogDelegate::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   // Ignores context menu.
   return true;
diff --git a/chrome/browser/ui/user_manager.h b/chrome/browser/ui/user_manager.h
index 7c37bfd5..cf0e188 100644
--- a/chrome/browser/ui/user_manager.h
+++ b/chrome/browser/ui/user_manager.h
@@ -98,7 +98,8 @@
     BaseDialogDelegate();
 
     // content::WebContentsDelegate:
-    bool HandleContextMenu(const content::ContextMenuParams& params) override;
+    bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                           const content::ContextMenuParams& params) override;
 
     // content::WebContentsDelegate:
     void LoadingStateChanged(content::WebContents* source,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
index b12ee4b..86b7ef64 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -270,9 +270,8 @@
 // TearDown.
 class BookmarkBarViewEventTestBase : public ViewEventTestBase {
  public:
-  BookmarkBarViewEventTestBase()
-      : ViewEventTestBase(),
-        model_(NULL) {}
+  BookmarkBarViewEventTestBase() = default;
+  ~BookmarkBarViewEventTestBase() override = default;
 
   void SetUp() override {
     content_client_.reset(new ChromeContentClient);
@@ -377,7 +376,7 @@
   // See comment above class description for what this does.
   virtual bool CreateBigMenu() { return false; }
 
-  BookmarkModel* model_;
+  BookmarkModel* model_ = nullptr;
   std::unique_ptr<BookmarkBarView> bb_view_;
   TestingPageNavigator navigator_;
 
@@ -717,12 +716,11 @@
   void Step2() {
     // Menu should be showing.
     views::MenuItemView* menu = bb_view_->GetMenu();
-    ASSERT_TRUE(menu != NULL);
-    ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
-
-    views::MenuItemView* child_menu =
-        menu->GetSubmenu()->GetMenuItemAt(0);
-    ASSERT_TRUE(child_menu != NULL);
+    ASSERT_NE(nullptr, menu);
+    views::SubmenuView* submenu = menu->GetSubmenu();
+    ASSERT_TRUE(submenu->IsShowing());
+    views::MenuItemView* child_menu = submenu->GetMenuItemAt(0);
+    ASSERT_NE(nullptr, child_menu);
 
     // Move mouse to center of menu and press button.
     ui_test_utils::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
@@ -827,12 +825,11 @@
   void Step2() {
     // Menu should be showing.
     views::MenuItemView* menu = bb_view_->GetMenu();
-    ASSERT_TRUE(menu != NULL);
-    ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
-
-    views::MenuItemView* child_menu =
-        menu->GetSubmenu()->GetMenuItemAt(0);
-    ASSERT_TRUE(child_menu != NULL);
+    ASSERT_NE(nullptr, menu);
+    views::SubmenuView* submenu = menu->GetSubmenu();
+    ASSERT_TRUE(submenu->IsShowing());
+    views::MenuItemView* child_menu = submenu->GetMenuItemAt(0);
+    ASSERT_NE(nullptr, child_menu);
 
     // Move mouse to center of menu and press button.
     ui_test_utils::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
@@ -842,9 +839,8 @@
 
   void Step3() {
     // Drag over other button.
-    views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    gfx::Point loc(other_button->width() / 2, other_button->height() / 2);
-    views::View::ConvertPointToScreen(other_button, &loc);
+    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
+        bb_view_->other_bookmarks_button());
 
 #if defined(USE_AURA)
     // TODO: fix this. Aura requires an additional mouse event to trigger drag
@@ -865,9 +861,8 @@
 
   void Step3A() {
     // Drag over other button.
-    views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    gfx::Point loc(other_button->width() / 2, other_button->height() / 2);
-    views::View::ConvertPointToScreen(other_button, &loc);
+    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
+        bb_view_->other_bookmarks_button());
 
     ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
         loc.x(), loc.y(),
@@ -876,14 +871,15 @@
 
   void Step4() {
     views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
-    ASSERT_TRUE(drop_menu != NULL);
-    ASSERT_TRUE(drop_menu->GetSubmenu()->IsShowing());
+    ASSERT_NE(nullptr, drop_menu);
+    views::SubmenuView* drop_submenu = drop_menu->GetSubmenu();
+    ASSERT_TRUE(drop_submenu->IsShowing());
+
     // The button should be highlighted now.
     views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    ASSERT_EQ(views::Button::STATE_PRESSED, other_button->state());
+    EXPECT_EQ(views::Button::STATE_PRESSED, other_button->state());
 
-    views::MenuItemView* target_menu =
-        drop_menu->GetSubmenu()->GetMenuItemAt(0);
+    views::MenuItemView* target_menu = drop_submenu->GetMenuItemAt(0);
     gfx::Point loc(1, 1);
     views::View::ConvertPointToScreen(target_menu, &loc);
     ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
@@ -897,10 +893,10 @@
   }
 
   void Step6() {
-    ASSERT_TRUE(model_->other_node()->GetChild(0)->url() == url_dragging_);
+    EXPECT_EQ(url_dragging_, model_->other_node()->GetChild(0)->url());
     // The button should be in normal state now.
     views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    ASSERT_EQ(views::Button::STATE_NORMAL, other_button->state());
+    EXPECT_EQ(views::Button::STATE_NORMAL, other_button->state());
     Done();
   }
 
@@ -934,12 +930,11 @@
   void Step2() {
     // Menu should be showing.
     views::MenuItemView* menu = bb_view_->GetMenu();
-    ASSERT_TRUE(menu != NULL);
-    ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
-
-    views::MenuItemView* child_menu =
-        menu->GetSubmenu()->GetMenuItemAt(0);
-    ASSERT_TRUE(child_menu != NULL);
+    ASSERT_NE(nullptr, menu);
+    views::SubmenuView* submenu = menu->GetSubmenu();
+    ASSERT_TRUE(submenu->IsShowing());
+    views::MenuItemView* child_menu = submenu->GetMenuItemAt(0);
+    ASSERT_NE(nullptr, child_menu);
 
     // Move mouse to center of menu and press button.
     ui_test_utils::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
@@ -949,9 +944,8 @@
 
   void Step3() {
     // Drag over other button.
-    views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    gfx::Point loc(other_button->width() / 2, other_button->height() / 2);
-    views::View::ConvertPointToScreen(other_button, &loc);
+    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
+        bb_view_->other_bookmarks_button());
 
     // Start a drag.
 #if defined(USE_AURA)
@@ -971,9 +965,8 @@
 
   void Step3A() {
     // Drag over other button.
-    views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    gfx::Point loc(other_button->width() / 2, other_button->height() / 2);
-    views::View::ConvertPointToScreen(other_button, &loc);
+    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
+        bb_view_->other_bookmarks_button());
 
     ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
         loc.x() + 10, loc.y(),
@@ -982,13 +975,12 @@
 
   void Step4() {
     views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
-    ASSERT_TRUE(drop_menu != NULL);
+    ASSERT_NE(nullptr, drop_menu);
     ASSERT_TRUE(drop_menu->GetSubmenu()->IsShowing());
 
     // Now drag back over first menu.
-    views::LabelButton* button = GetBookmarkButton(0);
-    gfx::Point loc(button->width() / 2, button->height() / 2);
-    views::View::ConvertPointToScreen(button, &loc);
+    gfx::Point loc =
+        ui_test_utils::GetCenterInScreenCoordinates(GetBookmarkButton(0));
     ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
         loc.x(), loc.y(),
         base::BindOnce(&BookmarkBarViewTest8::Step5, base::Unretained(this))));
@@ -997,11 +989,11 @@
   void Step5() {
     // Drop on folder F11.
     views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
-    ASSERT_TRUE(drop_menu != NULL);
-    ASSERT_TRUE(drop_menu->GetSubmenu()->IsShowing());
+    ASSERT_NE(nullptr, drop_menu);
+    views::SubmenuView* drop_submenu = drop_menu->GetSubmenu();
+    ASSERT_TRUE(drop_submenu->IsShowing());
 
-    views::MenuItemView* target_menu =
-        drop_menu->GetSubmenu()->GetMenuItemAt(1);
+    views::MenuItemView* target_menu = drop_submenu->GetMenuItemAt(1);
     ui_test_utils::MoveMouseToCenterAndPress(
         target_menu, ui_controls::LEFT, ui_controls::UP,
         CreateEventTask(this, &BookmarkBarViewTest8::Step6));
@@ -1011,7 +1003,7 @@
     // Make sure drop was processed.
     GURL final_url = model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->
         GetChild(1)->url();
-    ASSERT_TRUE(final_url == url_dragging_);
+    EXPECT_EQ(url_dragging_, final_url);
     Done();
   }
 
@@ -2025,13 +2017,13 @@
  private:
   void Step2() {
     // Menu should be showing.
-    views::MenuItemView* menu = bb_view_->GetMenu();
-    ASSERT_TRUE(menu);
-    ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+    views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
+    ASSERT_NE(nullptr, drop_menu);
+    views::SubmenuView* drop_submenu = drop_menu->GetSubmenu();
+    ASSERT_TRUE(drop_submenu->IsShowing());
 
-    views::MenuItemView* child_menu =
-        menu->GetSubmenu()->GetMenuItemAt(0);
-    ASSERT_TRUE(child_menu != NULL);
+    views::MenuItemView* child_menu = drop_submenu->GetMenuItemAt(0);
+    ASSERT_NE(nullptr, child_menu);
 
     // Move mouse to center of menu and press button.
     ui_test_utils::MoveMouseToCenterAndPress(
diff --git a/chrome/browser/ui/views/crostini/OWNERS b/chrome/browser/ui/views/crostini/OWNERS
index 83af899..9d9bf0c4 100644
--- a/chrome/browser/ui/views/crostini/OWNERS
+++ b/chrome/browser/ui/views/crostini/OWNERS
@@ -1,3 +1 @@
-benwells@chromium.org
-nverne@chromium.org
-timloh@chromium.org
+file://chrome/browser/chromeos/crostini/OWNERS
diff --git a/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc b/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc
index b1d571ac..32a2c37 100644
--- a/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc
+++ b/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc
@@ -59,8 +59,10 @@
     browser_process_platform_part_test_api_->InitializeCrosComponentManager(
         std::move(cros_component_manager));
   }
-  void ServiceManagerConnectionStarted(
-      content::ServiceManagerConnection* connection) override {
+  // Ideally we'd call SetConnectionType in ServiceManagerConnectionStarted,
+  // but currently we have to wait for PreProfileInit to complete, since that
+  // creates the ash::Shell that AshService needs in order to start.
+  void PostProfileInit() override {
     connection_change_simulator_.SetConnectionType(
         network::mojom::ConnectionType::CONNECTION_WIFI);
   }
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
index a6c79f7..ce82d09 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
@@ -34,7 +34,8 @@
 
 void ReopenTabPromoController::ShowPromo() {
   // This shouldn't be called more than once. Check that state is fresh.
-  DCHECK_EQ(StepAtDismissal::kBubbleShown, promo_step_);
+  DCHECK(!is_showing_);
+  is_showing_ = true;
 
   // Here, we start the promo display. We highlight the app menu button and open
   // the promo bubble.
@@ -52,8 +53,7 @@
 void ReopenTabPromoController::OnTabReopened(int command_id) {
   iph_service_->TabReopened();
 
-  if (command_id == AppMenuModel::kMinRecentTabsCommandId) {
-    DCHECK_EQ(StepAtDismissal::kMenuOpened, promo_step_);
+  if (is_showing_ && command_id == AppMenuModel::kMinRecentTabsCommandId) {
     promo_step_ = StepAtDismissal::kTabReopened;
   }
 }
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
index f445e94..a70be45 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
@@ -67,6 +67,9 @@
   // flow ends.
   StepAtDismissal promo_step_ = StepAtDismissal::kBubbleShown;
 
+  // Whether we are showing the promo.
+  bool is_showing_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(ReopenTabPromoController);
 };
 
diff --git a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
index bc0464f..687cf337 100644
--- a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
+++ b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
@@ -89,7 +89,7 @@
   void DispatchKeyEventPostIME(
       std::unique_ptr<ui::Event> event,
       DispatchKeyEventPostIMECallback callback) override {
-    std::move(callback).Run(false);
+    std::move(callback).Run(false, false);
   }
   void EnsureCaretNotInRect(const gfx::Rect& rect) override {}
   void SetEditableSelectionRange(const gfx::Range& range) override {}
diff --git a/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc b/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
index 40186ad..a5653fc 100644
--- a/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
+++ b/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
@@ -14,8 +14,10 @@
     : remote_client_(std::move(client)), details_(std::move(details)) {}
 
 RemoteTextInputClient::~RemoteTextInputClient() {
-  while (!pending_callbacks_.empty())
-    RunNextPendingCallback(false);
+  while (!pending_callbacks_.empty()) {
+    RunNextPendingCallback(/* handled */ false,
+                           /* stopped_propagation */ false);
+  }
 }
 
 void RemoteTextInputClient::SetTextInputState(
@@ -32,8 +34,10 @@
   details_->data = std::move(data);
 }
 
-void RemoteTextInputClient::OnDispatchKeyEventPostIMECompleted(bool completed) {
-  RunNextPendingCallback(completed);
+void RemoteTextInputClient::OnDispatchKeyEventPostIMECompleted(
+    bool handled,
+    bool stopped_propagation) {
+  RunNextPendingCallback(handled, stopped_propagation);
 }
 
 void RemoteTextInputClient::SetCompositionText(
@@ -139,7 +143,8 @@
 bool RemoteTextInputClient::GetTextFromRange(const gfx::Range& range,
                                              base::string16* text) const {
   if (!details_->data->text.has_value() ||
-      range.GetMin() >= details_->data->text->length()) {
+      !details_->data->text_range.has_value() ||
+      !details_->data->text_range->Contains(range)) {
     return false;
   }
 
@@ -195,8 +200,8 @@
 
 ui::EventDispatchDetails RemoteTextInputClient::DispatchKeyEventPostIME(
     ui::KeyEvent* event,
-    base::OnceCallback<void(bool)> ack_callback) {
-  pending_callbacks_.push(std::move(ack_callback));
+    DispatchKeyEventPostIMECallback callback) {
+  pending_callbacks_.push(std::move(callback));
   remote_client_->DispatchKeyEventPostIME(
       ui::Event::Clone(*event),
       base::BindOnce(&RemoteTextInputClient::OnDispatchKeyEventPostIMECompleted,
@@ -204,11 +209,12 @@
   return ui::EventDispatchDetails();
 }
 
-void RemoteTextInputClient::RunNextPendingCallback(bool completed) {
+void RemoteTextInputClient::RunNextPendingCallback(bool handled,
+                                                   bool stopped_propagation) {
   DCHECK(!pending_callbacks_.empty());
-  base::OnceCallback<void(bool)> callback =
+  DispatchKeyEventPostIMECallback callback =
       std::move(pending_callbacks_.front());
   pending_callbacks_.pop();
   if (callback)
-    std::move(callback).Run(completed);
+    std::move(callback).Run(handled, stopped_propagation);
 }
diff --git a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h b/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
index 24292eb..169ea9f 100644
--- a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
+++ b/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
@@ -27,7 +27,8 @@
 
  private:
   // See |pending_callbacks_| for details.
-  void OnDispatchKeyEventPostIMECompleted(bool completed);
+  void OnDispatchKeyEventPostIMECompleted(bool handled,
+                                          bool stopped_propagation);
 
   // ui::TextInputClient:
   void SetCompositionText(const ui::CompositionText& composition) override;
@@ -65,11 +66,11 @@
   // ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* event,
-      base::OnceCallback<void(bool)> ack_callback) override;
+      DispatchKeyEventPostIMECallback callback) override;
 
   // Removes the callback at the front of |pending_callbacks_| and runs it with
-  // |completed| as the argument.
-  void RunNextPendingCallback(bool completed);
+  // |handled| and |stopped_propagation| as arguments.
+  void RunNextPendingCallback(bool handled, bool stopped_propagation);
 
   ws::mojom::TextInputClientPtr remote_client_;
   ws::mojom::SessionDetailsPtr details_;
@@ -80,7 +81,7 @@
   // This is done to ensure if we are destroyed all the callbacks are run.
   // This is necessary as the callbacks may have originated from a remote
   // client.
-  base::queue<base::OnceCallback<void(bool)>> pending_callbacks_;
+  base::queue<DispatchKeyEventPostIMECallback> pending_callbacks_;
 
   base::WeakPtrFactory<RemoteTextInputClient> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
index 13f3887..6e0d3c5 100644
--- a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
+++ b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
@@ -53,12 +53,13 @@
 // A simple view to serve as a drop target.
 class TestTargetView : public views::View {
  public:
-  TestTargetView();
-  ~TestTargetView() override;
+  TestTargetView() = default;
+  ~TestTargetView() override = default;
 
-  // Initializes this view to have the same bounds as |parent| and two draggable
-  // child views.
-  void Init(views::View* parent);
+  // Initializes this view to have the same bounds as its parent, and to have
+  // two draggable child views.
+  void Init();
+
   bool dragging() const { return dragging_; }
   bool dropped() const { return dropped_; }
 
@@ -74,20 +75,17 @@
   void OnDragExited() override;
 
   // Whether or not we are currently dragging.
-  bool dragging_;
+  bool dragging_ = false;
 
   // Whether or not a drop has been performed on the view.
-  bool dropped_;
+  bool dropped_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestTargetView);
 };
 
-TestTargetView::TestTargetView() : dragging_(false), dropped_(false) {
-}
-
-void TestTargetView::Init(views::View* parent) {
-  // First, match the parent's size.
-  SetSize(parent->size());
+void TestTargetView::Init() {
+  // First, fill the parent completely.
+  SetBoundsRect(parent()->GetLocalBounds());
 
   // Then add two draggable views, each 10x2.
   views::View* first = new TestDragView();
@@ -99,9 +97,6 @@
   second->SetBounds(15, 2, 10, 2);
 }
 
-TestTargetView::~TestTargetView() {
-}
-
 bool TestTargetView::GetDropFormats(
     int* formats,
     std::set<ui::ClipboardFormatType>* format_types) {
@@ -141,18 +136,18 @@
 
 class MenuViewDragAndDropTest : public MenuTestBase {
  public:
-  MenuViewDragAndDropTest();
-  ~MenuViewDragAndDropTest() override;
+  MenuViewDragAndDropTest() = default;
+  ~MenuViewDragAndDropTest() override = default;
 
  protected:
+  // MenuTestBase:
+  void BuildMenu(views::MenuItemView* menu) override;
+
   TestTargetView* target_view() { return target_view_; }
   bool asked_to_close() const { return asked_to_close_; }
   bool performed_in_menu_drop() const { return performed_in_menu_drop_; }
 
  private:
-  // MenuTestBase:
-  void BuildMenu(views::MenuItemView* menu) override;
-
   // views::MenuDelegate:
   bool GetDropFormats(views::MenuItemView* menu,
                       int* formats,
@@ -173,34 +168,23 @@
   bool ShouldCloseOnDragComplete() override;
 
   // The special view in the menu, which supports its own drag and drop.
-  TestTargetView* target_view_;
+  TestTargetView* target_view_ = nullptr;
 
   // Whether or not we have been asked to close on drag complete.
-  bool asked_to_close_;
+  bool asked_to_close_ = false;
 
   // Whether or not a drop was performed in-menu (i.e., not including drops
   // in separate child views).
-  bool performed_in_menu_drop_;
+  bool performed_in_menu_drop_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(MenuViewDragAndDropTest);
 };
 
-MenuViewDragAndDropTest::MenuViewDragAndDropTest()
-    : target_view_(NULL),
-      asked_to_close_(false),
-      performed_in_menu_drop_(false) {
-}
-
-MenuViewDragAndDropTest::~MenuViewDragAndDropTest() {
-}
-
 void MenuViewDragAndDropTest::BuildMenu(views::MenuItemView* menu) {
   // Build a menu item that has a nested view that supports its own drag and
   // drop...
-  views::MenuItemView* menu_item_view =
-      menu->AppendMenuItem(1,
-                           base::ASCIIToUTF16("item 1"),
-                           views::MenuItemView::NORMAL);
+  views::MenuItemView* menu_item_view = menu->AppendMenuItem(
+      1, base::ASCIIToUTF16("item 1"), views::MenuItemView::NORMAL);
   target_view_ = new TestTargetView();
   menu_item_view->AddChildView(target_view_);
   // ... as well as two other, normal items.
@@ -261,13 +245,14 @@
 
 class MenuViewDragAndDropTestTestInMenuDrag : public MenuViewDragAndDropTest {
  public:
-  MenuViewDragAndDropTestTestInMenuDrag() {}
-  ~MenuViewDragAndDropTestTestInMenuDrag() override {}
+  MenuViewDragAndDropTestTestInMenuDrag() = default;
+  ~MenuViewDragAndDropTestTestInMenuDrag() override = default;
 
- private:
+ protected:
   // MenuViewDragAndDropTest:
   void DoTestWithMenuOpen() override;
 
+ private:
   void Step2();
   void Step3();
   void Step4();
@@ -282,17 +267,15 @@
 
   // We do this here (instead of in BuildMenu()) so that the menu is already
   // built and the bounds are correct.
-  target_view()->Init(submenu->GetMenuItemAt(0));
+  target_view()->Init();
 
   // We're going to drag the second menu element.
   views::MenuItemView* drag_view = submenu->GetMenuItemAt(1);
-  ASSERT_TRUE(drag_view != NULL);
+  ASSERT_NE(nullptr, drag_view);
 
   // Move mouse to center of menu and press button.
   ui_test_utils::MoveMouseToCenterAndPress(
-      drag_view,
-      ui_controls::LEFT,
-      ui_controls::DOWN,
+      drag_view, ui_controls::LEFT, ui_controls::DOWN,
       CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step2));
 }
 
@@ -303,8 +286,7 @@
 
   // Start a drag.
   ui_controls::SendMouseMoveNotifyWhenDone(
-      loc.x() + 10,
-      loc.y(),
+      loc.x() + 10, loc.y(),
       CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step3));
 
   ScheduleMouseMoveInBackground(loc.x(), loc.y());
@@ -318,13 +300,11 @@
   ui_controls::SendMouseMove(loc.x(), loc.y());
 
   ui_controls::SendMouseEventsNotifyWhenDone(
-      ui_controls::LEFT,
-      ui_controls::UP,
+      ui_controls::LEFT, ui_controls::UP,
       CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step4));
 }
 
 void MenuViewDragAndDropTestTestInMenuDrag::Step4() {
-  // Verify our state.
   // We should have performed an in-menu drop, and the nested view should not
   // have had a drag and drop. Since the drag happened in menu code, the
   // delegate should not have been asked whether or not to close, and the menu
@@ -347,13 +327,14 @@
 
 class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest {
  public:
-  MenuViewDragAndDropTestNestedDrag() {}
-  ~MenuViewDragAndDropTestNestedDrag() override {}
+  MenuViewDragAndDropTestNestedDrag() = default;
+  ~MenuViewDragAndDropTestNestedDrag() override = default;
 
- private:
+ protected:
   // MenuViewDragAndDropTest:
   void DoTestWithMenuOpen() override;
 
+ private:
   void Step2();
   void Step3();
   void Step4();
@@ -370,23 +351,21 @@
   views::View* first_view = submenu->GetMenuItemAt(0);
   ASSERT_EQ(1, first_view->child_count());
   views::View* child_view = first_view->child_at(0);
-  ASSERT_EQ(child_view, target_view());
+  EXPECT_EQ(child_view, target_view());
 
   // We do this here (instead of in BuildMenu()) so that the menu is already
   // built and the bounds are correct.
-  target_view()->Init(submenu->GetMenuItemAt(0));
+  target_view()->Init();
 
   // The target view should now have two children.
   ASSERT_EQ(2, target_view()->child_count());
 
   views::View* drag_view = target_view()->child_at(0);
-  ASSERT_TRUE(drag_view != NULL);
+  ASSERT_NE(nullptr, drag_view);
 
   // Move mouse to center of menu and press button.
   ui_test_utils::MoveMouseToCenterAndPress(
-      drag_view,
-      ui_controls::LEFT,
-      ui_controls::DOWN,
+      drag_view, ui_controls::LEFT, ui_controls::DOWN,
       CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step2));
 }
 
@@ -397,8 +376,7 @@
 
   // Start a drag.
   ui_controls::SendMouseMoveNotifyWhenDone(
-      loc.x() + 3,
-      loc.y(),
+      loc.x() + 3, loc.y(),
       CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step3));
 
   ScheduleMouseMoveInBackground(loc.x(), loc.y());
@@ -415,13 +393,11 @@
   ui_controls::SendMouseMove(loc.x(), loc.y());
 
   ui_controls::SendMouseEventsNotifyWhenDone(
-      ui_controls::LEFT,
-      ui_controls::UP,
+      ui_controls::LEFT, ui_controls::UP,
       CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step4));
 }
 
 void MenuViewDragAndDropTestNestedDrag::Step4() {
-  // Check our state.
   // The target view should have finished its drag, and should have dropped the
   // view. The main menu should not have done any drag, and the delegate should
   // have been asked if it wanted to close. Since the delegate did not want to
@@ -430,10 +406,11 @@
   EXPECT_TRUE(target_view()->dropped());
   EXPECT_FALSE(performed_in_menu_drop());
   EXPECT_TRUE(asked_to_close());
-  EXPECT_TRUE(menu()->GetSubmenu()->IsShowing());
+  views::SubmenuView* submenu = menu()->GetSubmenu();
+  EXPECT_TRUE(submenu->IsShowing());
 
   // Clean up.
-  menu()->GetSubmenu()->Close();
+  submenu->Close();
 
   Done();
 }
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index bbdb96f2..caa8249d 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -611,6 +611,7 @@
 }
 
 bool ProfileChooserView::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   // Suppresses the context menu because some features, such as inspecting
   // elements, are not appropriate in a bubble.
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.h b/chrome/browser/ui/views/profiles/profile_chooser_view.h
index c94f8b8..5b2f65f 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.h
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.h
@@ -95,7 +95,8 @@
   base::string16 GetAccessibleWindowTitle() const override;
 
   // content::WebContentsDelegate:
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
 
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index 37d031c..3a8357c2 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -173,6 +173,7 @@
 }
 
 bool SigninViewControllerDelegateViews::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   // Discard the context menu
   return true;
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
index 474d251..c27280d8 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
@@ -53,7 +53,8 @@
   content::WebContents* GetWebContents() override;
 
   // content::WebContentsDelegate:
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
   bool HandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event) override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
index 172f53e..53360c73 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -51,8 +51,16 @@
   void AppMenuShown() override;
 
  protected:
-  AppMenuButton* app_menu_button() { return app_menu_button_; }
-  BrowserActionsContainer* browser_actions() { return browser_actions_; }
+  AppMenuButton* GetAppMenuButton() {
+    return BrowserView::GetBrowserViewForBrowser(browser())
+        ->toolbar_button_provider()
+        ->GetAppMenuButton();
+  }
+  BrowserActionsContainer* GetBrowserActions() {
+    return BrowserView::GetBrowserViewForBrowser(browser())
+        ->toolbar_button_provider()
+        ->GetBrowserActionsContainer();
+  }
 
   // Performs a drag-and-drop operation by moving the mouse to |start|, clicking
   // the left button, moving the mouse to |end|, and releasing the left button.
@@ -60,12 +68,8 @@
 
  private:
   // InProcessBrowserTest:
-  void SetUpCommandLine(base::CommandLine* command_line) override;
   void SetUpOnMainThread() override;
-  void TearDownOnMainThread() override;
 
-  AppMenuButton* app_menu_button_ = nullptr;
-  BrowserActionsContainer* browser_actions_ = nullptr;
   bool menu_shown_ = false;
   base::OnceClosure quit_closure_;
 };
@@ -85,17 +89,17 @@
 
   // Begin listening for the app menu to open.
   ScopedObserver<AppMenuButton, AppMenuButtonObserver> observer(this);
-  observer.Add(app_menu_button());
+  observer.Add(GetAppMenuButton());
 
   // Send the mouse to |start|, and click.  The event queue must be flushed
   // after processing the click, or the next mouse move sent may get processed
   // before the click is fully handled, causing the test to fail.
-  ASSERT_TRUE(ui_controls::SendMouseMove(start.x(), start.y()));
-  ASSERT_TRUE(
+  EXPECT_TRUE(ui_controls::SendMouseMove(start.x(), start.y()));
+  EXPECT_TRUE(
       ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
 
   // Enqueue an event to move the mouse, which will start a drag.
-  ASSERT_TRUE(ui_controls::SendMouseMove(end.x() + 10, end.y()));
+  EXPECT_TRUE(ui_controls::SendMouseMove(end.x() + 10, end.y()));
 
   // Enqueue an event to move the mouse to |end|.  This must be done on a
   // background thread, since starting a drag triggers a nested message loop
@@ -113,32 +117,19 @@
   base::RunLoop run_loop;
   quit_closure_ = run_loop.QuitWhenIdleClosure();
   run_loop.Run();
+
+  // Verify postconditions.
   EXPECT_TRUE(menu_shown_);
-
   // The app menu should have closed once the drag-and-drop completed.
-  EXPECT_FALSE(app_menu_button()->IsMenuShowing());
-}
-
-void ToolbarViewInteractiveUITest::SetUpCommandLine(
-    base::CommandLine* command_line) {
-  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
-  ToolbarActionsBar::disable_animations_for_testing_ = true;
-  BrowserAppMenuButton::g_open_app_immediately_for_testing = true;
+  EXPECT_FALSE(GetAppMenuButton()->IsMenuShowing());
 }
 
 void ToolbarViewInteractiveUITest::SetUpOnMainThread() {
   extensions::ExtensionBrowserTest::SetUpOnMainThread();
+
+  BrowserAppMenuButton::g_open_app_immediately_for_testing = true;
   ExtensionToolbarMenuView::set_close_menu_delay_for_testing(base::TimeDelta());
-
-  auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
-  auto* toolbar_button_provider = browser_view->toolbar_button_provider();
-  app_menu_button_ = toolbar_button_provider->GetAppMenuButton();
-  browser_actions_ = toolbar_button_provider->GetBrowserActionsContainer();
-}
-
-void ToolbarViewInteractiveUITest::TearDownOnMainThread() {
-  ToolbarActionsBar::disable_animations_for_testing_ = false;
-  BrowserAppMenuButton::g_open_app_immediately_for_testing = false;
+  ToolbarActionsBar::disable_animations_for_testing_ = true;
 }
 
 #if defined(OS_LINUX) && defined(USE_AURA)
@@ -161,17 +152,20 @@
   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                           .AppendASCII("browser_action")
                                           .AppendASCII("basics")));
-  base::RunLoop().RunUntilIdle();  // Ensure the extension is fully loaded.
+  // Ensure the extension is fully loaded, and that the next steps will happen
+  // with a clean slate.
+  base::RunLoop().RunUntilIdle();
 
-  ASSERT_EQ(1u, browser_actions()->VisibleBrowserActions());
-
-  ToolbarActionView* view = browser_actions()->GetToolbarActionViewAt(0);
-  ASSERT_TRUE(view);
+  BrowserActionsContainer* const browser_actions = GetBrowserActions();
+  ASSERT_EQ(1u, browser_actions->VisibleBrowserActions());
+  ToolbarActionView* toolbar_action =
+      browser_actions->GetToolbarActionViewAt(0);
+  ASSERT_TRUE(toolbar_action);
 
   gfx::Point browser_action_view_loc =
-      ui_test_utils::GetCenterInScreenCoordinates(view);
+      ui_test_utils::GetCenterInScreenCoordinates(toolbar_action);
   gfx::Point app_button_loc =
-      ui_test_utils::GetCenterInScreenCoordinates(app_menu_button());
+      ui_test_utils::GetCenterInScreenCoordinates(GetAppMenuButton());
 
   // Perform a drag and drop from the browser action view to the app button,
   // which should open the app menu.
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
index 382b4a25..c06cf2b 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
 #include "chrome/browser/ui/views/chrome_web_dialog_view.h"
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 #include "chrome/common/url_constants.h"
@@ -149,7 +150,8 @@
   PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
   const bool completed =
       prefs->GetBoolean(arc::prefs::kVoiceInteractionEnabled) &&
-      prefs->GetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted);
+      (::assistant::prefs::GetConsentStatus(prefs) ==
+       ash::mojom::ConsentStatus::kActivityControlAccepted);
   std::move(callback_).Run(completed);
   SystemWebDialogDelegate::OnDialogClosed(json_retval);
 }
diff --git a/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc b/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc
index 523bbed..d05e0369 100644
--- a/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc
@@ -9,7 +9,7 @@
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/bind.h"
 #include "base/values.h"
-#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "content/public/browser/web_ui.h"
@@ -18,7 +18,7 @@
 
 bool IsAssistantAllowed() {
   return ash::mojom::AssistantAllowedState::ALLOWED ==
-         arc::IsAssistantAllowedForProfile(
+         assistant::IsAssistantAllowedForProfile(
              ProfileManager::GetActiveUserProfile());
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
index 58821e2..fa78c67 100644
--- a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/login/screens/assistant_optin_flow_screen.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/services/assistant/public/features.h"
@@ -216,8 +217,8 @@
             weak_factory_.GetWeakPtr()));
   } else {
     RecordAssistantOptInStatus(ACTIVITY_CONTROL_SKIPPED);
-    profile->GetPrefs()->SetBoolean(
-        arc::prefs::kVoiceInteractionActivityControlAccepted, false);
+    ::assistant::prefs::SetConsentStatus(profile->GetPrefs(),
+                                         ash::mojom::ConsentStatus::kUnknown);
     HandleFlowFinished();
   }
 }
@@ -314,12 +315,17 @@
     // No need to consent. Move to the next screen.
     activity_control_needed_ = false;
     PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-    prefs->SetBoolean(
-        arc::prefs::kVoiceInteractionActivityControlAccepted,
-        (settings_ui.consent_flow_ui().consent_status() ==
-             assistant::ConsentFlowUi_ConsentStatus_ALREADY_CONSENTED ||
-         settings_ui.consent_flow_ui().consent_status() ==
-             assistant::ConsentFlowUi_ConsentStatus_ASK_FOR_CONSENT));
+
+    bool consented =
+        settings_ui.consent_flow_ui().consent_status() ==
+            assistant::ConsentFlowUi_ConsentStatus_ALREADY_CONSENTED ||
+        settings_ui.consent_flow_ui().consent_status() ==
+            assistant::ConsentFlowUi_ConsentStatus_ASK_FOR_CONSENT;
+
+    ::assistant::prefs::SetConsentStatus(
+        prefs, consented ? ash::mojom::ConsentStatus::kActivityControlAccepted
+                         : ash::mojom::ConsentStatus::kUnknown);
+
     // Skip activity control and users will be in opted out mode.
     ShowNextScreen();
   } else {
@@ -380,8 +386,8 @@
     } else if (activity_control_needed_) {
       activity_control_needed_ = false;
       PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
-      prefs->SetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted,
-                        true);
+      ::assistant::prefs::SetConsentStatus(
+          prefs, ash::mojom::ConsentStatus::kActivityControlAccepted);
     }
   }
 
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index cc33a62..6c591c5 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -261,8 +261,8 @@
 
   return new LookalikeUrlInterstitialPage(
       web_contents, safe_url,
-      std::make_unique<LookalikeUrlControllerClient>(web_contents, nullptr,
-                                                     request_url, safe_url));
+      std::make_unique<LookalikeUrlControllerClient>(web_contents, request_url,
+                                                     safe_url));
 }
 
 safe_browsing::SafeBrowsingBlockingPage* CreateSafeBrowsingBlockingPage(
diff --git a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
index a9542d86..d504f1e 100644
--- a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
@@ -118,9 +118,11 @@
 
 InterventionsInternalsPageHandler::InterventionsInternalsPageHandler(
     mojom::InterventionsInternalsPageHandlerRequest request,
-    previews::PreviewsUIService* previews_ui_service)
+    previews::PreviewsUIService* previews_ui_service,
+    network::NetworkQualityTracker* network_quality_tracker)
     : binding_(this, std::move(request)),
       previews_ui_service_(previews_ui_service),
+      network_quality_tracker_(network_quality_tracker),
       current_estimated_ect_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
   logger_ = previews_ui_service_->previews_logger();
   DCHECK(logger_);
@@ -129,7 +131,8 @@
 InterventionsInternalsPageHandler::~InterventionsInternalsPageHandler() {
   DCHECK(logger_);
   logger_->RemoveObserver(this);
-  g_browser_process->network_quality_tracker()
+  (network_quality_tracker_ ? network_quality_tracker_
+                            : g_browser_process->network_quality_tracker())
       ->RemoveEffectiveConnectionTypeObserver(this);
 }
 
@@ -138,7 +141,8 @@
   page_ = std::move(page);
   DCHECK(page_);
   logger_->AddAndNotifyObserver(this);
-  g_browser_process->network_quality_tracker()
+  (network_quality_tracker_ ? network_quality_tracker_
+                            : g_browser_process->network_quality_tracker())
       ->AddEffectiveConnectionTypeObserver(this);
 }
 
diff --git a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.h b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.h
index 49a50b6..87a45b7 100644
--- a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.h
+++ b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.h
@@ -24,7 +24,8 @@
  public:
   InterventionsInternalsPageHandler(
       mojom::InterventionsInternalsPageHandlerRequest request,
-      previews::PreviewsUIService* previews_ui_service);
+      previews::PreviewsUIService* previews_ui_service,
+      network::NetworkQualityTracker* network_quality_tracker);
   ~InterventionsInternalsPageHandler() override;
 
   // mojom::InterventionsInternalsPageHandler:
@@ -58,6 +59,10 @@
   // guaranteed to outlive |this|.
   previews::PreviewsUIService* previews_ui_service_;
 
+  // Passed in during construction. If null, the main browser process tracker
+  // will be used instead.
+  network::NetworkQualityTracker* network_quality_tracker_;
+
   // The current estimated effective connection type.
   net::EffectiveConnectionType current_estimated_ect_;
 
diff --git a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
index b88f0037..972c029 100644
--- a/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
@@ -284,7 +284,8 @@
     mojom::InterventionsInternalsPageHandlerRequest handler_request =
         mojo::MakeRequest(&page_handler_ptr);
     page_handler_ = std::make_unique<InterventionsInternalsPageHandler>(
-        std::move(handler_request), previews_ui_service_.get());
+        std::move(handler_request), previews_ui_service_.get(),
+        &test_network_quality_tracker_);
 
     mojom::InterventionsInternalsPagePtr page_ptr;
     mojom::InterventionsInternalsPageRequest page_request =
diff --git a/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc b/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc
index c3b1370..106ee247 100644
--- a/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc
+++ b/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc
@@ -67,6 +67,6 @@
 void InterventionsInternalsUI::BindInterventionsInternalsPageHandler(
     mojom::InterventionsInternalsPageHandlerRequest request) {
   DCHECK(previews_ui_service_);
-  page_handler_.reset(new InterventionsInternalsPageHandler(
-      std::move(request), previews_ui_service_));
+  page_handler_ = std::make_unique<InterventionsInternalsPageHandler>(
+      std::move(request), previews_ui_service_, nullptr);
 }
diff --git a/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc b/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc
index c0999a2..5e008a09 100644
--- a/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc
+++ b/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc
@@ -219,7 +219,8 @@
   void OnDialogClosed(const std::string& json_retval) override;
   void OnCloseContents(WebContents* source, bool* out_close_dialog) override;
   bool ShouldShowDialogTitle() const override;
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
 
   // Overridden from ResponseDelegate:
   void SetDialogInteractionResult(MakeChromeDefaultResult result) override;
@@ -347,6 +348,7 @@
 }
 
 bool SetAsDefaultBrowserDialogImpl::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   return true;
 }
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index b3926a9..be6b6620 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -60,6 +60,7 @@
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
@@ -2183,7 +2184,7 @@
 void AddSearchStrings(content::WebUIDataSource* html_source, Profile* profile) {
 #if defined(OS_CHROMEOS)
   const bool is_assistant_allowed =
-      arc::IsAssistantAllowedForProfile(profile) ==
+      assistant::IsAssistantAllowedForProfile(profile) ==
       ash::mojom::AssistantAllowedState::ALLOWED;
 #endif
 
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 359a4107..209387fa 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -649,20 +649,19 @@
   ]
 }
 
-if (is_win) {
-  # This is split out here instead of being part of //chrome/test:browser_tests
-  # because the tests have hardware requirements and this is cleaner than trying
-  # to conditionally skip tests based on hardware (browser tests don't have
-  # the equivalent to Android's @Restriction annotation that allows you to
-  # easily skip tests at runtime).
+# This is split out here instead of being part of //chrome/test:browser_tests
+# because the tests have hardware requirements and this is cleaner than trying
+# to conditionally skip tests based on hardware (browser tests don't have
+# the equivalent to Android's @Restriction annotation that allows you to
+# easily skip tests at runtime).
+if (!is_android) {
   test("xr_browser_tests") {
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+    # Common infrastructure.
     sources = [
-      "test/browser_test_browser_renderer_browser_interface.cc",
-      "test/browser_test_browser_renderer_browser_interface.h",
       "test/mock_openvr_device_hook_base.cc",
       "test/mock_openvr_device_hook_base.h",
-      "test/ui_utils.cc",
-      "test/ui_utils.h",
       "test/webvr_browser_test.cc",
       "test/webvr_browser_test.h",
       "test/webxr_browser_test.cc",
@@ -671,32 +670,26 @@
       "test/webxr_vr_browser_test.h",
       "test/xr_browser_test.cc",
       "test/xr_browser_test.h",
-      "webxr_vr_frame_pose_browser_test.cc",
-      "webxr_vr_input_browser_test.cc",
-      "webxr_vr_permission_request_browser_test.cc",
-      "webxr_vr_pixel_browser_test.cc",
-      "webxr_vr_tab_browser_test.cc",
-      "webxr_vr_transition_browser_test.cc",
     ]
 
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    # Common/partially common tests.
+    sources += [ "webxr_vr_transition_browser_test.cc" ]
 
+    # Common dependencies.
     deps = [
       ":vr_common",
       "//base",
+      "//chrome:packed_resources",
       "//chrome/test:browser_tests_runner",
       "//chrome/test:test_support",
       "//chrome/test:test_support_ui",
       "//device/vr:vr",
       "//device/vr/public/mojom:mojom",
       "//device/vr/public/mojom:test_mojom",
-      "//third_party/openvr:openvr",
     ]
+    data_deps = []
 
-    data_deps = [
-      "//device/vr:openvr_mock",
-    ]
-
+    # Common data
     data = [
       "//chrome/test/data/xr/e2e_test_files/",
       "//third_party/blink/web_tests/resources/testharness.js",
@@ -705,5 +698,29 @@
       "$root_out_dir/chrome_200_percent.pak",
       "$root_out_dir/resources.pak",
     ]
+
+    # Windows-only tests and dependencies.
+    if (is_win) {
+      # Infrastructure.
+      sources += [
+        "test/browser_test_browser_renderer_browser_interface.cc",
+        "test/browser_test_browser_renderer_browser_interface.h",
+        "test/ui_utils.cc",
+        "test/ui_utils.h",
+      ]
+
+      # Tests.
+      sources += [
+        "webxr_vr_frame_pose_browser_test.cc",
+        "webxr_vr_input_browser_test.cc",
+        "webxr_vr_permission_request_browser_test.cc",
+        "webxr_vr_pixel_browser_test.cc",
+        "webxr_vr_tab_browser_test.cc",
+      ]
+
+      deps += [ "//third_party/openvr:openvr" ]
+
+      data_deps += [ "//device/vr:openvr_mock" ]
+    }
   }
 }
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.cc b/chrome/browser/vr/metrics/session_metrics_helper.cc
index 09871f1a..8446460 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.cc
+++ b/chrome/browser/vr/metrics/session_metrics_helper.cc
@@ -51,10 +51,6 @@
   MODE_BROWSER_WITH_VIDEO,
   MODE_WEBVR_WITH_VIDEO,
   SESSION_VR_WITH_VIDEO,
-  MODE_FULLSCREEN_DLA,
-  MODE_BROWSER_DLA,
-  MODE_WEBVR_DLA,
-  SESSION_VR_DLA,
 };
 
 const char* HistogramNameFromSessionType(SessionEventName name) {
@@ -67,10 +63,6 @@
   static constexpr char kWebVrVideo[] = "VRSessionVideoTime.WebVR";
   static constexpr char kBrowserVideo[] = "VRSessionVideoTime.Browser";
   static constexpr char kFullscreenVideo[] = "VRSessionVideoTime.Fullscreen";
-  static constexpr char kVrSessionDla[] = "VRSessionTimeFromDLA";
-  static constexpr char kWebVrDla[] = "VRSessionTimeFromDLA.WebVR";
-  static constexpr char kBrowserDla[] = "VRSessionTimeFromDLA.Browser";
-  static constexpr char kFullscreenDla[] = "VRSessionTimeFromDLA.Fullscreen";
 
   switch (name) {
     case MODE_FULLSCREEN:
@@ -89,14 +81,6 @@
       return kWebVrVideo;
     case SESSION_VR_WITH_VIDEO:
       return kVrSessionVideo;
-    case MODE_FULLSCREEN_DLA:
-      return kFullscreenDla;
-    case MODE_BROWSER_DLA:
-      return kBrowserDla;
-    case MODE_WEBVR_DLA:
-      return kWebVrDla;
-    case SESSION_VR_DLA:
-      return kVrSessionDla;
     default:
       NOTREACHED();
       return nullptr;
@@ -221,16 +205,13 @@
 }
 SessionMetricsHelper* SessionMetricsHelper::CreateForWebContents(
     content::WebContents* contents,
-    Mode initial_mode,
-    bool started_with_autopresentation) {
+    Mode initial_mode) {
   // This is not leaked as the SessionMetricsHelperData will clean it up.
-  return new SessionMetricsHelper(contents, initial_mode,
-                                  started_with_autopresentation);
+  return new SessionMetricsHelper(contents, initial_mode);
 }
 
 SessionMetricsHelper::SessionMetricsHelper(content::WebContents* contents,
-                                           Mode initial_mode,
-                                           bool started_with_autopresentation) {
+                                           Mode initial_mode) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(contents);
 
@@ -243,15 +224,9 @@
 
   is_webvr_ = initial_mode == Mode::kWebXrVrPresentation;
   is_vr_enabled_ = initial_mode != Mode::kNoVr;
-  started_with_autopresentation_ = started_with_autopresentation;
 
-  if (started_with_autopresentation) {
-    session_timer_ = std::make_unique<SessionTimerImpl<SESSION_VR_DLA>>(
-        kMaximumVideoSessionGap, kMinimumVideoSessionDuration);
-  } else {
-    session_timer_ = std::make_unique<SessionTimerImpl<SESSION_VR>>(
-        kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  }
+  session_timer_ = std::make_unique<SessionTimerImpl<SESSION_VR>>(
+      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
   session_video_timer_ =
       std::make_unique<SessionTimerImpl<SESSION_VR_WITH_VIDEO>>(
           kMaximumVideoSessionGap, kMinimumVideoSessionDuration);
@@ -462,26 +437,16 @@
 }
 
 void SessionMetricsHelper::OnEnterRegularBrowsing() {
-  if (started_with_autopresentation_) {
-    mode_timer_ = std::make_unique<SessionTimerImpl<MODE_BROWSER_DLA>>(
-        kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  } else {
-    mode_timer_ = std::make_unique<SessionTimerImpl<MODE_BROWSER>>(
-        kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  }
+  mode_timer_ = std::make_unique<SessionTimerImpl<MODE_BROWSER>>(
+      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
   mode_video_timer_ =
       std::make_unique<SessionTimerImpl<MODE_BROWSER_WITH_VIDEO>>(
           kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
 }
 
 void SessionMetricsHelper::OnEnterPresentation() {
-  if (started_with_autopresentation_) {
-    mode_timer_ = std::make_unique<SessionTimerImpl<MODE_WEBVR_DLA>>(
-        kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  } else {
-    mode_timer_ = std::make_unique<SessionTimerImpl<MODE_WEBVR>>(
-        kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  }
+  mode_timer_ = std::make_unique<SessionTimerImpl<MODE_WEBVR>>(
+      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
 
   mode_video_timer_ = std::make_unique<SessionTimerImpl<MODE_WEBVR_WITH_VIDEO>>(
       kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
@@ -515,13 +480,8 @@
 }
 
 void SessionMetricsHelper::OnEnterFullscreenBrowsing() {
-  if (started_with_autopresentation_) {
-    mode_timer_ = std::make_unique<SessionTimerImpl<MODE_FULLSCREEN_DLA>>(
-        kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  } else {
-    mode_timer_ = std::make_unique<SessionTimerImpl<MODE_FULLSCREEN>>(
-        kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
-  }
+  mode_timer_ = std::make_unique<SessionTimerImpl<MODE_FULLSCREEN>>(
+      kMaximumHeadsetSessionGap, kMinimumHeadsetSessionDuration);
 
   mode_video_timer_ =
       std::make_unique<SessionTimerImpl<MODE_FULLSCREEN_WITH_VIDEO>>(
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.h b/chrome/browser/vr/metrics/session_metrics_helper.h
index eb8f0d4d..16f2fc8a 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.h
+++ b/chrome/browser/vr/metrics/session_metrics_helper.h
@@ -155,8 +155,7 @@
   static SessionMetricsHelper* FromWebContents(content::WebContents* contents);
   static SessionMetricsHelper* CreateForWebContents(
       content::WebContents* contents,
-      Mode initial_mode,
-      bool started_with_autopresentation);
+      Mode initial_mode);
 
   ~SessionMetricsHelper() override;
 
@@ -170,9 +169,7 @@
   void ReportRequestPresent();
 
  private:
-  SessionMetricsHelper(content::WebContents* contents,
-                       Mode initial_mode,
-                       bool started_with_autopresentation);
+  SessionMetricsHelper(content::WebContents* contents, Mode initial_mode);
 
   // WebContentObserver
   void MediaStartedPlaying(const MediaPlayerInfo& media_info,
@@ -215,7 +212,6 @@
   bool is_fullscreen_ = false;
   bool is_webvr_ = false;
   bool is_vr_enabled_ = false;
-  bool started_with_autopresentation_ = false;
 
   GURL last_requested_url_;
   NavigationMethod last_url_request_method_;
diff --git a/chrome/browser/vr/service/xr_device_impl.cc b/chrome/browser/vr/service/xr_device_impl.cc
index 88c03ec..5a0b162 100644
--- a/chrome/browser/vr/service/xr_device_impl.cc
+++ b/chrome/browser/vr/service/xr_device_impl.cc
@@ -175,8 +175,8 @@
   if (!metrics_helper) {
     // This will only happen if we are not already in VR, set start params
     // accordingly.
-    metrics_helper = SessionMetricsHelper::CreateForWebContents(
-        web_contents, Mode::kNoVr, false);
+    metrics_helper =
+        SessionMetricsHelper::CreateForWebContents(web_contents, Mode::kNoVr);
   }
   metrics_helper->ReportRequestPresent();
 }
diff --git a/chrome/browser/vr/test/webvr_browser_test.h b/chrome/browser/vr/test/webvr_browser_test.h
index 674006f..1ee7466 100644
--- a/chrome/browser/vr/test/webvr_browser_test.h
+++ b/chrome/browser/vr/test/webvr_browser_test.h
@@ -32,6 +32,16 @@
   using WebXrBrowserTestBase::EndSessionOrFail;
 };
 
+// Test class with OpenVR support disabled.
+class WebVrBrowserTestOpenVrDisabled : public WebVrBrowserTestBase {
+ public:
+  WebVrBrowserTestOpenVrDisabled() {
+    append_switches_.push_back(switches::kEnableWebVR);
+  }
+};
+
+// OpenVR feature only defined on Windows.
+#ifdef OS_WIN
 // Test class with standard features enabled: WebVR and OpenVR support.
 class WebVrBrowserTestStandard : public WebVrBrowserTestBase {
  public:
@@ -48,14 +58,7 @@
     enable_features_.push_back(features::kOpenVR);
   }
 };
-
-// Test class with OpenVR support disabled.
-class WebVrBrowserTestOpenVrDisabled : public WebVrBrowserTestBase {
- public:
-  WebVrBrowserTestOpenVrDisabled() {
-    append_switches_.push_back(switches::kEnableWebVR);
-  }
-};
+#endif  // OS_WIN
 
 }  // namespace vr
 
diff --git a/chrome/browser/vr/test/webxr_vr_browser_test.h b/chrome/browser/vr/test/webxr_vr_browser_test.h
index e8f47129..fdc042a 100644
--- a/chrome/browser/vr/test/webxr_vr_browser_test.h
+++ b/chrome/browser/vr/test/webxr_vr_browser_test.h
@@ -31,6 +31,16 @@
   using WebXrBrowserTestBase::EndSessionOrFail;
 };
 
+// Test class with OpenVR disabled.
+class WebXrVrBrowserTestOpenVrDisabled : public WebXrVrBrowserTestBase {
+ public:
+  WebXrVrBrowserTestOpenVrDisabled() {
+    enable_features_.push_back(features::kWebXr);
+  }
+};
+
+// OpenVR feature only defined on Windows.
+#ifdef OS_WIN
 // Test class with standard features enabled: WebXR and OpenVR.
 class WebXrVrBrowserTestStandard : public WebXrVrBrowserTestBase {
  public:
@@ -47,14 +57,7 @@
     enable_features_.push_back(features::kOpenVR);
   }
 };
-
-// Test class with OpenVR disabled.
-class WebXrVrBrowserTestOpenVrDisabled : public WebXrVrBrowserTestBase {
- public:
-  WebXrVrBrowserTestOpenVrDisabled() {
-    enable_features_.push_back(features::kWebXr);
-  }
-};
+#endif  // OS_WIN
 
 }  // namespace vr
 
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc
index 52062d4..077282a 100644
--- a/chrome/browser/vr/testapp/vr_test_context.cc
+++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -525,7 +525,6 @@
     webvr_frames_received_ = false;
     UiInitialState state;
     state.in_web_vr = true;
-    state.web_vr_autopresentation_expected = true;
     ui_instance_->ReinitializeForTest(state);
   } else {
     ui_instance_->ReinitializeForTest(UiInitialState());
diff --git a/chrome/browser/vr/ui_initial_state.h b/chrome/browser/vr/ui_initial_state.h
index 377acf1..af7dc56 100644
--- a/chrome/browser/vr/ui_initial_state.h
+++ b/chrome/browser/vr/ui_initial_state.h
@@ -15,7 +15,6 @@
   UiInitialState();
   UiInitialState(const UiInitialState& other);
   bool in_web_vr = false;
-  bool web_vr_autopresentation_expected = false;
   bool browsing_disabled = false;
   bool has_or_can_request_record_audio_permission = true;
   bool assets_supported = false;
diff --git a/chrome/browser/vr/ui_pixeltest.cc b/chrome/browser/vr/ui_pixeltest.cc
index 958741a..b377c73 100644
--- a/chrome/browser/vr/ui_pixeltest.cc
+++ b/chrome/browser/vr/ui_pixeltest.cc
@@ -22,7 +22,6 @@
   // Set up scene.
   UiInitialState ui_initial_state;
   ui_initial_state.in_web_vr = false;
-  ui_initial_state.web_vr_autopresentation_expected = false;
   MakeUi(ui_initial_state,
          LocationBarState(GURL("https://example.com"), security_state::SECURE,
                           &omnibox::kHttpsValidIcon, true, false));
diff --git a/chrome/browser/vr/webxr_vr_transition_browser_test.cc b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
index 7fde682..173e061 100644
--- a/chrome/browser/vr/webxr_vr_transition_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
@@ -15,6 +15,58 @@
 
 namespace vr {
 
+// Tests that WebVR/WebXR is not exposed if the flag is not on and the page does
+// not have an origin trial token.
+void TestApiDisabledWithoutFlagSetImpl(WebXrVrBrowserTestBase* t,
+                                       std::string filename) {
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
+  t->WaitOnJavaScriptStep();
+  t->EndTest();
+}
+
+// Tests that WebVR does not return any devices if OpenVR support is disabled.
+IN_PROC_BROWSER_TEST_F(WebVrBrowserTestOpenVrDisabled,
+                       TestWebVrNoDevicesWithoutOpenVr) {
+  LoadUrlAndAwaitInitialization(
+      GetFileUrlForHtmlTestFile("generic_webvr_page"));
+  EXPECT_FALSE(XrDeviceFound())
+      << "Found a VRDisplay even with OpenVR disabled";
+  AssertNoJavaScriptErrors();
+}
+
+// Tests that WebXR does not return any devices if OpenVR support is disabled.
+IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestOpenVrDisabled,
+                       TestWebXrNoDevicesWithoutOpenVr) {
+  LoadUrlAndAwaitInitialization(
+      GetFileUrlForHtmlTestFile("test_webxr_does_not_return_device"));
+  WaitOnJavaScriptStep();
+  EndTest();
+}
+
+// Windows-specific tests.
+#ifdef OS_WIN
+
+IN_PROC_BROWSER_TEST_F(WebVrBrowserTestWebVrDisabled,
+                       TestWebVrDisabledWithoutFlagSet) {
+  TestApiDisabledWithoutFlagSetImpl(this,
+                                    "test_webvr_disabled_without_flag_set");
+}
+IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestWebXrDisabled,
+                       TestWebXrDisabledWithoutFlagSet) {
+  TestApiDisabledWithoutFlagSetImpl(this,
+                                    "test_webxr_disabled_without_flag_set");
+}
+
+// Tests that window.requestAnimationFrame continues to fire when we have a
+// non-immersive WebXR session.
+IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
+                       TestWindowRafFiresDuringNonImmersiveSession) {
+  LoadUrlAndAwaitInitialization(GetFileUrlForHtmlTestFile(
+      "test_window_raf_fires_during_non_immersive_session"));
+  WaitOnJavaScriptStep();
+  EndTest();
+}
+
 // Tests that a successful requestPresent or requestSession call enters
 // an immersive session.
 void TestPresentationEntryImpl(WebXrVrBrowserTestBase* t,
@@ -55,58 +107,6 @@
       this, "webxr_test_window_raf_fires_while_presenting");
 }
 
-// Tests that WebVR/WebXR is not exposed if the flag is not on and the page does
-// not have an origin trial token. Since the API isn't actually used, we can
-// remove the GPU requirement.
-void TestApiDisabledWithoutFlagSetImpl(WebXrVrBrowserTestBase* t,
-                                       std::string filename) {
-  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
-  t->WaitOnJavaScriptStep();
-  t->EndTest();
-}
-
-IN_PROC_BROWSER_TEST_F(WebVrBrowserTestWebVrDisabled,
-                       TestWebVrDisabledWithoutFlagSet) {
-  TestApiDisabledWithoutFlagSetImpl(this,
-                                    "test_webvr_disabled_without_flag_set");
-}
-IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestWebXrDisabled,
-                       TestWebXrDisabledWithoutFlagSet) {
-  TestApiDisabledWithoutFlagSetImpl(this,
-                                    "test_webxr_disabled_without_flag_set");
-}
-
-// Tests that WebVR does not return any devices if OpenVR support is disabled.
-// Since WebVR isn't actually used, we can remove the GPU requirement.
-IN_PROC_BROWSER_TEST_F(WebVrBrowserTestOpenVrDisabled,
-                       TestWebVrNoDevicesWithoutOpenVr) {
-  LoadUrlAndAwaitInitialization(
-      GetFileUrlForHtmlTestFile("generic_webvr_page"));
-  EXPECT_FALSE(XrDeviceFound())
-      << "Found a VRDisplay even with OpenVR disabled";
-  AssertNoJavaScriptErrors();
-}
-
-// Tests that WebXR does not return any devices if OpenVR support is disabled.
-// Since WebXR isn't actually used, we can remove the GPU requirement.
-IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestOpenVrDisabled,
-                       TestWebXrNoDevicesWithoutOpenVr) {
-  LoadUrlAndAwaitInitialization(
-      GetFileUrlForHtmlTestFile("test_webxr_does_not_return_device"));
-  WaitOnJavaScriptStep();
-  EndTest();
-}
-
-// Tests that window.requestAnimationFrame continues to fire when we have a
-// non-immersive WebXR session.
-IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
-                       TestWindowRafFiresDuringNonImmersiveSession) {
-  LoadUrlAndAwaitInitialization(GetFileUrlForHtmlTestFile(
-      "test_window_raf_fires_during_non_immersive_session"));
-  WaitOnJavaScriptStep();
-  EndTest();
-}
-
 // Tests that non-immersive sessions stop receiving rAFs during an immersive
 // session, but resume once the immersive session ends.
 IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
@@ -121,4 +121,6 @@
   EndTest();
 }
 
+#endif  // OS_WIN
+
 }  // namespace vr
diff --git a/chrome/common/cloud_print/cloud_print_helpers.cc b/chrome/common/cloud_print/cloud_print_helpers.cc
index 54187afa7..c36ae44 100644
--- a/chrome/common/cloud_print/cloud_print_helpers.cc
+++ b/chrome/common/cloud_print/cloud_print_helpers.cc
@@ -8,6 +8,7 @@
 
 #include <limits>
 #include <memory>
+#include <utility>
 
 #include "base/json/json_reader.h"
 #include "base/logging.h"
@@ -184,23 +185,15 @@
   return cloud_print_server_url.ReplaceComponents(replacements);
 }
 
-std::unique_ptr<base::DictionaryValue> ParseResponseJSON(
-    const std::string& response_data,
-    bool* succeeded) {
-  std::unique_ptr<base::Value> message_value(
-      base::JSONReader::ReadDeprecated(response_data));
-  if (!message_value.get())
-    return std::unique_ptr<base::DictionaryValue>();
+base::Value ParseResponseJSON(const std::string& response_data,
+                              bool* succeeded) {
+  base::Optional<base::Value> message_value =
+      base::JSONReader::Read(response_data);
+  if (!message_value || !message_value->is_dict())
+    return base::Value();
 
-  if (!message_value->is_dict())
-    return std::unique_ptr<base::DictionaryValue>();
-
-  std::unique_ptr<base::DictionaryValue> response_dict(
-      static_cast<base::DictionaryValue*>(message_value.release()));
-  if (succeeded &&
-      !response_dict->GetBoolean(kSuccessValue, succeeded))
-    *succeeded = false;
-  return response_dict;
+  *succeeded = message_value->FindBoolKey(kSuccessValue).value_or(false);
+  return std::move(*message_value);
 }
 
 std::string GetMultipartMimeType(const std::string& mime_boundary) {
diff --git a/chrome/common/cloud_print/cloud_print_helpers.h b/chrome/common/cloud_print/cloud_print_helpers.h
index b62f00f..904f934 100644
--- a/chrome/common/cloud_print/cloud_print_helpers.h
+++ b/chrome/common/cloud_print/cloud_print_helpers.h
@@ -10,11 +10,10 @@
 #include <string>
 #include <vector>
 
-
 class GURL;
 
 namespace base {
-class DictionaryValue;
+class Value;
 }
 
 // Helper consts and methods for both cloud print and chrome browser.
@@ -60,12 +59,11 @@
                           const std::string& proxy_id);
 
 // Parses the response data for any cloud print server request. The method
-// returns null if there was an error in parsing the JSON. The succeeded
-// value returns the value of the "success" value in the response JSON.
-// Returns the response as a dictionary value.
-std::unique_ptr<base::DictionaryValue> ParseResponseJSON(
-    const std::string& response_data,
-    bool* succeeded);
+// returns none Value if there was an error in parsing the JSON. The |succeeded|
+// parameters returns the value of the "success" value in the response JSON.
+// Returns the response as a dictionary value on success.
+base::Value ParseResponseJSON(const std::string& response_data,
+                              bool* succeeded);
 
 // Returns the MIME type of multipart with |mime_boundary|.
 std::string GetMultipartMimeType(const std::string& mime_boundary);
diff --git a/chrome/common/safe_browsing/archive_analyzer_results.h b/chrome/common/safe_browsing/archive_analyzer_results.h
index 374bfac..86d1900 100644
--- a/chrome/common/safe_browsing/archive_analyzer_results.h
+++ b/chrome/common/safe_browsing/archive_analyzer_results.h
@@ -33,6 +33,8 @@
       ClientDownloadRequest_DetachedCodeSignature>
       detached_code_signatures;
 #endif  // OS_MACOSX
+  int file_count;
+  int directory_count;
   ArchiveAnalyzerResults();
   ArchiveAnalyzerResults(const ArchiveAnalyzerResults& other);
   ~ArchiveAnalyzerResults();
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index 75f9ce7..773f8bb 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -71,6 +71,8 @@
   bool contains_zip = false;
   bool advanced = true;
   int zip_entry_count = 0;
+  results->file_count = 0;
+  results->directory_count = 0;
   for (; reader.HasMore(); advanced = reader.AdvanceToNextEntry()) {
     if (!advanced) {
       DVLOG(1) << "Could not advance to next entry, aborting zip scan.";
@@ -95,6 +97,11 @@
         FILE_PATH_LITERAL(".zip"))
       contains_zip = true;
     zip_entry_count++;
+
+    if (reader.current_entry_info()->is_directory())
+      results->directory_count++;
+    else
+      results->file_count++;
   }
 
   results->success = true;
diff --git a/chrome/credential_provider/gaiacp/os_user_manager.cc b/chrome/credential_provider/gaiacp/os_user_manager.cc
index 3d5f376..5d3de3b 100644
--- a/chrome/credential_provider/gaiacp/os_user_manager.cc
+++ b/chrome/credential_provider/gaiacp/os_user_manager.cc
@@ -422,9 +422,9 @@
                           local_domain_buffer, &domain_length, &use)) {
     hr = HRESULT_FROM_WIN32(::GetLastError());
     if (hr != HRESULT_FROM_WIN32(ERROR_NONE_MAPPED)) {
-      if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
-        if ((username_size > 0 && name_length >= username_size))
-          hr = S_OK;
+      if (username_size == 0 &&
+          hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
+        hr = S_OK;
       }
     }
   }
diff --git a/chrome/installer/mini_installer/mini_installer_exe_main.cc b/chrome/installer/mini_installer/mini_installer_exe_main.cc
index 59dd5bc..2967b5b 100644
--- a/chrome/installer/mini_installer/mini_installer_exe_main.cc
+++ b/chrome/installer/mini_installer/mini_installer_exe_main.cc
@@ -4,6 +4,7 @@
 
 #include <stdint.h>
 
+#include "build/build_config.h"
 #include "chrome/installer/mini_installer/mini_installer.h"
 
 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
@@ -27,6 +28,9 @@
 }
 #endif
 
+// We don't link with the CRT so we have to implement CRT functions that the
+// compiler generates calls to.
+
 // VC Express editions don't come with the memset CRT obj file and linking to
 // the obj files between versions becomes a bit problematic. Therefore,
 // simply implement memset.
@@ -56,4 +60,22 @@
     *scan++ = static_cast<uint8_t>(c);
   return dest;
 }
+
+#if defined(_DEBUG) && defined(ARCH_CPU_ARM64)
+// The compiler generates calls to memcpy for ARM64 debug builds so we need to
+// supply a memcpy implementation in that configuration.
+// See comments above for why we do this incantation.
+#ifdef __clang__
+__attribute__((used))
+#else
+#pragma function(memcpy)
+#endif
+void* memcpy(void* destination, const void* source, size_t count) {
+  auto* dst = reinterpret_cast<uint8_t*>(destination);
+  auto* src = reinterpret_cast<const uint8_t*>(source);
+  while (count--)
+    *dst++ = *src++;
+  return destination;
+}
+#endif
 }  // extern "C"
diff --git a/chrome/renderer/translate/translate_helper_browsertest.cc b/chrome/renderer/translate/translate_helper_browsertest.cc
index d8888c7..93e6f1b 100644
--- a/chrome/renderer/translate/translate_helper_browsertest.cc
+++ b/chrome/renderer/translate/translate_helper_browsertest.cc
@@ -89,8 +89,7 @@
     trans_result_error_type_ = translate::TranslateErrors::NONE;
 
     // Will get new result values via OnPageTranslated.
-    Translate(translate_script, network::mojom::URLLoaderFactoryPtr(),
-              source_lang, target_lang,
+    Translate(translate_script, source_lang, target_lang,
               base::Bind(&TestTranslateHelper::OnPageTranslated,
                          base::Unretained(this)));
   }
diff --git a/chrome/service/cloud_print/cloud_print_auth.cc b/chrome/service/cloud_print/cloud_print_auth.cc
index 8acf7d6..e956456 100644
--- a/chrome/service/cloud_print/cloud_print_auth.cc
+++ b/chrome/service/cloud_print/cloud_print_auth.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/service/cloud_print/cloud_print_auth.h"
 
+#include <vector>
+
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/metrics/histogram_macros.h"
@@ -164,8 +166,10 @@
 CloudPrintURLFetcher::ResponseAction CloudPrintAuth::HandleJSONData(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
+  DCHECK(json_data.is_dict());
+
   if (!succeeded) {
     VLOG(1) << "CP_AUTH: Creating robot account failed";
     UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent",
@@ -175,8 +179,8 @@
     return CloudPrintURLFetcher::STOP_PROCESSING;
   }
 
-  std::string auth_code;
-  if (!json_data->GetString(kOAuthCodeValue, &auth_code)) {
+  const std::string* auth_code = json_data.FindStringKey(kOAuthCodeValue);
+  if (!auth_code) {
     VLOG(1) << "CP_AUTH: Creating robot account returned invalid json response";
     UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent",
                               AUTH_EVENT_ROBO_JSON_ERROR,
@@ -189,14 +193,14 @@
                               AUTH_EVENT_ROBO_SUCCEEDED,
                               AUTH_EVENT_MAX);
 
-  json_data->GetString(kXMPPJidValue, &robot_email_);
+  const std::string* robot_email = json_data.FindStringKey(kXMPPJidValue);
+  if (robot_email)
+    robot_email_ = *robot_email;
   // Now that we have an auth code we need to get the refresh and access tokens.
-  oauth_client_.reset(
-      new gaia::GaiaOAuthClient(client_->GetURLLoaderFactory()));
-  oauth_client_->GetTokensFromAuthCode(oauth_client_info_,
-                                       auth_code,
-                                       kCloudPrintAPIMaxRetryCount,
-                                       this);
+  oauth_client_ =
+      std::make_unique<gaia::GaiaOAuthClient>(client_->GetURLLoaderFactory());
+  oauth_client_->GetTokensFromAuthCode(oauth_client_info_, *auth_code,
+                                       kCloudPrintAPIMaxRetryCount, this);
 
   return CloudPrintURLFetcher::STOP_PROCESSING;
 }
diff --git a/chrome/service/cloud_print/cloud_print_auth.h b/chrome/service/cloud_print/cloud_print_auth.h
index a295398..143b2cb 100644
--- a/chrome/service/cloud_print/cloud_print_auth.h
+++ b/chrome/service/cloud_print/cloud_print_auth.h
@@ -79,7 +79,7 @@
   CloudPrintURLFetcher::ResponseAction HandleJSONData(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded) override;
   CloudPrintURLFetcher::ResponseAction OnRequestAuthError() override;
   std::string GetAuthHeader() override;
diff --git a/chrome/service/cloud_print/cloud_print_connector.cc b/chrome/service/cloud_print/cloud_print_connector.cc
index 2e5ca0d..34a3f53cc5 100644
--- a/chrome/service/cloud_print/cloud_print_connector.cc
+++ b/chrome/service/cloud_print/cloud_print_connector.cc
@@ -6,6 +6,10 @@
 
 #include <stddef.h>
 
+#include <algorithm>
+#include <limits>
+#include <vector>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/location.h"
@@ -187,7 +191,7 @@
 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleJSONData(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   if (!IsRunning())  // Orphan response. Connector has been stopped already.
     return CloudPrintURLFetcher::STOP_PROCESSING;
@@ -208,11 +212,10 @@
 CloudPrintConnector::~CloudPrintConnector() {}
 
 CloudPrintURLFetcher::ResponseAction
-CloudPrintConnector::HandlePrinterListResponse(
-    const net::URLFetcher* source,
-    const GURL& url,
-    const base::DictionaryValue* json_data,
-    bool succeeded) {
+CloudPrintConnector::HandlePrinterListResponse(const net::URLFetcher* source,
+                                               const GURL& url,
+                                               const base::Value& json_data,
+                                               bool succeeded) {
   DCHECK(succeeded);
   if (!succeeded)
     return CloudPrintURLFetcher::RETRY_REQUEST;
@@ -240,25 +243,29 @@
   }
 
   // Go through the list of the cloud printers and init print job handlers.
-  const base::ListValue* printer_list = nullptr;
   // There may be no "printers" value in the JSON
-  if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) {
-    for (size_t i = 0; i < printer_list->GetSize(); ++i) {
-      const base::DictionaryValue* printer_data = nullptr;
-      if (!printer_list->GetDictionary(i, &printer_data))
+  const base::Value* printer_list =
+      json_data.FindKeyOfType(kPrinterListValue, base::Value::Type::LIST);
+  if (printer_list) {
+    for (const auto& printer : printer_list->GetList()) {
+      if (!printer.is_dict())
         continue;
 
       std::string printer_name;
-      printer_data->GetString(kNameValue, &printer_name);
       std::string printer_id;
-      printer_data->GetString(kIdValue, &printer_id);
+      const std::string* str = printer.FindStringKey(kNameValue);
+      if (str)
+        printer_name = *str;
+      str = printer.FindStringKey(kIdValue);
+      if (str)
+        printer_id = *str;
 
       if (!settings_.ShouldConnect(printer_name)) {
         VLOG(1) << "CP_CONNECTOR: Deleting " << printer_name
                 << " id: " << printer_id << " as blacklisted";
         AddPendingDeleteTask(printer_id);
       } else if (RemovePrinterFromList(printer_name, &local_printers)) {
-        InitJobHandlerForPrinter(printer_data);
+        InitJobHandlerForPrinter(printer);
       } else {
         // Cloud printer is not found on the local system.
         if (full_list || settings_.delete_on_enum_fail()) {
@@ -291,7 +298,7 @@
 CloudPrintConnector::HandlePrinterListResponseSettingsUpdate(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   DCHECK(succeeded);
   if (!succeeded)
@@ -302,11 +309,10 @@
 }
 
 CloudPrintURLFetcher::ResponseAction
-CloudPrintConnector::HandlePrinterDeleteResponse(
-    const net::URLFetcher* source,
-    const GURL& url,
-    const base::DictionaryValue* json_data,
-    bool succeeded) {
+CloudPrintConnector::HandlePrinterDeleteResponse(const net::URLFetcher* source,
+                                                 const GURL& url,
+                                                 const base::Value& json_data,
+                                                 bool succeeded) {
   VLOG(1) << "CP_CONNECTOR: Handler printer delete response"
           << ", succeeded: " << succeeded
           << ", url: " << url;
@@ -318,17 +324,18 @@
 CloudPrintConnector::HandleRegisterPrinterResponse(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   VLOG(1) << "CP_CONNECTOR: Handler printer register response"
           << ", succeeded: " << succeeded
           << ", url: " << url;
   if (succeeded) {
-    const base::ListValue* printer_list = nullptr;
+    const base::Value* printer_list =
+        json_data.FindKeyOfType(kPrinterListValue, base::Value::Type::LIST);
     // There should be a "printers" value in the JSON
-    if (json_data->GetList(kPrinterListValue, &printer_list)) {
-      const base::DictionaryValue* printer_data = nullptr;
-      if (printer_list->GetDictionary(0, &printer_data))
+    if (printer_list && !printer_list->GetList().empty()) {
+      const base::Value& printer_data = printer_list->GetList()[0];
+      if (printer_data.is_dict())
         InitJobHandlerForPrinter(printer_data);
     }
   }
@@ -336,7 +343,6 @@
   return CloudPrintURLFetcher::STOP_PROCESSING;
 }
 
-
 void CloudPrintConnector::StartGetRequest(const GURL& url,
                                           int max_retries,
                                           ResponseHandler handler) {
@@ -391,10 +397,13 @@
 }
 
 void CloudPrintConnector::InitJobHandlerForPrinter(
-    const base::DictionaryValue* printer_data) {
-  DCHECK(printer_data);
+    const base::Value& printer_data) {
+  DCHECK(printer_data.is_dict());
+
   PrinterJobHandler::PrinterInfoFromCloud printer_info_cloud;
-  printer_data->GetString(kIdValue, &printer_info_cloud.printer_id);
+  const std::string* str = printer_data.FindStringKey(kIdValue);
+  if (str)
+    printer_info_cloud.printer_id = *str;
   DCHECK(!printer_info_cloud.printer_id.empty());
   VLOG(1) << "CP_CONNECTOR: Init job handler"
           << ", printer id: " << printer_info_cloud.printer_id;
@@ -402,34 +411,38 @@
     return;  // Nothing to do if we already have a job handler for this printer.
 
   printing::PrinterBasicInfo printer_info;
-  printer_data->GetString(kNameValue, &printer_info.printer_name);
+  str = printer_data.FindStringKey(kNameValue);
+  if (str)
+    printer_info.printer_name = *str;
   DCHECK(!printer_info.printer_name.empty());
-  printer_data->GetString(kPrinterDescValue,
-                          &printer_info.printer_description);
+  str = printer_data.FindStringKey(kPrinterDescValue);
+  if (str)
+    printer_info.printer_description = *str;
   // Printer status is a string value which actually contains an integer.
-  std::string printer_status;
-  if (printer_data->GetString(kPrinterStatusValue, &printer_status)) {
-    base::StringToInt(printer_status, &printer_info.printer_status);
-  }
-  printer_data->GetString(kPrinterCapsHashValue,
-      &printer_info_cloud.caps_hash);
-  const base::ListValue* tags_list = nullptr;
-  if (printer_data->GetList(kTagsValue, &tags_list) && tags_list) {
-    for (size_t i = 0; i < tags_list->GetSize(); ++i) {
-      std::string tag;
-      if (tags_list->GetString(i, &tag) &&
-          base::StartsWith(tag, kCloudPrintServiceTagsHashTagName,
+  str = printer_data.FindStringKey(kPrinterStatusValue);
+  if (str)
+    base::StringToInt(*str, &printer_info.printer_status);
+  str = printer_data.FindStringKey(kPrinterCapsHashValue);
+  if (str)
+    printer_info_cloud.caps_hash = *str;
+
+  const base::Value* tags_list =
+      printer_data.FindKeyOfType(kTagsValue, base::Value::Type::LIST);
+  if (tags_list) {
+    for (const auto& tag : tags_list->GetList()) {
+      if (tag.is_string() &&
+          base::StartsWith(tag.GetString(), kCloudPrintServiceTagsHashTagName,
                            base::CompareCase::INSENSITIVE_ASCII)) {
         std::vector<std::string> tag_parts = base::SplitString(
-            tag, "=", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+            tag.GetString(), "=", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
         if (tag_parts.size() == 2)
           printer_info_cloud.tags_hash = tag_parts[1];
       }
     }
   }
 
-  int xmpp_timeout = 0;
-  printer_data->GetInteger(kLocalSettingsPendingXmppValue, &xmpp_timeout);
+  int xmpp_timeout =
+      printer_data.FindIntKey(kLocalSettingsPendingXmppValue).value_or(0);
   printer_info_cloud.current_xmpp_timeout = settings_.xmpp_ping_timeout_sec();
   printer_info_cloud.pending_xmpp_timeout = xmpp_timeout;
 
@@ -444,17 +457,19 @@
 }
 
 void CloudPrintConnector::UpdateSettingsFromPrintersList(
-    const base::DictionaryValue* json_data) {
-  const base::ListValue* printer_list = nullptr;
+    const base::Value& json_data) {
   int min_xmpp_timeout = std::numeric_limits<int>::max();
   // There may be no "printers" value in the JSON
-  if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) {
-    for (size_t i = 0; i < printer_list->GetSize(); ++i) {
-      const base::DictionaryValue* printer_data = nullptr;
-      if (printer_list->GetDictionary(i, &printer_data)) {
+  const base::Value* printer_list =
+      json_data.FindKeyOfType(kPrinterListValue, base::Value::Type::LIST);
+  if (printer_list) {
+    for (const auto& printer : printer_list->GetList()) {
+      if (printer.is_dict()) {
         int xmpp_timeout = 0;
-        if (printer_data->GetInteger(kLocalSettingsPendingXmppValue,
-                                     &xmpp_timeout)) {
+        base::Optional<int> timeout =
+            printer.FindIntKey(kLocalSettingsPendingXmppValue);
+        if (timeout) {
+          xmpp_timeout = *timeout;
           min_xmpp_timeout = std::min(xmpp_timeout, min_xmpp_timeout);
         }
       }
diff --git a/chrome/service/cloud_print/cloud_print_connector.h b/chrome/service/cloud_print/cloud_print_connector.h
index 7f123a8..8648495 100644
--- a/chrome/service/cloud_print/cloud_print_connector.h
+++ b/chrome/service/cloud_print/cloud_print_connector.h
@@ -64,11 +64,10 @@
 
   // Prototype for a response handler.
   typedef CloudPrintURLFetcher::ResponseAction (
-      CloudPrintConnector::*ResponseHandler)(
-      const net::URLFetcher* source,
-      const GURL& url,
-      const base::DictionaryValue* json_data,
-      bool succeeded);
+      CloudPrintConnector::*ResponseHandler)(const net::URLFetcher* source,
+                                             const GURL& url,
+                                             const base::Value& json_data,
+                                             bool succeeded);
 
   enum PendingTaskType {
     PENDING_PRINTERS_NONE,
@@ -105,7 +104,7 @@
   CloudPrintURLFetcher::ResponseAction HandleJSONData(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded) override;
   CloudPrintURLFetcher::ResponseAction OnRequestAuthError() override;
   std::string GetAuthHeader() override;
@@ -114,25 +113,25 @@
   CloudPrintURLFetcher::ResponseAction HandlePrinterListResponse(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
 
   CloudPrintURLFetcher::ResponseAction HandlePrinterListResponseSettingsUpdate(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
 
   CloudPrintURLFetcher::ResponseAction HandlePrinterDeleteResponse(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
 
   CloudPrintURLFetcher::ResponseAction HandleRegisterPrinterResponse(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
   // End response handlers
 
@@ -154,9 +153,9 @@
   bool RemovePrinterFromList(const std::string& printer_name,
                              printing::PrinterList* printer_list);
 
-  void InitJobHandlerForPrinter(const base::DictionaryValue* printer_data);
+  void InitJobHandlerForPrinter(const base::Value& printer_data);
 
-  void UpdateSettingsFromPrintersList(const base::DictionaryValue* json_data);
+  void UpdateSettingsFromPrintersList(const base::Value& json_data);
 
   void AddPendingAvailableTask();
   void AddPendingDeleteTask(const std::string& id);
diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher.cc b/chrome/service/cloud_print/cloud_print_url_fetcher.cc
index 4a984b0c..075b8ba 100644
--- a/chrome/service/cloud_print/cloud_print_url_fetcher.cc
+++ b/chrome/service/cloud_print/cloud_print_url_fetcher.cc
@@ -121,11 +121,10 @@
 }
 
 CloudPrintURLFetcher::ResponseAction
-CloudPrintURLFetcher::Delegate::HandleJSONData(
-    const net::URLFetcher* source,
-    const GURL& url,
-    const base::DictionaryValue* json_data,
-    bool succeeded) {
+CloudPrintURLFetcher::Delegate::HandleJSONData(const net::URLFetcher* source,
+                                               const GURL& url,
+                                               const base::Value& json_data,
+                                               bool succeeded) {
   return CONTINUE_PROCESSING;
 }
 
@@ -198,14 +197,11 @@
       // response, we will retry (to handle the case where we got redirected
       // to a non-cloudprint-server URL eg. for authentication).
       bool succeeded = false;
-      std::unique_ptr<base::DictionaryValue> response_dict =
-          ParseResponseJSON(data, &succeeded);
+      base::Value response_dict = ParseResponseJSON(data, &succeeded);
 
-      if (response_dict) {
-        action = delegate_->HandleJSONData(source,
-                                           source->GetURL(),
-                                           response_dict.get(),
-                                           succeeded);
+      if (response_dict.is_dict()) {
+        action = delegate_->HandleJSONData(source, source->GetURL(),
+                                           response_dict, succeeded);
       } else {
         action = RETRY_REQUEST;
       }
diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher.h b/chrome/service/cloud_print/cloud_print_url_fetcher.h
index 50cc6c7..fb1acb4cc 100644
--- a/chrome/service/cloud_print/cloud_print_url_fetcher.h
+++ b/chrome/service/cloud_print/cloud_print_url_fetcher.h
@@ -16,7 +16,7 @@
 class GURL;
 
 namespace base {
-class DictionaryValue;
+class Value;
 }
 
 namespace net {
@@ -89,11 +89,10 @@
     // This will be invoked only if HandleRawResponse and HandleRawData return
     // CONTINUE_PROCESSING AND if the response contains a valid JSON dictionary.
     // |succeeded| is the value of the "success" field in the response JSON.
-    virtual ResponseAction HandleJSONData(
-        const net::URLFetcher* source,
-        const GURL& url,
-        const base::DictionaryValue* json_data,
-        bool succeeded);
+    virtual ResponseAction HandleJSONData(const net::URLFetcher* source,
+                                          const GURL& url,
+                                          const base::Value& json_data,
+                                          bool succeeded);
 
     // Invoked when the retry limit for this request has been reached (if there
     // was a retry limit - a limit of -1 implies no limit).
diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc
index 5ec24513..c368165 100644
--- a/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc
+++ b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc
@@ -164,7 +164,7 @@
   CloudPrintURLFetcher::ResponseAction HandleJSONData(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded) override;
 
   void SetHandleRawResponse(bool handle_raw_response) {
@@ -280,11 +280,10 @@
 }
 
 CloudPrintURLFetcher::ResponseAction
-CloudPrintURLFetcherBasicTest::HandleJSONData(
-    const net::URLFetcher* source,
-    const GURL& url,
-    const base::DictionaryValue* json_data,
-    bool succeeded) {
+CloudPrintURLFetcherBasicTest::HandleJSONData(const net::URLFetcher* source,
+                                              const GURL& url,
+                                              const base::Value& json_data,
+                                              bool succeeded) {
   // We should never get here if we returned true in one of the above methods.
   EXPECT_FALSE(handle_raw_response_);
   EXPECT_FALSE(handle_raw_data_);
diff --git a/chrome/service/cloud_print/cloud_print_wipeout.cc b/chrome/service/cloud_print/cloud_print_wipeout.cc
index 134ae98..40db126 100644
--- a/chrome/service/cloud_print/cloud_print_wipeout.cc
+++ b/chrome/service/cloud_print/cloud_print_wipeout.cc
@@ -49,7 +49,7 @@
 CloudPrintURLFetcher::ResponseAction CloudPrintWipeout::HandleJSONData(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   // We don't care if delete was successful or not here.
   UnregisterNextPrinter();
diff --git a/chrome/service/cloud_print/cloud_print_wipeout.h b/chrome/service/cloud_print/cloud_print_wipeout.h
index 4a3a3d2..1e17f75 100644
--- a/chrome/service/cloud_print/cloud_print_wipeout.h
+++ b/chrome/service/cloud_print/cloud_print_wipeout.h
@@ -38,7 +38,7 @@
   CloudPrintURLFetcher::ResponseAction HandleJSONData(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded) override;
   void OnRequestGiveUp() override;
   CloudPrintURLFetcher::ResponseAction OnRequestAuthError() override;
diff --git a/chrome/service/cloud_print/job_status_updater.cc b/chrome/service/cloud_print/job_status_updater.cc
index 76b8d9b..063faf4 100644
--- a/chrome/service/cloud_print/job_status_updater.cc
+++ b/chrome/service/cloud_print/job_status_updater.cc
@@ -99,7 +99,7 @@
 CloudPrintURLFetcher::ResponseAction JobStatusUpdater::HandleJSONData(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   if (IsTerminalJobState(last_job_details_.status)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/service/cloud_print/job_status_updater.h b/chrome/service/cloud_print/job_status_updater.h
index ab5e81a2..7308731 100644
--- a/chrome/service/cloud_print/job_status_updater.h
+++ b/chrome/service/cloud_print/job_status_updater.h
@@ -50,7 +50,7 @@
   CloudPrintURLFetcher::ResponseAction HandleJSONData(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded) override;
   CloudPrintURLFetcher::ResponseAction OnRequestAuthError() override;
   std::string GetAuthHeader() override;
diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc
index a497a3a..093cef0 100644
--- a/chrome/service/cloud_print/printer_job_handler.cc
+++ b/chrome/service/cloud_print/printer_job_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/service/cloud_print/printer_job_handler.h"
 
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -173,7 +174,7 @@
 CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleJSONData(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   DCHECK(next_json_data_handler_);
   return (this->*next_json_data_handler_)(source, url, json_data, succeeded);
@@ -287,11 +288,10 @@
 
 // Begin Response handlers
 CloudPrintURLFetcher::ResponseAction
-PrinterJobHandler::HandlePrinterUpdateResponse(
-    const net::URLFetcher* source,
-    const GURL& url,
-    const base::DictionaryValue* json_data,
-    bool succeeded) {
+PrinterJobHandler::HandlePrinterUpdateResponse(const net::URLFetcher* source,
+                                               const GURL& url,
+                                               const base::Value& json_data,
+                                               bool succeeded) {
   VLOG(1) << "CP_CONNECTOR: Handling printer update response"
           << ", printer id: " << printer_info_cloud_.printer_id;
   // We are done here. Go to the Stop state
@@ -303,16 +303,15 @@
 }
 
 CloudPrintURLFetcher::ResponseAction
-PrinterJobHandler::HandleJobMetadataResponse(
-    const net::URLFetcher* source,
-    const GURL& url,
-    const base::DictionaryValue* json_data,
-    bool succeeded) {
+PrinterJobHandler::HandleJobMetadataResponse(const net::URLFetcher* source,
+                                             const GURL& url,
+                                             const base::Value& json_data,
+                                             bool succeeded) {
   VLOG(1) << "CP_CONNECTOR: Handling job metadata response"
           << ", printer id: " << printer_info_cloud_.printer_id;
   if (succeeded) {
     std::vector<JobDetails> jobs =
-        job_queue_handler_.GetJobsFromQueue(*json_data);
+        job_queue_handler_.GetJobsFromQueue(json_data);
     if (!jobs.empty()) {
       if (jobs[0].time_remaining_.is_zero()) {
         job_details_ = jobs[0];
@@ -414,7 +413,7 @@
 PrinterJobHandler::HandleInProgressStatusUpdateResponse(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   VLOG(1) << "CP_CONNECTOR: Handling success status update response"
           << ", printer id: " << printer_info_cloud_.printer_id;
@@ -427,7 +426,7 @@
 PrinterJobHandler::HandleFailureStatusUpdateResponse(
     const net::URLFetcher* source,
     const GURL& url,
-    const base::DictionaryValue* json_data,
+    const base::Value& json_data,
     bool succeeded) {
   VLOG(1) << "CP_CONNECTOR: Handling failure status update response"
           << ", printer id: " << printer_info_cloud_.printer_id;
diff --git a/chrome/service/cloud_print/printer_job_handler.h b/chrome/service/cloud_print/printer_job_handler.h
index ebf9d4a..efa30bd 100644
--- a/chrome/service/cloud_print/printer_job_handler.h
+++ b/chrome/service/cloud_print/printer_job_handler.h
@@ -127,7 +127,7 @@
   CloudPrintURLFetcher::ResponseAction HandleJSONData(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded) override;
   void OnRequestGiveUp() override;
   CloudPrintURLFetcher::ResponseAction OnRequestAuthError() override;
@@ -160,11 +160,10 @@
 
   // Prototype for a JSON data handler.
   typedef CloudPrintURLFetcher::ResponseAction (
-      PrinterJobHandler::*JSONDataHandler)(
-      const net::URLFetcher* source,
-      const GURL& url,
-      const base::DictionaryValue* json_data,
-      bool succeeded);
+      PrinterJobHandler::*JSONDataHandler)(const net::URLFetcher* source,
+                                           const GURL& url,
+                                           const base::Value& json_data,
+                                           bool succeeded);
   // Prototype for a data handler.
   typedef CloudPrintURLFetcher::ResponseAction (
       PrinterJobHandler::*DataHandler)(const net::URLFetcher* source,
@@ -177,13 +176,13 @@
   CloudPrintURLFetcher::ResponseAction HandlePrinterUpdateResponse(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
 
   CloudPrintURLFetcher::ResponseAction HandleJobMetadataResponse(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
 
   CloudPrintURLFetcher::ResponseAction HandlePrintTicketResponse(
@@ -199,13 +198,13 @@
   CloudPrintURLFetcher::ResponseAction HandleInProgressStatusUpdateResponse(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
 
   CloudPrintURLFetcher::ResponseAction HandleFailureStatusUpdateResponse(
       const net::URLFetcher* source,
       const GURL& url,
-      const base::DictionaryValue* json_data,
+      const base::Value& json_data,
       bool succeeded);
   // End request handlers for each state in the state machine
 
diff --git a/chrome/service/cloud_print/printer_job_queue_handler.cc b/chrome/service/cloud_print/printer_job_queue_handler.cc
index f8577716..36cf82e 100644
--- a/chrome/service/cloud_print/printer_job_queue_handler.cc
+++ b/chrome/service/cloud_print/printer_job_queue_handler.cc
@@ -25,22 +25,33 @@
   return base::Time::Now();
 }
 
-JobDetails ConstructJobDetailsFromJson(const base::DictionaryValue& job_data) {
-  JobDetails job_details;
+JobDetails ConstructJobDetailsFromJson(const base::Value& job_data) {
+  DCHECK(job_data.is_dict());
 
-  job_data.GetString(kIdValue, &job_details.job_id_);
-  job_data.GetString(kTitleValue, &job_details.job_title_);
-  job_data.GetString(kOwnerValue, &job_details.job_owner_);
-  job_data.GetString(kTicketUrlValue, &job_details.print_ticket_url_);
-  job_data.GetString(kFileUrlValue, &job_details.print_data_url_);
+  JobDetails job_details;
+  const std::string* str = job_data.FindStringKey(kIdValue);
+  if (str)
+    job_details.job_id_ = *str;
+  str = job_data.FindStringKey(kTitleValue);
+  if (str)
+    job_details.job_title_ = *str;
+  str = job_data.FindStringKey(kOwnerValue);
+  if (str)
+    job_details.job_owner_ = *str;
+  str = job_data.FindStringKey(kTicketUrlValue);
+  if (str)
+    job_details.print_ticket_url_ = *str;
+  str = job_data.FindStringKey(kFileUrlValue);
+  if (str)
+    job_details.print_data_url_ = *str;
 
   // Get tags for print job.
-  const base::ListValue* tags = nullptr;
-  if (job_data.GetList(kTagsValue, &tags)) {
-    for (size_t i = 0; i < tags->GetSize(); i++) {
-      std::string value;
-      if (tags->GetString(i, &value))
-        job_details.tags_.push_back(value);
+  const base::Value* tags =
+      job_data.FindKeyOfType(kTagsValue, base::Value::Type::LIST);
+  if (tags) {
+    for (const auto& tag : tags->GetList()) {
+      if (tag.is_string())
+        job_details.tags_.push_back(tag.GetString());
     }
   }
   return job_details;
@@ -109,20 +120,22 @@
 }
 
 std::vector<JobDetails> PrinterJobQueueHandler::GetJobsFromQueue(
-    const base::DictionaryValue& json_data) {
+    const base::Value& json_data) {
+  DCHECK(json_data.is_dict());
+
   std::vector<JobDetails> jobs;
 
-  const base::ListValue* job_list = nullptr;
-  if (!json_data.GetList(kJobListValue, &job_list))
+  const base::Value* job_list =
+      json_data.FindKeyOfType(kJobListValue, base::Value::Type::LIST);
+  if (!job_list)
     return jobs;
 
   std::vector<JobDetails> jobs_with_timeouts;
-  for (const auto& job_value : *job_list) {
-    const base::DictionaryValue* job_data = nullptr;
-    if (!job_value.GetAsDictionary(&job_data))
+  for (const auto& job_value : job_list->GetList()) {
+    if (!job_value.is_dict())
       continue;
 
-    JobDetails job_details_current = ConstructJobDetailsFromJson(*job_data);
+    JobDetails job_details_current = ConstructJobDetailsFromJson(job_value);
     job_details_current.time_remaining_ =
         ComputeBackoffTime(job_details_current.job_id_);
     if (job_details_current.time_remaining_.is_zero()) {
diff --git a/chrome/service/cloud_print/printer_job_queue_handler.h b/chrome/service/cloud_print/printer_job_queue_handler.h
index 4bf813679..6a2e9425 100644
--- a/chrome/service/cloud_print/printer_job_queue_handler.h
+++ b/chrome/service/cloud_print/printer_job_queue_handler.h
@@ -18,7 +18,7 @@
 #include "chrome/common/cloud_print/cloud_print_constants.h"
 
 namespace base {
-class DictionaryValue;
+class Value;
 }
 
 namespace cloud_print {
@@ -64,9 +64,8 @@
 
   // Returns a vector with details of all jobs in the queue, sorted by time
   // until they are ready to print, lowest to highest. Jobs that are ready to
-  // print will have a time_remaining_ of 0.
-  std::vector<JobDetails> GetJobsFromQueue(
-      const base::DictionaryValue& json_data);
+  // print will have a |time_remaining_| of 0.
+  std::vector<JobDetails> GetJobsFromQueue(const base::Value& json_data);
 
   // Marks a job fetch as failed. Returns "true" if the job will be retried.
   bool JobFetchFailed(const std::string& job_id);
diff --git a/chrome/service/cloud_print/printer_job_queue_handler_unittest.cc b/chrome/service/cloud_print/printer_job_queue_handler_unittest.cc
index 920d2d89..ded9038 100644
--- a/chrome/service/cloud_print/printer_job_queue_handler_unittest.cc
+++ b/chrome/service/cloud_print/printer_job_queue_handler_unittest.cc
@@ -8,8 +8,8 @@
 
 #include <set>
 
-#include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
+#include "base/test/values_test_util.h"
 #include "base/values.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -68,16 +68,12 @@
 
 class PrinterJobQueueHandlerTest : public ::testing::Test {
  protected:
-  void SetUp() override {
-    base::JSONReader json_reader;
-    data_ = base::DictionaryValue::From(
-        json_reader.ReadDeprecated(kJobListResponse));
-  }
+  void SetUp() override { data_ = base::test::ParseJson(kJobListResponse); }
 
-  const base::DictionaryValue& GetDictionary() const { return *data_; }
+  const base::Value& GetDictionary() const { return data_; }
 
  private:
-  std::unique_ptr<base::DictionaryValue> data_;
+  base::Value data_;
 };
 
 TEST_F(PrinterJobQueueHandlerTest, BasicJobReadTest) {
diff --git a/chrome/services/file_util/public/mojom/safe_archive_analyzer_param_traits.h b/chrome/services/file_util/public/mojom/safe_archive_analyzer_param_traits.h
index a0b7080..a297d76 100644
--- a/chrome/services/file_util/public/mojom/safe_archive_analyzer_param_traits.h
+++ b/chrome/services/file_util/public/mojom/safe_archive_analyzer_param_traits.h
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 // Multiply-included param traits file, so no include guard.
+// Disabling the presubmit warning with:
+//   no-include-guard-because-multiply-included
 
 #if !defined(FULL_SAFE_BROWSING)
 #error FULL_SAFE_BROWSING should be defined.
@@ -103,4 +105,6 @@
   IPC_STRUCT_TRAITS_MEMBER(signature_blob)
   IPC_STRUCT_TRAITS_MEMBER(detached_code_signatures)
 #endif  // OS_MACOSX
+  IPC_STRUCT_TRAITS_MEMBER(file_count)
+  IPC_STRUCT_TRAITS_MEMBER(directory_count)
 IPC_STRUCT_TRAITS_END()
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5d144be..addef4b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -690,7 +690,7 @@
       "../browser/loader/cors_origin_access_list_browsertest.cc",
       "../browser/loadtimes_extension_bindings_browsertest.cc",
       "../browser/locale_tests_browsertest.cc",
-      "../browser/lookalikes/lookalike_url_navigation_observer_browsertest.cc",
+      "../browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc",
       "../browser/media/autoplay_metrics_browsertest.cc",
       "../browser/media/cast_mirroring_service_host_browsertest.cc",
       "../browser/media/defer_background_media_browsertest.cc",
@@ -3242,7 +3242,7 @@
       "../browser/importer/firefox_profile_lock_unittest.cc",
       "../browser/importer/profile_writer_unittest.cc",
       "../browser/lifetime/application_lifetime_unittest.cc",
-      "../browser/lookalikes/lookalike_url_navigation_observer_unittest.cc",
+      "../browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc",
 
       # Media remoting is not supported on Android for now.
       "../browser/media/cast_remoting_connector_unittest.cc",
diff --git a/chrome/test/base/interactive_test_utils_common_views.cc b/chrome/test/base/interactive_test_utils_common_views.cc
index 1e399b2..9c3f6fd 100644
--- a/chrome/test/base/interactive_test_utils_common_views.cc
+++ b/chrome/test/base/interactive_test_utils_common_views.cc
@@ -31,12 +31,17 @@
       animator->StopAnimating();
   }
 
-  gfx::Point view_center(view->width() / 2, view->height() / 2);
-  views::View::ConvertPointToScreen(view, &view_center);
+  gfx::Point view_center = GetCenterInScreenCoordinates(view);
   ui_controls::SendMouseMoveNotifyWhenDone(
       view_center.x(), view_center.y(),
       base::BindOnce(&internal::ClickTask, button, button_state, closure,
                      accelerator_state));
 }
 
+gfx::Point GetCenterInScreenCoordinates(const views::View* view) {
+  gfx::Point center = view->GetLocalBounds().CenterPoint();
+  views::View::ConvertPointToScreen(view, &center);
+  return center;
+}
+
 }  // namespace ui_test_utils
diff --git a/chrome/test/base/interactive_test_utils_views.cc b/chrome/test/base/interactive_test_utils_views.cc
index 3f04aa0..f62d538 100644
--- a/chrome/test/base/interactive_test_utils_views.cc
+++ b/chrome/test/base/interactive_test_utils_views.cc
@@ -44,10 +44,4 @@
   view->RequestFocus();
 }
 
-gfx::Point GetCenterInScreenCoordinates(const views::View* view) {
-  gfx::Point center(view->width() / 2, view->height() / 2);
-  views::View::ConvertPointToScreen(view, &center);
-  return center;
-}
-
 }  // namespace ui_test_utils
diff --git a/chrome/test/data/android/manage_render_test_goldens.py b/chrome/test/data/android/manage_render_test_goldens.py
index bf6c057cf..51764f1f 100755
--- a/chrome/test/data/android/manage_render_test_goldens.py
+++ b/chrome/test/data/android/manage_render_test_goldens.py
@@ -45,12 +45,18 @@
 
 
 def download(directory):
-  subprocess.check_call([
-      'download_from_google_storage',
-      '--bucket', STORAGE_BUCKET,
-      '-d', directory,
-      '-t', str(THREAD_COUNT),
-  ])
+  # Downloading the files can be very spammy, so only show the output if
+  # something actually goes wrong.
+  try:
+    subprocess.check_output([
+        'download_from_google_storage',
+        '--bucket', STORAGE_BUCKET,
+        '-d', directory,
+        '-t', str(THREAD_COUNT),
+    ], stderr=subprocess.STDOUT)
+  except subprocess.CalledProcessError as e:
+    print ('Downloading RenderTest goldens in directory %s failed with error '
+           '%d: %s') % (directory, e.returncode, e.output)
 
 
 def upload(directory):
diff --git a/chrome/test/data/appcache/french_page_with_appcache.html b/chrome/test/data/appcache/french_page_with_appcache.html
deleted file mode 100644
index 16df202b..0000000
--- a/chrome/test/data/appcache/french_page_with_appcache.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html manifest="simple_page.manifest">
-<head>
-  <head><title>Cette page est en Français</title></head>
-  <script type="text/javascript">
-    function onCachedEvent() {
-      window.document.title = "AppCache mis à jour";
-    }
-
-    function onLoad() {
-      window.applicationCache.addEventListener('cached', onCachedEvent, false);
-    }
-  </script>
-</head>
-<body onload="onLoad()">
-Cette page a été rédigée en français. Saviez-vous que le Français est la langue officielle des jeux olympiques? Ça vous en bouche un coin, pas vrai?
-</body>
-</html>
diff --git a/chrome/test/data/appcache/simple_page.manifest b/chrome/test/data/appcache/simple_page.manifest
deleted file mode 100644
index a6f40c55..0000000
--- a/chrome/test/data/appcache/simple_page.manifest
+++ /dev/null
@@ -1,4 +0,0 @@
-CACHE MANIFEST
-
-NETWORK:
-*
diff --git a/chrome/test/data/extensions/api_test/worker/background.js b/chrome/test/data/extensions/api_test/worker/background.js
index 7706614..b52c81b0 100644
--- a/chrome/test/data/extensions/api_test/worker/background.js
+++ b/chrome/test/data/extensions/api_test/worker/background.js
@@ -88,11 +88,10 @@
       undefined,
       () => { return new Worker(redirectedWorkerUrl) },
       workerUrl),
-    // TODO(crbug.com/861564): expectedUrl should be the response URL.
     sameOriginRedirectTest.bind(
       undefined,
       () => { return new Worker(redirectedWorkerUrl, {type: 'module'}) },
-      redirectedWorkerUrl),
+      workerUrl),
     sameOriginRedirectTest.bind(
       undefined,
       () => { return new SharedWorker(redirectedSharedWorkerUrl) },
diff --git a/chrome/test/data/webui/settings/cups_printer_page_tests.js b/chrome/test/data/webui/settings/cups_printer_page_tests.js
index 6e52b0a..5a0b14e 100644
--- a/chrome/test/data/webui/settings/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/cups_printer_page_tests.js
@@ -410,4 +410,42 @@
           assertDeepEquals(usbInfo, printer.printerUsbInfo);
         });
   });
+
+  /**
+   * Test that the close button exists on the configure dialog.
+   */
+  test('ConfigureDialogCancelDisabled', function() {
+    // Starts in discovery dialog, select add manually button.
+    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
+    assertTrue(!!discoveryDialog);
+    discoveryDialog.$$('.secondary-button').click();
+    Polymer.dom.flush();
+
+    // Now we should be in the manually add dialog.
+    const addDialog = dialog.$$('add-printer-manually-dialog');
+    assertTrue(!!addDialog);
+    fillAddManuallyDialog(addDialog);
+
+    // Advance to the configure dialog.
+    addDialog.$$('.action-button').click();
+    Polymer.dom.flush();
+
+    // Configure is shown.
+    const configureDialog = dialog.$$('add-printer-configuring-dialog');
+    assertTrue(!!configureDialog);
+
+    const closeButton = configureDialog.$$('.cancel-button');
+    assertTrue(!!closeButton);
+    assertFalse(closeButton.disabled);
+
+    const waitForClose = test_util.eventToPromise('close', configureDialog);
+
+    closeButton.click();
+    Polymer.dom.flush();
+
+    return waitForClose.then(() => {
+      dialog = page.$$('settings-cups-add-printer-dialog');
+      assertFalse(dialog.showConfiguringDialog_);
+    });
+  });
 });
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_webxr_poses.html b/chrome/test/data/xr/e2e_test_files/html/test_webxr_poses.html
index 866a3e50..32ac995 100644
--- a/chrome/test/data/xr/e2e_test_files/html/test_webxr_poses.html
+++ b/chrome/test/data/xr/e2e_test_files/html/test_webxr_poses.html
@@ -17,6 +17,7 @@
     <script>
       var frame_id = 0;
       var frame_data_array = {};
+      var pose_array = {};
       // We exit presentation before checking stuff that needs the frame of
       // reference, so we need to cache its value.
       var cached_frame_of_ref = null;
@@ -42,13 +43,12 @@
 
       function checkFrameView(frame_id, eye, expected) {
         let frame_data = frame_data_array[frame_id];
-        let pose = frame_data.getViewerPose(cached_frame_of_ref);
+        let pose = pose_array[frame_id];
         return MatrixCompare(pose.views[eye].viewMatrix, expected);
       }
 
       function checkFramePose(frame_id, expected) {
-        let frame_data = frame_data_array[frame_id];
-        let pose = frame_data.getViewerPose(cached_frame_of_ref);
+        let pose = pose_array[frame_id];
         if (!pose) {
           console.log("unexpected - null pose");
           return false;
@@ -63,6 +63,7 @@
         frame_id++;
         frame_data_array[frame_id] = frame;
         cached_frame_of_ref = sessionInfos[sessionTypes.IMMERSIVE].currentRefSpace;
+        pose_array[frame_id] = frame.getViewerPose(cached_frame_of_ref);
 
         var encoded_frame_id = {};
         encoded_frame_id.r = frame_id % 256;
diff --git a/chromecast/browser/cast_media_blocker_unittest.cc b/chromecast/browser/cast_media_blocker_unittest.cc
index 7ebcf27..760d206 100644
--- a/chromecast/browser/cast_media_blocker_unittest.cc
+++ b/chromecast/browser/cast_media_blocker_unittest.cc
@@ -45,6 +45,11 @@
   MOCK_METHOD0(NextTrack, void());
   MOCK_METHOD0(SkipAd, void());
   MOCK_METHOD1(SetAudioFocusGroupId, void(const base::UnguessableToken&));
+  MOCK_METHOD4(GetMediaImageBitmap,
+               void(const media_session::MediaImage&,
+                    int minimum_size_px,
+                    int desired_size_px,
+                    GetMediaImageBitmapCallback callback));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockMediaSession);
diff --git a/chromecast/browser/extension_request_protocol_handler.cc b/chromecast/browser/extension_request_protocol_handler.cc
index 0c0cb09..b673ed88 100644
--- a/chromecast/browser/extension_request_protocol_handler.cc
+++ b/chromecast/browser/extension_request_protocol_handler.cc
@@ -11,6 +11,7 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/info_map.h"
 #include "extensions/common/extension.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/upload_data_stream.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request_context.h"
@@ -23,7 +24,7 @@
 
 class UploadDataStreamRedirect : public net::UploadDataStream {
  public:
-  UploadDataStreamRedirect(net::UploadDataStream* parent);
+  explicit UploadDataStreamRedirect(net::UploadDataStream* parent);
   ~UploadDataStreamRedirect() override;
 
  private:
@@ -105,7 +106,7 @@
       net::ResponseHeadersCallback callback) override;
   bool GetMimeType(std::string* mime_type) const override;
   int GetResponseCode() const override;
-  net::HostPortPair GetSocketAddress() const override;
+  net::IPEndPoint GetResponseRemoteEndpoint() const override;
   void StopCaching() override;
   bool GetFullRequestHeaders(net::HttpRequestHeaders* headers) const override;
   int64_t GetTotalReceivedBytes() const override;
@@ -114,7 +115,7 @@
   bool GetCharset(std::string* charset) override;
   void GetResponseInfo(net::HttpResponseInfo* info) override;
   void GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override;
-  bool GetRemoteEndpoint(net::IPEndPoint* endpoint) const override;
+  bool GetTransactionRemoteEndpoint(net::IPEndPoint* endpoint) const override;
   void PopulateNetErrorDetails(net::NetErrorDetails* details) const override;
   bool IsRedirectResponse(GURL* location,
                           int* http_status_code,
@@ -218,8 +219,8 @@
   return sub_request_->GetResponseCode();
 }
 
-net::HostPortPair CastExtensionURLRequestJob::GetSocketAddress() const {
-  return sub_request_->GetSocketAddress();
+net::IPEndPoint CastExtensionURLRequestJob::GetResponseRemoteEndpoint() const {
+  return sub_request_->GetResponseRemoteEndpoint();
 }
 
 void CastExtensionURLRequestJob::StopCaching() {
@@ -257,9 +258,9 @@
   sub_request_->GetLoadTimingInfo(load_timing_info);
 }
 
-bool CastExtensionURLRequestJob::GetRemoteEndpoint(
+bool CastExtensionURLRequestJob::GetTransactionRemoteEndpoint(
     net::IPEndPoint* endpoint) const {
-  return sub_request_->GetRemoteEndpoint(endpoint);
+  return sub_request_->GetTransactionRemoteEndpoint(endpoint);
 }
 
 void CastExtensionURLRequestJob::PopulateNetErrorDetails(
diff --git a/chromecast/common/mojom/typemaps.gni b/chromecast/common/mojom/typemaps.gni
deleted file mode 100644
index 75d50bf..0000000
--- a/chromecast/common/mojom/typemaps.gni
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-typemaps = [ "//chromecast/common/mojom/multiroom.typemap" ]
diff --git a/chromecast/media/cma/BUILD.gn b/chromecast/media/cma/BUILD.gn
index 427c96c..bf49cf6 100644
--- a/chromecast/media/cma/BUILD.gn
+++ b/chromecast/media/cma/BUILD.gn
@@ -77,7 +77,16 @@
 
   if (enable_video_with_mixed_audio) {
     defines = [ "ENABLE_VIDEO_WITH_MIXED_AUDIO" ]
-    libs = ["videodecoderformixer"]
+
+    # libvideodecoderformixer is linked here for the
+    # VideoDecoderForMixer::InitializeGraphicsForTesting symbol. Currently only
+    # alsa platforms do anything useful in this initialization, so otherwise
+    # just link in a dummy.
+    if (use_alsa) {
+      libs = [ "videodecoderformixer" ]
+    } else {
+      sources += [ "backend/dummy_initialize_graphics_for_testing.cc" ]
+    }
   }
 
   data = [
diff --git a/chromecast/media/cma/backend/dummy_initialize_graphics_for_testing.cc b/chromecast/media/cma/backend/dummy_initialize_graphics_for_testing.cc
new file mode 100644
index 0000000..df6928f
--- /dev/null
+++ b/chromecast/media/cma/backend/dummy_initialize_graphics_for_testing.cc
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/video_decoder_for_mixer.h"
+
+namespace chromecast {
+namespace media {
+
+CHROMECAST_EXPORT void VideoDecoderForMixer::InitializeGraphicsForTesting() {
+  // No initialization required.
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/typemaps.gni b/chromecast/typemaps.gni
new file mode 100644
index 0000000..e3efd00
--- /dev/null
+++ b/chromecast/typemaps.gni
@@ -0,0 +1,12 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromecast_build.gni")
+
+typemaps = [ "//chromecast/common/mojom/multiroom.typemap" ]
+
+if (chromecast_branding != "public") {
+  _typemap_internal = read_file("//chromecast/internal/typemaps.gni", "scope")
+  typemaps += _typemap_internal.typemaps
+}
diff --git a/chromeos/services/assistant/media_session/assistant_media_session.h b/chromeos/services/assistant/media_session/assistant_media_session.h
index eb38975..7b0e3eb 100644
--- a/chromeos/services/assistant/media_session/assistant_media_session.h
+++ b/chromeos/services/assistant/media_session/assistant_media_session.h
@@ -43,6 +43,10 @@
   void SkipAd() override {}
   void Seek(base::TimeDelta seek_time) override {}
   void Stop(SuspendType suspend_type) override {}
+  void GetMediaImageBitmap(const media_session::MediaImage& image,
+                           int minimum_size_px,
+                           int desired_size_px,
+                           GetMediaImageBitmapCallback callback) override {}
 
   // Requests/abandons audio focus to the AudioFocusManager.
   void RequestAudioFocus(media_session::mojom::AudioFocusType audio_focus_type);
diff --git a/components/arc/arc_prefs.cc b/components/arc/arc_prefs.cc
index e72aa38..de576c5 100644
--- a/components/arc/arc_prefs.cc
+++ b/components/arc/arc_prefs.cc
@@ -109,10 +109,6 @@
 // engagement time was last recorded. Accumulated results are sent to UMA if day
 // ID has changed.
 const char kEngagementTimeDayId[] = "arc.metrics.engagement_time.day_id";
-// A preference that indicates the user has accepted voice interaction activity
-// control settings.
-const char kVoiceInteractionActivityControlAccepted[] =
-    "settings.voice_interaction.activity_control.accepted";
 // A preference that indicates the user has allowed voice interaction services
 // to access the "context" (text and graphic content that is currently on
 // screen).
@@ -160,8 +156,6 @@
       static_cast<int>(ArcSupervisionTransition::NO_TRANSITION));
 
   // Sorted in lexicographical order.
-  registry->RegisterBooleanPref(kVoiceInteractionActivityControlAccepted,
-                                false);
   registry->RegisterBooleanPref(kAlwaysOnVpnLockdown, false);
   registry->RegisterStringPref(kAlwaysOnVpnPackage, std::string());
   registry->RegisterBooleanPref(kArcDataRemoveRequested, false);
diff --git a/components/arc/arc_prefs.h b/components/arc/arc_prefs.h
index 5da55db..cf17451 100644
--- a/components/arc/arc_prefs.h
+++ b/components/arc/arc_prefs.h
@@ -45,7 +45,6 @@
 ARC_EXPORT extern const char kEngagementTimeTotal[];
 
 // TODO(b/110211045): Move Assistant related prefs to ash.
-ARC_EXPORT extern const char kVoiceInteractionActivityControlAccepted[];
 ARC_EXPORT extern const char kVoiceInteractionContextEnabled[];
 ARC_EXPORT extern const char kVoiceInteractionEnabled[];
 ARC_EXPORT extern const char kVoiceInteractionHotwordAlwaysOn[];
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 0a749d1..e9871cd2 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -435,9 +435,10 @@
   // |FieldTypeGroup|.
   bool FormHasAddressField(const FormData& form) WARN_UNUSED_RESULT;
 
-  // Returns a list of values from the stored profiles that match |type| and the
-  // value of |field| and returns the labels of the matching profiles. |labels|
-  // is filled with the Profile label.
+  // Returns Suggestions corresponding to both the |autofill_field| type and
+  // stored profiles whose values match the contents of |field|. |form| stores
+  // data about the form with which the user is interacting, e.g. the number and
+  // types of form fields.
   std::vector<Suggestion> GetProfileSuggestions(
       const FormStructure& form,
       const FormFieldData& field,
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index a7e6cde..5fdf760 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -254,6 +254,11 @@
     "AutofillShowAutocompleteConsoleWarnings",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether suggestions' labels use the full disclosure format to
+// display disambiguation information.
+const base::Feature kAutofillShowFullDisclosureLabel{
+    "AutofillShowFullDisclosureLabel", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls attaching the autofill type predictions to their respective
 // element in the DOM.
 const base::Feature kAutofillShowTypePredictions{
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 485e42c..ec9c4784 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -68,6 +68,7 @@
 extern const base::Feature kAutofillSettingsCardTypeSplit;
 extern const base::Feature kAutofillShowAllSuggestionsOnPrefilledForms;
 extern const base::Feature kAutofillShowAutocompleteConsoleWarnings;
+extern const base::Feature kAutofillShowFullDisclosureLabel;
 extern const base::Feature kAutofillShowTypePredictions;
 extern const base::Feature kAutofillSkipComparingInferredLabels;
 extern const base::Feature kAutofillSuppressDisusedAddresses;
diff --git a/components/crash/content/app/crashpad.cc b/components/crash/content/app/crashpad.cc
index 72c9703..5eba3742 100644
--- a/components/crash/content/app/crashpad.cc
+++ b/components/crash/content/app/crashpad.cc
@@ -24,6 +24,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/crash/content/app/crash_reporter_client.h"
@@ -170,6 +171,9 @@
   pid_key.Set(base::NumberToString(::GetCurrentProcessId()));
 #endif
 
+  static crashpad::StringAnnotation<24> osarch_key("osarch");
+  osarch_key.Set(base::SysInfo::OperatingSystemArchitecture());
+
   logging::SetLogMessageHandler(LogMessageHandler);
 
   // If clients called CRASHPAD_SIMULATE_CRASH() instead of
diff --git a/components/domain_reliability/monitor.cc b/components/domain_reliability/monitor.cc
index 0e09a8f..6f094f1 100644
--- a/components/domain_reliability/monitor.cc
+++ b/components/domain_reliability/monitor.cc
@@ -17,7 +17,6 @@
 #include "components/domain_reliability/google_configs.h"
 #include "components/domain_reliability/header.h"
 #include "components/domain_reliability/quic_error_mapping.h"
-#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
@@ -243,7 +242,7 @@
   request.GetLoadTimingInfo(&load_timing_info);
   request.GetConnectionAttempts(&connection_attempts);
   request.PopulateNetErrorDetails(&details);
-  if (!request.GetRemoteEndpoint(&remote_endpoint))
+  if (!request.GetTransactionRemoteEndpoint(&remote_endpoint))
     remote_endpoint = net::IPEndPoint();
 }
 
diff --git a/components/domain_reliability/monitor_unittest.cc b/components/domain_reliability/monitor_unittest.cc
index 1619bee..cf32e1d7 100644
--- a/components/domain_reliability/monitor_unittest.cc
+++ b/components/domain_reliability/monitor_unittest.cc
@@ -19,7 +19,6 @@
 #include "components/domain_reliability/config.h"
 #include "components/domain_reliability/google_configs.h"
 #include "components/domain_reliability/test_util.h"
-#include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
@@ -72,8 +71,8 @@
   static RequestInfo MakeRequestInfo() {
     RequestInfo request;
     request.status = net::URLRequestStatus();
-    request.response_info.socket_address =
-        net::HostPortPair::FromString("12.34.56.78:80");
+    request.response_info.remote_endpoint =
+        net::IPEndPoint(net::IPAddress(12, 34, 56, 78), 80);
     request.response_info.headers = MakeHttpResponseHeaders(
         "HTTP/1.1 200 OK\n\n");
     request.response_info.was_cached = false;
@@ -202,8 +201,8 @@
   RequestInfo request = MakeRequestInfo();
   request.url = GURL("http://example/");
   request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET);
-  request.response_info.socket_address =
-      net::HostPortPair::FromString("127.0.0.1:3128");
+  request.response_info.remote_endpoint =
+      net::IPEndPoint(net::IPAddress(127, 0, 0, 1), 3128);
   request.response_info.was_fetched_via_proxy = true;
   OnRequestLegComplete(request);
 
diff --git a/components/download/internal/common/download_response_handler.cc b/components/download/internal/common/download_response_handler.cc
index acccd91..7ab6994 100644
--- a/components/download/internal/common/download_response_handler.cc
+++ b/components/download/internal/common/download_response_handler.cc
@@ -129,7 +129,7 @@
   create_info->total_bytes = head.content_length > 0 ? head.content_length : 0;
   create_info->result = result;
   if (result == DOWNLOAD_INTERRUPT_REASON_NONE)
-    create_info->remote_address = head.socket_address.host();
+    create_info->remote_address = head.remote_endpoint.ToStringWithoutPort();
   create_info->method = method_;
   create_info->connection_info = head.connection_info;
   create_info->url_chain = url_chain_;
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index 372d5cd..15b50bd 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -18,7 +18,9 @@
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/splitview/split_view_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_window_drag_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_resizer.h"
@@ -1086,8 +1088,50 @@
   ASSERT_FALSE(window_state->is_dragged());
 }
 
+namespace {
+
+class ClientControlledShellSurfaceDragTest : public test::ExoTestBase {
+ public:
+  ClientControlledShellSurfaceDragTest() = default;
+  ~ClientControlledShellSurfaceDragTest() override = default;
+
+  // Sends a gesture scroll sequence to TabletModeAppWindowDragController.
+  void SendGestureEvents(aura::Window* window,
+                         const gfx::Point& location,
+                         bool fling = false,
+                         float velocity = 0.f) {
+    ash::wm::WindowState* window_state = ash::wm::GetWindowState(window);
+    window_state->CreateDragDetails(gfx::Point(0, 0), HTCLIENT,
+                                    ::wm::WINDOW_MOVE_SOURCE_TOUCH);
+    std::unique_ptr<ash::TabletModeWindowDragController> controller_ =
+        std::make_unique<ash::TabletModeWindowDragController>(
+            window_state,
+            std::make_unique<ash::TabletModeBrowserWindowDragDelegate>());
+    controller_->drag_delegate_for_testing()
+        ->set_drag_start_deadline_for_testing(base::Time::Now());
+    controller_->Drag(location, 0);
+    if (fling) {
+      ui::GestureEventDetails details =
+          ui::GestureEventDetails(ui::ET_SCROLL_FLING_START, 0, velocity);
+      ui::GestureEvent event =
+          ui::GestureEvent(location.x(), location.y(), ui::EF_NONE,
+                           base::TimeTicks::Now(), details);
+      ui::Event::DispatcherApi(&event).set_target(window);
+      controller_->FlingOrSwipe(&event);
+    } else {
+      controller_->CompleteDrag();
+    }
+    ash::wm::GetWindowState(window)->DeleteDragDetails();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ClientControlledShellSurfaceDragTest);
+};
+
+}  // namespace
+
 // Test the functionalities of dragging a window from top in tablet mode.
-TEST_F(ClientControlledShellSurfaceTest, DragWindowFromTopInTabletMode) {
+TEST_F(ClientControlledShellSurfaceDragTest, DragWindowFromTopInTabletMode) {
   UpdateDisplay("800x600");
   ash::Shell* shell = ash::Shell::Get();
   shell->tablet_mode_controller()->EnableTabletModeWindowManager(true);
@@ -1106,26 +1150,18 @@
   ASSERT_TRUE(ash::wm::GetWindowState(window)->IsMaximized());
   surface->SetFrame(SurfaceFrameType::AUTOHIDE);
   surface->Commit();
-  ui::test::EventGenerator* event_generator = GetEventGenerator();
 
   // Drag the window by a small amount of distance will maximize the window
   // again.
-  const gfx::Point start(0, 0);
-  gfx::Point end(0, 10);
-  event_generator->GestureScrollSequence(
-      start, end, base::TimeDelta::FromMilliseconds(100), 2);
+  SendGestureEvents(window, gfx::Point(0, 10));
   EXPECT_TRUE(ash::wm::GetWindowState(window)->IsMaximized());
+  EXPECT_FALSE(shell->overview_controller()->IsSelecting());
 
   // FLING the window not inisde preview area with large enough y veloicty
   // (larger than kFlingToOverviewThreshold) will drop the window into overview.
-  EXPECT_FALSE(shell->overview_controller()->IsSelecting());
-  end = gfx::Point(400, 210);
-  const base::TimeDelta duration =
-      event_generator->CalculateScrollDurationForFlingVelocity(
-          start, end,
-          ash::TabletModeWindowDragDelegate::kFlingToOverviewThreshold + 10.f,
-          200);
-  event_generator->GestureScrollSequence(start, end, duration, 200);
+  SendGestureEvents(
+      window, gfx::Point(400, 10), /*fling=*/true,
+      ash::TabletModeWindowDragDelegate::kFlingToOverviewThreshold + 10.f);
   ASSERT_TRUE(shell->overview_controller()->IsSelecting());
   EXPECT_TRUE(
       shell->overview_controller()->overview_session()->IsWindowInOverview(
@@ -1133,12 +1169,8 @@
 
   // Drag the window long enough (pass one fourth of the screen vertical
   // height) to snap the window to splitscreen.
-  end = gfx::Point(0, 210);
   shell->overview_controller()->ToggleOverview();
-  EXPECT_FALSE(shell->overview_controller()->IsSelecting());
-  EXPECT_TRUE(ash::wm::GetWindowState(window)->IsMaximized());
-  event_generator->GestureScrollSequence(
-      start, end, base::TimeDelta::FromMilliseconds(100), 20);
+  SendGestureEvents(window, gfx::Point(0, 210));
   EXPECT_EQ(ash::wm::GetWindowState(window)->GetStateType(),
             ash::mojom::WindowStateType::LEFT_SNAPPED);
 }
diff --git a/components/language/content/browser/ulp_language_code_locator/s2langquadtree.h b/components/language/content/browser/ulp_language_code_locator/s2langquadtree.h
index efee31e..a7ff935 100644
--- a/components/language/content/browser/ulp_language_code_locator/s2langquadtree.h
+++ b/components/language/content/browser/ulp_language_code_locator/s2langquadtree.h
@@ -23,7 +23,7 @@
 // absent language.
 class SerializedLanguageTree {
  public:
-  virtual ~SerializedLanguageTree(){};
+  virtual ~SerializedLanguageTree() {}
 
   virtual std::string GetLanguageAt(const size_t pos) const = 0;
   virtual size_t GetNumLanguages() const = 0;
@@ -42,7 +42,7 @@
   BitsetSerializedLanguageTree(std::vector<std::string> languages,
                                std::bitset<numbits> bits)
       : languages_(languages), bits_(bits) {}
-  ~BitsetSerializedLanguageTree() override{};
+  ~BitsetSerializedLanguageTree() override {}
 
   // SerializedTree implementation
   std::string GetLanguageAt(const size_t pos) const override {
diff --git a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java
index 85d1b679..879472a 100644
--- a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java
+++ b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/ModuleInstaller.java
@@ -49,6 +49,17 @@
         updateCrashKeys();
     }
 
+    /**
+     * Needs to be called in attachBaseContext of the activities that want to have access to
+     * splits prior to application restart.
+     *
+     * For details, see:
+     * https://developer.android.com/reference/com/google/android/play/core/splitcompat/SplitCompat.html#install(android.content.Context)
+     */
+    public static void initActivity(Context context) {
+        SplitCompat.install(context);
+    }
+
     /** Writes fully installed and emulated modules to crash keys. */
     public static void updateCrashKeys() {
         Context context = ContextUtils.getApplicationContext();
diff --git a/components/module_installer/android/java/src-stub/org/chromium/components/module_installer/ModuleInstaller.java b/components/module_installer/android/java/src-stub/org/chromium/components/module_installer/ModuleInstaller.java
index 65ee45b..5dd14d3 100644
--- a/components/module_installer/android/java/src-stub/org/chromium/components/module_installer/ModuleInstaller.java
+++ b/components/module_installer/android/java/src-stub/org/chromium/components/module_installer/ModuleInstaller.java
@@ -4,11 +4,16 @@
 
 package org.chromium.components.module_installer;
 
+import android.content.Context;
+
 import org.chromium.base.VisibleForTesting;
 
 /** Dummy fallback of ModuleInstaller for APK builds. */
 public class ModuleInstaller {
     public static void init() {}
+
+    public static void initActivity(Context context) {}
+
     public static void updateCrashKeys(){};
 
     public static void install(
diff --git a/components/module_installer/android/java/src-test/org/chromium/components/module_installer/ModuleInstaller.java b/components/module_installer/android/java/src-test/org/chromium/components/module_installer/ModuleInstaller.java
index 9df2a24..720b8e8 100644
--- a/components/module_installer/android/java/src-test/org/chromium/components/module_installer/ModuleInstaller.java
+++ b/components/module_installer/android/java/src-test/org/chromium/components/module_installer/ModuleInstaller.java
@@ -4,6 +4,8 @@
 
 package org.chromium.components.module_installer;
 
+import android.content.Context;
+
 import org.chromium.base.VisibleForTesting;
 
 import java.util.HashSet;
@@ -14,6 +16,9 @@
     private static Set<String> sModulesRequestedDeffered = new HashSet<>();
 
     public static void init() {}
+
+    public static void initActivity(Context context) {}
+
     public static void updateCrashKeys(){};
 
     public static void install(
diff --git a/components/network_session_configurator/browser/network_session_configurator.cc b/components/network_session_configurator/browser/network_session_configurator.cc
index c1968ee6..711ea17 100644
--- a/components/network_session_configurator/browser/network_session_configurator.cc
+++ b/components/network_session_configurator/browser/network_session_configurator.cc
@@ -325,13 +325,6 @@
       "true");
 }
 
-bool ShouldQuicRaceStaleDNSOnConnection(
-    const VariationParameters& quic_trial_params) {
-  return base::LowerCaseEqualsASCII(
-      GetVariationParam(quic_trial_params, "race_stale_dns_on_connection"),
-      "true");
-}
-
 int GetQuicMaxTimeOnNonDefaultNetworkSeconds(
     const VariationParameters& quic_trial_params) {
   int value;
@@ -489,8 +482,6 @@
         ShouldQuicRetryOnAlternateNetworkBeforeHandshake(quic_trial_params);
     params->quic_go_away_on_path_degrading =
         ShouldQuicGoawayOnPathDegrading(quic_trial_params);
-    params->quic_race_stale_dns_on_connection =
-        ShouldQuicRaceStaleDNSOnConnection(quic_trial_params);
     int idle_session_migration_period_seconds =
         GetQuicIdleSessionMigrationPeriodSeconds(quic_trial_params);
     if (idle_session_migration_period_seconds > 0) {
diff --git a/components/network_session_configurator/browser/network_session_configurator_unittest.cc b/components/network_session_configurator/browser/network_session_configurator_unittest.cc
index fdd273b..9d2ec009 100644
--- a/components/network_session_configurator/browser/network_session_configurator_unittest.cc
+++ b/components/network_session_configurator/browser/network_session_configurator_unittest.cc
@@ -121,7 +121,6 @@
   EXPECT_FALSE(params_.quic_estimate_initial_rtt);
   EXPECT_FALSE(params_.quic_migrate_sessions_on_network_change_v2);
   EXPECT_FALSE(params_.quic_migrate_sessions_early_v2);
-  EXPECT_FALSE(params_.quic_race_stale_dns_on_connection);
   EXPECT_FALSE(params_.quic_retry_on_alternate_network_before_handshake);
   EXPECT_FALSE(params_.quic_go_away_on_path_degrading);
   EXPECT_FALSE(params_.quic_allow_server_migration);
@@ -376,18 +375,6 @@
 }
 
 TEST_F(NetworkSessionConfiguratorTest,
-       QuicRaceStaleDNSOnCOnnectionFromFieldTrialParams) {
-  std::map<std::string, std::string> field_trial_params;
-  field_trial_params["race_stale_dns_on_connection"] = "true";
-  variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
-  base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
-
-  ParseFieldTrials();
-
-  EXPECT_TRUE(params_.quic_race_stale_dns_on_connection);
-}
-
-TEST_F(NetworkSessionConfiguratorTest,
        QuicGoawayOnPathDegradingFromFieldTrialParams) {
   std::map<std::string, std::string> field_trial_params;
   field_trial_params["go_away_on_path_degrading"] = "true";
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_request.cc b/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
index ea2270f2..9a612a8 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
@@ -22,6 +22,7 @@
     int max_bundle_size_bytes,
     const std::vector<std::string>& page_urls,
     version_info::Channel channel,
+    bool send_testing_header,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     PrefetchRequestFinishedCallback callback)
     : callback_(std::move(callback)), requested_urls_(page_urls) {
@@ -41,7 +42,8 @@
   request.SerializeToString(&upload_data);
 
   fetcher_ = PrefetchRequestFetcher::CreateForPost(
-      GeneratePageBundleRequestURL(channel), upload_data, url_loader_factory,
+      GeneratePageBundleRequestURL(channel), upload_data, send_testing_header,
+      url_loader_factory,
       base::BindOnce(&GeneratePageBundleRequest::OnCompleted,
                      // Fetcher is owned by this instance.
                      base::Unretained(this)));
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_request.h b/components/offline_pages/core/prefetch/generate_page_bundle_request.h
index 3c4af0c..61df8ed 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_request.h
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_request.h
@@ -29,6 +29,7 @@
       int max_bundle_size_bytes,
       const std::vector<std::string>& page_urls,
       version_info::Channel channel,
+      bool send_testing_header,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefetchRequestFinishedCallback callback);
   ~GeneratePageBundleRequest();
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc b/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
index c99ca90..9436ed7 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
@@ -39,19 +39,21 @@
 class GeneratePageBundleRequestTest : public PrefetchRequestTestBase {
  public:
   std::unique_ptr<GeneratePageBundleRequest> CreateRequest(
+      bool testing_header,
       PrefetchRequestFinishedCallback callback) {
     std::vector<std::string> page_urls = {kTestURL, kTestURL2};
     return std::unique_ptr<GeneratePageBundleRequest>(
         new GeneratePageBundleRequest(
             kTestUserAgent, kTestGCMID, kTestMaxBundleSize, page_urls,
-            kTestChannel, shared_url_loader_factory(), std::move(callback)));
+            kTestChannel, testing_header, shared_url_loader_factory(),
+            std::move(callback)));
   }
 };
 
 TEST_F(GeneratePageBundleRequestTest, RequestData) {
   base::MockCallback<PrefetchRequestFinishedCallback> callback;
   std::unique_ptr<GeneratePageBundleRequest> request(
-      CreateRequest(callback.Get()));
+      CreateRequest(false, callback.Get()));
 
   EXPECT_EQ(2UL, request->requested_urls().size());
   EXPECT_THAT(request->requested_urls(), Contains(kTestURL));
@@ -94,7 +96,7 @@
 
   base::MockCallback<PrefetchRequestFinishedCallback> callback;
   std::unique_ptr<GeneratePageBundleRequest> request(
-      CreateRequest(callback.Get()));
+      CreateRequest(false, callback.Get()));
   network::TestURLLoaderFactory::PendingRequest* pending_request =
       GetPendingRequest();
   DCHECK(pending_request);
@@ -104,10 +106,34 @@
             GetExperiementHeaderValue(pending_request));
 }
 
+TEST_F(GeneratePageBundleRequestTest, NoTestingHeaderInRequestData) {
+  base::MockCallback<PrefetchRequestFinishedCallback> callback;
+  // Make a request without the header.
+  std::unique_ptr<GeneratePageBundleRequest> request(
+      CreateRequest(false, callback.Get()));
+
+  std::string testing_header;
+  bool has_header = GetPendingRequest()->request.headers.GetHeader(
+      "X-Offline-Prefetch-Testing", &testing_header);
+  EXPECT_FALSE(has_header);
+}
+
+TEST_F(GeneratePageBundleRequestTest, TestingHeaderInRequestData) {
+  base::MockCallback<PrefetchRequestFinishedCallback> callback;
+  // Make a request with the header.
+  std::unique_ptr<GeneratePageBundleRequest> request(
+      CreateRequest(true, callback.Get()));
+  std::string testing_header;
+  bool has_header = GetPendingRequest()->request.headers.GetHeader(
+      "X-Offline-Prefetch-Testing", &testing_header);
+  EXPECT_TRUE(has_header);
+  EXPECT_EQ("true", testing_header);
+}
+
 TEST_F(GeneratePageBundleRequestTest, EmptyResponse) {
   base::MockCallback<PrefetchRequestFinishedCallback> callback;
   std::unique_ptr<GeneratePageBundleRequest> request(
-      CreateRequest(callback.Get()));
+      CreateRequest(false, callback.Get()));
 
   PrefetchRequestStatus status;
   std::string operation_name;
@@ -126,7 +152,7 @@
 TEST_F(GeneratePageBundleRequestTest, InvalidResponse) {
   base::MockCallback<PrefetchRequestFinishedCallback> callback;
   std::unique_ptr<GeneratePageBundleRequest> request(
-      CreateRequest(callback.Get()));
+      CreateRequest(false, callback.Get()));
 
   PrefetchRequestStatus status;
   std::string operation_name;
diff --git a/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc b/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc
index f63ce7aa..f510a99f 100644
--- a/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc
@@ -70,10 +70,12 @@
                             ? kMaxBundleSizeForLimitlessBytes
                             : kMaxBundleSizeBytes;
   uint64_t request_id = GetNextRequestId();
+  bool send_testing_header =
+      prefetch_prefs::ShouldSendPrefetchTestingHeader(prefs_);
   generate_page_bundle_requests_[request_id] =
       std::make_unique<GeneratePageBundleRequest>(
           user_agent_, gcm_registration_id, max_bundle_size, url_strings,
-          channel_, url_loader_factory_,
+          channel_, send_testing_header, url_loader_factory_,
           base::BindOnce(
               &PrefetchNetworkRequestFactoryImpl::GeneratePageBundleRequestDone,
               weak_factory_.GetWeakPtr(), std::move(callback), request_id));
diff --git a/components/offline_pages/core/prefetch/prefetch_prefs.cc b/components/offline_pages/core/prefetch/prefetch_prefs.cc
index 3a4ee084..88524f3 100644
--- a/components/offline_pages/core/prefetch/prefetch_prefs.cc
+++ b/components/offline_pages/core/prefetch/prefetch_prefs.cc
@@ -16,6 +16,8 @@
 const char kEnabled[] = "offline_prefetch.enabled";
 const char kLimitlessPrefetchingEnabledTimePref[] =
     "offline_prefetch.limitless_prefetching_enabled_time";
+const char kSendPrefetchTestingHeaderPref[] =
+    "offline_prefetch.send_testing_header";
 }  // namespace
 
 const char kBackoff[] = "offline_prefetch.backoff";
@@ -25,6 +27,7 @@
   registry->RegisterBooleanPref(kEnabled, true);
   registry->RegisterTimePref(kLimitlessPrefetchingEnabledTimePref,
                              base::Time());
+  registry->RegisterBooleanPref(kSendPrefetchTestingHeaderPref, false);
 }
 
 void SetPrefetchingEnabledInSettings(PrefService* prefs, bool enabled) {
@@ -58,5 +61,15 @@
   return (now >= enabled_time) && (now < (enabled_time + max_duration));
 }
 
+void SetSendPrefetchTestingHeader(PrefService* prefs, bool enabled) {
+  DCHECK(prefs);
+  prefs->SetBoolean(kSendPrefetchTestingHeaderPref, enabled);
+}
+
+bool ShouldSendPrefetchTestingHeader(PrefService* prefs) {
+  DCHECK(prefs);
+  return prefs->GetBoolean(kSendPrefetchTestingHeaderPref);
+}
+
 }  // namespace prefetch_prefs
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_prefs.h b/components/offline_pages/core/prefetch/prefetch_prefs.h
index 7e590cc2..4cbb794d 100644
--- a/components/offline_pages/core/prefetch/prefetch_prefs.h
+++ b/components/offline_pages/core/prefetch/prefetch_prefs.h
@@ -28,6 +28,13 @@
 
 bool IsLimitlessPrefetchingEnabled(PrefService* prefs);
 
+// Sets whether a header "X-Offline-Prefetch_Testing: true" should be sent with
+// GeneratePageBundle requests. This header exempts the request from country
+// filtering so that prefetching can be tested in filtered countries.
+void SetSendPrefetchTestingHeader(PrefService* prefs, bool enabled);
+
+bool ShouldSendPrefetchTestingHeader(PrefService* prefs);
+
 }  // namespace prefetch_prefs
 }  // namespace offline_pages
 
diff --git a/components/offline_pages/core/prefetch/prefetch_prefs_unittest.cc b/components/offline_pages/core/prefetch/prefetch_prefs_unittest.cc
index 2ba8077..990f591 100644
--- a/components/offline_pages/core/prefetch/prefetch_prefs_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_prefs_unittest.cc
@@ -70,4 +70,14 @@
   EXPECT_FALSE(prefetch_prefs::IsLimitlessPrefetchingEnabled(prefs()));
 }
 
+TEST_F(PrefetchPrefsTest, SendTestingHeaderPref) {
+  EXPECT_FALSE(prefetch_prefs::ShouldSendPrefetchTestingHeader(prefs()));
+
+  prefetch_prefs::SetSendPrefetchTestingHeader(prefs(), true);
+  EXPECT_TRUE(prefetch_prefs::ShouldSendPrefetchTestingHeader(prefs()));
+
+  prefetch_prefs::SetSendPrefetchTestingHeader(prefs(), false);
+  EXPECT_FALSE(prefetch_prefs::ShouldSendPrefetchTestingHeader(prefs()));
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc b/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
index 2a5833cc..ce80feb 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
+++ b/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
@@ -34,22 +34,25 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     FinishedCallback callback) {
   return base::WrapUnique(new PrefetchRequestFetcher(
-      url, std::string(), url_loader_factory, std::move(callback)));
+      url, std::string(), false, url_loader_factory, std::move(callback)));
 }
 
 // static
 std::unique_ptr<PrefetchRequestFetcher> PrefetchRequestFetcher::CreateForPost(
     const GURL& url,
     const std::string& message,
+    bool send_testing_header,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     FinishedCallback callback) {
-  return base::WrapUnique(new PrefetchRequestFetcher(
-      url, message, url_loader_factory, std::move(callback)));
+  return base::WrapUnique(
+      new PrefetchRequestFetcher(url, message, send_testing_header,
+                                 url_loader_factory, std::move(callback)));
 }
 
 PrefetchRequestFetcher::PrefetchRequestFetcher(
     const GURL& url,
     const std::string& message,
+    bool send_testing_header,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     FinishedCallback callback)
     : url_loader_factory_(url_loader_factory), callback_(std::move(callback)) {
@@ -85,6 +88,9 @@
   if (!experiment_header.empty())
     resource_request->headers.AddHeaderFromString(experiment_header);
 
+  if (send_testing_header)
+    resource_request->headers.AddHeaderFromString(kPrefetchTestingHeader);
+
   if (message.empty())
     resource_request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
                                         kRequestContentType);
diff --git a/components/offline_pages/core/prefetch/prefetch_request_fetcher.h b/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
index d025a86..83171e2 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
+++ b/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
@@ -34,6 +34,7 @@
   static std::unique_ptr<PrefetchRequestFetcher> CreateForPost(
       const GURL& url,
       const std::string& message,
+      bool send_testing_header,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       FinishedCallback callback);
 
@@ -47,6 +48,7 @@
   PrefetchRequestFetcher(
       const GURL& url,
       const std::string& message,
+      bool send_testing_header,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       FinishedCallback callback);
 
diff --git a/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc b/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
index aa0a2777..5978e41 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
@@ -72,9 +72,11 @@
     base::OnceCallback<void(void)> respond_callback,
     std::string* data_received) {
   base::MockCallback<PrefetchRequestFetcher::FinishedCallback> callback;
+  bool send_prefetch_testing_header = false;
   std::unique_ptr<PrefetchRequestFetcher> fetcher =
       PrefetchRequestFetcher::CreateForPost(
-          kTestURL, kTestMessage, shared_url_loader_factory(), callback.Get());
+          kTestURL, kTestMessage, send_prefetch_testing_header,
+          shared_url_loader_factory(), callback.Get());
 
   PrefetchRequestStatus status;
   std::string data;
diff --git a/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc b/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
index b203671..48dacb3 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
@@ -56,7 +56,8 @@
     std::vector<std::string> pages = {kTestURL, kTestURL2};
     fetcher_.reset(new GeneratePageBundleRequest(
         kTestUserAgent, kTestGCMID, kTestMaxBundleSize, pages, kTestChannel,
-        url_loader_factory, std::move(callback)));
+        /*send_prefetch_testing_header=*/false, url_loader_factory,
+        std::move(callback)));
   }
 
  private:
diff --git a/components/offline_pages/core/prefetch/prefetch_server_urls.cc b/components/offline_pages/core/prefetch/prefetch_server_urls.cc
index 818ab80..411fe32 100644
--- a/components/offline_pages/core/prefetch/prefetch_server_urls.cc
+++ b/components/offline_pages/core/prefetch/prefetch_server_urls.cc
@@ -14,6 +14,7 @@
 const char kPrefetchServer[] = "https://offlinepages-pa.googleapis.com/";
 const char kPrefetchExperimentHeaderName[] = "X-Offline-Prefetch-Experiment";
 const char kPrefetchOperationHeaderName[] = "X-Offline-Prefetch-Operation";
+const char kPrefetchTestingHeader[] = "X-Offline-Prefetch-Testing: true";
 
 namespace {
 
diff --git a/components/offline_pages/core/prefetch/prefetch_server_urls.h b/components/offline_pages/core/prefetch/prefetch_server_urls.h
index c42eab8a..efd6827 100644
--- a/components/offline_pages/core/prefetch/prefetch_server_urls.h
+++ b/components/offline_pages/core/prefetch/prefetch_server_urls.h
@@ -14,6 +14,7 @@
 extern const char kPrefetchServer[];
 extern const char kPrefetchExperimentHeaderName[];
 extern const char kPrefetchOperationHeaderName[];
+extern const char kPrefetchTestingHeader[];
 
 // Returns the URL to send a request to generate page bundle.
 GURL GeneratePageBundleRequestURL(version_info::Channel channel);
diff --git a/components/policy/core/common/cloud/device_management_service.cc b/components/policy/core/common/cloud/device_management_service.cc
index 074db44..a22d894 100644
--- a/components/policy/core/common/cloud/device_management_service.cc
+++ b/components/policy/core/common/cloud/device_management_service.cc
@@ -64,7 +64,7 @@
     case net::ERR_PROXY_CONNECTION_FAILED:
     case net::ERR_TUNNEL_CONNECTION_FAILED:
     case net::ERR_PROXY_AUTH_UNSUPPORTED:
-    case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
+    case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT:
     case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED:
     case net::ERR_PROXY_CERTIFICATE_INVALID:
     case net::ERR_SOCKS_CONNECTION_FAILED:
diff --git a/components/safe_browsing/browser/threat_details_cache.cc b/components/safe_browsing/browser/threat_details_cache.cc
index c8fca75..8ad043b 100644
--- a/components/safe_browsing/browser/threat_details_cache.cc
+++ b/components/safe_browsing/browser/threat_details_cache.cc
@@ -17,7 +17,7 @@
 #include "components/safe_browsing/browser/threat_details_cache.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
@@ -203,7 +203,7 @@
       !current_load_->ResponseInfo()->proxy_server.is_direct();
   if (!was_fetched_via_proxy) {
     pb_response->set_remote_ip(
-        current_load_->ResponseInfo()->socket_address.ToString());
+        current_load_->ResponseInfo()->remote_endpoint.ToString());
   }
 }
 
diff --git a/components/safe_browsing/features.cc b/components/safe_browsing/features.cc
index 41a24b1..f00d697 100644
--- a/components/safe_browsing/features.cc
+++ b/components/safe_browsing/features.cc
@@ -23,8 +23,9 @@
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Controls the billing interstitial UI.
+// TODO(http://crbug.com/933807): remove this base::Feature after a full launch.
 const base::Feature kBillingInterstitial{"BillingInterstitial",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If enabled in pre-network-service world, SafeBrowsing URL checks are done by
 // applying SafeBrowsing's URLLoaderThrottle subclasses to ThrottlingURLLoader.
diff --git a/components/send_tab_to_self/BUILD.gn b/components/send_tab_to_self/BUILD.gn
index 7c92a1b8..900b9b2 100644
--- a/components/send_tab_to_self/BUILD.gn
+++ b/components/send_tab_to_self/BUILD.gn
@@ -17,10 +17,14 @@
   deps = [
     "//base",
     "//components/keyed_service/core",
+    "//components/send_tab_to_self/proto:send_tab_to_self_proto",
     "//components/sync",
     "//components/version_info",
     "//url",
   ]
+  public_deps = [
+    "//components/send_tab_to_self/proto:send_tab_to_self_proto",
+  ]
 }
 
 source_set("unit_tests") {
@@ -33,6 +37,7 @@
     ":send_tab_to_self",
     "//base",
     "//base/test:test_support",
+    "//components/send_tab_to_self/proto:send_tab_to_self_proto",
     "//components/sync",
     "//components/sync:test_support_driver",
     "//components/sync:test_support_model",
diff --git a/components/send_tab_to_self/proto/BUILD.gn b/components/send_tab_to_self/proto/BUILD.gn
new file mode 100644
index 0000000..57e0838f
--- /dev/null
+++ b/components/send_tab_to_self/proto/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+copy("send_tab_to_self_header_include") {
+  sources = [
+    "send_tab_to_self_header_include.h",
+  ]
+  outputs = [
+    "$root_gen_dir" +
+        "/components/send_tab_to_self/proto/send_tab_to_self_specifics.pb.h",
+  ]
+  deps = [
+    "//components/sync/protocol:protocol",
+  ]
+}
+
+proto_library("send_tab_to_self_proto") {
+  import_dirs = [ "//components/sync/protocol" ]
+  sources = [
+    "send_tab_to_self.proto",
+  ]
+  deps = [
+    ":send_tab_to_self_header_include",
+    "//components/sync/protocol:protocol",
+  ]
+}
diff --git a/components/send_tab_to_self/proto/DEPS b/components/send_tab_to_self/proto/DEPS
new file mode 100644
index 0000000..60ba8970
--- /dev/null
+++ b/components/send_tab_to_self/proto/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/sync/protocol",
+]
diff --git a/components/send_tab_to_self/proto/send_tab_to_self.proto b/components/send_tab_to_self/proto/send_tab_to_self.proto
new file mode 100644
index 0000000..3294d38e
--- /dev/null
+++ b/components/send_tab_to_self/proto/send_tab_to_self.proto
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Sync protocol datatype extension for the send tab to self entries.
+
+syntax = "proto2";
+
+import "send_tab_to_self_specifics.proto";
+
+option optimize_for = LITE_RUNTIME;
+
+package send_tab_to_self;
+
+// Local Send tab to self entry. This proto contains the fields stored locally
+// for a send tab to self entry.
+message SendTabToSelfLocal {
+  // The Send tab to self specifics proto.
+  optional sync_pb.SendTabToSelfSpecifics specifics = 1;
+}
diff --git a/components/send_tab_to_self/proto/send_tab_to_self_header_include.h b/components/send_tab_to_self/proto/send_tab_to_self_header_include.h
new file mode 100644
index 0000000..b2bc370
--- /dev/null
+++ b/components/send_tab_to_self/proto/send_tab_to_self_header_include.h
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEND_TAB_TO_SELF_PROTO_SEND_TAB_TO_SELF_HEADER_INCLUDE_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_PROTO_SEND_TAB_TO_SELF_HEADER_INCLUDE_H_
+
+// This is a bridge to include the generated proto header for the
+// send tab to self proto.
+#include "components/sync/protocol/send_tab_to_self_specifics.pb.h"
+
+#endif  // COMPONENTS_SEND_TAB_TO_SELF_PROTO_SEND_TAB_TO_SELF_HEADER_INCLUDE_H_
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.cc b/components/send_tab_to_self/send_tab_to_self_bridge.cc
index 7f2875be..e05aaf1 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "base/time/clock.h"
+#include "components/send_tab_to_self/proto/send_tab_to_self.pb.h"
 #include "components/sync/device_info/device_info.h"
 #include "components/sync/device_info/local_device_info_provider.h"
 #include "components/sync/model/entity_change.h"
@@ -17,32 +18,48 @@
 #include "components/sync/model/metadata_change_list.h"
 #include "components/sync/model/model_type_change_processor.h"
 #include "components/sync/model/mutable_data_batch.h"
-#include "components/sync/model_impl/in_memory_metadata_change_list.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 
 namespace send_tab_to_self {
 
+namespace {
+
+using syncer::ModelTypeStore;
+
+// Converts a time field from sync protobufs to a time object.
+base::Time ProtoTimeToTime(int64_t proto_t) {
+  return base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromMicroseconds(proto_t));
+}
+
+// Allocate a EntityData and copies |specifics| into it.
+std::unique_ptr<syncer::EntityData> CopyToEntityData(
+    const sync_pb::SendTabToSelfSpecifics& specifics) {
+  auto entity_data = std::make_unique<syncer::EntityData>();
+  *entity_data->specifics.mutable_send_tab_to_self() = specifics;
+  entity_data->non_unique_name = specifics.url();
+  entity_data->creation_time = ProtoTimeToTime(specifics.shared_time_usec());
+  return entity_data;
+}
+
+}  // namespace
+
 SendTabToSelfBridge::SendTabToSelfBridge(
     std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
     syncer::LocalDeviceInfoProvider* local_device_info_provider,
-    base::Clock* clock)
+    base::Clock* clock,
+    syncer::OnceModelTypeStoreFactory create_store_callback)
     : ModelTypeSyncBridge(std::move(change_processor)),
+      clock_(clock),
       local_device_info_provider_(local_device_info_provider),
-      clock_(clock) {
+      weak_ptr_factory_(this) {
   DCHECK(local_device_info_provider);
   DCHECK(clock_);
 
-  if (local_device_info_provider->GetLocalDeviceInfo()) {
-    OnDeviceProviderInitialized();
-  } else {
-    device_subscription_ =
-        local_device_info_provider->RegisterOnInitializedCallback(
-            base::BindRepeating(
-                &SendTabToSelfBridge::OnDeviceProviderInitialized,
-                base::Unretained(this)));
-  }
-
-  // TODO(jeffreycohen): Create a local store and read meta data from it.
+  std::move(create_store_callback)
+      .Run(syncer::SEND_TAB_TO_SELF,
+           base::BindOnce(&SendTabToSelfBridge::OnStoreCreated,
+                          weak_ptr_factory_.GetWeakPtr()));
 }
 
 SendTabToSelfBridge::~SendTabToSelfBridge() {
@@ -50,12 +67,13 @@
 
 std::unique_ptr<syncer::MetadataChangeList>
 SendTabToSelfBridge::CreateMetadataChangeList() {
-  return std::make_unique<syncer::InMemoryMetadataChangeList>();
+  return ModelTypeStore::WriteBatch::CreateMetadataChangeList();
 }
 
 base::Optional<syncer::ModelError> SendTabToSelfBridge::MergeSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
+  DCHECK(entries_.empty());
   return ApplySyncChanges(std::move(metadata_change_list),
                           std::move(entity_data));
 }
@@ -65,57 +83,71 @@
     syncer::EntityChangeList entity_changes) {
   std::vector<const SendTabToSelfEntry*> added;
   std::vector<std::string> removed;
+  std::unique_ptr<ModelTypeStore::WriteBatch> batch =
+      store_->CreateWriteBatch();
+
   for (syncer::EntityChange& change : entity_changes) {
+    const std::string& guid = change.storage_key();
     if (change.type() == syncer::EntityChange::ACTION_DELETE) {
-      entries_.erase(change.storage_key());
-      removed.push_back(change.storage_key());
+      if (entries_.find(guid) != entries_.end()) {
+        entries_.erase(change.storage_key());
+        batch->DeleteData(guid);
+        removed.push_back(change.storage_key());
+      }
     } else {
       // syncer::EntityChange::ACTION_UPDATE is not supported by this bridge
       DCHECK(change.type() == syncer::EntityChange::ACTION_ADD);
 
       const sync_pb::SendTabToSelfSpecifics& specifics =
           change.data().specifics.send_tab_to_self();
-      // TODO(jeffreycohen): FromProto expects a valid entry. External data
-      // needs to be sanitized before it's passed through.
+
       std::unique_ptr<SendTabToSelfEntry> entry =
           SendTabToSelfEntry::FromProto(specifics, clock_->Now());
       // This entry is new. Add it to the model.
       added.push_back(entry.get());
+      SendTabToSelfLocal entry_pb = entry->AsLocalProto();
       entries_[entry->GetGUID()] = std::move(entry);
+
+      // Write to the store.
+      batch->WriteData(guid, entry_pb.SerializeAsString());
     }
   }
+
+  batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
+  Commit(std::move(batch));
+
   if (!removed.empty()) {
     NotifyRemoteSendTabToSelfEntryDeleted(removed);
   }
   if (!added.empty()) {
     NotifyRemoteSendTabToSelfEntryAdded(added);
   }
+
   return base::nullopt;
 }
 
 void SendTabToSelfBridge::GetData(StorageKeyList storage_keys,
                                   DataCallback callback) {
-  syncer::InMemoryMetadataChangeList metadata_change_list;
+  auto batch = std::make_unique<syncer::MutableDataBatch>();
 
   for (const std::string& guid : storage_keys) {
     const SendTabToSelfEntry* entry = GetEntryByGUID(guid);
     if (!entry) {
       continue;
     }
-    std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics =
-        entry->AsProto();
-    auto entity_data = std::make_unique<syncer::EntityData>();
-    *entity_data->specifics.mutable_send_tab_to_self() = *specifics;
-    entity_data->non_unique_name = specifics->url();
 
-    change_processor()->Put(specifics->guid(), std::move(entity_data),
-                            &metadata_change_list);
+    batch->Put(guid, CopyToEntityData(entry->AsLocalProto().specifics()));
   }
+  std::move(callback).Run(std::move(batch));
 }
 
 void SendTabToSelfBridge::GetAllDataForDebugging(DataCallback callback) {
-  // TODO(jeffreycohen): Add once we have a batch model.
-  NOTIMPLEMENTED();
+  auto batch = std::make_unique<syncer::MutableDataBatch>();
+  for (const auto& it : entries_) {
+    batch->Put(it.first,
+               CopyToEntityData(it.second->AsLocalProto().specifics()));
+  }
+  std::move(callback).Run(std::move(batch));
 }
 
 std::string SendTabToSelfBridge::GetClientTag(
@@ -142,12 +174,19 @@
     DCHECK_EQ(0ul, entries_.size());
     return;
   }
-  syncer::InMemoryMetadataChangeList metadata_change_list;
 
-  for (const auto& key : GetAllGuids()) {
-    change_processor()->Delete(key, &metadata_change_list);
+  std::unique_ptr<ModelTypeStore::WriteBatch> batch =
+      store_->CreateWriteBatch();
+
+  std::vector<std::string> all_guids = GetAllGuids();
+
+  for (const auto& guid : all_guids) {
+    change_processor()->Delete(guid, batch->GetMetadataChangeList());
+    batch->DeleteData(guid);
   }
   entries_.clear();
+
+  NotifyRemoteSendTabToSelfEntryDeleted(all_guids);
 }
 
 const SendTabToSelfEntry* SendTabToSelfBridge::GetEntryByGUID(
@@ -177,17 +216,22 @@
       guid, url, trimmed_title, clock_->Now(), navigation_time,
       local_device_name_);
 
-  syncer::InMemoryMetadataChangeList metadata_change_list;
-
+  std::unique_ptr<ModelTypeStore::WriteBatch> batch =
+      store_->CreateWriteBatch();
   // This entry is new. Add it to the store and model.
-  auto entity_data = std::make_unique<syncer::EntityData>();
-  *entity_data->specifics.mutable_send_tab_to_self() = *entry->AsProto();
-  entity_data->non_unique_name = entry->GetURL().spec();
-  entity_data->creation_time = entry->GetSharedTime();
+  auto entity_data = CopyToEntityData(entry->AsLocalProto().specifics());
 
-  change_processor()->Put(guid, std::move(entity_data), &metadata_change_list);
+  change_processor()->Put(guid, std::move(entity_data),
+                          batch->GetMetadataChangeList());
 
-  return entries_.emplace(guid, std::move(entry)).first->second.get();
+  const SendTabToSelfEntry* result =
+      entries_.emplace(guid, std::move(entry)).first->second.get();
+  SendTabToSelfLocal specifics = result->AsLocalProto();
+
+  batch->WriteData(guid, specifics.SerializeAsString());
+
+  Commit(std::move(batch));
+  return result;
 }
 
 void SendTabToSelfBridge::DeleteEntry(const std::string& guid) {
@@ -197,11 +241,14 @@
   }
 
   DCHECK(change_processor()->IsTrackingMetadata());
-  syncer::InMemoryMetadataChangeList metadata_change_list;
-  change_processor()->Delete(guid, &metadata_change_list);
+
+  std::unique_ptr<ModelTypeStore::WriteBatch> batch =
+      store_->CreateWriteBatch();
+  change_processor()->Delete(guid, batch->GetMetadataChangeList());
 
   entries_.erase(guid);
-
+  batch->DeleteData(guid);
+  Commit(std::move(batch));
 }
 
 void SendTabToSelfBridge::DismissEntry(const std::string& guid) {
@@ -214,6 +261,13 @@
   // TODO(jeffreycohen) Implement once there is local storage.
 }
 
+// static
+std::unique_ptr<syncer::ModelTypeStore>
+SendTabToSelfBridge::DestroyAndStealStoreForTest(
+    std::unique_ptr<SendTabToSelfBridge> bridge) {
+  return std::move(bridge->store_);
+}
+
 void SendTabToSelfBridge::NotifyRemoteSendTabToSelfEntryAdded(
     const std::vector<const SendTabToSelfEntry*>& new_entries) {
   for (SendTabToSelfModelObserver& observer : observers_) {
@@ -228,14 +282,89 @@
   }
 }
 
+void SendTabToSelfBridge::NotifySendTabToSelfModelLoaded() {
+  for (SendTabToSelfModelObserver& observer : observers_) {
+    observer.SendTabToSelfModelLoaded();
+  }
+}
+
+void SendTabToSelfBridge::OnStoreCreated(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore> store) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  store_ = std::move(store);
+  store_->ReadAllData(base::BindOnce(&SendTabToSelfBridge::OnReadAllData,
+                                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SendTabToSelfBridge::OnReadAllData(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore::RecordList> record_list) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  for (const syncer::ModelTypeStore::Record& r : *record_list) {
+    auto specifics = std::make_unique<SendTabToSelfLocal>();
+    if (specifics->ParseFromString(r.value)) {
+      entries_[specifics->specifics().guid()] =
+          SendTabToSelfEntry::FromLocalProto(*specifics, clock_->Now());
+    } else {
+      change_processor()->ReportError(
+          {FROM_HERE, "Failed to deserialize specifics."});
+      return;
+    }
+  }
+
+  if (local_device_info_provider_->GetLocalDeviceInfo()) {
+    OnDeviceProviderInitialized();
+  } else {
+    device_subscription_ =
+        local_device_info_provider_->RegisterOnInitializedCallback(
+            base::BindRepeating(
+                &SendTabToSelfBridge::OnDeviceProviderInitialized,
+                base::Unretained(this)));
+  }
+}
+
 void SendTabToSelfBridge::OnDeviceProviderInitialized() {
   device_subscription_.reset();
   local_device_name_ =
       local_device_info_provider_->GetLocalDeviceInfo()->client_name();
 
-  auto batch = std::make_unique<syncer::MetadataBatch>();
+  // now that all of the providers are ready we can read the metadata.
+  store_->ReadAllMetadata(base::BindOnce(
+      &SendTabToSelfBridge::OnReadAllMetadata, weak_ptr_factory_.GetWeakPtr()));
+}
 
-  this->change_processor()->ModelReadyToSync(std::move(batch));
+void SendTabToSelfBridge::OnReadAllMetadata(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+  change_processor()->ModelReadyToSync(std::move(metadata_batch));
+  NotifySendTabToSelfModelLoaded();
+}
+
+void SendTabToSelfBridge::OnCommit(
+    const base::Optional<syncer::ModelError>& error) {
+  if (error) {
+    change_processor()->ReportError(*error);
+  }
+}
+
+void SendTabToSelfBridge::Commit(
+    std::unique_ptr<ModelTypeStore::WriteBatch> batch) {
+  store_->CommitWriteBatch(std::move(batch),
+                           base::BindOnce(&SendTabToSelfBridge::OnCommit,
+                                          weak_ptr_factory_.GetWeakPtr()));
 }
 
 }  // namespace send_tab_to_self
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.h b/components/send_tab_to_self/send_tab_to_self_bridge.h
index 89da9b1..feb5a4e6 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.h
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.h
@@ -12,14 +12,15 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "components/send_tab_to_self/send_tab_to_self_entry.h"
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/device_info/local_device_info_provider.h"
+#include "components/sync/model/model_type_store.h"
 #include "components/sync/model/model_type_sync_bridge.h"
 
 namespace syncer {
-class LocalDeviceInfoProvider;
 class ModelTypeChangeProcessor;
 }  // namespace syncer
 
@@ -39,7 +40,8 @@
   SendTabToSelfBridge(
       std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
       syncer::LocalDeviceInfoProvider* local_device_info_provider,
-      base::Clock* clock);
+      base::Clock* clock,
+      syncer::OnceModelTypeStoreFactory create_store_callback);
   ~SendTabToSelfBridge() override;
 
   // syncer::ModelTypeSyncBridge overrides.
@@ -68,6 +70,10 @@
   void DeleteEntry(const std::string& guid) override;
   void DismissEntry(const std::string& guid) override;
 
+  // For testing only.
+  static std::unique_ptr<syncer::ModelTypeStore> DestroyAndStealStoreForTest(
+      std::unique_ptr<SendTabToSelfBridge> bridge);
+
  private:
   using SendTabToSelfEntries =
       std::map<std::string, std::unique_ptr<SendTabToSelfEntry>>;
@@ -82,23 +88,47 @@
   void NotifyRemoteSendTabToSelfEntryDeleted(
       const std::vector<std::string>& guids);
 
+  // Notify all observers that the model is loaded;
+  void NotifySendTabToSelfModelLoaded();
+
+  // Methods used as callbacks given to DataTypeStore.
+  void OnStoreCreated(const base::Optional<syncer::ModelError>& error,
+                      std::unique_ptr<syncer::ModelTypeStore> store);
+  void OnReadAllData(
+      const base::Optional<syncer::ModelError>& error,
+      std::unique_ptr<syncer::ModelTypeStore::RecordList> record_list);
+
   // Used as callback given to LocalDeviceInfoProvider.
   void OnDeviceProviderInitialized();
 
+  void OnReadAllMetadata(const base::Optional<syncer::ModelError>& error,
+                         std::unique_ptr<syncer::MetadataBatch> metadata_batch);
+  void OnCommit(const base::Optional<syncer::ModelError>& error);
+
+  // Persists the changes in the given aggregators
+  void Commit(std::unique_ptr<syncer::ModelTypeStore::WriteBatch> batch);
+
   // |entries_| is keyed by GUIDs.
   SendTabToSelfEntries entries_;
 
+  // |clock_| isn't owned.
+  const base::Clock* const clock_;
+
+  // |local_device_info_provider_| isn't owned.
   syncer::LocalDeviceInfoProvider* const local_device_info_provider_;
 
   std::string local_device_name_;
 
-  const base::Clock* const clock_;
+  // In charge of actually persisting changes to disk, or loading previous data.
+  std::unique_ptr<syncer::ModelTypeStore> store_;
 
   // Used to listen for provider initialization. If the provider is already
   // initialized during our constructor then the subscription is never used.
   std::unique_ptr<syncer::LocalDeviceInfoProvider::Subscription>
       device_subscription_;
 
+  base::WeakPtrFactory<SendTabToSelfBridge> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(SendTabToSelfBridge);
 };
 
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
index ac447ad..be7650e1 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
@@ -10,12 +10,18 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_clock.h"
+#include "components/send_tab_to_self/proto/send_tab_to_self.pb.h"
 #include "components/sync/device_info/local_device_info_provider_mock.h"
+#include "components/sync/model/entity_change.h"
 #include "components/sync/model/metadata_batch.h"
 #include "components/sync/model/mock_model_type_change_processor.h"
+#include "components/sync/model/model_type_store_test_util.h"
 #include "components/sync/model_impl/in_memory_metadata_change_list.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+#include "components/sync/test/test_matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -24,16 +30,69 @@
 namespace {
 
 using testing::_;
+using testing::IsEmpty;
+using testing::Return;
+using testing::SizeIs;
+
+const char kGuidFormat[] = "guid %d";
+const char kURLFormat[] = "https://www.url%d.com/";
+const char kTitleFormat[] = "title %d";
+const char kDeviceFormat[] = "device %d";
+
+sync_pb::SendTabToSelfSpecifics CreateSpecifics(
+    int suffix,
+    base::Time shared_time = base::Time::Now(),
+    base::Time navigation_time = base::Time::Now()) {
+  sync_pb::SendTabToSelfSpecifics specifics;
+  specifics.set_guid(base::StringPrintf(kGuidFormat, suffix));
+  specifics.set_url(base::StringPrintf(kURLFormat, suffix));
+  specifics.set_device_name(base::StringPrintf(kDeviceFormat, suffix));
+  specifics.set_title(base::StringPrintf(kTitleFormat, suffix));
+  specifics.set_shared_time_usec(
+      shared_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  specifics.set_navigation_time_usec(
+      navigation_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  return specifics;
+}
+
+sync_pb::ModelTypeState StateWithEncryption(
+    const std::string& encryption_key_name) {
+  sync_pb::ModelTypeState state;
+  state.set_encryption_key_name(encryption_key_name);
+  return state;
+}
+class MockSendTabToSelfModelObserver : public SendTabToSelfModelObserver {
+ public:
+  MOCK_METHOD0(SendTabToSelfModelLoaded, void());
+  MOCK_METHOD1(EntriesAddedRemotely,
+               void(const std::vector<const SendTabToSelfEntry*>&));
+
+  MOCK_METHOD1(EntriesRemovedRemotely, void(const std::vector<std::string>&));
+};
 
 class SendTabToSelfBridgeTest : public testing::Test {
  protected:
   SendTabToSelfBridgeTest()
-      : bridge_(mock_processor_.CreateForwardingProcessor(),
-                &provider_,
-                &clock_) {
+      : store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
     provider_.Initialize("cache_guid", "machine");
-    ON_CALL(mock_processor_, IsTrackingMetadata())
-        .WillByDefault(testing::Return(true));
+    ON_CALL(mock_processor_, IsTrackingMetadata()).WillByDefault(Return(true));
+  }
+
+  // Initialized the bridge based on the current local device and store. Can
+  // only be called once per run, as it passes |store_|.
+  void InitializeBridge() {
+    bridge_ = std::make_unique<SendTabToSelfBridge>(
+        mock_processor_.CreateForwardingProcessor(), &provider_, &clock_,
+        syncer::ModelTypeStoreTestUtil::MoveStoreToFactory(std::move(store_)));
+    bridge_->AddObserver(&mock_observer_);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void ShutdownBridge() {
+    bridge_->RemoveObserver(&mock_observer_);
+    store_ =
+        SendTabToSelfBridge::DestroyAndStealStoreForTest(std::move(bridge_));
+    base::RunLoop().RunUntilIdle();
   }
 
   base::Time AdvanceAndGetTime() {
@@ -42,12 +101,12 @@
   }
 
   syncer::EntityDataPtr MakeEntityData(const SendTabToSelfEntry& entry) {
-    std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics =
-        entry.AsProto();
+    SendTabToSelfLocal specifics = entry.AsLocalProto();
 
     auto entity_data = std::make_unique<syncer::EntityData>();
 
-    *(entity_data->specifics.mutable_send_tab_to_self()) = *specifics;
+    *(entity_data->specifics.mutable_send_tab_to_self()) =
+        specifics.specifics();
     entity_data->non_unique_name = entry.GetURL().spec();
     // The client_tag_hash field is unused by the send_tab_to_self_bridge, but
     // is required for a valid entity_data.
@@ -55,15 +114,42 @@
     return entity_data->PassToPtr();
   }
 
+  // Helper method to reduce duplicated code between tests. Wraps the given
+  // specifics objects in an EntityData and EntityChange of type ACTION_ADD, and
+  // returns an EntityChangeList containing them all. Order is maintained.
+  syncer::EntityChangeList EntityAddList(
+      const std::vector<sync_pb::SendTabToSelfSpecifics>& specifics_list) {
+    syncer::EntityChangeList changes;
+    for (const auto& specifics : specifics_list) {
+      auto entity_data = std::make_unique<syncer::EntityData>();
+
+      *(entity_data->specifics.mutable_send_tab_to_self()) = specifics;
+      entity_data->non_unique_name = specifics.url();
+      // The client_tag_hash field is unused by the send_tab_to_self_bridge, but
+      // is required for a valid entity_data.
+      entity_data->client_tag_hash = "someclienttaghash";
+
+      changes.push_back(syncer::EntityChange::CreateAdd(
+          specifics.guid(), entity_data->PassToPtr()));
+    }
+    return changes;
+  }
+
   // For Model Tests.
   void AddSampleEntries() {
     // Adds timer to avoid having two entries with the same shared timestamp.
-    bridge_.AddEntry(GURL("http://a.com"), "a", AdvanceAndGetTime());
-    bridge_.AddEntry(GURL("http://b.com"), "b", AdvanceAndGetTime());
-    bridge_.AddEntry(GURL("http://c.com"), "c", AdvanceAndGetTime());
-    bridge_.AddEntry(GURL("http://d.com"), "d", AdvanceAndGetTime());
+    bridge_->AddEntry(GURL("http://a.com"), "a", AdvanceAndGetTime());
+    bridge_->AddEntry(GURL("http://b.com"), "b", AdvanceAndGetTime());
+    bridge_->AddEntry(GURL("http://c.com"), "c", AdvanceAndGetTime());
+    bridge_->AddEntry(GURL("http://d.com"), "d", AdvanceAndGetTime());
   }
 
+  syncer::MockModelTypeChangeProcessor* processor() { return &mock_processor_; }
+
+  SendTabToSelfBridge* bridge() { return bridge_.get(); }
+  MockSendTabToSelfModelObserver* mock_observer() { return &mock_observer_; }
+
+ private:
   base::SimpleTestClock clock_;
 
   // In memory model type store needs to be able to post tasks.
@@ -71,21 +157,28 @@
 
   syncer::LocalDeviceInfoProviderMock provider_;
 
-  syncer::MockModelTypeChangeProcessor mock_processor_;
+  std::unique_ptr<syncer::ModelTypeStore> store_;
 
-  SendTabToSelfBridge bridge_;
+  testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
 
- private:
+  std::unique_ptr<SendTabToSelfBridge> bridge_;
+
+  testing::NiceMock<MockSendTabToSelfModelObserver> mock_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(SendTabToSelfBridgeTest);
 };
 
 TEST_F(SendTabToSelfBridgeTest, CheckEmpties) {
-  EXPECT_EQ(0ul, bridge_.GetAllGuids().size());
+  InitializeBridge();
+
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(_)).Times(0);
+  EXPECT_EQ(0ul, bridge()->GetAllGuids().size());
   AddSampleEntries();
-  EXPECT_EQ(4ul, bridge_.GetAllGuids().size());
+  EXPECT_EQ(4ul, bridge()->GetAllGuids().size());
 }
 
 TEST_F(SendTabToSelfBridgeTest, SyncAddOneEntry) {
+  InitializeBridge();
   syncer::EntityChangeList remote_input;
 
   SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
@@ -95,14 +188,33 @@
       syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry)));
   auto metadata_change_list =
       std::make_unique<syncer::InMemoryMetadataChangeList>();
-  bridge_.MergeSyncData(std::move(metadata_change_list), remote_input);
-  EXPECT_EQ(1ul, bridge_.GetAllGuids().size());
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(SizeIs(1)));
+  bridge()->MergeSyncData(std::move(metadata_change_list), remote_input);
+  EXPECT_EQ(1ul, bridge()->GetAllGuids().size());
+}
+
+TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesAddTwoSpecifics) {
+  InitializeBridge();
+
+  const sync_pb::SendTabToSelfSpecifics specifics1 = CreateSpecifics(1);
+  const sync_pb::SendTabToSelfSpecifics specifics2 = CreateSpecifics(2);
+
+  sync_pb::ModelTypeState state = StateWithEncryption("ekn");
+  std::unique_ptr<syncer::MetadataChangeList> metadata_changes =
+      bridge()->CreateMetadataChangeList();
+  metadata_changes->UpdateModelTypeState(state);
+
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(SizeIs(2)));
+
+  auto error = bridge()->ApplySyncChanges(
+      std::move(metadata_changes), EntityAddList({specifics1, specifics2}));
+  EXPECT_FALSE(error);
 }
 
 TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneAdd) {
+  InitializeBridge();
   SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
                            AdvanceAndGetTime(), AdvanceAndGetTime(), "device");
-  std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics = entry.AsProto();
 
   syncer::EntityChangeList add_changes;
 
@@ -110,28 +222,111 @@
       syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry)));
   auto metadata_change_list =
       std::make_unique<syncer::InMemoryMetadataChangeList>();
-  bridge_.ApplySyncChanges(std::move(metadata_change_list), add_changes);
-  EXPECT_EQ(1ul, bridge_.GetAllGuids().size());
+
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(SizeIs(1)));
+  bridge()->ApplySyncChanges(std::move(metadata_change_list), add_changes);
+  EXPECT_EQ(1ul, bridge()->GetAllGuids().size());
 }
 
 // Tests that the send tab to self entry is correctly removed.
 TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneDeletion) {
+  InitializeBridge();
   SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
                            AdvanceAndGetTime(), AdvanceAndGetTime(), "device");
-  std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics = entry.AsProto();
 
   syncer::EntityChangeList add_changes;
 
   add_changes.push_back(
       syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry)));
-  auto metadata_change_list =
-      std::make_unique<syncer::InMemoryMetadataChangeList>();
-  bridge_.ApplySyncChanges(std::move(metadata_change_list), add_changes);
-  EXPECT_EQ(1ul, bridge_.GetAllGuids().size());
+
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(SizeIs(1)));
+  bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(), add_changes);
+  EXPECT_EQ(1ul, bridge()->GetAllGuids().size());
   syncer::EntityChangeList delete_changes;
   delete_changes.push_back(syncer::EntityChange::CreateDelete("guid1"));
-  bridge_.ApplySyncChanges(std::move(metadata_change_list), delete_changes);
-  EXPECT_EQ(0ul, bridge_.GetAllGuids().size());
+
+  EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(SizeIs(1)));
+  bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                             delete_changes);
+  EXPECT_EQ(0ul, bridge()->GetAllGuids().size());
+}
+
+TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesEmpty) {
+  InitializeBridge();
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(_)).Times(0);
+
+  auto error = bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(),
+                                          syncer::EntityChangeList());
+  EXPECT_FALSE(error);
+}
+
+TEST_F(SendTabToSelfBridgeTest, AddEntryAndRestartBridge) {
+  InitializeBridge();
+
+  const sync_pb::SendTabToSelfSpecifics specifics = CreateSpecifics(1);
+  sync_pb::ModelTypeState state = StateWithEncryption("ekn");
+  std::unique_ptr<syncer::MetadataChangeList> metadata_changes =
+      bridge()->CreateMetadataChangeList();
+  metadata_changes->UpdateModelTypeState(state);
+
+  auto error = bridge()->ApplySyncChanges(std::move(metadata_changes),
+                                          EntityAddList({specifics}));
+  ASSERT_FALSE(error);
+
+  ShutdownBridge();
+
+  EXPECT_CALL(*processor(),
+              ModelReadyToSync(MetadataBatchContains(
+                  syncer::HasEncryptionKeyName(state.encryption_key_name()),
+                  /*entities=*/IsEmpty())));
+
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(_)).Times(0);
+  InitializeBridge();
+
+  std::vector<std::string> guids = bridge()->GetAllGuids();
+  EXPECT_EQ(1ul, guids.size());
+  EXPECT_EQ(specifics.url(),
+            bridge()->GetEntryByGUID(guids[0])->GetURL().spec());
+}
+
+TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesInMemory) {
+  InitializeBridge();
+
+  const sync_pb::SendTabToSelfSpecifics specifics = CreateSpecifics(1);
+  std::unique_ptr<syncer::MetadataChangeList> metadata_changes =
+      bridge()->CreateMetadataChangeList();
+
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(SizeIs(1)));
+
+  auto error_on_add = bridge()->ApplySyncChanges(
+      bridge()->CreateMetadataChangeList(), EntityAddList({specifics}));
+
+  EXPECT_FALSE(error_on_add);
+
+  EXPECT_EQ(1ul, bridge()->GetAllGuids().size());
+
+  EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(SizeIs(1)));
+
+  auto error_on_delete = bridge()->ApplySyncChanges(
+      bridge()->CreateMetadataChangeList(),
+      {syncer::EntityChange::CreateDelete(specifics.guid())});
+
+  EXPECT_FALSE(error_on_delete);
+  EXPECT_EQ(0ul, bridge()->GetAllGuids().size());
+}
+
+TEST_F(SendTabToSelfBridgeTest, ApplyDeleteNonexistent) {
+  InitializeBridge();
+  EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(_)).Times(0);
+
+  std::unique_ptr<syncer::MetadataChangeList> metadata_changes =
+      bridge()->CreateMetadataChangeList();
+
+  EXPECT_CALL(*processor(), Delete(_, _)).Times(0);
+  auto error =
+      bridge()->ApplySyncChanges(std::move(metadata_changes),
+                                 {syncer::EntityChange::CreateDelete("guid")});
+  EXPECT_FALSE(error);
 }
 
 }  // namespace
diff --git a/components/send_tab_to_self/send_tab_to_self_entry.cc b/components/send_tab_to_self/send_tab_to_self_entry.cc
index 433c782..9e6c964 100644
--- a/components/send_tab_to_self/send_tab_to_self_entry.cc
+++ b/components/send_tab_to_self/send_tab_to_self_entry.cc
@@ -7,6 +7,7 @@
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "components/send_tab_to_self/proto/send_tab_to_self.pb.h"
 #include "components/sync/protocol/send_tab_to_self_specifics.pb.h"
 
 namespace send_tab_to_self {
@@ -69,9 +70,9 @@
   return device_name_;
 }
 
-std::unique_ptr<sync_pb::SendTabToSelfSpecifics> SendTabToSelfEntry::AsProto()
-    const {
-  auto pb_entry = std::make_unique<sync_pb::SendTabToSelfSpecifics>();
+SendTabToSelfLocal SendTabToSelfEntry::AsLocalProto() const {
+  SendTabToSelfLocal local_entry;
+  auto* pb_entry = local_entry.mutable_specifics();
 
   pb_entry->set_guid(GetGUID());
   pb_entry->set_title(GetTitle());
@@ -81,7 +82,7 @@
       TimeToProtoTime(GetOriginalNavigationTime()));
   pb_entry->set_device_name(GetDeviceName());
 
-  return pb_entry;
+  return local_entry;
 }
 
 std::unique_ptr<SendTabToSelfEntry> SendTabToSelfEntry::FromProto(
@@ -108,4 +109,10 @@
                                               pb_entry.device_name());
 }
 
+std::unique_ptr<SendTabToSelfEntry> SendTabToSelfEntry::FromLocalProto(
+    const SendTabToSelfLocal& local_entry,
+    base::Time now) {
+  return FromProto(local_entry.specifics(), now);
+}
+
 }  // namespace send_tab_to_self
diff --git a/components/send_tab_to_self/send_tab_to_self_entry.h b/components/send_tab_to_self/send_tab_to_self_entry.h
index ab0b98e..b8c9003 100644
--- a/components/send_tab_to_self/send_tab_to_self_entry.h
+++ b/components/send_tab_to_self/send_tab_to_self_entry.h
@@ -17,6 +17,8 @@
 
 namespace send_tab_to_self {
 
+class SendTabToSelfLocal;
+
 // A tab that is being shared. The URL is a unique identifier for an entry, as
 // such it should not be empty and is the only thing considered when comparing
 // entries.
@@ -49,8 +51,8 @@
   const std::string& GetDeviceName() const;
 
   // Returns a protobuf encoding the content of this SendTabToSelfEntry for
-  // sync.
-  std::unique_ptr<sync_pb::SendTabToSelfSpecifics> AsProto() const;
+  // local storage.
+  SendTabToSelfLocal AsLocalProto() const;
 
   // Creates a SendTabToSelfEntry from the protobuf format.
   // If creation time is not set, it will be set to |now|.
@@ -58,6 +60,12 @@
       const sync_pb::SendTabToSelfSpecifics& pb_entry,
       base::Time now);
 
+  // Creates a SendTabToSelfEntry from the protobuf format.
+  // If creation time is not set, it will be set to |now|.
+  static std::unique_ptr<SendTabToSelfEntry> FromLocalProto(
+      const SendTabToSelfLocal& pb_entry,
+      base::Time now);
+
  private:
   std::string guid_;
   GURL url_;
diff --git a/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc b/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
index c276a9d..a100601 100644
--- a/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
+++ b/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/test/simple_test_tick_clock.h"
+#include "components/send_tab_to_self/proto/send_tab_to_self.pb.h"
 #include "components/sync/protocol/send_tab_to_self_specifics.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -68,8 +69,8 @@
   SendTabToSelfEntry entry("1", GURL("http://example.com"), "bar",
                            base::Time::FromTimeT(10), base::Time::FromTimeT(10),
                            "device");
-  std::unique_ptr<sync_pb::SendTabToSelfSpecifics> pb_entry(entry.AsProto());
-  EXPECT_TRUE(IsEqualForTesting(entry, *pb_entry));
+  SendTabToSelfLocal pb_entry(entry.AsLocalProto());
+  EXPECT_TRUE(IsEqualForTesting(entry, pb_entry.specifics()));
 }
 
 // Tests that the send tab to self entry is correctly parsed from
diff --git a/components/send_tab_to_self/send_tab_to_self_sync_service.cc b/components/send_tab_to_self/send_tab_to_self_sync_service.cc
index f22b5d0..200ba05 100644
--- a/components/send_tab_to_self/send_tab_to_self_sync_service.cc
+++ b/components/send_tab_to_self/send_tab_to_self_sync_service.cc
@@ -10,18 +10,21 @@
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/device_info/local_device_info_provider.h"
+#include "components/sync/model/model_type_store.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 
 namespace send_tab_to_self {
 
 SendTabToSelfSyncService::SendTabToSelfSyncService(
     version_info::Channel channel,
-    syncer::LocalDeviceInfoProvider* local_device_info_provider) {
+    syncer::LocalDeviceInfoProvider* local_device_info_provider,
+    syncer::OnceModelTypeStoreFactory create_store_callback) {
   bridge_ = std::make_unique<send_tab_to_self::SendTabToSelfBridge>(
       std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
           syncer::SEND_TAB_TO_SELF,
           base::BindRepeating(&syncer::ReportUnrecoverableError, channel)),
-      local_device_info_provider, base::DefaultClock::GetInstance());
+      local_device_info_provider, base::DefaultClock::GetInstance(),
+      std::move(create_store_callback));
 }
 
 SendTabToSelfSyncService::~SendTabToSelfSyncService() = default;
diff --git a/components/send_tab_to_self/send_tab_to_self_sync_service.h b/components/send_tab_to_self/send_tab_to_self_sync_service.h
index 703eaed..18af7e1 100644
--- a/components/send_tab_to_self/send_tab_to_self_sync_service.h
+++ b/components/send_tab_to_self/send_tab_to_self_sync_service.h
@@ -10,6 +10,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/version_info/channel.h"
 
 namespace syncer {
@@ -26,7 +27,8 @@
  public:
   SendTabToSelfSyncService(
       version_info::Channel channel,
-      syncer::LocalDeviceInfoProvider* local_device_info_provider);
+      syncer::LocalDeviceInfoProvider* local_device_info_provider,
+      syncer::OnceModelTypeStoreFactory create_store_callback);
   ~SendTabToSelfSyncService() override;
 
   SendTabToSelfModel* GetSendTabToSelfModel();
diff --git a/components/sync/android/BUILD.gn b/components/sync/android/BUILD.gn
index e16c41d..37b8fc2 100644
--- a/components/sync/android/BUILD.gn
+++ b/components/sync/android/BUILD.gn
@@ -20,7 +20,7 @@
   java_files = [
     "java/src/org/chromium/components/sync/AndroidSyncSettings.java",
     "java/src/org/chromium/components/sync/ModelTypeHelper.java",
-    "java/src/org/chromium/components/sync/PassphraseType.java",
+    "java/src/org/chromium/components/sync/Passphrase.java",
     "java/src/org/chromium/components/sync/SyncConstants.java",
     "java/src/org/chromium/components/sync/SyncContentResolverDelegate.java",
     "java/src/org/chromium/components/sync/SystemSyncContentResolverDelegate.java",
diff --git a/components/sync/android/java/src/org/chromium/components/sync/Passphrase.java b/components/sync/android/java/src/org/chromium/components/sync/Passphrase.java
new file mode 100644
index 0000000..4cf01b11
--- /dev/null
+++ b/components/sync/android/java/src/org/chromium/components/sync/Passphrase.java
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.sync;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class describes the type of passphrase required, if any, to decrypt synced data.
+ * It maps the native enum PassphraseType.
+ */
+public class Passphrase {
+    @IntDef({Type.IMPLICIT, Type.KEYSTORE, Type.FROZEN_IMPLICIT, Type.CUSTOM})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {
+        // Update {@link ProfileSyncService#getPassphraseType()} after changing lowest value.
+        int IMPLICIT = 0; // GAIA-based passphrase (deprecated).
+        int KEYSTORE = 1; // Keystore passphrase.
+        int FROZEN_IMPLICIT = 2; // Frozen GAIA passphrase.
+        int CUSTOM = 3; // User-provided passphrase.
+        int NUM_ENTRIES = 4;
+    }
+
+    /**
+     * Get the types that are displayed for the current type.
+     */
+    public static List<Integer /* @Type */> getVisibleTypes(@Type int type) {
+        List<Integer /* @Type */> visibleTypes = new ArrayList<>();
+        switch (type) {
+            case Type.IMPLICIT: // Intentional fall through.
+            case Type.KEYSTORE:
+                visibleTypes.add(Type.CUSTOM);
+                visibleTypes.add(type);
+                break;
+            case Type.FROZEN_IMPLICIT: // Intentional fall through.
+            case Type.CUSTOM:
+                visibleTypes.add(type);
+                visibleTypes.add(Type.KEYSTORE);
+                break;
+            default:
+                assert false;
+        }
+        return visibleTypes;
+    }
+
+    /**
+     * Get the types that are allowed to be enabled from the current type.
+     * @param isEncryptEverythingAllowed Whether encrypting all data is allowed.
+     */
+    public static List<Integer /* @Type */> getAllowedTypes(
+            @Type int type, boolean isEncryptEverythingAllowed) {
+        List<Integer /* @Type */> allowedTypes = new ArrayList<>();
+        if (type == Type.IMPLICIT || type == Type.KEYSTORE) {
+            allowedTypes.add(type);
+            if (isEncryptEverythingAllowed) allowedTypes.add(Type.CUSTOM);
+        }
+        return allowedTypes;
+    }
+}
\ No newline at end of file
diff --git a/components/sync/android/java/src/org/chromium/components/sync/PassphraseType.java b/components/sync/android/java/src/org/chromium/components/sync/PassphraseType.java
deleted file mode 100644
index 0bc2063..0000000
--- a/components/sync/android/java/src/org/chromium/components/sync/PassphraseType.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.sync;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This enum describes the type of passphrase required, if any, to decrypt synced data.
- *
- * It implements the Android {@link Parcelable} interface so it is easy to pass around in intents.
- *
- * It maps the native enum PassphraseType.
- */
-public enum PassphraseType implements Parcelable {
-    IMPLICIT_PASSPHRASE(0), // GAIA-based passphrase (deprecated).
-    KEYSTORE_PASSPHRASE(1), // Keystore passphrase.
-    FROZEN_IMPLICIT_PASSPHRASE(2), // Frozen GAIA passphrase.
-    CUSTOM_PASSPHRASE(3); // User-provided passphrase.
-
-    public static final Parcelable.Creator<PassphraseType> CREATOR =
-            new Parcelable.Creator<PassphraseType>() {
-                @Override
-                public PassphraseType createFromParcel(Parcel parcel) {
-                    return fromInternalValue(parcel.readInt());
-                }
-
-                @Override
-                public PassphraseType[] newArray(int size) {
-                    return new PassphraseType[size];
-                }
-            };
-
-    public static PassphraseType fromInternalValue(int value) {
-        for (PassphraseType type : values()) {
-            if (type.internalValue() == value) {
-                return type;
-            }
-        }
-        throw new IllegalArgumentException("No value for " + value + " found.");
-    }
-
-    private final int mNativeValue;
-
-    private PassphraseType(int nativeValue) {
-        mNativeValue = nativeValue;
-    }
-
-    public Set<PassphraseType> getVisibleTypes() {
-        Set<PassphraseType> visibleTypes = new HashSet<>();
-        switch (this) {
-            case IMPLICIT_PASSPHRASE: // Intentional fall through.
-            case KEYSTORE_PASSPHRASE:
-                visibleTypes.add(this);
-                visibleTypes.add(CUSTOM_PASSPHRASE);
-                break;
-            case FROZEN_IMPLICIT_PASSPHRASE:
-                visibleTypes.add(KEYSTORE_PASSPHRASE);
-                visibleTypes.add(FROZEN_IMPLICIT_PASSPHRASE);
-                break;
-            case CUSTOM_PASSPHRASE:
-                visibleTypes.add(KEYSTORE_PASSPHRASE);
-                visibleTypes.add(CUSTOM_PASSPHRASE);
-                break;
-        }
-        return visibleTypes;
-    }
-
-    /**
-     * Get the types that are allowed to be enabled from the current type.
-     *
-     * @param isEncryptEverythingAllowed Whether encrypting all data is allowed.
-     */
-    public Set<PassphraseType> getAllowedTypes(boolean isEncryptEverythingAllowed) {
-        Set<PassphraseType> allowedTypes = new HashSet<>();
-        switch (this) {
-            case IMPLICIT_PASSPHRASE: // Intentional fall through.
-            case KEYSTORE_PASSPHRASE:
-                allowedTypes.add(this);
-                if (isEncryptEverythingAllowed) {
-                    allowedTypes.add(CUSTOM_PASSPHRASE);
-                }
-                break;
-            case FROZEN_IMPLICIT_PASSPHRASE: // Intentional fall through.
-            case CUSTOM_PASSPHRASE: // Intentional fall through.
-            default:
-                break;
-        }
-        return allowedTypes;
-    }
-
-    public int internalValue() {
-        // Since the values in this enums are constant and very small, this cast is safe.
-        return mNativeValue;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mNativeValue);
-    }
-}
diff --git a/components/translate/content/DEPS b/components/translate/content/DEPS
index 83c2ce4..c57486b 100644
--- a/components/translate/content/DEPS
+++ b/components/translate/content/DEPS
@@ -1,6 +1,4 @@
 include_rules = [
   "+content/public/common",
   "+mojo/public",
-  "+services/network/public",
-  "+third_party/blink/public",
 ]
diff --git a/components/translate/content/browser/content_translate_driver.cc b/components/translate/content/browser/content_translate_driver.cc
index cee7663b..7c91e182 100644
--- a/components/translate/content/browser/content_translate_driver.cc
+++ b/components/translate/content/browser/content_translate_driver.cc
@@ -4,9 +4,6 @@
 
 #include "components/translate/content/browser/content_translate_driver.h"
 
-#include <string>
-#include <utility>
-
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -16,7 +13,6 @@
 #include "components/search_engines/template_url_service.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/translate/core/browser/translate_manager.h"
-#include "components/translate/core/common/translate_util.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_details.h"
@@ -24,15 +20,11 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/page_navigator.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/common/referrer.h"
 #include "net/http/http_status_code.h"
-#include "services/network/public/mojom/network_context.mojom.h"
 #include "url/gurl.h"
 
-namespace translate {
-
 namespace {
 
 // The maximum number of attempts we'll do to see if the page has finshed
@@ -41,6 +33,8 @@
 
 }  // namespace
 
+namespace translate {
+
 ContentTranslateDriver::ContentTranslateDriver(
     content::NavigationController* nav_controller,
     const TemplateURLService* template_url_service,
@@ -113,23 +107,6 @@
     observer.OnIsPageTranslatedChanged(web_contents);
 }
 
-network::mojom::URLLoaderFactoryPtr
-ContentTranslateDriver::CreateURLLoaderFactory() {
-  // Find the renderer process that will need to use the URLLoaderFactory.
-  // Currently translate requests are only sent to the main frame process.
-  content::RenderProcessHost* process =
-      web_contents()->GetMainFrame()->GetProcess();
-
-  // Create a new URLLoaderFactory, locking the initiator origin to the one
-  // returned by GetTranslateSecurityOrigin.
-  network::mojom::URLLoaderFactoryPtr factory;
-  url::Origin origin = url::Origin::Create(GetTranslateSecurityOrigin());
-  network::mojom::TrustedURLLoaderHeaderClientPtrInfo null_header_client;
-  process->CreateURLLoaderFactory(origin, std::move(null_header_client),
-                                  mojo::MakeRequest(&factory));
-  return factory;
-}
-
 void ContentTranslateDriver::TranslatePage(int page_seq_no,
                                            const std::string& translate_script,
                                            const std::string& source_lang,
@@ -139,7 +116,7 @@
     return;  // This page has navigated away.
 
   it->second->Translate(
-      translate_script, CreateURLLoaderFactory(), source_lang, target_lang,
+      translate_script, source_lang, target_lang,
       base::BindOnce(&ContentTranslateDriver::OnPageTranslated,
                      base::Unretained(this)));
 }
diff --git a/components/translate/content/browser/content_translate_driver.h b/components/translate/content/browser/content_translate_driver.h
index f937d82..9b05c7e 100644
--- a/components/translate/content/browser/content_translate_driver.h
+++ b/components/translate/content/browser/content_translate_driver.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_TRANSLATE_CONTENT_BROWSER_CONTENT_TRANSLATE_DRIVER_H_
 
 #include <map>
-#include <string>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -16,7 +15,6 @@
 #include "components/translate/core/common/translate_errors.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
 
 namespace content {
 class NavigationController;
@@ -125,13 +123,6 @@
   bool IsDefaultSearchEngineOriginator(
       const url::Origin& originating_origin) const;
 
-  // Creates a URLLoaderFactory that may be used by the translate scripts that
-  // get injected into isolated worlds within the page to be translated.  Such
-  // scripts (or rather, their isolated worlds) are associated with a
-  // translate-specific origin like https://translate.googleapis.com and use
-  // this origin as |request_initiator| of http requests.
-  network::mojom::URLLoaderFactoryPtr CreateURLLoaderFactory();
-
   // The navigation controller of the tab we are associated with.
   content::NavigationController* navigation_controller_;
 
diff --git a/components/translate/content/common/BUILD.gn b/components/translate/content/common/BUILD.gn
index ae52137..a5b6a678 100644
--- a/components/translate/content/common/BUILD.gn
+++ b/components/translate/content/common/BUILD.gn
@@ -11,7 +11,6 @@
 
   public_deps = [
     "//mojo/public/mojom/base",
-    "//services/network/public/mojom:mojom",
     "//url/mojom:url_mojom_gurl",
   ]
 }
diff --git a/components/translate/content/common/translate.mojom b/components/translate/content/common/translate.mojom
index 0893def..7d09c8f 100644
--- a/components/translate/content/common/translate.mojom
+++ b/components/translate/content/common/translate.mojom
@@ -6,7 +6,6 @@
 
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/string16.mojom";
-import "services/network/public/mojom/url_loader_factory.mojom";
 import "url/mojom/url.mojom";
 
 enum TranslateError {
@@ -40,12 +39,6 @@
   // Requests that the page be translated from |source_lang| to
   // |target_lang|.
   //
-  // |loader_factory_for_translate_script| should provide a factory that the
-  // translation scripts can use for making fetch requests associated with the
-  // "translate" origin (the default factory's |request_initiator_site_lock| is
-  // not compatible with |request_initiator| of fetch requests initiated by the
-  // "translate" origin).
-  //
   // If a Translate request is already in progress with a matching
   // |target_lang|, this request will respond with |cancelled| set
   // to |true|.
@@ -56,10 +49,7 @@
   //
   // If |cancelled| is |true| all other response values should be
   // ignored.
-  Translate(string translate_script,
-            network.mojom.URLLoaderFactory loader_factory_for_translate_script,
-            string source_lang,
-            string target_lang)
+  Translate(string translate_script, string source_lang, string target_lang)
       => (bool cancelled, string original_lang, string translated_lang,
           TranslateError error);
 
diff --git a/components/translate/content/renderer/BUILD.gn b/components/translate/content/renderer/BUILD.gn
index 72a8873..d04d8d6 100644
--- a/components/translate/content/renderer/BUILD.gn
+++ b/components/translate/content/renderer/BUILD.gn
@@ -15,6 +15,8 @@
     "//components/translate/content/common",
     "//components/translate/core/common",
     "//components/translate/core/language_detection",
+    "//content/public/common",
+    "//content/public/renderer",
     "//services/service_manager/public/cpp",
     "//third_party/blink/public:blink",
     "//url",
@@ -22,8 +24,6 @@
   ]
 
   public_deps = [
-    "//content/public/common",
-    "//content/public/renderer",
     "//third_party/blink/public:blink_headers",
   ]
 }
diff --git a/components/translate/content/renderer/translate_helper.cc b/components/translate/content/renderer/translate_helper.cc
index 8b8a8a6..2cc44a6 100644
--- a/components/translate/content/renderer/translate_helper.cc
+++ b/components/translate/content/renderer/translate_helper.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
-#include "base/feature_list.h"
 #include "base/json/string_escape.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -26,9 +25,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
-#include "services/network/public/cpp/features.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/platform/web_isolated_world_info.h"
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_language_detection_details.h"
@@ -285,20 +282,10 @@
 }
 
 // mojom::Page implementations.
-void TranslateHelper::Translate(
-    const std::string& translate_script,
-    network::mojom::URLLoaderFactoryPtr loader_factory_for_translate_script,
-    const std::string& source_lang,
-    const std::string& target_lang,
-    TranslateCallback callback) {
-  url::Origin translate_origin =
-      url::Origin::Create(GetTranslateSecurityOrigin());
-
-  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    render_frame()->MarkInitiatorAsRequiringSeparateURLLoaderFactory(
-        translate_origin, std::move(loader_factory_for_translate_script));
-  }
-
+void TranslateHelper::Translate(const std::string& translate_script,
+                                const std::string& source_lang,
+                                const std::string& target_lang,
+                                TranslateCallback callback) {
   WebLocalFrame* main_frame = render_frame()->GetWebFrame();
   if (!main_frame) {
     // Cancelled.
@@ -335,7 +322,8 @@
   // Set up v8 isolated world with proper content-security-policy and
   // security-origin.
   blink::WebIsolatedWorldInfo info;
-  info.security_origin = WebSecurityOrigin::Create(translate_origin.GetURL());
+  info.security_origin =
+      WebSecurityOrigin::Create(GetTranslateSecurityOrigin());
   info.content_security_policy = WebString::FromUTF8(kContentSecurityPolicy);
   main_frame->SetIsolatedWorldInfo(world_id_, info);
 
diff --git a/components/translate/content/renderer/translate_helper.h b/components/translate/content/renderer/translate_helper.h
index 5dba2be..494985d0 100644
--- a/components/translate/content/renderer/translate_helper.h
+++ b/components/translate/content/renderer/translate_helper.h
@@ -17,7 +17,6 @@
 #include "components/translate/core/common/translate_errors.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "url/gurl.h"
 
 namespace blink {
@@ -45,12 +44,10 @@
   void PrepareForUrl(const GURL& url);
 
   // mojom::Page implementation.
-  void Translate(
-      const std::string& translate_script,
-      network::mojom::URLLoaderFactoryPtr loader_factory_for_translate_script,
-      const std::string& source_lang,
-      const std::string& target_lang,
-      TranslateCallback callback) override;
+  void Translate(const std::string& translate_script,
+                 const std::string& source_lang,
+                 const std::string& target_lang,
+                 TranslateCallback callback) override;
   void RevertTranslation() override;
 
  protected:
diff --git a/components/update_client/BUILD.gn b/components/update_client/BUILD.gn
index 3a24b18..06cb620 100644
--- a/components/update_client/BUILD.gn
+++ b/components/update_client/BUILD.gn
@@ -53,6 +53,7 @@
     "crx_downloader.cc",
     "crx_downloader.h",
     "crx_update_item.h",
+    "network.cc",
     "network.h",
     "persisted_data.cc",
     "persisted_data.h",
diff --git a/components/update_client/net/network_impl.cc b/components/update_client/net/network_impl.cc
index e7f4ece..bd97998 100644
--- a/components/update_client/net/network_impl.cc
+++ b/components/update_client/net/network_impl.cc
@@ -47,6 +47,37 @@
           }
         })");
 
+// Returns the string value of a header of the server response or an empty
+// string if the header is not available. Only the first header is returned
+// if multiple instances of the same header are present.
+std::string GetStringHeader(const network::SimpleURLLoader* simple_url_loader,
+                            const char* header_name) {
+  DCHECK(simple_url_loader);
+
+  const auto* response_info = simple_url_loader->ResponseInfo();
+  if (!response_info || !response_info->headers)
+    return {};
+
+  std::string header_value;
+  return response_info->headers->EnumerateHeader(nullptr, header_name,
+                                                 &header_value)
+             ? header_value
+             : std::string{};
+}
+
+// Returns the integral value of a header of the server response or -1 if
+// if the header is not available or a conversion error has occured.
+int64_t GetInt64Header(const network::SimpleURLLoader* simple_url_loader,
+                       const char* header_name) {
+  DCHECK(simple_url_loader);
+
+  const auto* response_info = simple_url_loader->ResponseInfo();
+  if (!response_info || !response_info->headers)
+    return -1;
+
+  return response_info->headers->GetInt64HeaderValue(header_name);
+}
+
 }  // namespace
 
 namespace update_client {
@@ -83,7 +114,17 @@
   constexpr size_t kMaxResponseSize = 1024 * 1024;
   simple_url_loader_->DownloadToString(
       shared_url_network_factory_.get(),
-      std::move(post_request_complete_callback), kMaxResponseSize);
+      base::BindOnce(
+          [](const network::SimpleURLLoader* simple_url_loader,
+             PostRequestCompleteCallback post_request_complete_callback,
+             std::unique_ptr<std::string> response_body) {
+            std::move(post_request_complete_callback)
+                .Run(std::move(response_body), simple_url_loader->NetError(),
+                     GetStringHeader(simple_url_loader, kHeaderEtag),
+                     GetInt64Header(simple_url_loader, kHeaderXRetryAfter));
+          },
+          simple_url_loader_.get(), std::move(post_request_complete_callback)),
+      kMaxResponseSize);
 }
 
 void NetworkFetcherImpl::DownloadToFile(
@@ -109,42 +150,17 @@
       std::move(response_started_callback)));
   simple_url_loader_->DownloadToFile(
       shared_url_network_factory_.get(),
-      std::move(download_to_file_complete_callback), file_path);
-}
-
-int64_t NetworkFetcherImpl::GetContentSize() const {
-  DCHECK(simple_url_loader_);
-  return simple_url_loader_->GetContentSize();
-}
-
-int NetworkFetcherImpl::NetError() const {
-  DCHECK(simple_url_loader_);
-  return simple_url_loader_->NetError();
-}
-
-std::string NetworkFetcherImpl::GetStringHeaderValue(
-    const char* header_name) const {
-  DCHECK(simple_url_loader_);
-
-  const auto* response_info = simple_url_loader_->ResponseInfo();
-  if (!response_info || !response_info->headers)
-    return {};
-
-  std::string header_value;
-  return response_info->headers->EnumerateHeader(nullptr, header_name,
-                                                 &header_value)
-             ? header_value
-             : std::string{};
-}
-
-int64_t NetworkFetcherImpl::GetInt64HeaderValue(const char* header_name) const {
-  DCHECK(simple_url_loader_);
-
-  const auto* response_info = simple_url_loader_->ResponseInfo();
-  if (!response_info || !response_info->headers)
-    return -1;
-
-  return response_info->headers->GetInt64HeaderValue(header_name);
+      base::BindOnce(
+          [](const network::SimpleURLLoader* simple_url_loader,
+             DownloadToFileCompleteCallback download_to_file_complete_callback,
+             base::FilePath file_path) {
+            std::move(download_to_file_complete_callback)
+                .Run(file_path, simple_url_loader->NetError(),
+                     simple_url_loader->GetContentSize());
+          },
+          simple_url_loader_.get(),
+          std::move(download_to_file_complete_callback)),
+      file_path);
 }
 
 void NetworkFetcherImpl::OnResponseStartedCallback(
diff --git a/components/update_client/net/network_impl.h b/components/update_client/net/network_impl.h
index 493af60b..c7f2b10 100644
--- a/components/update_client/net/network_impl.h
+++ b/components/update_client/net/network_impl.h
@@ -40,11 +40,6 @@
                       ResponseStartedCallback response_started_callback,
                       DownloadToFileCompleteCallback
                           download_to_file_complete_callback) override;
-  int NetError() const override;
-  std::string GetStringHeaderValue(const char* header_name) const override;
-  int64_t GetInt64HeaderValue(const char* header_name) const override;
-  int64_t GetContentSize() const override;
-
  private:
   void OnResponseStartedCallback(
       ResponseStartedCallback response_started_callback,
diff --git a/components/update_client/network.cc b/components/update_client/network.cc
new file mode 100644
index 0000000..cb8e151
--- /dev/null
+++ b/components/update_client/network.cc
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/network.h"
+
+namespace update_client {
+
+constexpr char NetworkFetcher::kHeaderEtag[];
+constexpr char NetworkFetcher::kHeaderXRetryAfter[];
+
+}  // namespace update_client
diff --git a/components/update_client/network.h b/components/update_client/network.h
index 7d871aa..c42d222 100644
--- a/components/update_client/network.h
+++ b/components/update_client/network.h
@@ -26,12 +26,27 @@
 class NetworkFetcher {
  public:
   using PostRequestCompleteCallback =
-      base::OnceCallback<void(std::unique_ptr<std::string> response_body)>;
-  using DownloadToFileCompleteCallback =
-      base::OnceCallback<void(base::FilePath path)>;
+      base::OnceCallback<void(std::unique_ptr<std::string> response_body,
+                              int net_error,
+                              const std::string& header_etag,
+                              int64_t xheader_retry_after_sec)>;
+  using DownloadToFileCompleteCallback = base::OnceCallback<
+      void(base::FilePath path, int net_error, int64_t content_size)>;
   using ResponseStartedCallback = base::OnceCallback<
       void(const GURL& final_url, int response_code, int64_t content_length)>;
 
+  // The ETag header carries the ECSDA signature of the POST response, if
+  // signing has been used.
+  static constexpr char kHeaderEtag[] = "ETag";
+
+  // The server uses the optional X-Retry-After header to indicate that the
+  // current request should not be attempted again.
+  //
+  // The value of the header is the number of seconds to wait before trying to
+  // do a subsequent update check. Only the values retrieved over HTTPS are
+  // trusted.
+  static constexpr char kHeaderXRetryAfter[] = "X-Retry-After";
+
   virtual ~NetworkFetcher() = default;
 
   virtual void PostRequest(
@@ -45,17 +60,6 @@
       const base::FilePath& file_path,
       ResponseStartedCallback response_started_callback,
       DownloadToFileCompleteCallback download_to_file_complete_callback) = 0;
-  virtual int NetError() const = 0;
-
-  // Returns the string value of a header of the server response or an empty
-  // string if the header is not available. Only the first header is returned
-  // if multiple instances of the same header are present.
-  virtual std::string GetStringHeaderValue(const char* header_name) const = 0;
-
-  // Returns the integral value of a header of the server response or -1 if
-  // if the header is not available or a conversion error has occured.
-  virtual int64_t GetInt64HeaderValue(const char* header_name) const = 0;
-  virtual int64_t GetContentSize() const = 0;
 
  protected:
   NetworkFetcher() = default;
diff --git a/components/update_client/request_sender.cc b/components/update_client/request_sender.cc
index 6747d39..ee911c9 100644
--- a/components/update_client/request_sender.cc
+++ b/components/update_client/request_sender.cc
@@ -4,13 +4,13 @@
 
 #include "components/update_client/request_sender.h"
 
-#include <algorithm>
 #include <utility>
 
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/client_update_protocol/ecdsa.h"
@@ -29,25 +29,6 @@
     "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsVwVMmIJaWBjktSx9m1JrZWYBvMm"
     "bsrGGQPhScDtao+DloD871YmEeunAaQvRMZgDh1nCaWkVG6wo75+yDbKDA==";
 
-// The ETag header carries the ECSDA signature of the protocol response, if
-// signing has been used.
-constexpr const char* kHeaderEtag = "ETag";
-
-// The server uses the optional X-Retry-After header to indicate that the
-// current request should not be attempted again. Any response received along
-// with the X-Retry-After header should be interpreted as it would have been
-// without the X-Retry-After header.
-//
-// In addition to the presence of the header, the value of the header is
-// used as a signal for when to do future update checks, but only when the
-// response is over https. Values over http are not trusted and are ignored.
-//
-// The value of the header is the number of seconds to wait before trying to do
-// a subsequent update check. The upper bound for the number of seconds to wait
-// before trying to do a subsequent update check is capped at 24 hours.
-constexpr const char* kHeaderXRetryAfter = "X-Retry-After";
-constexpr int64_t kMaxRetryAfterSec = 24 * 60 * 60;
-
 }  // namespace
 
 RequestSender::RequestSender(scoped_refptr<Configurator> config)
@@ -165,33 +146,33 @@
 
 void RequestSender::OnNetworkFetcherComplete(
     const GURL& original_url,
-    std::unique_ptr<std::string> response_body) {
+    std::unique_ptr<std::string> response_body,
+    int net_error,
+    const std::string& header_etag,
+    int64_t xheader_retry_after_sec) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   VLOG(1) << "request completed from url: " << original_url.spec();
 
-  int fetch_error = -1;
+  int error = -1;
   if (response_body && response_code_ == 200) {
-    fetch_error = 0;
+    DCHECK_EQ(0, net_error);
+    error = 0;
   } else if (response_code_ != -1) {
-    fetch_error = response_code_;
+    error = response_code_;
   } else {
-    fetch_error = network_fetcher_->NetError();
+    error = net_error;
   }
 
-  int64_t retry_after_sec(-1);
-  if (original_url.SchemeIsCryptographic() && fetch_error > 0) {
-    retry_after_sec = network_fetcher_->GetInt64HeaderValue(kHeaderXRetryAfter);
-    retry_after_sec = std::min(retry_after_sec, kMaxRetryAfterSec);
-  }
+  int retry_after_sec = -1;
+  if (original_url.SchemeIsCryptographic() && error > 0)
+    retry_after_sec = base::saturated_cast<int>(xheader_retry_after_sec);
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&RequestSender::SendInternalComplete,
-                     base::Unretained(this), fetch_error,
-                     response_body ? *response_body : std::string(),
-                     network_fetcher_->GetStringHeaderValue(kHeaderEtag),
-                     static_cast<int>(retry_after_sec)));
+      FROM_HERE, base::BindOnce(&RequestSender::SendInternalComplete,
+                                base::Unretained(this), error,
+                                response_body ? *response_body : std::string(),
+                                header_etag, retry_after_sec));
 }
 
 void RequestSender::HandleSendError(int error, int retry_after_sec) {
diff --git a/components/update_client/request_sender.h b/components/update_client/request_sender.h
index cc851d3e..7b9e21b 100644
--- a/components/update_client/request_sender.h
+++ b/components/update_client/request_sender.h
@@ -70,7 +70,10 @@
                          int64_t content_length);
 
   void OnNetworkFetcherComplete(const GURL& original_url,
-                                std::unique_ptr<std::string> response_body);
+                                std::unique_ptr<std::string> response_body,
+                                int net_error,
+                                const std::string& header_etag,
+                                int64_t xheader_retry_after_sec);
 
   // Implements the error handling and url fallback mechanism.
   void SendInternal();
diff --git a/components/update_client/update_engine.cc b/components/update_client/update_engine.cc
index 3340adc..a6d6920 100644
--- a/components/update_client/update_engine.cc
+++ b/components/update_client/update_engine.cc
@@ -4,6 +4,7 @@
 
 #include "components/update_client/update_engine.h"
 
+#include <algorithm>
 #include <memory>
 #include <utility>
 
@@ -199,12 +200,12 @@
 
   update_context->retry_after_sec = retry_after_sec;
 
-  const int throttle_sec(update_context->retry_after_sec);
-  DCHECK_LE(throttle_sec, 24 * 60 * 60);
-
   // Only positive values for throttle_sec are effective. 0 means that no
-  // throttling occurs and has the effect of resetting the member.
+  // throttling occurs and it resets |throttle_updates_until_|.
   // Negative values are not trusted and are ignored.
+  constexpr int kMaxRetryAfterSec = 24 * 60 * 60;  // 24 hours.
+  const int throttle_sec =
+      std::min(update_context->retry_after_sec, kMaxRetryAfterSec);
   if (throttle_sec >= 0) {
     throttle_updates_until_ =
         throttle_sec ? base::TimeTicks::Now() +
diff --git a/components/update_client/url_fetcher_downloader.cc b/components/update_client/url_fetcher_downloader.cc
index 898c112..b802e5f 100644
--- a/components/update_client/url_fetcher_downloader.cc
+++ b/components/update_client/url_fetcher_downloader.cc
@@ -90,7 +90,9 @@
   download_start_time_ = base::TimeTicks::Now();
 }
 
-void UrlFetcherDownloader::OnNetworkFetcherComplete(base::FilePath file_path) {
+void UrlFetcherDownloader::OnNetworkFetcherComplete(base::FilePath file_path,
+                                                    int net_error,
+                                                    int64_t content_size) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   const base::TimeTicks download_end_time(base::TimeTicks::Now());
@@ -102,41 +104,39 @@
   // Consider a 5xx response from the server as an indication to terminate
   // the request and avoid overloading the server in this case.
   // is not accepting requests for the moment.
-  int fetch_error = -1;
+  int error = -1;
   if (!file_path.empty() && response_code_ == 200) {
-    fetch_error = 0;
+    DCHECK_EQ(0, net_error);
+    error = 0;
   } else if (response_code_ != -1) {
-    fetch_error = response_code_;
+    error = response_code_;
   } else {
-    fetch_error = network_fetcher_->NetError();
+    error = net_error;
   }
 
-  const bool is_handled = fetch_error == 0 || IsHttpServerError(fetch_error);
+  const bool is_handled = error == 0 || IsHttpServerError(error);
 
   Result result;
-  result.error = fetch_error;
-  if (!fetch_error) {
+  result.error = error;
+  if (!error) {
     result.response = file_path;
   }
 
   DownloadMetrics download_metrics;
   download_metrics.url = url();
   download_metrics.downloader = DownloadMetrics::kUrlFetcher;
-  download_metrics.error = fetch_error;
+  download_metrics.error = error;
   // Tests expected -1, in case of failures and no content is available.
-  download_metrics.downloaded_bytes =
-      fetch_error && !network_fetcher_->GetContentSize()
-          ? -1
-          : network_fetcher_->GetContentSize();
+  download_metrics.downloaded_bytes = error ? -1 : content_size;
   download_metrics.total_bytes = total_bytes_;
   download_metrics.download_time_ms = download_time.InMilliseconds();
 
-  VLOG(1) << "Downloaded " << network_fetcher_->GetContentSize() << " bytes in "
+  VLOG(1) << "Downloaded " << content_size << " bytes in "
           << download_time.InMilliseconds() << "ms from " << final_url_.spec()
           << " to " << result.response.value();
 
   // Delete the download directory in the error cases.
-  if (fetch_error && !download_dir_.empty())
+  if (error && !download_dir_.empty())
     base::PostTaskWithTraits(
         FROM_HERE, kTaskTraits,
         base::BindOnce(IgnoreResult(&base::DeleteFile), download_dir_, true));
diff --git a/components/update_client/url_fetcher_downloader.h b/components/update_client/url_fetcher_downloader.h
index f35cbb3..da4dc170 100644
--- a/components/update_client/url_fetcher_downloader.h
+++ b/components/update_client/url_fetcher_downloader.h
@@ -35,7 +35,9 @@
 
   void CreateDownloadDir();
   void StartURLFetch(const GURL& url);
-  void OnNetworkFetcherComplete(base::FilePath file_path);
+  void OnNetworkFetcherComplete(base::FilePath file_path,
+                                int net_error,
+                                int64_t content_size);
   void OnResponseStarted(const GURL& final_url,
                          int response_code,
                          int64_t content_length);
diff --git a/components/url_pattern_index/fuzzy_pattern_matching.h b/components/url_pattern_index/fuzzy_pattern_matching.h
index 52dc75b..3feb7701 100644
--- a/components/url_pattern_index/fuzzy_pattern_matching.h
+++ b/components/url_pattern_index/fuzzy_pattern_matching.h
@@ -6,8 +6,10 @@
 // separator character, which is any ASCII symbol except letters, digits, and
 // the following: '_', '-', '.', '%'. Note that the separator placeholder
 // character '^' is itself a separator, as well as '\0'.
-// TODO(pkalinnikov): In addition, a separator placeholder at the end of the
-// pattern can be matched by the end of |text|.
+//
+// In addition, a separator placeholder at the end of the pattern can be matched
+// by the end of |text|. This should be handled by the clients using the
+// following utility functions.
 //
 // We define a fuzzy occurrence as an occurrence of a |subpattern| in |text|
 // such that all its non-placeholder characters are equal to the corresponding
diff --git a/components/url_pattern_index/url_pattern.cc b/components/url_pattern_index/url_pattern.cc
index a9762fa..81b80bfb 100644
--- a/components/url_pattern_index/url_pattern.cc
+++ b/components/url_pattern_index/url_pattern.cc
@@ -144,6 +144,80 @@
   return base::StringPiece::npos;
 }
 
+// Helper for DoesTextMatchLastSubpattern. Treats kSeparatorPlaceholder as not
+// matching the end of the text.
+bool DoesTextMatchLastSubpatternInternal(proto::AnchorType anchor_left,
+                                         proto::AnchorType anchor_right,
+                                         base::StringPiece text,
+                                         url::Component url_host,
+                                         base::StringPiece subpattern) {
+  // Enumerate all possible combinations of |anchor_left| and |anchor_right|.
+  if (anchor_left == proto::ANCHOR_TYPE_NONE &&
+      anchor_right == proto::ANCHOR_TYPE_NONE) {
+    return FindSubpattern(text, subpattern) != base::StringPiece::npos;
+  }
+
+  if (anchor_left == proto::ANCHOR_TYPE_NONE &&
+      anchor_right == proto::ANCHOR_TYPE_BOUNDARY) {
+    return EndsWithFuzzy(text, subpattern);
+  }
+
+  if (anchor_left == proto::ANCHOR_TYPE_BOUNDARY &&
+      anchor_right == proto::ANCHOR_TYPE_NONE) {
+    return StartsWithFuzzy(text, subpattern);
+  }
+
+  if (anchor_left == proto::ANCHOR_TYPE_BOUNDARY &&
+      anchor_right == proto::ANCHOR_TYPE_BOUNDARY) {
+    return text.size() == subpattern.size() &&
+           StartsWithFuzzy(text, subpattern);
+  }
+
+  if (anchor_left == proto::ANCHOR_TYPE_SUBDOMAIN &&
+      anchor_right == proto::ANCHOR_TYPE_NONE) {
+    return url_host.is_nonempty() &&
+           FindSubdomainAnchoredSubpattern(text, url_host, subpattern) !=
+               base::StringPiece::npos;
+  }
+
+  if (anchor_left == proto::ANCHOR_TYPE_SUBDOMAIN &&
+      anchor_right == proto::ANCHOR_TYPE_BOUNDARY) {
+    return url_host.is_nonempty() && text.size() >= subpattern.size() &&
+           IsSubdomainAnchored(text, url_host,
+                               text.size() - subpattern.size()) &&
+           EndsWithFuzzy(text, subpattern);
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+// Matches the last |subpattern| against |text|. Special treatment is required
+// for the last subpattern since |kSeparatorPlaceholder| can also match the end
+// of the text.
+bool DoesTextMatchLastSubpattern(proto::AnchorType anchor_left,
+                                 proto::AnchorType anchor_right,
+                                 base::StringPiece text,
+                                 url::Component url_host,
+                                 base::StringPiece subpattern) {
+  DCHECK(!subpattern.empty());
+
+  if (DoesTextMatchLastSubpatternInternal(anchor_left, anchor_right, text,
+                                          url_host, subpattern)) {
+    return true;
+  }
+
+  // If the last |subpattern| ends with kSeparatorPlaceholder, then it can also
+  // match the end of text.
+  if (subpattern.back() == kSeparatorPlaceholder) {
+    subpattern.remove_suffix(1);
+    return DoesTextMatchLastSubpatternInternal(
+        anchor_left, proto::ANCHOR_TYPE_BOUNDARY, text, url_host, subpattern);
+  }
+
+  return false;
+}
+
 // Returns whether the given |url_pattern| matches the given |url_spec|.
 // Compares the pattern the the url in a case-sensitive manner.
 bool IsCaseSensitiveMatch(base::StringPiece url_pattern,
@@ -157,6 +231,7 @@
   auto subpattern_it = subpatterns.begin();
   auto subpattern_end = subpatterns.end();
 
+  // No subpatterns.
   if (subpattern_it == subpattern_end) {
     return anchor_left == proto::ANCHOR_TYPE_NONE ||
            anchor_right == proto::ANCHOR_TYPE_NONE;
@@ -165,22 +240,10 @@
   base::StringPiece subpattern = *subpattern_it;
   ++subpattern_it;
 
-  // If there is only one |subpattern|, and it has a right anchor, then simply
-  // check that it is a suffix of the |url_spec|, and the left anchor is
-  // fulfilled.
-  if (subpattern_it == subpattern_end &&
-      anchor_right == proto::ANCHOR_TYPE_BOUNDARY) {
-    if (!EndsWithFuzzy(url_spec, subpattern))
-      return false;
-    if (anchor_left == proto::ANCHOR_TYPE_BOUNDARY)
-      return url_spec.size() == subpattern.size();
-    if (anchor_left == proto::ANCHOR_TYPE_SUBDOMAIN) {
-      DCHECK_LE(subpattern.size(), url_spec.size());
-      return url_host.is_nonempty() &&
-             IsSubdomainAnchored(url_spec, url_host,
-                                 url_spec.size() - subpattern.size());
-    }
-    return true;
+  // There is only one |subpattern|.
+  if (subpattern_it == subpattern_end) {
+    return DoesTextMatchLastSubpattern(anchor_left, anchor_right, url_spec,
+                                       url_host, subpattern);
   }
 
   // Otherwise, the first |subpattern| does not have to be a suffix. But it
@@ -189,10 +252,6 @@
   if (anchor_left == proto::ANCHOR_TYPE_BOUNDARY) {
     if (!StartsWithFuzzy(url_spec, subpattern))
       return false;
-    if (subpattern_it == subpattern_end) {
-      DCHECK_EQ(anchor_right, proto::ANCHOR_TYPE_NONE);
-      return true;
-    }
     text.remove_prefix(subpattern.size());
   } else if (anchor_left == proto::ANCHOR_TYPE_SUBDOMAIN) {
     if (!url_host.is_nonempty())
@@ -201,10 +260,6 @@
         FindSubdomainAnchoredSubpattern(url_spec, url_host, subpattern);
     if (match_begin == base::StringPiece::npos)
       return false;
-    if (subpattern_it == subpattern_end) {
-      DCHECK_EQ(anchor_right, proto::ANCHOR_TYPE_NONE);
-      return true;
-    }
     text.remove_prefix(match_begin + subpattern.size());
   } else {
     DCHECK_EQ(anchor_left, proto::ANCHOR_TYPE_NONE);
@@ -212,26 +267,24 @@
     subpattern_it = subpatterns.begin();
   }
 
-  // Consecutively find all the remaining subpatterns in the |text|. If the
-  // pattern has a right anchor, don't search for the last subpattern, but
-  // instead check that it is a suffix of the |text|.
-  while (subpattern_it != subpattern_end) {
-    subpattern = *subpattern_it;
-    DCHECK(!subpattern.empty());
+  DCHECK(subpattern_it != subpattern_end);
+  subpattern = *subpattern_it;
 
-    if (++subpattern_it == subpattern_end &&
-        anchor_right == proto::ANCHOR_TYPE_BOUNDARY) {
-      break;
-    }
+  // Consecutively find all the remaining subpatterns in the |text|. Handle the
+  // last subpattern outside the loop.
+  while (++subpattern_it != subpattern_end) {
+    DCHECK(!subpattern.empty());
 
     const size_t match_position = FindSubpattern(text, subpattern);
     if (match_position == base::StringPiece::npos)
       return false;
     text.remove_prefix(match_position + subpattern.size());
+
+    subpattern = *subpattern_it;
   }
 
-  return anchor_right != proto::ANCHOR_TYPE_BOUNDARY ||
-         EndsWithFuzzy(text, subpattern);
+  return DoesTextMatchLastSubpattern(proto::ANCHOR_TYPE_NONE, anchor_right,
+                                     text, url::Component(), subpattern);
 }
 
 }  // namespace
diff --git a/components/url_pattern_index/url_pattern_unittest.cc b/components/url_pattern_index/url_pattern_unittest.cc
index d067d12..950c907 100644
--- a/components/url_pattern_index/url_pattern_unittest.cc
+++ b/components/url_pattern_index/url_pattern_unittest.cc
@@ -90,7 +90,9 @@
 
       {{"^^a^^"}, "http://ex.com/?a=/", true},
       {{"^^a^^"}, "http://ex.com/?a=/&b=0", true},
-      {{"^^a^^"}, "http://ex.com/?a=", false},
+      {{"^^a^^"}, "http://ex.com/?a=x", false},
+      // The last ^ matches the end of the url.
+      {{"^^a^^"}, "http://ex.com/?a=", true},
 
       {{"ex.com^path^*k=v^"}, "http://ex.com/path/?k1=v1&ak=v&kk=vv", true},
       {{"ex.com^path^*k=v^"}, "http://ex.com/p/path/?k1=v1&ak=v&kk=vv", false},
@@ -152,6 +154,38 @@
       {{"abc*def^", proto::URL_PATTERN_TYPE_WILDCARDED, kDonotMatchCase},
        "http://a.com/abcxAdef/vo",
        true},
+      {{"abc^", kAnchorNone, kAnchorNone}, "https://xyz.com/abc/123", true},
+      {{"abc^", kAnchorNone, kAnchorNone}, "https://xyz.com/abc", true},
+      {{"abc^", kAnchorNone, kAnchorNone}, "https://abc.com", false},
+      {{"abc^", kAnchorNone, kBoundary}, "https://xyz.com/abc/", true},
+      {{"abc^", kAnchorNone, kBoundary}, "https://xyz.com/abc", true},
+      {{"abc^", kAnchorNone, kBoundary}, "https://xyz.com/abc/123", false},
+      {{"http://abc.com/x^", kBoundary, kAnchorNone}, "http://abc.com/x", true},
+      {{"http://abc.com/x^", kBoundary, kAnchorNone},
+       "http://abc.com/x/",
+       true},
+      {{"http://abc.com/x^", kBoundary, kAnchorNone},
+       "http://abc.com/x/123",
+       true},
+      {{"http://abc.com/x^", kBoundary, kBoundary}, "http://abc.com/x", true},
+      {{"http://abc.com/x^", kBoundary, kBoundary}, "http://abc.com/x/", true},
+      {{"http://abc.com/x^", kBoundary, kBoundary},
+       "http://abc.com/x/123",
+       false},
+      {{"abc.com^", kSubdomain, kAnchorNone}, "http://xyz.abc.com/123", true},
+      {{"abc.com^", kSubdomain, kAnchorNone}, "http://xyz.abc.com", true},
+      {{"abc.com^", kSubdomain, kAnchorNone},
+       "http://abc.com.xyz.com?q=abc.com",
+       false},
+      {{"abc.com^", kSubdomain, kBoundary}, "http://xyz.abc.com/123", false},
+      {{"abc.com^", kSubdomain, kBoundary}, "http://xyz.abc.com", true},
+      {{"abc.com^", kSubdomain, kBoundary},
+       "http://abc.com.xyz.com?q=abc.com/",
+       false},
+      {{"abc*^", kAnchorNone, kAnchorNone}, "https://abc.com", true},
+      {{"abc*^", kAnchorNone, kAnchorNone}, "https://abc.com?q=123", true},
+      {{"abc*^", kAnchorNone, kBoundary}, "https://abc.com", true},
+      {{"abc*^", kAnchorNone, kBoundary}, "https://abc.com?q=123", true},
   };
 
   for (const auto& test_case : kTestCases) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 778f9c2..57ef4d7 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -198,7 +198,6 @@
     "//ui/base",
     "//ui/base:buildflags",
     "//ui/base/clipboard",
-    "//ui/base/idle",
     "//ui/base/ime",
     "//ui/display",
     "//ui/display/types",
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index b56e5a95..7d9e0ac 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -924,6 +924,11 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityAriaSetCountsWithTreeLevels) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-set-counts-with-tree-levels.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
                        AccessibilityAriaSortOnHtmlTable) {
   RunAriaTest(FILE_PATH_LITERAL("aria-sort-html-table.html"));
 }
diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
index 80d4f29..df359f8c 100644
--- a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
+++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc
@@ -242,7 +242,7 @@
   if (!JavaBridgeThread::CurrentlyOn()) {
     JavaBridgeThread::GetTaskRunner()->PostTask(
         FROM_HERE,
-        base::Bind(
+        base::BindOnce(
             &GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection,
             this, allow));
     return;
diff --git a/content/browser/appcache/appcache_update_url_loader_request.cc b/content/browser/appcache/appcache_update_url_loader_request.cc
index d43a7cb..8bf8c6a 100644
--- a/content/browser/appcache/appcache_update_url_loader_request.cc
+++ b/content/browser/appcache/appcache_update_url_loader_request.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "content/browser/appcache/appcache_request_handler.h"
 #include "content/browser/appcache/appcache_update_url_fetcher.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_response_info.h"
 #include "net/url_request/url_request_context.h"
 
@@ -145,7 +146,7 @@
   http_response_info_->alpn_negotiated_protocol =
       response_head.alpn_negotiated_protocol;
   http_response_info_->connection_info = response_head.connection_info;
-  http_response_info_->socket_address = response_head.socket_address;
+  http_response_info_->remote_endpoint = response_head.remote_endpoint;
   fetcher_->OnResponseStarted(net::OK);
 }
 
diff --git a/content/browser/appcache/appcache_url_loader_job.cc b/content/browser/appcache/appcache_url_loader_job.cc
index c6603a6..c9b6bd9 100644
--- a/content/browser/appcache/appcache_url_loader_job.cc
+++ b/content/browser/appcache/appcache_url_loader_job.cc
@@ -12,6 +12,7 @@
 #include "content/browser/appcache/appcache_url_loader_request.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/public/common/resource_type.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/net_adapters.h"
 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
@@ -293,7 +294,7 @@
       is_range_request() ? range_response_info_->headers->GetContentLength()
                          : info_->response_data_size();
   response_head.connection_info = http_info.connection_info;
-  response_head.socket_address = http_info.socket_address;
+  response_head.remote_endpoint = http_info.remote_endpoint;
   response_head.was_fetched_via_spdy = http_info.was_fetched_via_spdy;
   response_head.was_alpn_negotiated = http_info.was_alpn_negotiated;
   response_head.alpn_negotiated_protocol = http_info.alpn_negotiated_protocol;
diff --git a/content/browser/appcache/appcache_url_request_job.cc b/content/browser/appcache/appcache_url_request_job.cc
index 158ef9e..fff66184 100644
--- a/content/browser/appcache/appcache_url_request_job.cc
+++ b/content/browser/appcache/appcache_url_request_job.cc
@@ -288,10 +288,10 @@
   return net::ERR_IO_PENDING;
 }
 
-net::HostPortPair AppCacheURLRequestJob::GetSocketAddress() const {
+net::IPEndPoint AppCacheURLRequestJob::GetResponseRemoteEndpoint() const {
   if (!http_info())
-    return net::HostPortPair();
-  return http_info()->socket_address;
+    return net::IPEndPoint();
+  return http_info()->remote_endpoint;
 }
 
 void AppCacheURLRequestJob::SetExtraRequestHeaders(
diff --git a/content/browser/appcache/appcache_url_request_job.h b/content/browser/appcache/appcache_url_request_job.h
index 67c3242..b6ed1e91 100644
--- a/content/browser/appcache/appcache_url_request_job.h
+++ b/content/browser/appcache/appcache_url_request_job.h
@@ -14,6 +14,7 @@
 #include "content/browser/appcache/appcache_job.h"
 #include "content/browser/appcache/appcache_storage.h"
 #include "content/common/content_export.h"
+#include "net/base/ip_endpoint.h"
 #include "net/url_request/url_request_job.h"
 
 namespace net {
@@ -100,7 +101,7 @@
   bool GetCharset(std::string* charset) override;
   void GetResponseInfo(net::HttpResponseInfo* info) override;
   int ReadRawData(net::IOBuffer* buf, int buf_size) override;
-  net::HostPortPair GetSocketAddress() const override;
+  net::IPEndPoint GetResponseRemoteEndpoint() const override;
 
   // Sets extra request headers for Job types that support request headers.
   // This is how we get informed of range-requests.
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 69d2ffae..8abb1b1 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -51,6 +51,8 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/cert/ct_policy_status.h"
@@ -1552,8 +1554,9 @@
       response->SetHeadersText(raw_info->response_headers_text);
   }
   response->SetProtocol(GetProtocol(url, info));
-  response->SetRemoteIPAddress(info.socket_address.HostForURL());
-  response->SetRemotePort(info.socket_address.port());
+  response->SetRemoteIPAddress(
+      net::HostPortPair::FromIPEndPoint(info.remote_endpoint).HostForURL());
+  response->SetRemotePort(info.remote_endpoint.port());
   if (info.ssl_info.has_value())
     response->SetSecurityDetails(BuildSecurityDetails(*info.ssl_info));
 
diff --git a/content/browser/dom_storage/session_storage_context_mojo.cc b/content/browser/dom_storage/session_storage_context_mojo.cc
index 2696fe9..4e1fdfa 100644
--- a/content/browser/dom_storage/session_storage_context_mojo.cc
+++ b/content/browser/dom_storage/session_storage_context_mojo.cc
@@ -152,8 +152,7 @@
   }
 
   PurgeUnusedAreasIfNeeded();
-  found->second->Bind(std::move(request), process_id);
-  std::move(bind_done).Run();
+  found->second->Bind(std::move(request), process_id, std::move(bind_done));
 
   size_t total_cache_size, unused_area_count;
   GetStatistics(&total_cache_size, &unused_area_count);
diff --git a/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc b/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc
index f163725c..5873c2a 100644
--- a/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc
+++ b/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc
@@ -96,17 +96,19 @@
 
 void SessionStorageNamespaceImplMojo::Bind(
     blink::mojom::SessionStorageNamespaceRequest request,
-    int process_id) {
+    int process_id,
+    base::OnceClosure bind_done) {
   if (waiting_on_clone_population_) {
     bind_waiting_on_clone_population_ = true;
-    run_after_clone_population_.push_back(
-        base::BindOnce(&SessionStorageNamespaceImplMojo::Bind,
-                       base::Unretained(this), std::move(request), process_id));
+    run_after_clone_population_.push_back(base::BindOnce(
+        &SessionStorageNamespaceImplMojo::Bind, base::Unretained(this),
+        std::move(request), process_id, std::move(bind_done)));
     return;
   }
   DCHECK(IsPopulated());
   bindings_.AddBinding(this, std::move(request), process_id);
   bind_waiting_on_clone_population_ = false;
+  std::move(bind_done).Run();
 }
 
 void SessionStorageNamespaceImplMojo::PurgeUnboundAreas() {
diff --git a/content/browser/dom_storage/session_storage_namespace_impl_mojo.h b/content/browser/dom_storage/session_storage_namespace_impl_mojo.h
index d0e2b12..1b285a8 100644
--- a/content/browser/dom_storage/session_storage_namespace_impl_mojo.h
+++ b/content/browser/dom_storage/session_storage_namespace_impl_mojo.h
@@ -122,7 +122,8 @@
   // eventually be called before the SessionStorageNamespaceRequest can be
   // bound.
   void Bind(blink::mojom::SessionStorageNamespaceRequest request,
-            int process_id);
+            int process_id,
+            base::OnceClosure bind_done);
 
   bool IsBound() const {
     return !bindings_.empty() || bind_waiting_on_clone_population_;
diff --git a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
index a29283d4..ab25a58 100644
--- a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
+++ b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
@@ -192,7 +192,8 @@
       &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace;
-  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
+  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1,
+                       base::DoNothing());
 
   blink::mojom::StorageAreaAssociatedPtr leveldb_1;
   ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
@@ -223,7 +224,8 @@
       &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace;
-  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
+  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1,
+                       base::DoNothing());
 
   blink::mojom::StorageAreaAssociatedPtr leveldb_1;
   ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
@@ -263,7 +265,7 @@
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace1;
   namespace_impl1->Bind(mojo::MakeRequest(&ss_namespace1),
-                        kTestProcessIdOrigin1);
+                        kTestProcessIdOrigin1, base::DoNothing());
   ss_namespace1->Clone(test_namespace_id2_);
   ss_namespace1.FlushForTesting();
 
@@ -271,7 +273,7 @@
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace2;
   namespace_impl2->Bind(mojo::MakeRequest(&ss_namespace2),
-                        kTestProcessIdOrigin1);
+                        kTestProcessIdOrigin1, base::DoNothing());
   blink::mojom::StorageAreaAssociatedPtr leveldb_2;
   ss_namespace2->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_2));
 
@@ -318,7 +320,7 @@
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace1;
   namespace_impl1->Bind(mojo::MakeRequest(&ss_namespace1),
-                        kTestProcessIdOrigin1);
+                        kTestProcessIdOrigin1, base::DoNothing());
 
   // Set that we are waiting for clone, so binding is possible.
   namespace_impl2->SetWaitingForClonePopulation();
@@ -329,7 +331,7 @@
   // Get a new area.
   blink::mojom::SessionStorageNamespacePtr ss_namespace2;
   namespace_impl2->Bind(mojo::MakeRequest(&ss_namespace2),
-                        kTestProcessIdAllOrigins);
+                        kTestProcessIdAllOrigins, base::DoNothing());
   blink::mojom::StorageAreaAssociatedPtr leveldb_n2_o1;
   blink::mojom::StorageAreaAssociatedPtr leveldb_n2_o2;
   ss_namespace2->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_n2_o1));
@@ -379,7 +381,8 @@
       &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace;
-  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
+  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1,
+                       base::DoNothing());
 
   blink::mojom::StorageAreaAssociatedPtr leveldb_1;
   ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
@@ -449,7 +452,8 @@
       &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace;
-  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
+  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1,
+                       base::DoNothing());
   blink::mojom::StorageAreaAssociatedPtr leveldb_1;
   ss_namespace->OpenArea(test_origin3_, mojo::MakeRequest(&leveldb_1));
   ss_namespace.FlushForTesting();
@@ -474,7 +478,8 @@
       &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace;
-  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
+  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1,
+                       base::DoNothing());
 
   blink::mojom::StorageAreaAssociatedPtr leveldb_1;
   ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
@@ -506,7 +511,7 @@
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace_o1;
   namespace_impl->Bind(mojo::MakeRequest(&ss_namespace_o1),
-                       kTestProcessIdOrigin1);
+                       kTestProcessIdOrigin1, base::DoNothing());
   blink::mojom::StorageAreaAssociatedPtr leveldb_1;
   ss_namespace_o1->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
   ss_namespace_o1.FlushForTesting();
@@ -518,7 +523,7 @@
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace_o2;
   namespace_impl->Bind(mojo::MakeRequest(&ss_namespace_o2),
-                       kTestProcessIdOrigin3);
+                       kTestProcessIdOrigin3, base::DoNothing());
   blink::mojom::StorageAreaAssociatedPtr leveldb_2;
   ss_namespace_o2->OpenArea(test_origin3_, mojo::MakeRequest(&leveldb_2));
   ss_namespace_o2.FlushForTesting();
@@ -547,7 +552,8 @@
       &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
 
   blink::mojom::SessionStorageNamespacePtr ss_namespace;
-  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
+  namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1,
+                       base::DoNothing());
 
   blink::mojom::StorageAreaAssociatedPtr leveldb_1;
   ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
diff --git a/content/browser/download/download_request_core.cc b/content/browser/download/download_request_core.cc
index 2a707e8b7a..0458f8d 100644
--- a/content/browser/download/download_request_core.cc
+++ b/content/browser/download/download_request_core.cc
@@ -36,6 +36,7 @@
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
@@ -224,8 +225,10 @@
       new download::DownloadCreateInfo(base::Time::Now(),
                                        std::move(save_info_)));
 
-  if (result == download::DOWNLOAD_INTERRUPT_REASON_NONE)
-    create_info->remote_address = request()->GetSocketAddress().host();
+  if (result == download::DOWNLOAD_INTERRUPT_REASON_NONE) {
+    create_info->remote_address =
+        request()->GetResponseRemoteEndpoint().ToStringWithoutPort();
+  }
   create_info->method = request()->method();
   create_info->connection_info = request()->response_info().connection_info;
   create_info->url_chain = request()->url_chain();
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 2770942..88da921 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -435,12 +435,12 @@
   return previous_url_;
 }
 
-net::HostPortPair NavigationHandleImpl::GetSocketAddress() {
+net::IPEndPoint NavigationHandleImpl::GetSocketAddress() {
   // This is CANCELING because although the data comes in after
   // WILL_PROCESS_RESPONSE, it's possible for the navigation to be cancelled
   // after and the caller might want this value.
   DCHECK(state_ >= CANCELING);
-  return socket_address_;
+  return remote_endpoint_;
 }
 
 void NavigationHandleImpl::Resume(NavigationThrottle* resuming_throttle) {
@@ -676,7 +676,7 @@
     RenderFrameHostImpl* render_frame_host,
     scoped_refptr<net::HttpResponseHeaders> response_headers,
     net::HttpResponseInfo::ConnectionInfo connection_info,
-    const net::HostPortPair& socket_address,
+    const net::IPEndPoint& remote_endpoint,
     const net::SSLInfo& ssl_info,
     const GlobalRequestID& request_id,
     bool is_download,
@@ -698,7 +698,7 @@
   was_cached_ = was_cached;
   state_ = PROCESSING_WILL_PROCESS_RESPONSE;
   ssl_info_ = ssl_info;
-  socket_address_ = socket_address;
+  remote_endpoint_ = remote_endpoint;
   complete_callback_ = std::move(callback);
 
   // Notify each throttle of the response.
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index ccf10021..297e1df 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -31,6 +31,7 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/navigation_type.h"
 #include "content/public/browser/restore_type.h"
+#include "net/base/ip_endpoint.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/platform/web_mixed_content_context_type.h"
 #include "url/gurl.h"
@@ -114,7 +115,7 @@
   bool DidReplaceEntry() override;
   bool ShouldUpdateHistory() override;
   const GURL& GetPreviousURL() override;
-  net::HostPortPair GetSocketAddress() override;
+  net::IPEndPoint GetSocketAddress() override;
   const net::HttpRequestHeaders& GetRequestHeaders() override;
   void RemoveRequestHeader(const std::string& header_name) override;
   void SetRequestHeader(const std::string& header_name,
@@ -270,7 +271,7 @@
       RenderFrameHostImpl* render_frame_host,
       scoped_refptr<net::HttpResponseHeaders> response_headers,
       net::HttpResponseInfo::ConnectionInfo connection_info,
-      const net::HostPortPair& socket_address,
+      const net::IPEndPoint& remote_endpoint,
       const net::SSLInfo& ssl_info,
       const GlobalRequestID& request_id,
       bool is_download,
@@ -531,7 +532,7 @@
 
   GURL previous_url_;
   GURL base_url_;
-  net::HostPortPair socket_address_;
+  net::IPEndPoint remote_endpoint_;
   NavigationType navigation_type_;
 
   // Used to inform a RenderProcessHost that we expect this navigation to commit
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index 9333980..ab6e174 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -148,7 +148,7 @@
     // in both cases.
     test_handle_->WillProcessResponse(
         main_test_rfh(), scoped_refptr<net::HttpResponseHeaders>(),
-        net::HttpResponseInfo::CONNECTION_INFO_QUIC_35, net::HostPortPair(),
+        net::HttpResponseInfo::CONNECTION_INFO_QUIC_35, net::IPEndPoint(),
         net::SSLInfo(), GlobalRequestID(), false, false, false, false,
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 20a2e540..08c67f5 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -63,6 +63,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/web_preferences.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -1236,7 +1237,7 @@
   // Check if the navigation should be allowed to proceed.
   navigation_handle_->WillProcessResponse(
       render_frame_host, response->head.headers.get(),
-      response->head.connection_info, response->head.socket_address, ssl_info_,
+      response->head.connection_info, response->head.remote_endpoint, ssl_info_,
       request_id, is_download_, is_stream,
       response->head.is_signed_exchange_inner_response,
       response->head.was_fetched_via_cache,
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 546efb0..1376c91 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -180,6 +180,7 @@
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/frame/frame_policy.h"
+#include "third_party/blink/public/mojom/frame/frame_host_test_interface.mojom.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
 #include "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
@@ -5069,13 +5070,7 @@
   if (!GetContentClient()->browser()->CanCommitURL(GetProcess(), url))
     return false;
 
-  // TODO(nasko): This check should be updated to apply to all URLs, not just
-  // standard ones.
-  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  if (url.IsStandard() &&
-      !policy->CanAccessDataForOrigin(GetProcess()->GetID(), url)) {
-    return false;
-  }
+  // TODO(nasko): Consider checking the |url| against CanAccessDataForOrigin.
 
   return true;
 }
@@ -5755,7 +5750,19 @@
 
 // This is a test-only interface, not exposed in production.
 void RenderFrameHostImpl::GetFrameHostTestInterface(
-    blink::mojom::FrameHostTestInterfaceRequest request) {}
+    blink::mojom::FrameHostTestInterfaceRequest request) {
+  class FrameHostTestInterfaceImpl
+      : public blink::mojom::FrameHostTestInterface {
+   public:
+    void Ping(const GURL& url, const std::string& event) override {}
+    void GetName(GetNameCallback callback) override {
+      std::move(callback).Run("RenderFrameHostImpl");
+    }
+  };
+
+  mojo::MakeStrongBinding(std::make_unique<FrameHostTestInterfaceImpl>(),
+                          std::move(request));
+}
 
 std::unique_ptr<NavigationRequest>
 RenderFrameHostImpl::TakeNavigationRequestForSameDocumentCommit(
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index cb41cda..fbb1b7c 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -32,6 +32,7 @@
 #include "content/public/common/navigation_policy.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/cert/symantec_certs.h"
 #include "net/http/http_response_headers.h"
@@ -85,7 +86,7 @@
   response->head.alpn_negotiated_protocol =
       response_info.alpn_negotiated_protocol;
   response->head.connection_info = response_info.connection_info;
-  response->head.socket_address = response_info.socket_address;
+  response->head.remote_endpoint = response_info.remote_endpoint;
   response->head.proxy_server = request->proxy_server();
   response->head.network_accessed = response_info.network_accessed;
   response->head.async_revalidation_requested =
diff --git a/content/browser/media/session/media_session_browsertest.cc b/content/browser/media/session/media_session_browsertest.cc
index a9b7b95..f91ea1c 100644
--- a/content/browser/media/session/media_session_browsertest.cc
+++ b/content/browser/media/session/media_session_browsertest.cc
@@ -5,6 +5,7 @@
 #include "content/public/browser/media_session.h"
 
 #include "base/command_line.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
@@ -16,19 +17,67 @@
 #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/http_request.h"
 #include "services/media_session/public/cpp/features.h"
 #include "services/media_session/public/cpp/test/audio_focus_test_util.h"
+#include "services/media_session/public/cpp/test/mock_media_session.h"
 
 namespace content {
 
 namespace {
 
+const char kMediaSessionImageTestURL[] = "/media/session/image_test_page.html";
+const char kMediaSessionImageTestPageVideoElement[] = "video";
+
+const char kMediaSessionTestImagePath[] = "/media/session/test_image.jpg";
+
+class MediaImageGetterHelper {
+ public:
+  MediaImageGetterHelper(content::MediaSession* media_session,
+                         const media_session::MediaImage& image,
+                         int min_size,
+                         int desired_size) {
+    media_session->GetMediaImageBitmap(
+        image, min_size, desired_size,
+        base::BindOnce(&MediaImageGetterHelper::OnComplete,
+                       base::Unretained(this)));
+  }
+
+  void Wait() {
+    if (bitmap_.has_value())
+      return;
+
+    run_loop_.Run();
+  }
+
+  const SkBitmap& bitmap() { return *bitmap_; }
+
+ private:
+  void OnComplete(const SkBitmap& bitmap) {
+    bitmap_ = bitmap;
+    run_loop_.Quit();
+  }
+
+  base::RunLoop run_loop_;
+  base::Optional<SkBitmap> bitmap_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaImageGetterHelper);
+};
+
 // Integration tests for content::MediaSession that do not take into
 // consideration the implementation details contrary to
 // MediaSessionImplBrowserTest.
 class MediaSessionBrowserTest : public ContentBrowserTest {
  public:
-  MediaSessionBrowserTest() = default;
+  MediaSessionBrowserTest() {
+    embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
+        &MediaSessionBrowserTest::OnServerRequest, base::Unretained(this)));
+  }
+
+  void SetUp() override {
+    ContentBrowserTest::SetUp();
+    visited_urls_.clear();
+  }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitchASCII(
@@ -79,6 +128,40 @@
     return result;
   }
 
+  bool WasURLVisited(const GURL& url) {
+    return base::ContainsKey(visited_urls_, url);
+  }
+
+  MediaSession* SetupMediaImageTest() {
+    NavigateToURL(shell(),
+                  embedded_test_server()->GetURL(kMediaSessionImageTestURL));
+    StartPlaybackAndWait(shell(), kMediaSessionImageTestPageVideoElement);
+
+    MediaSession* media_session = MediaSession::Get(shell()->web_contents());
+
+    std::vector<media_session::MediaImage> expected_images;
+    expected_images.push_back(CreateTestImageWithSize(1));
+    expected_images.push_back(CreateTestImageWithSize(10));
+
+    media_session::test::MockMediaSessionMojoObserver observer(*media_session);
+    observer.WaitForExpectedImagesOfType(
+        media_session::mojom::MediaSessionImageType::kArtwork, expected_images);
+
+    return media_session;
+  }
+
+  media_session::MediaImage CreateTestImageWithSize(int size) const {
+    media_session::MediaImage image;
+    image.src = GetTestImageURL();
+    image.type = base::ASCIIToUTF16("image/jpeg");
+    image.sizes.push_back(gfx::Size(size, size));
+    return image;
+  }
+
+  GURL GetTestImageURL() const {
+    return embedded_test_server()->GetURL(kMediaSessionTestImagePath);
+  }
+
  private:
   class MediaStartStopObserver : public WebContentsObserver {
    public:
@@ -114,6 +197,12 @@
     DISALLOW_COPY_AND_ASSIGN(MediaStartStopObserver);
   };
 
+  void OnServerRequest(const net::test_server::HttpRequest& request) {
+    visited_urls_.insert(request.GetURL());
+  }
+
+  std::set<GURL> visited_urls_;
+
   base::test::ScopedFeatureList disabled_feature_list_;
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -239,4 +328,80 @@
 }
 #endif  // defined(OS_ANDROID)
 
+IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, GetMediaImageBitmap) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  MediaSession* media_session = SetupMediaImageTest();
+  ASSERT_NE(nullptr, media_session);
+
+  media_session::MediaImage image;
+  image.src = embedded_test_server()->GetURL("/media/session/test_image.jpg");
+  image.type = base::ASCIIToUTF16("image/jpeg");
+  image.sizes.push_back(gfx::Size(1, 1));
+
+  MediaImageGetterHelper helper(media_session, CreateTestImageWithSize(1), 0,
+                                10);
+  helper.Wait();
+
+  // The test image is a 1x1 test image.
+  EXPECT_EQ(1, helper.bitmap().width());
+  EXPECT_EQ(1, helper.bitmap().height());
+  EXPECT_EQ(kRGBA_8888_SkColorType, helper.bitmap().colorType());
+
+  EXPECT_TRUE(WasURLVisited(GetTestImageURL()));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
+                       GetMediaImageBitmap_ImageTooSmall) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  MediaSession* media_session = SetupMediaImageTest();
+  ASSERT_NE(nullptr, media_session);
+
+  MediaImageGetterHelper helper(media_session, CreateTestImageWithSize(10), 10,
+                                10);
+  helper.Wait();
+
+  // The |image| is too small but we do not know that until after we have
+  // downloaded it. We should still receive a null image though.
+  EXPECT_TRUE(helper.bitmap().isNull());
+  EXPECT_TRUE(WasURLVisited(GetTestImageURL()));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
+                       GetMediaImageBitmap_ImageTooSmall_BeforeDownload) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  MediaSession* media_session = SetupMediaImageTest();
+  ASSERT_NE(nullptr, media_session);
+
+  MediaImageGetterHelper helper(media_session, CreateTestImageWithSize(1), 10,
+                                10);
+  helper.Wait();
+
+  // Since |image| is too small but we know this in advance we should not
+  // download it and instead we should receive a null image.
+  EXPECT_TRUE(helper.bitmap().isNull());
+  EXPECT_FALSE(WasURLVisited(GetTestImageURL()));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
+                       GetMediaImageBitmap_InvalidImage) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  MediaSession* media_session = SetupMediaImageTest();
+  ASSERT_NE(nullptr, media_session);
+
+  media_session::MediaImage image = CreateTestImageWithSize(1);
+  image.src = embedded_test_server()->GetURL("/blank.jpg");
+
+  MediaImageGetterHelper helper(media_session, image, 0, 10);
+  helper.Wait();
+
+  // Since |image| is not an image that is associated with the test page we
+  // should not download it and instead we should receive a null image.
+  EXPECT_TRUE(helper.bitmap().isNull());
+  EXPECT_FALSE(WasURLVisited(image.src));
+}
+
 }  // namespace content
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index 1342957..123dab9 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -24,6 +24,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 #include "media/base/media_content_type.h"
+#include "services/media_session/public/cpp/media_image_manager.h"
 #include "services/media_session/public/mojom/audio_focus.mojom.h"
 #include "third_party/blink/public/platform/modules/mediasession/media_session.mojom.h"
 
@@ -102,6 +103,10 @@
     vector.push_back(str);
 }
 
+bool IsSizeAtLeast(const gfx::Size& size, int min_size) {
+  return size.width() >= min_size || size.height() >= min_size;
+}
+
 }  // anonymous namespace
 
 MediaSessionImpl::PlayerIdentifier::PlayerIdentifier(
@@ -544,6 +549,43 @@
   AbandonSystemAudioFocusIfNeeded();
 }
 
+void MediaSessionImpl::OnImageDownloadComplete(
+    GetMediaImageBitmapCallback callback,
+    int minimum_size_px,
+    int desired_size_px,
+    int id,
+    int http_status_code,
+    const GURL& image_url,
+    const std::vector<SkBitmap>& bitmaps,
+    const std::vector<gfx::Size>& sizes) {
+  DCHECK(bitmaps.size() == sizes.size());
+  SkBitmap image;
+  double best_image_score = 0.0;
+
+  // Rank |sizes| and |bitmaps| using MediaImageManager.
+  for (size_t i = 0; i < bitmaps.size(); i++) {
+    double image_score = media_session::MediaImageManager::GetImageSizeScore(
+        minimum_size_px, desired_size_px, sizes.at(i));
+
+    if (image_score > best_image_score)
+      image = bitmaps.at(i);
+  }
+
+  // If the image is the wrong color type then we should convert it.
+  SkBitmap bitmap;
+  if (!image.isNull()) {
+    if (image.colorType() == kRGBA_8888_SkColorType) {
+      bitmap = image;
+    } else {
+      SkImageInfo info = image.info().makeColorType(kRGBA_8888_SkColorType);
+      if (bitmap.tryAllocPixels(info))
+        image.readPixels(info, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
+    }
+  }
+
+  std::move(callback).Run(bitmap);
+}
+
 void MediaSessionImpl::OnSystemAudioFocusRequested(bool result) {
   uma_helper_.RecordRequestAudioFocusResult(result);
   if (result)
@@ -790,6 +832,34 @@
   DidReceiveAction(media_session::mojom::MediaSessionAction::kSkipAd);
 }
 
+void MediaSessionImpl::GetMediaImageBitmap(
+    const media_session::MediaImage& image,
+    int minimum_size_px,
+    int desired_size_px,
+    GetMediaImageBitmapCallback callback) {
+  // We should make sure |image| is in |images_|.
+  bool found = false;
+  for (auto& image_type : images_)
+    found = found || base::ContainsValue(image_type.second, image);
+
+  // Check that |image.sizes| contains a size that is above the minimum size.
+  bool check_size = false;
+  for (auto& size : image.sizes)
+    check_size = check_size || IsSizeAtLeast(size, minimum_size_px);
+
+  if (!found || !check_size) {
+    std::move(callback).Run(SkBitmap());
+    return;
+  }
+
+  web_contents()->DownloadImage(
+      image.src, false, desired_size_px /* max_bitmap_size */,
+      false /* bypass_cache */,
+      base::BindOnce(&MediaSessionImpl::OnImageDownloadComplete,
+                     base::Unretained(this), std::move(callback),
+                     minimum_size_px, desired_size_px));
+}
+
 void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() {
   if (audio_focus_state_ == State::INACTIVE || !normal_players_.empty() ||
       !pepper_players_.empty() || !one_shot_players_.empty()) {
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index 66686b6..43cd7e8 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -236,6 +236,15 @@
   // Skip ad.
   CONTENT_EXPORT void SkipAd() override;
 
+  // Downloads the bitmap version of a MediaImage at least |minimum_size_px|
+  // and closest to |desired_size_px|. If the download failed, was too small or
+  // the image did not come from the media session then returns a null image.
+  CONTENT_EXPORT void GetMediaImageBitmap(
+      const media_session::MediaImage& image,
+      int minimum_size_px,
+      int desired_size_px,
+      GetMediaImageBitmapCallback callback) override;
+
   const base::UnguessableToken& audio_focus_group_id() const {
     return audio_focus_group_id_;
   }
@@ -284,6 +293,16 @@
 
   void Initialize();
 
+  // Called when we have finished downloading an image.
+  void OnImageDownloadComplete(GetMediaImageBitmapCallback callback,
+                               int minimum_size_px,
+                               int desired_size_px,
+                               int id,
+                               int http_status_code,
+                               const GURL& image_url,
+                               const std::vector<SkBitmap>& bitmaps,
+                               const std::vector<gfx::Size>& sizes);
+
   // Called when system audio focus has been requested and whether the request
   // was granted.
   void OnSystemAudioFocusRequested(bool result);
diff --git a/content/browser/renderer_host/render_view_host_browsertest.cc b/content/browser/renderer_host/render_view_host_browsertest.cc
index 50a50af..2367087 100644
--- a/content/browser/renderer_host/render_view_host_browsertest.cc
+++ b/content/browser/renderer_host/render_view_host_browsertest.cc
@@ -22,7 +22,7 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "net/base/filename_util.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 namespace content {
@@ -47,13 +47,13 @@
 
     NavigationHandleImpl* impl =
         static_cast<NavigationHandleImpl*>(navigation_handle);
-    observed_socket_address_ = navigation_handle->GetSocketAddress();
+    observed_remote_endpoint_ = navigation_handle->GetSocketAddress();
     base_url_ = impl->base_url();
     ++navigation_count_;
   }
 
-  const net::HostPortPair& observed_socket_address() const {
-    return observed_socket_address_;
+  const net::IPEndPoint& observed_remote_endpoint() const {
+    return observed_remote_endpoint_;
   }
 
   GURL base_url() const {
@@ -63,7 +63,7 @@
   int navigation_count() const { return navigation_count_; }
 
  private:
-  net::HostPortPair observed_socket_address_;
+  net::IPEndPoint observed_remote_endpoint_;
   GURL base_url_;
   int navigation_count_;
 
@@ -77,9 +77,9 @@
   GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
   NavigateToURL(shell(), test_url);
 
-  EXPECT_EQ(net::HostPortPair::FromURL(
-                embedded_test_server()->base_url()).ToString(),
-            observer.observed_socket_address().ToString());
+  EXPECT_EQ(
+      net::HostPortPair::FromURL(embedded_test_server()->base_url()),
+      net::HostPortPair::FromIPEndPoint(observer.observed_remote_endpoint()));
   EXPECT_EQ(1, observer.navigation_count());
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 96d1d48..763bfcc8 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -346,8 +346,11 @@
   }
 
   ~KeyEventResultTracker() {
-    if (is_async_ && async_callback_)
-      std::move(async_callback_).Run(false);
+    if (is_async_ && async_callback_) {
+      std::move(async_callback_)
+          .Run(/* handled */ false,
+               /* stopped_propagation */ false);
+    }
   }
 
   base::WeakPtr<KeyEventResultTracker> GetWeakPtr() {
@@ -371,10 +374,13 @@
   // Called when processing is complete. This may never be called, in which case
   // the destructor is responsible for updating the callback from the event.
   void OnEventProcessingDone(bool handled) {
-    if (is_async_ && async_callback_)
-      std::move(async_callback_).Run(handled);
-    else if (!is_async_ && handled)
+    if (is_async_ && async_callback_) {
+      // This supplies false for |stopped_propagation| so that InsertChar() gets
+      // called. Content never calls StopPropagation().
+      std::move(async_callback_).Run(handled, /* stopped_propagation */ false);
+    } else if (!is_async_ && handled) {
       key_event_->SetHandled();
+    }
   }
 
  private:
@@ -386,7 +392,7 @@
 
   // Callback from the event. This is obtained from |key_event_| if the event is
   // handled async.
-  base::OnceCallback<void(bool)> async_callback_;
+  base::OnceCallback<void(bool, bool)> async_callback_;
 
   base::WeakPtrFactory<KeyEventResultTracker> weak_factory_{this};
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 2fbfa3c0..4d93d58 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -5307,13 +5307,16 @@
   view_->InitAsChild(nullptr);
   view_->Show();
   bool async_callback_run = false;
-  bool async_callback_result = false;
+  bool async_callback_handled_result = false;
+  bool async_callback_stopped_propagation_result = false;
   ui::KeyEvent key_event1(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
   ui::KeyEvent::KeyDispatcherApi(&key_event1)
-      .set_async_callback(base::BindLambdaForTesting([&](bool handled) {
-        async_callback_result = handled;
-        async_callback_run = true;
-      }));
+      .set_async_callback(base::BindLambdaForTesting(
+          [&](bool handled, bool stopped_propagation) {
+            async_callback_handled_result = handled;
+            async_callback_stopped_propagation_result = stopped_propagation;
+            async_callback_run = true;
+          }));
   view_->OnKeyEvent(&key_event1);
   // Normally event should be handled.
   EXPECT_TRUE(key_event1.handled());
@@ -5330,20 +5333,25 @@
   EXPECT_EQ("RawKeyDown", GetMessageNames(events));
   events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
   EXPECT_TRUE(async_callback_run);
-  EXPECT_TRUE(async_callback_result);
+  EXPECT_TRUE(async_callback_handled_result);
+  // |async_callback_stopped_propagation_result| should always be false.
+  EXPECT_FALSE(async_callback_stopped_propagation_result);
 }
 
 TEST_F(RenderWidgetHostViewAuraTest, KeyEventAsyncUnhandled) {
   view_->InitAsChild(nullptr);
   view_->Show();
   bool async_callback_run = false;
-  bool async_callback_result = false;
+  bool async_callback_handled_result = false;
+  bool async_callback_stopped_propagation_result = false;
   ui::KeyEvent key_event1(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
   ui::KeyEvent::KeyDispatcherApi(&key_event1)
-      .set_async_callback(base::BindLambdaForTesting([&](bool handled) {
-        async_callback_result = handled;
-        async_callback_run = true;
-      }));
+      .set_async_callback(base::BindLambdaForTesting(
+          [&](bool handled, bool stopped_propagation) {
+            async_callback_handled_result = handled;
+            async_callback_stopped_propagation_result = stopped_propagation;
+            async_callback_run = true;
+          }));
   view_->OnKeyEvent(&key_event1);
   // Normally event should be handled.
   EXPECT_TRUE(key_event1.handled());
@@ -5360,20 +5368,25 @@
   EXPECT_EQ("RawKeyDown", GetMessageNames(events));
   events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
   EXPECT_TRUE(async_callback_run);
-  EXPECT_FALSE(async_callback_result);
+  EXPECT_FALSE(async_callback_handled_result);
+  // |async_callback_stopped_propagation_result| should always be false.
+  EXPECT_FALSE(async_callback_stopped_propagation_result);
 }
 
 TEST_F(RenderWidgetHostViewAuraTest, KeyEventAsyncNotifiedWhenRouterChanges) {
   view_->InitAsChild(nullptr);
   view_->Show();
   bool async_callback_run = false;
-  bool async_callback_result = false;
+  bool async_callback_handled_result = false;
+  bool async_callback_stopped_propagation_result = false;
   ui::KeyEvent key_event1(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
   ui::KeyEvent::KeyDispatcherApi(&key_event1)
-      .set_async_callback(base::BindLambdaForTesting([&](bool handled) {
-        async_callback_result = handled;
-        async_callback_run = true;
-      }));
+      .set_async_callback(base::BindLambdaForTesting(
+          [&](bool handled, bool stopped_propagation) {
+            async_callback_handled_result = handled;
+            async_callback_stopped_propagation_result = stopped_propagation;
+            async_callback_run = true;
+          }));
   view_->OnKeyEvent(&key_event1);
   // Normally event should be handled.
   EXPECT_TRUE(key_event1.handled());
@@ -5384,7 +5397,9 @@
   // RendererExited() should result in running the callback.
   widget_host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
   EXPECT_TRUE(async_callback_run);
-  EXPECT_FALSE(async_callback_result);
+  EXPECT_FALSE(async_callback_handled_result);
+  // |async_callback_stopped_propagation_result| should always be false.
+  EXPECT_FALSE(async_callback_stopped_propagation_result);
 
   // RendererExited() results in destroying the view.
   view_ = nullptr;
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 45f7063..83d0a79 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1284,59 +1284,4 @@
   EXPECT_EQ(bad_message::INVALID_INITIATOR_ORIGIN, kill_waiter.Wait());
 }
 
-namespace {
-
-// An interceptor class that allows replacing the URL of the commit IPC from
-// the renderer process to the browser process.
-class DidCommitUrlReplacer : public DidCommitProvisionalLoadInterceptor {
- public:
-  DidCommitUrlReplacer(WebContents* web_contents, const GURL& replacement_url)
-      : DidCommitProvisionalLoadInterceptor(web_contents),
-        replacement_url_(replacement_url) {}
-  ~DidCommitUrlReplacer() override = default;
-
- protected:
-  bool WillDispatchDidCommitProvisionalLoad(
-      RenderFrameHost* render_frame_host,
-      ::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
-      mojom::DidCommitProvisionalLoadInterfaceParamsPtr& interface_params)
-      override {
-    params->url = replacement_url_;
-    return true;
-  }
-
- private:
-  GURL replacement_url_;
-
-  DISALLOW_COPY_AND_ASSIGN(DidCommitUrlReplacer);
-};
-
-}  // namespace
-
-// Test which verifies that when an exploited renderer process sends a commit
-// message with URL that the process is not allowed to commit.
-IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, DidCommitInvalidURL) {
-  // Explicitly isolating foo.com helps ensure that this test is applicable on
-  // platforms without site-per-process.
-  IsolateOrigin("foo.com");
-
-  // Navigate to foo.com initially.
-  GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), foo_url));
-
-  // Create the interceoptor object which will replace the URL of the subsequent
-  // navigation with bar.com based URL.
-  GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title3.html"));
-  DidCommitUrlReplacer url_replacer(shell()->web_contents(), bar_url);
-
-  // Navigate to another URL within foo.com, which would usually be committed
-  // succsesfuly, but when the URL is modified it should result in the
-  // termination of the renderer process.
-  RenderProcessHostKillWaiter kill_waiter(
-      shell()->web_contents()->GetMainFrame()->GetProcess());
-  NavigateToURL(shell(),
-                embedded_test_server()->GetURL("foo.com", "/title2.html"));
-  EXPECT_EQ(bad_message::RFH_CAN_COMMIT_URL_BLOCKED, kill_waiter.Wait());
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_installed_script_loader.cc b/content/browser/service_worker/service_worker_installed_script_loader.cc
index d25fe43..2a6df6c4 100644
--- a/content/browser/service_worker/service_worker_installed_script_loader.cc
+++ b/content/browser/service_worker/service_worker_installed_script_loader.cc
@@ -14,6 +14,7 @@
 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/common/service_worker/service_worker_utils.h"
+#include "net/base/ip_endpoint.h"
 #include "net/cert/cert_status_flags.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
@@ -98,7 +99,7 @@
   head.was_alpn_negotiated = info->was_alpn_negotiated;
   head.connection_info = info->connection_info;
   head.alpn_negotiated_protocol = info->alpn_negotiated_protocol;
-  head.socket_address = info->socket_address;
+  head.remote_endpoint = info->remote_endpoint;
   head.cert_status = info->ssl_info.cert_status;
 
   if (options_ & network::mojom::kURLLoadOptionSendSSLInfoWithResponse)
diff --git a/content/browser/service_worker/service_worker_new_script_loader.cc b/content/browser/service_worker/service_worker_new_script_loader.cc
index 5a5475cb..9cc55e1 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.cc
+++ b/content/browser/service_worker/service_worker_new_script_loader.cc
@@ -17,6 +17,7 @@
 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/common/service_worker/service_worker_utils.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/cert/cert_status_flags.h"
 #include "services/network/public/cpp/resource_response.h"
@@ -189,7 +190,7 @@
   response_info->alpn_negotiated_protocol =
       response_head.alpn_negotiated_protocol;
   response_info->connection_info = response_head.connection_info;
-  response_info->socket_address = response_head.socket_address;
+  response_info->remote_endpoint = response_head.remote_endpoint;
   response_info->response_time = response_head.response_time;
 
   // The following sequence is equivalent to
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.cc b/content/browser/service_worker/service_worker_single_script_update_checker.cc
index 5851558d..946d38f 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.cc
@@ -9,6 +9,7 @@
 #include "content/browser/service_worker/service_worker_cache_writer.h"
 #include "content/public/common/resource_type.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
+#include "net/base/ip_endpoint.h"
 #include "services/network/public/cpp/net_adapters.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
@@ -123,7 +124,7 @@
   response_info->alpn_negotiated_protocol =
       response_head.alpn_negotiated_protocol;
   response_info->connection_info = response_head.connection_info;
-  response_info->socket_address = response_head.socket_address;
+  response_info->remote_endpoint = response_head.remote_endpoint;
 
   // TODO(momohatt): Check for header errors.
 
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 297fccb..3387960 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -4983,7 +4983,8 @@
 
   ~ContextMenuObserverDelegate() override {}
 
-  bool HandleContextMenu(const content::ContextMenuParams& params) override {
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override {
     context_menu_created_ = true;
     menu_params_ = params;
     message_loop_runner_->Quit();
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index 63639ea1..65ea05d 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -511,6 +511,10 @@
   }
 #endif
 
+  is_tracing_ = TracingControllerImpl::GetInstance()->StartTracing(
+      config, base::BindOnce(&BackgroundTracingManagerImpl::OnStartTracingDone,
+                             base::Unretained(this), preset));
+
   // Activate the categories immediately. StartTracing eventually does this
   // itself, but asynchronously via PostTask, and in the meantime events will be
   // dropped. This ensures that we start recording events for those categories
@@ -522,10 +526,6 @@
     base::trace_event::TraceLog::GetInstance()->SetEnabled(config, modes);
   }
 
-  is_tracing_ = TracingControllerImpl::GetInstance()->StartTracing(
-      config, base::BindOnce(&BackgroundTracingManagerImpl::OnStartTracingDone,
-                             base::Unretained(this), preset));
-
   RecordBackgroundTracingMetric(RECORDING_ENABLED);
 }
 
diff --git a/content/browser/tracing/cast_tracing_agent.cc b/content/browser/tracing/cast_tracing_agent.cc
index fa4ae658..770f64c 100644
--- a/content/browser/tracing/cast_tracing_agent.cc
+++ b/content/browser/tracing/cast_tracing_agent.cc
@@ -279,13 +279,14 @@
 
 // tracing::mojom::Agent. Called by Mojo internals on the UI thread.
 void CastTracingAgent::StartTracing(const std::string& config,
-                                    base::TimeTicks coordinator_time) {
+                                    base::TimeTicks coordinator_time,
+                                    Agent::StartTracingCallback callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!session_);
   session_ = std::make_unique<CastSystemTracingSession>(worker_task_runner_);
   session_->StartTracing(
       config, base::BindOnce(&CastTracingAgent::StartTracingCallbackProxy,
-                             base::Unretained(this)));
+                             base::Unretained(this), std::move(callback)));
 }
 
 void CastTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) {
@@ -305,10 +306,12 @@
 }
 
 void CastTracingAgent::StartTracingCallbackProxy(
+    Agent::StartTracingCallback callback,
     bool success) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (!success)
     session_.reset();
+  std::move(callback).Run(success);
 }
 
 void CastTracingAgent::HandleTraceData(chromecast::SystemTracer::Status status,
diff --git a/content/browser/tracing/cast_tracing_agent.h b/content/browser/tracing/cast_tracing_agent.h
index 8d59d1c..7b90f2a 100644
--- a/content/browser/tracing/cast_tracing_agent.h
+++ b/content/browser/tracing/cast_tracing_agent.h
@@ -33,10 +33,12 @@
 
   // tracing::mojom::Agent. Called by Mojo internals on the UI thread.
   void StartTracing(const std::string& config,
-                    base::TimeTicks coordinator_time) override;
+                    base::TimeTicks coordinator_time,
+                    Agent::StartTracingCallback callback) override;
   void StopAndFlush(tracing::mojom::RecorderPtr recorder) override;
 
-  void StartTracingCallbackProxy(bool success);
+  void StartTracingCallbackProxy(Agent::StartTracingCallback callback,
+                                 bool success);
   void HandleTraceData(chromecast::SystemTracer::Status status,
                        std::string trace_data);
 
diff --git a/content/browser/tracing/cros_tracing_agent.cc b/content/browser/tracing/cros_tracing_agent.cc
index 427f6d0..a28b7c56 100644
--- a/content/browser/tracing/cros_tracing_agent.cc
+++ b/content/browser/tracing/cros_tracing_agent.cc
@@ -227,12 +227,13 @@
 
 // tracing::mojom::Agent. Called by Mojo internals on the UI thread.
 void CrOSTracingAgent::StartTracing(const std::string& config,
-                                    base::TimeTicks coordinator_time) {
+                                    base::TimeTicks coordinator_time,
+                                    Agent::StartTracingCallback callback) {
   DCHECK(!session_);
   session_ = std::make_unique<CrOSSystemTracingSession>();
   session_->StartTracing(
       config, base::BindOnce(&CrOSTracingAgent::StartTracingCallbackProxy,
-                             base::Unretained(this)));
+                             base::Unretained(this), std::move(callback)));
 }
 
 void CrOSTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) {
@@ -245,9 +246,11 @@
 }
 
 void CrOSTracingAgent::StartTracingCallbackProxy(
+    Agent::StartTracingCallback callback,
     bool success) {
   if (!success)
     session_.reset();
+  std::move(callback).Run(success);
 }
 
 void CrOSTracingAgent::RecorderProxy(
diff --git a/content/browser/tracing/cros_tracing_agent.h b/content/browser/tracing/cros_tracing_agent.h
index 91ae13c..289db7c 100644
--- a/content/browser/tracing/cros_tracing_agent.h
+++ b/content/browser/tracing/cros_tracing_agent.h
@@ -32,10 +32,12 @@
 
   // tracing::mojom::Agent. Called by Mojo internals on the UI thread.
   void StartTracing(const std::string& config,
-                    base::TimeTicks coordinator_time) override;
+                    base::TimeTicks coordinator_time,
+                    Agent::StartTracingCallback callback) override;
   void StopAndFlush(tracing::mojom::RecorderPtr recorder) override;
 
-  void StartTracingCallbackProxy(bool success);
+  void StartTracingCallbackProxy(Agent::StartTracingCallback callback,
+                                 bool success);
   void RecorderProxy(const scoped_refptr<base::RefCountedString>& events);
 
   std::unique_ptr<CrOSSystemTracingSession> session_;
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 558c2c5..a78b1434 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -55,7 +55,7 @@
 #endif
 
 #if defined(OS_ANDROID)
-#include "base/debug/elf_reader_linux.h"
+#include "base/debug/elf_reader.h"
 
 // Symbol with virtual address of the start of ELF header of the current binary.
 extern char __ehdr_start;
@@ -143,22 +143,16 @@
 }
 
 TracingControllerImpl::TracingControllerImpl()
-    : delegate_(GetContentClient()->browser()->GetTracingDelegate()),
-      weak_ptr_factory_(this) {
+    : delegate_(GetContentClient()->browser()->GetTracingDelegate()) {
   DCHECK(!g_tracing_controller);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Deliberately leaked, like this class.
   base::FileTracing::SetProvider(new FileTracingProviderImpl);
   AddAgents();
-  base::trace_event::TraceLog::GetInstance()->AddAsyncEnabledStateObserver(
-      weak_ptr_factory_.GetWeakPtr());
   g_tracing_controller = this;
 }
 
-TracingControllerImpl::~TracingControllerImpl() {
-  base::trace_event::TraceLog::GetInstance()->RemoveAsyncEnabledStateObserver(
-      this);
-}
+TracingControllerImpl::~TracingControllerImpl() = default;
 
 void TracingControllerImpl::AddAgents() {
   tracing::TracedProcessImpl::GetInstance()->SetTaskRunner(
@@ -221,10 +215,10 @@
   // obtained from process maps since library can be mapped from apk directly.
   // This is not added as part of memory-infra os dumps since it is special case
   // only for chrome library.
-  base::Optional<std::string> soname =
+  base::Optional<base::StringPiece> soname =
       base::debug::ReadElfLibraryName(&__ehdr_start);
   if (soname)
-    metadata_dict->SetString("chrome-library-name", soname.value());
+    metadata_dict->SetString("chrome-library-name", *soname);
 #endif  // defined(OS_ANDROID)
   metadata_dict->SetInteger("chrome-bitness", 8 * sizeof(uintptr_t));
 
@@ -364,32 +358,20 @@
   trace_config_ =
       std::make_unique<base::trace_event::TraceConfig>(trace_config);
 
-  start_tracing_done_ = std::move(callback);
   ConnectToServiceIfNeeded();
-  coordinator_->StartTracing(trace_config.ToString());
-
-  if (start_tracing_done_ &&
-      (base::trace_event::TraceLog::GetInstance()->IsEnabled() ||
-       !trace_config.process_filter_config().IsEnabled(
-           base::Process::Current().Pid()))) {
-    // If we're already tracing, or if the current process is excluded from the
-    // process filter, we'll never receive a callback from the TraceLog, so then
-    // we just run the callback right away.
-    std::move(start_tracing_done_).Run();
-  }
-
+  coordinator_->StartTracing(
+      trace_config.ToString(),
+      base::BindOnce(
+          [](StartTracingDoneCallback callback, bool success) {
+            if (!callback.is_null())
+              std::move(callback).Run();
+          },
+          std::move(callback)));
   // TODO(chiniforooshan): The actual success value should be sent by the
   // callback asynchronously.
   return true;
 }
 
-void TracingControllerImpl::OnTraceLogEnabled() {
-  if (start_tracing_done_)
-    std::move(start_tracing_done_).Run();
-}
-
-void TracingControllerImpl::OnTraceLogDisabled() {}
-
 bool TracingControllerImpl::StopTracing(
     const scoped_refptr<TraceDataEndpoint>& trace_data_endpoint) {
   return StopTracing(std::move(trace_data_endpoint), "");
diff --git a/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h
index 5a6f9e6..dbb0b86 100644
--- a/content/browser/tracing/tracing_controller_impl.h
+++ b/content/browser/tracing/tracing_controller_impl.h
@@ -12,8 +12,6 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/trace_event/trace_log.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/tracing_controller.h"
 #include "mojo/public/cpp/system/data_pipe_drainer.h"
@@ -38,10 +36,8 @@
 class TracingDelegate;
 class TracingUI;
 
-class TracingControllerImpl
-    : public TracingController,
-      public mojo::DataPipeDrainer::Client,
-      public base::trace_event::TraceLog::AsyncEnabledStateObserver {
+class TracingControllerImpl : public TracingController,
+                              public mojo::DataPipeDrainer::Client {
  public:
   // Create an endpoint for dumping the trace data to a callback.
   CONTENT_EXPORT static scoped_refptr<TraceDataEndpoint> CreateCallbackEndpoint(
@@ -87,10 +83,6 @@
   void OnDataAvailable(const void* data, size_t num_bytes) override;
   void OnDataComplete() override;
 
-  // base::trace_event::TraceLog::AsyncEnabledStateObserver
-  void OnTraceLogEnabled() override;
-  void OnTraceLogDisabled() override;
-
   void OnMetadataAvailable(base::Value metadata);
 
   void CompleteFlush();
@@ -99,7 +91,6 @@
   std::vector<std::unique_ptr<tracing::BaseAgent>> agents_;
   std::unique_ptr<TracingDelegate> delegate_;
   std::unique_ptr<base::trace_event::TraceConfig> trace_config_;
-  StartTracingDoneCallback start_tracing_done_;
   std::unique_ptr<mojo::DataPipeDrainer> drainer_;
   scoped_refptr<TraceDataEndpoint> trace_data_endpoint_;
   std::unique_ptr<base::DictionaryValue> filtered_metadata_;
@@ -107,8 +98,6 @@
   bool is_data_complete_ = false;
   bool is_metadata_available_ = false;
 
-  // NOTE: Weak pointers must be invalidated before all other member variables.
-  base::WeakPtrFactory<TracingControllerImpl> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(TracingControllerImpl);
 };
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 20b3c89..69498d13 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -615,8 +615,7 @@
 #endif  // !defined(OS_ANDROID)
 
 #if defined(OS_ANDROID)
-  if (base::FeatureList::IsEnabled(features::kDisplayCutoutAPI))
-    display_cutout_host_impl_ = std::make_unique<DisplayCutoutHostImpl>(this);
+  display_cutout_host_impl_ = std::make_unique<DisplayCutoutHostImpl>(this);
 #endif
 
   registry_.AddInterface(base::BindRepeating(
@@ -5265,7 +5264,8 @@
 
   ContextMenuParams context_menu_params(params);
   // Allow WebContentsDelegates to handle the context menu operation first.
-  if (delegate_ && delegate_->HandleContextMenu(context_menu_params))
+  if (delegate_ &&
+      delegate_->HandleContextMenu(render_frame_host, context_menu_params))
     return;
 
   render_view_host_delegate_view_->ShowContextMenu(render_frame_host,
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index d8a9f7c..be9986ee 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -58,6 +58,7 @@
 #include "content/test/content_browser_test_utils_internal.h"
 #include "content/test/test_content_browser_client.h"
 #include "net/base/features.h"
+#include "net/base/ip_endpoint.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/controllable_http_response.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -711,9 +712,9 @@
       if (!first_network_request)
         EXPECT_GT(resource_load_info->request_id, 0);
       EXPECT_EQ(mime_type, resource_load_info->mime_type);
-      ASSERT_TRUE(resource_load_info->network_info->ip_port_pair);
-      EXPECT_EQ(ip_address,
-                resource_load_info->network_info->ip_port_pair->host());
+      ASSERT_TRUE(resource_load_info->network_info->remote_endpoint);
+      EXPECT_EQ(ip_address, resource_load_info->network_info->remote_endpoint
+                                ->ToStringWithoutPort());
       EXPECT_EQ(was_cached, resource_load_info->was_cached);
       // Simple sanity check of the load timing info.
       auto CheckTime = [before_request, after_request](auto actual) {
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index dfc5758b..19eab86 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -3364,4 +3364,28 @@
   EXPECT_TRUE(observer.observed_did_first_visually_non_empty_paint());
 }
 
+namespace {
+
+class MockWebContentsDelegate : public WebContentsDelegate {
+ public:
+  MOCK_METHOD2(HandleContextMenu,
+               bool(RenderFrameHost*, const ContextMenuParams&));
+};
+
+}  // namespace
+
+TEST_F(WebContentsImplTest, HandleContextMenuDelegate) {
+  MockWebContentsDelegate delegate;
+  contents()->SetDelegate(&delegate);
+
+  RenderFrameHost* rfh = main_test_rfh();
+  EXPECT_CALL(delegate, HandleContextMenu(rfh, ::testing::_))
+      .WillOnce(::testing::Return(true));
+
+  ContextMenuParams params;
+  contents()->ShowContextMenu(rfh, params);
+
+  contents()->SetDelegate(nullptr);
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc
index ab316c8..cf02399 100644
--- a/content/browser/web_contents/web_contents_view_android.cc
+++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -310,6 +310,7 @@
     RenderFrameHost* render_frame_host, const ContextMenuParams& params) {
   auto* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
       web_contents_->GetRenderWidgetHostView());
+
   // See if context menu is handled by SelectionController as a selection menu.
   // If not, use the delegate to show it.
   if (rwhv && rwhv->ShowSelectionMenu(params))
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index 7f4308b..08a0d43 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -255,12 +255,6 @@
 void WebContentsViewMac::ShowContextMenu(
     RenderFrameHost* render_frame_host,
     const ContextMenuParams& params) {
-  // Allow delegates to handle the context menu operation first.
-  if (web_contents_->GetDelegate() &&
-      web_contents_->GetDelegate()->HandleContextMenu(params)) {
-    return;
-  }
-
   if (delegate())
     delegate()->ShowContextMenu(render_frame_host, params);
   else
diff --git a/content/browser/web_package/signed_exchange_cert_fetcher.cc b/content/browser/web_package/signed_exchange_cert_fetcher.cc
index a4f67e2..d72a0ed8 100644
--- a/content/browser/web_package/signed_exchange_cert_fetcher.cc
+++ b/content/browser/web_package/signed_exchange_cert_fetcher.cc
@@ -21,6 +21,7 @@
 #include "content/public/common/url_loader_throttle.h"
 #include "ipc/ipc_message.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "services/network/loader_util.h"
@@ -218,9 +219,8 @@
                                                  resource_request_->url, head);
   }
 
-  if (reporter_) {
-    reporter_->set_cert_server_ip(head.socket_address.host());
-  }
+  if (reporter_)
+    reporter_->set_cert_server_ip_address(head.remote_endpoint.address());
 
   // |headers| is null when loading data URL.
   if (head.headers && head.headers->response_code() != net::HTTP_OK) {
diff --git a/content/browser/web_package/signed_exchange_reporter.cc b/content/browser/web_package/signed_exchange_reporter.cc
index f49859f..c1ec58c 100644
--- a/content/browser/web_package/signed_exchange_reporter.cc
+++ b/content/browser/web_package/signed_exchange_reporter.cc
@@ -11,6 +11,7 @@
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "content/public/common/content_features.h"
+#include "net/base/ip_endpoint.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/resource_response.h"
 
@@ -38,15 +39,15 @@
     base::OnceCallback<int(void)> frame_tree_node_id_getter)
     : outer_url_(outer_url),
       referrer_(referrer),
-      server_ip_(response.socket_address.host()),
+      server_ip_address_(response.remote_endpoint.address()),
       status_code_(response.headers ? response.headers->response_code() : 0),
       frame_tree_node_id_getter_(std::move(frame_tree_node_id_getter)) {}
 
 SignedExchangeReporter::~SignedExchangeReporter() = default;
 
-void SignedExchangeReporter::set_cert_server_ip(
-    const std::string& cert_server_ip) {
-  cert_server_ip_ = cert_server_ip;
+void SignedExchangeReporter::set_cert_server_ip_address(
+    const net::IPAddress& cert_server_ip_address) {
+  cert_server_ip_address_ = cert_server_ip_address;
 }
 
 void SignedExchangeReporter::set_inner_url(const GURL& inner_url) {
diff --git a/content/browser/web_package/signed_exchange_reporter.h b/content/browser/web_package/signed_exchange_reporter.h
index 4373e4e..84ea328 100644
--- a/content/browser/web_package/signed_exchange_reporter.h
+++ b/content/browser/web_package/signed_exchange_reporter.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "content/browser/web_package/signed_exchange_error.h"
 #include "content/common/content_export.h"
+#include "net/base/ip_address.h"
 #include "url/gurl.h"
 
 namespace network {
@@ -30,7 +31,7 @@
 
   ~SignedExchangeReporter();
 
-  void set_cert_server_ip(const std::string& cert_server_ip);
+  void set_cert_server_ip_address(const net::IPAddress& cert_server_ip_address);
   void set_inner_url(const GURL& inner_url);
   void set_cert_url(const GURL& cert_url);
 
@@ -45,10 +46,10 @@
 
   const GURL outer_url_;
   const std::string referrer_;
-  const std::string server_ip_;
+  const net::IPAddress server_ip_address_;
   const int status_code_;
   base::OnceCallback<int(void)> frame_tree_node_id_getter_;
-  std::string cert_server_ip_;
+  net::IPAddress cert_server_ip_address_;
   GURL inner_url_;
   GURL cert_url_;
 
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index d0eae9e..93e9548 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -141,13 +141,11 @@
       client->OnScriptLoadFailed();
       return;
     }
-    auto* storage_partition_impl = static_cast<StoragePartitionImpl*>(
-        render_process_host->GetStoragePartition());
 
     // Set up the default network loader factory.
     network::mojom::URLLoaderFactoryPtrInfo default_factory_info;
     CreateNetworkFactory(mojo::MakeRequest(&default_factory_info),
-                         storage_partition_impl->GetNetworkContext());
+                         render_process_host);
     subresource_loader_factories->default_factory_info() =
         std::move(default_factory_info);
 
@@ -196,16 +194,11 @@
   // |default_factory_connection_error_handler_holder_| for reference.
   // (https://crbug.com/906991)
   void CreateNetworkFactory(network::mojom::URLLoaderFactoryRequest request,
-                            network::mojom::NetworkContext* network_context) {
+                            RenderProcessHost* process) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    network::mojom::URLLoaderFactoryParamsPtr params =
-        network::mojom::URLLoaderFactoryParams::New();
-    params->process_id = process_id_;
-    // TODO(lukasza): https://crbug.com/792546: Start using CORB.
-    params->is_corb_enabled = false;
-
-    network_context->CreateURLLoaderFactory(std::move(request),
-                                            std::move(params));
+    network::mojom::TrustedURLLoaderHeaderClientPtrInfo no_header_client;
+    process->CreateURLLoaderFactory(origin_, std::move(no_header_client),
+                                    std::move(request));
   }
 
   void CreateWebUsbService(blink::mojom::WebUsbServiceRequest request) {
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index b3174c30..68951f4a 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -424,9 +424,8 @@
     WebRuntimeFeatures::EnablePageLifecycle(true);
 
 #if defined(OS_ANDROID)
-  if (base::FeatureList::IsEnabled(features::kDisplayCutoutAPI) &&
-      base::android::BuildInfo::GetInstance()->sdk_int() >=
-          base::android::SDK_VERSION_P) {
+  if (base::android::BuildInfo::GetInstance()->sdk_int() >=
+      base::android::SDK_VERSION_P) {
     // Display Cutout is limited to Android P+.
     WebRuntimeFeatures::EnableDisplayCutoutAPI(true);
   }
diff --git a/content/public/browser/media_session.h b/content/public/browser/media_session.h
index 725ef06..e8e0207 100644
--- a/content/public/browser/media_session.h
+++ b/content/public/browser/media_session.h
@@ -89,6 +89,14 @@
   // |type| represents the origin of the request.
   void Stop(SuspendType suspend_type) override = 0;
 
+  // Downloads the bitmap version of a MediaImage at least |minimum_size_px|
+  // and closest to |desired_size_px|. If the download failed, was too small or
+  // the image did not come from the media session then returns a null image.
+  void GetMediaImageBitmap(const media_session::MediaImage& image,
+                           int minimum_size_px,
+                           int desired_size_px,
+                           GetMediaImageBitmapCallback callback) override = 0;
+
  protected:
   MediaSession() = default;
 };
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index d20c820..beb8fe7f 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -14,7 +14,7 @@
 #include "content/public/browser/restore_type.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/transferrable_url_loader.mojom.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_info.h"
 #include "services/network/public/cpp/resource_request_body.h"
@@ -232,7 +232,7 @@
   virtual const GURL& GetPreviousURL() = 0;
 
   // Returns the remote address of the socket which fetched this resource.
-  virtual net::HostPortPair GetSocketAddress() = 0;
+  virtual net::IPEndPoint GetSocketAddress() = 0;
 
   // Returns the headers used for this request.
   virtual const net::HttpRequestHeaders& GetRequestHeaders() = 0;
diff --git a/content/public/browser/tracing_controller.h b/content/public/browser/tracing_controller.h
index 51151438..c2e3e98d 100644
--- a/content/public/browser/tracing_controller.h
+++ b/content/public/browser/tracing_controller.h
@@ -75,9 +75,8 @@
   // Tracing begins immediately locally, and asynchronously on child processes
   // as soon as they receive the StartTracing request.
   //
-  // Once tracing is enabled in the current process and trace events can be
-  // emitted (unless excluded from the config), StartTracingDoneCallback will
-  // be called back.
+  // Once all child processes have acked to the StartTracing request,
+  // StartTracingDoneCallback will be called back.
   //
   // |category_filter| is a filter to control what category groups should be
   // traced. A filter can have an optional '-' prefix to exclude category groups
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 0da8714..51bc80f 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -83,7 +83,8 @@
   callback.Run(true);
 }
 
-bool WebContentsDelegate::HandleContextMenu(const ContextMenuParams& params) {
+bool WebContentsDelegate::HandleContextMenu(RenderFrameHost* render_frame_host,
+                                            const ContextMenuParams& params) {
   return false;
 }
 
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 478e98a8..d1c1870 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -246,7 +246,8 @@
                            const base::Callback<void(bool)>& callback);
 
   // Returns true if the context menu operation was handled by the delegate.
-  virtual bool HandleContextMenu(const ContextMenuParams& params);
+  virtual bool HandleContextMenu(RenderFrameHost* render_frame_host,
+                                 const ContextMenuParams& params);
 
   // Allows delegates to handle keyboard events before sending to the renderer.
   // See enum for description of return values.
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 22adef2..cd52885 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -364,6 +364,7 @@
     ":resource_type_bindings",
     "//mojo/public/mojom/base:base",
     "//services/network/public/mojom",
+    "//services/network/public/mojom:mojom_ip_address",
     "//services/network/public/mojom:websocket_mojom",
     "//third_party/blink/public:mojo_bindings",
     "//ui/accessibility:ax_enums_mojo",
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 9c25da53..72987d3 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -687,11 +687,6 @@
 const base::Feature kAndroidAutofillAccessibility{
     "AndroidAutofillAccessibility", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enables developers to use the CSS safe-area-* and viewport-fit APIs which
-// allow them to support devices with a display cutout.
-const base::Feature kDisplayCutoutAPI{"DisplayCutoutAPI",
-                                      base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables hiding incorrectly-sized frames while in fullscreen.
 const base::Feature kHideIncorrectlySizedFullscreenFrames{
     "HideIncorrectlySizedFullscreenFrames", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 10e4aaf..8cb5719 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -156,7 +156,6 @@
 CONTENT_EXPORT extern const base::Feature kAndroidAutofillAccessibility;
 CONTENT_EXPORT extern const base::Feature
     kBackgroundMediaRendererHasModerateBinding;
-CONTENT_EXPORT extern const base::Feature kDisplayCutoutAPI;
 CONTENT_EXPORT extern const base::Feature kHideIncorrectlySizedFullscreenFrames;
 CONTENT_EXPORT extern const base::Feature kWebNfc;
 CONTENT_EXPORT extern const base::Feature kWebXrRenderPath;
diff --git a/content/public/common/profiling.cc b/content/public/common/profiling.cc
index fe6201c..d083588 100644
--- a/content/public/common/profiling.cc
+++ b/content/public/common/profiling.cc
@@ -65,7 +65,7 @@
     }
   }
   thread->task_runner()->PostDelayedTask(
-      FROM_HERE, base::Bind(&FlushProfilingData, thread),
+      FROM_HERE, base::BindOnce(&FlushProfilingData, thread),
       base::TimeDelta::FromSeconds(flush_seconds));
 }
 
@@ -80,8 +80,8 @@
       return;
     thread_ = new base::Thread("Profiling_Flush");
     thread_->Start();
-    thread_->task_runner()->PostTask(FROM_HERE,
-                                     base::Bind(&FlushProfilingData, thread_));
+    thread_->task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&FlushProfilingData, thread_));
   }
 
   void Stop() {
diff --git a/content/public/common/resource_load_info.mojom b/content/public/common/resource_load_info.mojom
index 3294653..6e17f0c 100644
--- a/content/public/common/resource_load_info.mojom
+++ b/content/public/common/resource_load_info.mojom
@@ -7,6 +7,7 @@
 import "content/public/common/load_timing_info.mojom";
 import "content/public/common/resource_type.mojom";
 import "services/network/public/mojom/ip_address.mojom";
+import "services/network/public/mojom/ip_endpoint.mojom";
 import "services/network/public/mojom/network_param.mojom";
 import "url/mojom/url.mojom";
 
@@ -20,7 +21,7 @@
   bool always_access_network;
 
   // The host IP and port of the response.
-  network.mojom.HostPortPair? ip_port_pair;
+  network.mojom.IPEndPoint? remote_endpoint;
 };
 
 // Information for a redirect.
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 59f14073..95179687 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -8,7 +8,6 @@
 #include <stddef.h>
 
 #include <memory>
-#include <string>
 
 #include "base/callback_forward.h"
 #include "base/single_thread_task_runner.h"
@@ -21,7 +20,6 @@
 #include "ppapi/buildflags/buildflags.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
-#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
@@ -296,16 +294,6 @@
   virtual void SetRenderFrameMediaPlaybackOptions(
       const RenderFrameMediaPlaybackOptions& opts) = 0;
 
-  // Requests that fetches initiated by |initiator_origin| should go through the
-  // provided |url_loader_factory|.  This method should be called before
-  // executing scripts in a isolated world - such scripts are typically
-  // associated with a security origin different from the main world (and
-  // therefore fetches from such scripts set |request_initiator| that is
-  // incompatible with |request_initiator_site_lock|.
-  virtual void MarkInitiatorAsRequiringSeparateURLLoaderFactory(
-      const url::Origin& initiator_origin,
-      network::mojom::URLLoaderFactoryPtr url_loader_factory) = 0;
-
  protected:
   ~RenderFrame() override {}
 
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index 7a43701..832b086 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -9,6 +9,7 @@
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
+#include "net/base/ip_endpoint.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "url/gurl.h"
 
@@ -66,7 +67,7 @@
   MOCK_METHOD0(DidReplaceEntry, bool());
   MOCK_METHOD0(ShouldUpdateHistory, bool());
   MOCK_METHOD0(GetPreviousURL, const GURL&());
-  MOCK_METHOD0(GetSocketAddress, net::HostPortPair());
+  MOCK_METHOD0(GetSocketAddress, net::IPEndPoint());
   const net::HttpRequestHeaders& GetRequestHeaders() override {
     return request_headers_;
   }
diff --git a/content/public/test/navigation_simulator.h b/content/public/test/navigation_simulator.h
index 124e687..ee42a3e 100644
--- a/content/public/test/navigation_simulator.h
+++ b/content/public/test/navigation_simulator.h
@@ -17,7 +17,7 @@
 class GURL;
 
 namespace net {
-class HostPortPair;
+class IPEndPoint;
 class HttpResponseHeaders;
 }  // namespace net
 
@@ -252,7 +252,7 @@
 
   // The following parameters can change at any point until the page fails or
   // commits. They should be specified before calling |Fail| or |Commit|.
-  virtual void SetSocketAddress(const net::HostPortPair& socket_address) = 0;
+  virtual void SetSocketAddress(const net::IPEndPoint& remote_endpoint) = 0;
 
   // Pretend the navigation is against an inner response of a signed exchange.
   virtual void SetIsSignedExchangeInnerResponse(
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index 13288f8..811c170 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -571,7 +571,9 @@
   delegate_->WillBeginCompositorFrame();
 }
 
-void LayerTreeView::DidBeginMainFrame() {}
+void LayerTreeView::DidBeginMainFrame() {
+  delegate_->DidBeginMainFrame();
+}
 
 void LayerTreeView::DidUpdateLayers() {
   // Dump property trees and layers if run with:
diff --git a/content/renderer/compositor/layer_tree_view_delegate.h b/content/renderer/compositor/layer_tree_view_delegate.h
index a5d2d82..c219245 100644
--- a/content/renderer/compositor/layer_tree_view_delegate.h
+++ b/content/renderer/compositor/layer_tree_view_delegate.h
@@ -56,6 +56,10 @@
   // Notifies that the compositor has issued a BeginMainFrame.
   virtual void BeginMainFrame(base::TimeTicks frame_time) = 0;
 
+  // Notifies that the layer tree host has completed a call to
+  // RequestMainFrameUpdate in response to a BeginMainFrame.
+  virtual void DidBeginMainFrame() = 0;
+
   // Requests a LayerTreeFrameSink to submit CompositorFrames to.
   virtual void RequestNewLayerTreeFrameSink(
       LayerTreeFrameSinkCallback callback) = 0;
diff --git a/content/renderer/loader/resource_load_stats.cc b/content/renderer/loader/resource_load_stats.cc
index e8122cd9..79bf325 100644
--- a/content/renderer/loader/resource_load_stats.cc
+++ b/content/renderer/loader/resource_load_stats.cc
@@ -10,6 +10,7 @@
 #include "content/public/common/resource_load_info.mojom.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_thread_impl.h"
+#include "net/base/ip_endpoint.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
@@ -131,8 +132,8 @@
       redirect_response.network_accessed;
   net_redirect_info->network_info->always_access_network =
       AlwaysAccessNetwork(redirect_response.headers);
-  net_redirect_info->network_info->ip_port_pair =
-      redirect_response.socket_address;
+  net_redirect_info->network_info->remote_endpoint =
+      redirect_response.remote_endpoint;
   resource_load_info->redirect_info_chain.push_back(
       std::move(net_redirect_info));
 }
@@ -159,7 +160,8 @@
       response_head.network_accessed;
   resource_load_info->network_info->always_access_network =
       AlwaysAccessNetwork(response_head.headers);
-  resource_load_info->network_info->ip_port_pair = response_head.socket_address;
+  resource_load_info->network_info->remote_endpoint =
+      response_head.remote_endpoint;
 
   auto task_runner = RenderThreadImpl::DeprecatedGetMainTaskRunner();
   if (!task_runner)
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index ec9e4e8..08b0d6e 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -42,6 +42,8 @@
 #include "content/renderer/loader/web_url_request_util.h"
 #include "net/base/data_url.h"
 #include "net/base/filename_util.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/cert/cert_status_flags.h"
@@ -1182,9 +1184,9 @@
   response->SetWasCached(!info.load_timing.request_start_time.is_null() &&
                          info.response_time <
                              info.load_timing.request_start_time);
-  response->SetRemoteIPAddress(
-      WebString::FromUTF8(info.socket_address.HostForURL()));
-  response->SetRemotePort(info.socket_address.port());
+  response->SetRemoteIPAddress(WebString::FromUTF8(
+      net::HostPortPair::FromIPEndPoint(info.remote_endpoint).HostForURL()));
+  response->SetRemotePort(info.remote_endpoint.port());
   response->SetConnectionID(info.load_timing.socket_log_id);
   response->SetConnectionReused(info.load_timing.socket_reused);
   response->SetWasFetchedViaSPDY(info.was_fetched_via_spdy);
diff --git a/content/renderer/loader/web_url_loader_impl_unittest.cc b/content/renderer/loader/web_url_loader_impl_unittest.cc
index 74b75758d..28b97e7 100644
--- a/content/renderer/loader/web_url_loader_impl_unittest.cc
+++ b/content/renderer/loader/web_url_loader_impl_unittest.cc
@@ -28,6 +28,7 @@
 #include "content/renderer/loader/sync_load_response.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/cert/x509_util.h"
 #include "net/http/http_response_headers.h"
@@ -560,15 +561,17 @@
       {"123.123.123.123", "123.123.123.123"},
       {"::1", "[::1]"},
       {"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
-       "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"},
-      {"2001:db8:85a3:0:0:8a2e:370:7334", "[2001:db8:85a3:0:0:8a2e:370:7334]"},
+       "[2001:db8:85a3::8a2e:370:7334]"},
+      {"2001:db8:85a3:0:0:8a2e:370:7334", "[2001:db8:85a3::8a2e:370:7334]"},
       {"2001:db8:85a3::8a2e:370:7334", "[2001:db8:85a3::8a2e:370:7334]"},
-      {"::ffff:192.0.2.128", "[::ffff:192.0.2.128]"}};
+      {"::ffff:192.0.2.128", "[::ffff:c000:280]"}};
 
   for (const auto& test : cases) {
     SCOPED_TRACE(test.ip);
     network::ResourceResponseInfo info;
-    info.socket_address = net::HostPortPair(test.ip, 443);
+    net::IPAddress address;
+    ASSERT_TRUE(address.AssignFromIPLiteral(test.ip));
+    info.remote_endpoint = net::IPEndPoint(address, 443);
     blink::WebURLResponse response;
     WebURLLoaderImpl::PopulateURLResponse(url, info, &response, true, -1);
     EXPECT_EQ(test.expected, response.RemoteIPAddress().Utf8());
diff --git a/content/renderer/media/android/stream_texture_factory.cc b/content/renderer/media/android/stream_texture_factory.cc
index 3500555..cfcb963 100644
--- a/content/renderer/media/android/stream_texture_factory.cc
+++ b/content/renderer/media/android/stream_texture_factory.cc
@@ -56,8 +56,9 @@
   }
   // Unretained is safe here only because the object is deleted on |loop_|
   // thread.
-  task_runner->PostTask(FROM_HERE, base::Bind(&StreamTextureProxy::BindOnThread,
-                                              base::Unretained(this)));
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(&StreamTextureProxy::BindOnThread,
+                                       base::Unretained(this)));
 }
 
 void StreamTextureProxy::BindOnThread() {
diff --git a/content/renderer/media/android/stream_texture_wrapper_impl.cc b/content/renderer/media/android/stream_texture_wrapper_impl.cc
index 7505e87..3319214 100644
--- a/content/renderer/media/android/stream_texture_wrapper_impl.cc
+++ b/content/renderer/media/android/stream_texture_wrapper_impl.cc
@@ -127,8 +127,8 @@
 
   if (!main_task_runner_->BelongsToCurrentThread()) {
     main_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&StreamTextureWrapperImpl::UpdateTextureSize,
-                              weak_factory_.GetWeakPtr(), new_size));
+        FROM_HERE, base::BindOnce(&StreamTextureWrapperImpl::UpdateTextureSize,
+                                  weak_factory_.GetWeakPtr(), new_size));
     return;
   }
 
@@ -156,9 +156,10 @@
   natural_size_ = natural_size;
 
   main_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&StreamTextureWrapperImpl::InitializeOnMainThread,
-                            weak_factory_.GetWeakPtr(), received_frame_cb,
-                            media::BindToCurrentLoop(init_cb)));
+      FROM_HERE,
+      base::BindOnce(&StreamTextureWrapperImpl::InitializeOnMainThread,
+                     weak_factory_.GetWeakPtr(), received_frame_cb,
+                     media::BindToCurrentLoop(init_cb)));
 }
 
 void StreamTextureWrapperImpl::InitializeOnMainThread(
@@ -187,8 +188,8 @@
     // base::Unretained is safe here because this function is the only one that
     // can call delete.
     main_task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&StreamTextureWrapperImpl::Destroy, base::Unretained(this)));
+        FROM_HERE, base::BindOnce(&StreamTextureWrapperImpl::Destroy,
+                                  base::Unretained(this)));
     return;
   }
 
diff --git a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
index ff923ef..79b968b 100644
--- a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
+++ b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.cc
@@ -43,6 +43,19 @@
   return 0;
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+FakeRTCRtpSender::DtlsTransport() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+webrtc::DtlsTransportInformation FakeRTCRtpSender::DtlsTransportInformation() {
+  NOTIMPLEMENTED();
+  static webrtc::DtlsTransportInformation dummy(
+      webrtc::DtlsTransportState::kNew);
+  return dummy;
+}
+
 blink::WebMediaStreamTrack FakeRTCRtpSender::Track() const {
   return track_id_ ? CreateWebMediaStreamTrack(*track_id_)
                    : blink::WebMediaStreamTrack();  // null
@@ -107,6 +120,20 @@
   return 0;
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+FakeRTCRtpReceiver::DtlsTransport() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+webrtc::DtlsTransportInformation
+FakeRTCRtpReceiver::DtlsTransportInformation() {
+  NOTIMPLEMENTED();
+  static webrtc::DtlsTransportInformation dummy(
+      webrtc::DtlsTransportState::kNew);
+  return dummy;
+}
+
 const blink::WebMediaStreamTrack& FakeRTCRtpReceiver::Track() const {
   return track_;
 }
diff --git a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
index 45b2eaf..afdc36ad 100644
--- a/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
+++ b/content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h
@@ -34,6 +34,8 @@
 
   std::unique_ptr<blink::WebRTCRtpSender> ShallowCopy() const override;
   uintptr_t Id() const override;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> DtlsTransport() override;
+  webrtc::DtlsTransportInformation DtlsTransportInformation() override;
   blink::WebMediaStreamTrack Track() const override;
   blink::WebVector<blink::WebString> StreamIds() const override;
   void ReplaceTrack(blink::WebMediaStreamTrack with_track,
@@ -62,6 +64,8 @@
 
   std::unique_ptr<blink::WebRTCRtpReceiver> ShallowCopy() const override;
   uintptr_t Id() const override;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> DtlsTransport() override;
+  webrtc::DtlsTransportInformation DtlsTransportInformation() override;
   const blink::WebMediaStreamTrack& Track() const override;
   blink::WebVector<blink::WebString> StreamIds() const override;
   blink::WebVector<std::unique_ptr<blink::WebRTCRtpSource>> GetSources()
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.cc b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
index 198a637..c26a010 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
@@ -125,6 +125,11 @@
   return track_;
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+FakeRtpSender::dtls_transport() const {
+  return transport_;
+}
+
 uint32_t FakeRtpSender::ssrc() const {
   NOTIMPLEMENTED();
   return 0;
@@ -177,6 +182,11 @@
   return track_;
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+FakeRtpReceiver::dtls_transport() const {
+  return transport_;
+}
+
 std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>
 FakeRtpReceiver::streams() const {
   return streams_;
@@ -221,8 +231,8 @@
 
 FakeRtpTransceiver::FakeRtpTransceiver(
     cricket::MediaType media_type,
-    rtc::scoped_refptr<webrtc::RtpSenderInterface> sender,
-    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
+    rtc::scoped_refptr<FakeRtpSender> sender,
+    rtc::scoped_refptr<FakeRtpReceiver> receiver,
     base::Optional<std::string> mid,
     bool stopped,
     webrtc::RtpTransceiverDirection direction,
@@ -277,6 +287,23 @@
   NOTIMPLEMENTED();
 }
 
+void FakeRtpTransceiver::SetTransport(
+    rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport) {
+  sender_->SetTransport(transport);
+  receiver_->SetTransport(transport);
+}
+
+FakeDtlsTransport::FakeDtlsTransport() {}
+
+rtc::scoped_refptr<webrtc::IceTransportInterface>
+FakeDtlsTransport::ice_transport() {
+  return nullptr;
+}
+
+webrtc::DtlsTransportInformation FakeDtlsTransport::Information() {
+  return webrtc::DtlsTransportInformation(webrtc::DtlsTransportState::kNew);
+}
+
 const char MockPeerConnectionImpl::kDummyOffer[] = "dummy offer";
 const char MockPeerConnectionImpl::kDummyAnswer[] = "dummy answer";
 
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.h b/content/renderer/media/webrtc/mock_peer_connection_impl.h
index 42e9a8f..1d691c26 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.h
@@ -30,6 +30,8 @@
 
   bool SetTrack(webrtc::MediaStreamTrackInterface* track) override;
   rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track() const override;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> dtls_transport()
+      const override;
   uint32_t ssrc() const override;
   cricket::MediaType media_type() const override;
   std::string id() const override;
@@ -41,9 +43,14 @@
       const webrtc::RtpParameters& parameters) override;
   rtc::scoped_refptr<webrtc::DtmfSenderInterface> GetDtmfSender()
       const override;
+  void SetTransport(
+      rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport) {
+    transport_ = transport;
+  }
 
  private:
   rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track_;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport_;
   std::vector<std::string> stream_ids_;
 };
 
@@ -55,6 +62,8 @@
   ~FakeRtpReceiver() override;
 
   rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track() const override;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> dtls_transport()
+      const override;
   std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> streams()
       const override;
   std::vector<std::string> stream_ids() const override;
@@ -64,9 +73,14 @@
   bool SetParameters(const webrtc::RtpParameters& parameters) override;
   void SetObserver(webrtc::RtpReceiverObserverInterface* observer) override;
   std::vector<webrtc::RtpSource> GetSources() const override;
+  void SetTransport(
+      rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport) {
+    transport_ = transport;
+  }
 
  private:
   rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track_;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport_;
   std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> streams_;
 };
 
@@ -74,8 +88,8 @@
  public:
   FakeRtpTransceiver(
       cricket::MediaType media_type,
-      rtc::scoped_refptr<webrtc::RtpSenderInterface> sender,
-      rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
+      rtc::scoped_refptr<FakeRtpSender> sender,
+      rtc::scoped_refptr<FakeRtpReceiver> receiver,
       base::Optional<std::string> mid,
       bool stopped,
       webrtc::RtpTransceiverDirection direction,
@@ -94,17 +108,29 @@
   absl::optional<webrtc::RtpTransceiverDirection> current_direction()
       const override;
   void Stop() override;
+  void SetTransport(
+      rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport);
 
  private:
   cricket::MediaType media_type_;
-  rtc::scoped_refptr<webrtc::RtpSenderInterface> sender_;
-  rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
+  rtc::scoped_refptr<FakeRtpSender> sender_;
+  rtc::scoped_refptr<FakeRtpReceiver> receiver_;
   absl::optional<std::string> mid_;
   bool stopped_;
   webrtc::RtpTransceiverDirection direction_;
   absl::optional<webrtc::RtpTransceiverDirection> current_direction_;
 };
 
+class FakeDtlsTransport : public webrtc::DtlsTransportInterface {
+ public:
+  FakeDtlsTransport();
+  rtc::scoped_refptr<webrtc::IceTransportInterface> ice_transport() override;
+  webrtc::DtlsTransportInformation Information() override;
+  void RegisterObserver(
+      webrtc::DtlsTransportObserverInterface* observer) override {}
+  void UnregisterObserver() override {}
+};
+
 // TODO(hbos): The use of fakes and mocks is the wrong approach for testing of
 // this. It introduces complexity, is error prone (not testing the right thing
 // and bugs in the mocks). This class is a maintenance burden and should be
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver.cc b/content/renderer/media/webrtc/rtc_rtp_receiver.cc
index 93bc443..8edb691 100644
--- a/content/renderer/media/webrtc/rtc_rtp_receiver.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_receiver.cc
@@ -21,6 +21,8 @@
     : main_task_runner_(std::move(main_task_runner)),
       signaling_task_runner_(std::move(signaling_task_runner)),
       webrtc_receiver_(std::move(webrtc_receiver)),
+      webrtc_dtls_transport_(webrtc_receiver_->dtls_transport()),
+      webrtc_dtls_transport_information_(webrtc::DtlsTransportState::kNew),
       is_initialized_(false),
       track_ref_(std::move(track_ref)),
       stream_ids_(std::move(stream_id)) {
@@ -28,12 +30,18 @@
   DCHECK(signaling_task_runner_);
   DCHECK(webrtc_receiver_);
   DCHECK(track_ref_);
+  if (webrtc_dtls_transport_) {
+    webrtc_dtls_transport_information_ = webrtc_dtls_transport_->Information();
+  }
 }
 
 RtpReceiverState::RtpReceiverState(RtpReceiverState&& other)
     : main_task_runner_(other.main_task_runner_),
       signaling_task_runner_(other.signaling_task_runner_),
       webrtc_receiver_(std::move(other.webrtc_receiver_)),
+      webrtc_dtls_transport_(std::move(other.webrtc_dtls_transport_)),
+      webrtc_dtls_transport_information_(
+          other.webrtc_dtls_transport_information_),
       is_initialized_(other.is_initialized_),
       track_ref_(std::move(other.track_ref_)),
       stream_ids_(std::move(other.stream_ids_)) {
@@ -55,6 +63,8 @@
   other.main_task_runner_ = nullptr;
   other.signaling_task_runner_ = nullptr;
   webrtc_receiver_ = std::move(other.webrtc_receiver_);
+  webrtc_dtls_transport_ = std::move(other.webrtc_dtls_transport_);
+  webrtc_dtls_transport_information_ = other.webrtc_dtls_transport_information_;
   track_ref_ = std::move(other.track_ref_);
   stream_ids_ = std::move(other.stream_ids_);
   return *this;
@@ -91,6 +101,18 @@
   return webrtc_receiver_;
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+RtpReceiverState::webrtc_dtls_transport() const {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  return webrtc_dtls_transport_;
+}
+
+webrtc::DtlsTransportInformation
+RtpReceiverState::webrtc_dtls_transport_information() const {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  return webrtc_dtls_transport_information_;
+}
+
 const std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>&
 RtpReceiverState::track_ref() const {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
@@ -241,6 +263,15 @@
   return getId(internal_->state().webrtc_receiver().get());
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+RTCRtpReceiver::DtlsTransport() {
+  return internal_->state().webrtc_dtls_transport();
+}
+
+webrtc::DtlsTransportInformation RTCRtpReceiver::DtlsTransportInformation() {
+  return internal_->state().webrtc_dtls_transport_information();
+}
+
 const blink::WebMediaStreamTrack& RTCRtpReceiver::Track() const {
   return internal_->state().track_ref()->web_track();
 }
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver.h b/content/renderer/media/webrtc/rtc_rtp_receiver.h
index f93ea71..8a8119db 100644
--- a/content/renderer/media/webrtc/rtc_rtp_receiver.h
+++ b/content/renderer/media/webrtc/rtc_rtp_receiver.h
@@ -79,6 +79,10 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner() const;
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner() const;
   scoped_refptr<webrtc::RtpReceiverInterface> webrtc_receiver() const;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> webrtc_dtls_transport()
+      const;
+  webrtc::DtlsTransportInformation webrtc_dtls_transport_information() const;
+
   const std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>&
   track_ref() const;
   const std::vector<std::string>& stream_ids() const;
@@ -87,6 +91,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
   scoped_refptr<webrtc::RtpReceiverInterface> webrtc_receiver_;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> webrtc_dtls_transport_;
+  webrtc::DtlsTransportInformation webrtc_dtls_transport_information_;
   bool is_initialized_;
   std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> track_ref_;
   std::vector<std::string> stream_ids_;
@@ -113,6 +119,9 @@
 
   std::unique_ptr<blink::WebRTCRtpReceiver> ShallowCopy() const override;
   uintptr_t Id() const override;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> DtlsTransport() override;
+  webrtc::DtlsTransportInformation DtlsTransportInformation() override;
+
   const blink::WebMediaStreamTrack& Track() const override;
   blink::WebVector<blink::WebString> StreamIds() const override;
   blink::WebVector<std::unique_ptr<blink::WebRTCRtpSource>> GetSources()
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.cc b/content/renderer/media/webrtc/rtc_rtp_sender.cc
index b0afdf9..0bea4bcf 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.cc
@@ -46,18 +46,26 @@
     : main_task_runner_(std::move(main_task_runner)),
       signaling_task_runner_(std::move(signaling_task_runner)),
       webrtc_sender_(std::move(webrtc_sender)),
+      webrtc_dtls_transport_(webrtc_sender_->dtls_transport()),
+      webrtc_dtls_transport_information_(webrtc::DtlsTransportState::kNew),
       is_initialized_(false),
       track_ref_(std::move(track_ref)),
       stream_ids_(std::move(stream_ids)) {
   DCHECK(main_task_runner_);
   DCHECK(signaling_task_runner_);
   DCHECK(webrtc_sender_);
+  if (webrtc_dtls_transport_) {
+    webrtc_dtls_transport_information_ = webrtc_dtls_transport_->Information();
+  }
 }
 
 RtpSenderState::RtpSenderState(RtpSenderState&& other)
     : main_task_runner_(other.main_task_runner_),
       signaling_task_runner_(other.signaling_task_runner_),
       webrtc_sender_(std::move(other.webrtc_sender_)),
+      webrtc_dtls_transport_(std::move(other.webrtc_dtls_transport_)),
+      webrtc_dtls_transport_information_(
+          other.webrtc_dtls_transport_information_),
       is_initialized_(other.is_initialized_),
       track_ref_(std::move(other.track_ref_)),
       stream_ids_(std::move(other.stream_ids_)) {
@@ -77,6 +85,8 @@
   other.main_task_runner_ = nullptr;
   other.signaling_task_runner_ = nullptr;
   webrtc_sender_ = std::move(other.webrtc_sender_);
+  webrtc_dtls_transport_ = std::move(other.webrtc_dtls_transport_);
+  webrtc_dtls_transport_information_ = other.webrtc_dtls_transport_information_;
   is_initialized_ = other.is_initialized_;
   track_ref_ = std::move(other.track_ref_);
   stream_ids_ = std::move(other.stream_ids_);
@@ -113,6 +123,18 @@
   return webrtc_sender_;
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+RtpSenderState::webrtc_dtls_transport() const {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  return webrtc_dtls_transport_;
+}
+
+webrtc::DtlsTransportInformation
+RtpSenderState::webrtc_dtls_transport_information() const {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  return webrtc_dtls_transport_information_;
+}
+
 const std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>&
 RtpSenderState::track_ref() const {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
@@ -384,6 +406,15 @@
   return getId(internal_->state().webrtc_sender().get());
 }
 
+rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+RTCRtpSender::DtlsTransport() {
+  return internal_->state().webrtc_dtls_transport();
+}
+
+webrtc::DtlsTransportInformation RTCRtpSender::DtlsTransportInformation() {
+  return internal_->state().webrtc_dtls_transport_information();
+}
+
 blink::WebMediaStreamTrack RTCRtpSender::Track() const {
   const auto& track_ref = internal_->state().track_ref();
   return track_ref ? track_ref->web_track() : blink::WebMediaStreamTrack();
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.h b/content/renderer/media/webrtc/rtc_rtp_sender.h
index 4a00277..c0e6bdd 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.h
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.h
@@ -80,6 +80,9 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner() const;
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner() const;
   scoped_refptr<webrtc::RtpSenderInterface> webrtc_sender() const;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> webrtc_dtls_transport()
+      const;
+  webrtc::DtlsTransportInformation webrtc_dtls_transport_information() const;
   const std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>&
   track_ref() const;
   void set_track_ref(
@@ -90,6 +93,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
   scoped_refptr<webrtc::RtpSenderInterface> webrtc_sender_;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> webrtc_dtls_transport_;
+  webrtc::DtlsTransportInformation webrtc_dtls_transport_information_;
   bool is_initialized_;
   std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> track_ref_;
   std::vector<std::string> stream_ids_;
@@ -122,6 +127,8 @@
   // blink::WebRTCRtpSender.
   std::unique_ptr<blink::WebRTCRtpSender> ShallowCopy() const override;
   uintptr_t Id() const override;
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> DtlsTransport() override;
+  webrtc::DtlsTransportInformation DtlsTransportInformation() override;
   blink::WebMediaStreamTrack Track() const override;
   blink::WebVector<blink::WebString> StreamIds() const override;
   void ReplaceTrack(blink::WebMediaStreamTrack with_track,
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc b/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
index fb0dc36..accdeb95 100644
--- a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
@@ -105,8 +105,8 @@
   }
 
   rtc::scoped_refptr<FakeRtpTransceiver> CreateWebRtcTransceiver(
-      rtc::scoped_refptr<webrtc::RtpSenderInterface> sender,
-      rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
+      rtc::scoped_refptr<FakeRtpSender> sender,
+      rtc::scoped_refptr<FakeRtpReceiver> receiver,
       base::Optional<std::string> mid,
       bool stopped,
       webrtc::RtpTransceiverDirection direction,
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc b/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
index 90bf88d2..d1aeb621 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
+++ b/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
@@ -55,28 +55,35 @@
         CreateBlinkLocalTrack(id));
   }
 
-  rtc::scoped_refptr<webrtc::RtpTransceiverInterface> CreateWebRtcTransceiver(
+  rtc::scoped_refptr<FakeRtpTransceiver> CreateWebRtcTransceiver(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> local_track,
       const std::string& local_stream_id,
       const std::string& remote_track_id,
-      const std::string& remote_stream_id) {
-    return new rtc::RefCountedObject<FakeRtpTransceiver>(
-        local_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind
-            ? cricket::MEDIA_TYPE_AUDIO
-            : cricket::MEDIA_TYPE_VIDEO,
-        CreateWebRtcSender(local_track, local_stream_id),
-        CreateWebRtcReceiver(remote_track_id, remote_stream_id), base::nullopt,
-        false, webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt);
+      const std::string& remote_stream_id,
+      rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport) {
+    rtc::scoped_refptr<FakeRtpTransceiver> transceiver =
+        new rtc::RefCountedObject<FakeRtpTransceiver>(
+            local_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind
+                ? cricket::MEDIA_TYPE_AUDIO
+                : cricket::MEDIA_TYPE_VIDEO,
+            CreateWebRtcSender(local_track, local_stream_id),
+            CreateWebRtcReceiver(remote_track_id, remote_stream_id),
+            base::nullopt, false, webrtc::RtpTransceiverDirection::kSendRecv,
+            base::nullopt);
+    if (transport.get()) {
+      transceiver->SetTransport(transport);
+    }
+    return transceiver;
   }
 
-  rtc::scoped_refptr<webrtc::RtpSenderInterface> CreateWebRtcSender(
+  rtc::scoped_refptr<FakeRtpSender> CreateWebRtcSender(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
       const std::string& stream_id) {
     return new rtc::RefCountedObject<FakeRtpSender>(
         std::move(track), std::vector<std::string>({stream_id}));
   }
 
-  rtc::scoped_refptr<webrtc::RtpReceiverInterface> CreateWebRtcReceiver(
+  rtc::scoped_refptr<FakeRtpReceiver> CreateWebRtcReceiver(
       const std::string& track_id,
       const std::string& stream_id) {
     rtc::scoped_refptr<webrtc::AudioTrackInterface> remote_track =
@@ -144,6 +151,15 @@
     EXPECT_EQ(sender_state->track_ref()->webrtc_track(),
               webrtc_sender->track().get());
     EXPECT_EQ(sender_state->stream_ids(), webrtc_sender->stream_ids());
+    EXPECT_EQ(sender_state->webrtc_dtls_transport(),
+              webrtc_sender->dtls_transport());
+    if (webrtc_sender->dtls_transport()) {
+      EXPECT_EQ(webrtc_sender->dtls_transport()->Information().state(),
+                sender_state->webrtc_dtls_transport_information().state());
+    } else {
+      EXPECT_EQ(webrtc::DtlsTransportState::kNew,
+                sender_state->webrtc_dtls_transport_information().state());
+    }
     // Inspect receiver states.
     const auto& receiver_state = transceiver_state.receiver_state();
     EXPECT_TRUE(receiver_state);
@@ -158,6 +174,15 @@
       receiver_stream_ids.push_back(stream->id());
     }
     EXPECT_EQ(receiver_state->stream_ids(), receiver_stream_ids);
+    EXPECT_EQ(receiver_state->webrtc_dtls_transport(),
+              webrtc_receiver->dtls_transport());
+    if (webrtc_receiver->dtls_transport()) {
+      EXPECT_EQ(webrtc_receiver->dtls_transport()->Information().state(),
+                receiver_state->webrtc_dtls_transport_information().state());
+    } else {
+      EXPECT_EQ(webrtc::DtlsTransportState::kNew,
+                receiver_state->webrtc_dtls_transport_information().state());
+    }
     // Inspect transceiver states.
     EXPECT_TRUE(
         OptionalEquals(transceiver_state.mid(), webrtc_transceiver->mid()));
@@ -228,9 +253,9 @@
 
 TEST_F(TransceiverStateSurfacerTest, SurfaceTransceiverBlockingly) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
-  auto webrtc_transceiver =
-      CreateWebRtcTransceiver(local_track_adapter->webrtc_track(),
-                              "local_stream", "remote_track", "remote_stream");
+  auto webrtc_transceiver = CreateWebRtcTransceiver(
+      local_track_adapter->webrtc_track(), "local_stream", "remote_track",
+      "remote_stream", nullptr);
   auto waitable_event =
       AsyncInitializeSurfacerWithWaitableEvent({webrtc_transceiver});
   waitable_event->Wait();
@@ -239,9 +264,22 @@
 
 TEST_F(TransceiverStateSurfacerTest, SurfaceTransceiverInCallback) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
-  auto webrtc_transceiver =
-      CreateWebRtcTransceiver(local_track_adapter->webrtc_track(),
-                              "local_stream", "remote_track", "remote_stream");
+  auto webrtc_transceiver = CreateWebRtcTransceiver(
+      local_track_adapter->webrtc_track(), "local_stream", "remote_track",
+      "remote_stream", nullptr);
+  auto run_loop = AsyncInitializeSurfacerWithCallback(
+      {webrtc_transceiver},
+      base::BindOnce(
+          &TransceiverStateSurfacerTest::ObtainStatesAndExpectInitialized,
+          base::Unretained(this), webrtc_transceiver));
+  run_loop->Run();
+}
+
+TEST_F(TransceiverStateSurfacerTest, SurfaceTransceiverWithTransport) {
+  auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
+  auto webrtc_transceiver = CreateWebRtcTransceiver(
+      local_track_adapter->webrtc_track(), "local_stream", "remote_track",
+      "remote_stream", new rtc::RefCountedObject<FakeDtlsTransport>());
   auto run_loop = AsyncInitializeSurfacerWithCallback(
       {webrtc_transceiver},
       base::BindOnce(
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
index 44e8fcc..b532406 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
@@ -262,7 +262,7 @@
         track_adapter_map_->GetOrCreateLocalTrackAdapter(web_local_track);
     scoped_refptr<webrtc::MediaStreamTrackInterface> local_track =
         local_track_adapter->webrtc_track();
-    rtc::scoped_refptr<webrtc::RtpSenderInterface> sender(
+    rtc::scoped_refptr<FakeRtpSender> sender(
         new rtc::RefCountedObject<FakeRtpSender>(
             local_track.get(), std::vector<std::string>({"local_stream"})));
     // A requirement of WebRtcSet[Local/Remote]DescriptionObserverHandler is
@@ -275,7 +275,7 @@
         MockWebRtcAudioTrack::Create("remote_track");
     scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
         new rtc::RefCountedObject<MockMediaStream>("remote_stream"));
-    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver(
+    rtc::scoped_refptr<FakeRtpReceiver> receiver(
         new rtc::RefCountedObject<FakeRtpReceiver>(
             remote_track.get(),
             std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
diff --git a/content/renderer/media_recorder/h264_encoder.cc b/content/renderer/media_recorder/h264_encoder.cc
index e85bc53..924dfdf 100644
--- a/content/renderer/media_recorder/h264_encoder.cc
+++ b/content/renderer/media_recorder/h264_encoder.cc
@@ -47,8 +47,8 @@
 H264Encoder::~H264Encoder() {
   main_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&H264Encoder::ShutdownEncoder, base::Passed(&encoding_thread_),
-                 base::Passed(&openh264_encoder_)));
+      base::BindOnce(&H264Encoder::ShutdownEncoder, std::move(encoding_thread_),
+                     std::move(openh264_encoder_)));
 }
 
 void H264Encoder::EncodeOnEncodingTaskRunner(
@@ -106,9 +106,10 @@
 
   const bool is_key_frame = info.eFrameType == videoFrameTypeIDR;
   origin_task_runner_->PostTask(
-      FROM_HERE, base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_,
-                            video_params, base::Passed(&data), nullptr,
-                            capture_timestamp, is_key_frame));
+      FROM_HERE,
+      base::BindOnce(OnFrameEncodeCompleted, on_encoded_video_callback_,
+                     video_params, std::move(data), nullptr, capture_timestamp,
+                     is_key_frame));
 }
 
 void H264Encoder::ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) {
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 7aad270..f3a505f 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3795,17 +3795,6 @@
       ->UpdateThisAndAllClones(std::move(subresource_loader_factories));
 }
 
-void RenderFrameImpl::MarkInitiatorAsRequiringSeparateURLLoaderFactory(
-    const url::Origin& initiator_origin,
-    network::mojom::URLLoaderFactoryPtr url_loader_factory) {
-  DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
-
-  auto factory_bundle = std::make_unique<blink::URLLoaderFactoryBundleInfo>();
-  factory_bundle->initiator_specific_factory_infos()[initiator_origin] =
-      url_loader_factory.PassInterface();
-  UpdateSubresourceLoaderFactories(std::move(factory_bundle));
-}
-
 void RenderFrameImpl::BindDevToolsAgent(
     blink::mojom::DevToolsAgentHostAssociatedPtrInfo host,
     blink::mojom::DevToolsAgentAssociatedRequest request) {
@@ -5047,8 +5036,8 @@
   // javascript onselectionchanged is triggered.
   // See crbug.com/729488
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&RenderFrameImpl::ShowDeferredContextMenu,
-                            weak_factory_.GetWeakPtr(), params));
+      FROM_HERE, base::BindOnce(&RenderFrameImpl::ShowDeferredContextMenu,
+                                weak_factory_.GetWeakPtr(), params));
 #else
   ShowDeferredContextMenu(params);
 #endif
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 1c651ea..eb842be2 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -613,9 +613,6 @@
   void UpdateSubresourceLoaderFactories(
       std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
           subresource_loader_factories) override;
-  void MarkInitiatorAsRequiringSeparateURLLoaderFactory(
-      const url::Origin& initiator_origin,
-      network::mojom::URLLoaderFactoryPtr url_loader_factory) override;
   void BindDevToolsAgent(
       blink::mojom::DevToolsAgentHostAssociatedPtrInfo host,
       blink::mojom::DevToolsAgentAssociatedRequest request) override;
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 0b5c55c..84b4e01 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -776,7 +776,7 @@
   }
 
  private:
-  // mojom::DocumentInterfaceBroker
+  // blink::mojom::DocumentInterfaceBroker
   void GetFrameHostTestInterface(
       blink::mojom::FrameHostTestInterfaceRequest request) override {
     binder_callback_.Run(std::move(request));
@@ -853,9 +853,11 @@
   }
 
  protected:
+  // blink::mojom::FrameHostTestInterface
   void Ping(const GURL& url, const std::string& event) override {
     ping_source_ = SourceAnnotation{url, event};
   }
+  void GetName(GetNameCallback callback) override {}
 
  private:
   mojo::Binding<blink::mojom::FrameHostTestInterface> binding_;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 1c27abb..43b44e5 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1102,6 +1102,12 @@
   GetWebWidget()->BeginFrame(frame_time, record_main_frame_metrics);
 }
 
+void RenderWidget::DidBeginMainFrame() {
+  if (!GetWebWidget())
+    return;
+  GetWebWidget()->DidBeginFrame();
+}
+
 void RenderWidget::RequestNewLayerTreeFrameSink(
     LayerTreeFrameSinkCallback callback) {
   // For widgets that are never visible, we don't start the compositor, so we
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 1443e70..a551d27 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -333,6 +333,7 @@
   void SendScrollEndEventFromImplSide(
       cc::ElementId scroll_latched_element_id) override;
   void BeginMainFrame(base::TimeTicks frame_time) override;
+  void DidBeginMainFrame() override;
   void RequestNewLayerTreeFrameSink(
       LayerTreeFrameSinkCallback callback) override;
   void DidCommitAndDrawCompositorFrame() override;
diff --git a/content/shell/renderer/DEPS b/content/shell/renderer/DEPS
index 244f046..8a6cc4d 100644
--- a/content/shell/renderer/DEPS
+++ b/content/shell/renderer/DEPS
@@ -3,6 +3,5 @@
   "+components/cdm",
   "+components/web_cache/renderer",
   "+components/plugins/renderer",
-  "+content/shell/test_runner",
   "+services/service_manager/public/cpp",
 ]
diff --git a/content/shell/renderer/web_test/DEPS b/content/shell/renderer/web_test/DEPS
new file mode 100644
index 0000000..8967d67
--- /dev/null
+++ b/content/shell/renderer/web_test/DEPS
@@ -0,0 +1,23 @@
+include_rules = [
+  # Web test code is interconnected with test_runner. It would be nice to merge
+  # these two layers and drop extra abstractions between them.
+  "+content/shell/test_runner",
+
+  # These files use //content/renderer/ for web test helpers, but those includes
+  # should not leak out to other parts of //content/shell unrelated to web
+  # tests. To avoid transitively including them to //content/shell through
+  # this directory, we avoid including them.
+  "-content/shell/test_runner/web_frame_test_proxy.h",
+  "-content/shell/test_runner/web_view_test_proxy.h",
+  "-content/shell/test_runner/web_widget_test_proxy.h",
+]
+
+specific_include_rules = {
+  # cc files do not leak includes so they are allowed to reference the test
+  # helpers that include //content/renderer.
+  ".*\.cc": [
+    "+content/shell/test_runner/web_frame_test_proxy.h",
+    "+content/shell/test_runner/web_view_test_proxy.h",
+    "+content/shell/test_runner/web_widget_test_proxy.h",
+  ],
+}
diff --git a/content/shell/renderer/web_test/blink_test_runner.cc b/content/shell/renderer/web_test/blink_test_runner.cc
index 838d015..3f2d825 100644
--- a/content/shell/renderer/web_test/blink_test_runner.cc
+++ b/content/shell/renderer/web_test/blink_test_runner.cc
@@ -52,7 +52,6 @@
 #include "content/shell/test_runner/pixel_dump.h"
 #include "content/shell/test_runner/web_test_interfaces.h"
 #include "content/shell/test_runner/web_test_runner.h"
-#include "content/shell/test_runner/web_view_test_proxy.h"
 #include "media/base/audio_capturer_source.h"
 #include "media/base/audio_parameters.h"
 #include "media/capture/video_capturer_source.h"
diff --git a/content/shell/renderer/web_test/web_test_content_renderer_client.cc b/content/shell/renderer/web_test/web_test_content_renderer_client.cc
index f8255052..3d2b7ee 100644
--- a/content/shell/renderer/web_test/web_test_content_renderer_client.cc
+++ b/content/shell/renderer/web_test/web_test_content_renderer_client.cc
@@ -39,7 +39,6 @@
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/web/blink.h"
-#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_plugin_params.h"
 #include "third_party/blink/public/web/web_testing_support.h"
 #include "third_party/blink/public/web/web_view.h"
diff --git a/content/shell/test_runner/DEPS b/content/shell/test_runner/DEPS
index 2b3abf1..4495fd6 100644
--- a/content/shell/test_runner/DEPS
+++ b/content/shell/test_runner/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+cc",
   "+content/public/test",
-  "+content/renderer",
   "+device/gamepad/public/cpp",
   "+device/gamepad/public/mojom",
   "+gin",
@@ -22,3 +21,18 @@
   "+ui/gfx",
   "+v8/include",
 ]
+
+# //content/renderer is allowed for web tests helpers, but should not leak out
+# to other parts of //content/shell unrelated to web tests. So we limit access
+# to things that are not used elsewhere.
+specific_include_rules = {
+  # These classes subclass RenderView and RenderWidget to inject behaviour
+  # for web tests.
+  "web_(frame|view|widget)_test_proxy\.h": [
+    "+content/renderer",
+  ],
+  # cc files will not leak //content/renderer includes.
+  ".*\.cc": [
+    "+content/renderer",
+  ],
+}
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc
index 2860354..a4cfdd8d 100644
--- a/content/shell/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -668,12 +668,6 @@
                    &WebAXObjectProxy::SelectionFocusOffset)
       .SetProperty("selectionFocusAffinity",
                    &WebAXObjectProxy::SelectionFocusAffinity)
-      .SetProperty("selectionStart", &WebAXObjectProxy::SelectionStart)
-      .SetProperty("selectionEnd", &WebAXObjectProxy::SelectionEnd)
-      .SetProperty("selectionStartLineNumber",
-                   &WebAXObjectProxy::SelectionStartLineNumber)
-      .SetProperty("selectionEndLineNumber",
-                   &WebAXObjectProxy::SelectionEndLineNumber)
       .SetProperty("isAtomic", &WebAXObjectProxy::IsAtomic)
       .SetProperty("isAutofillAvailable",
                    &WebAXObjectProxy::IsAutofillAvailable)
@@ -1047,26 +1041,6 @@
                                                              : "downstream";
 }
 
-int WebAXObjectProxy::SelectionStart() {
-  accessibility_object_.UpdateLayoutAndCheckValidity();
-  return accessibility_object_.SelectionStart();
-}
-
-int WebAXObjectProxy::SelectionEnd() {
-  accessibility_object_.UpdateLayoutAndCheckValidity();
-  return accessibility_object_.SelectionEnd();
-}
-
-int WebAXObjectProxy::SelectionStartLineNumber() {
-  accessibility_object_.UpdateLayoutAndCheckValidity();
-  return accessibility_object_.SelectionStartLineNumber();
-}
-
-int WebAXObjectProxy::SelectionEndLineNumber() {
-  accessibility_object_.UpdateLayoutAndCheckValidity();
-  return accessibility_object_.SelectionEndLineNumber();
-}
-
 bool WebAXObjectProxy::IsAtomic() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
   return accessibility_object_.LiveRegionAtomic();
diff --git a/content/test/data/accessibility/aria/aria-set-counts-with-tree-levels-expected-blink.txt b/content/test/data/accessibility/aria/aria-set-counts-with-tree-levels-expected-blink.txt
new file mode 100644
index 0000000..d313e6f
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-set-counts-with-tree-levels-expected-blink.txt
@@ -0,0 +1,26 @@
+rootWebArea
+++tree setSize=2
+++++treeItem name='Item A' hierarchicalLevel=1 setSize=2 posInSet=1 selected=false
+++++++staticText name='Item A'
+++++++++inlineTextBox name='Item A'
+++++treeItem name='Item A1' hierarchicalLevel=2 setSize=2 posInSet=1 selected=false
+++++++staticText name='Item A1'
+++++++++inlineTextBox name='Item A1'
+++++treeItem name='Item A1x' hierarchicalLevel=3 setSize=3 posInSet=1 selected=false
+++++++staticText name='Item A1x'
+++++++++inlineTextBox name='Item A1x'
+++++treeItem name='Item A1y' hierarchicalLevel=3 setSize=3 posInSet=2 selected=false
+++++++staticText name='Item A1y'
+++++++++inlineTextBox name='Item A1y'
+++++treeItem name='Item A1z' hierarchicalLevel=3 setSize=3 posInSet=3 selected=false
+++++++staticText name='Item A1z'
+++++++++inlineTextBox name='Item A1z'
+++++treeItem name='Item A2' hierarchicalLevel=2 setSize=2 posInSet=2 selected=false
+++++++staticText name='Item A2'
+++++++++inlineTextBox name='Item A2'
+++++treeItem name='Item B' hierarchicalLevel=1 setSize=2 posInSet=2 selected=false
+++++++staticText name='Item B'
+++++++++inlineTextBox name='Item B'
+++++treeItem name='Item B1' hierarchicalLevel=2 setSize=1 posInSet=1 selected=false
+++++++staticText name='Item B1'
+++++++++inlineTextBox name='Item B1'
diff --git a/content/test/data/accessibility/aria/aria-set-counts-with-tree-levels.html b/content/test/data/accessibility/aria/aria-set-counts-with-tree-levels.html
new file mode 100644
index 0000000..0a48def
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-set-counts-with-tree-levels.html
@@ -0,0 +1,25 @@
+<!--
+@BLINK-ALLOW:setSize*
+@BLINK-ALLOW:posInSet*
+@BLINK-DENY:setSize=0
+@BLINK-DENY:posInSet=0
+-->
+<!-- According to CORE-AAM:
+  For role="treeitem", walk the tree backward and forward until the explicit or
+  computed level becomes less than the current item's level. Count items only
+  if they are at the same level as the current item.
+-->
+<html>
+<body>
+<div role="tree">
+  <div tabIndex="0" aria-level="1" aria-setsize="2" aria-posinset="1" role="treeitem">Item A</div>
+  <div tabIndex="0" aria-level="2" aria-setsize="2" aria-posinset="1" role="treeitem">Item A1</div>
+  <div tabIndex="0" aria-level="3" aria-setsize="3" aria-posinset="1" role="treeitem">Item A1x</div>
+  <div tabIndex="0" aria-level="3" aria-setsize="3" aria-posinset="2" role="treeitem">Item A1y</div>
+  <div tabIndex="0" aria-level="3" aria-setsize="3" aria-posinset="3" role="treeitem">Item A1z</div>
+  <div tabIndex="0" aria-level="2" aria-setsize="2" aria-posinset="2" role="treeitem">Item A2</div>
+  <div tabIndex="0" aria-level="1" aria-setsize="2" aria-posinset="2" role="treeitem">Item B</div>
+  <div tabIndex="0" aria-level="2" aria-setsize="1" aria-posinset="1" role="treeitem">Item B1</div>
+</div>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/add-subtree-expected-win.txt b/content/test/data/accessibility/event/add-subtree-expected-win.txt
index 57b78be..de590142 100644
--- a/content/test/data/accessibility/event/add-subtree-expected-win.txt
+++ b/content/test/data/accessibility/event/add-subtree-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_REORDER on <ul> role=ROLE_SYSTEM_LIST SetSize=3
 EVENT_OBJECT_SHOW on <li> role=ROLE_SYSTEM_LISTITEM PosInSet=3 SetSize=3
-IA2_EVENT_TEXT_INSERTED on <ul> role=ROLE_SYSTEM_LIST SetSize=3 new_text={'<obj>' start=2 end=3}
+IA2_EVENT_TEXT_INSERTED on <ul> role=ROLE_SYSTEM_LIST SetSize=2 new_text={'<obj>' start=2 end=3}
diff --git a/content/test/data/accessibility/event/remove-hidden-attribute-expected-win.txt b/content/test/data/accessibility/event/remove-hidden-attribute-expected-win.txt
index 73cb5b6a..7c485e7 100644
--- a/content/test/data/accessibility/event/remove-hidden-attribute-expected-win.txt
+++ b/content/test/data/accessibility/event/remove-hidden-attribute-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_LIST SetSize=3
 EVENT_OBJECT_SHOW on <div#item3> role=ROLE_SYSTEM_LISTITEM name="Item 3" PosInSet=3 SetSize=3
-IA2_EVENT_TEXT_INSERTED on <div> role=ROLE_SYSTEM_LIST SetSize=3 new_text={'<obj>' start=2 end=3}
+IA2_EVENT_TEXT_INSERTED on <div> role=ROLE_SYSTEM_LIST SetSize=2 new_text={'<obj>' start=2 end=3}
diff --git a/content/test/data/accessibility/event/remove-hidden-attribute-subtree-expected-win.txt b/content/test/data/accessibility/event/remove-hidden-attribute-subtree-expected-win.txt
index 3dadf9f..1200268a 100644
--- a/content/test/data/accessibility/event/remove-hidden-attribute-subtree-expected-win.txt
+++ b/content/test/data/accessibility/event/remove-hidden-attribute-subtree-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_REORDER on <ul> role=ROLE_SYSTEM_LIST SetSize=3
 EVENT_OBJECT_SHOW on <li#item3> role=ROLE_SYSTEM_LISTITEM PosInSet=3 SetSize=3
-IA2_EVENT_TEXT_INSERTED on <ul> role=ROLE_SYSTEM_LIST SetSize=3 new_text={'<obj>' start=2 end=3}
+IA2_EVENT_TEXT_INSERTED on <ul> role=ROLE_SYSTEM_LIST SetSize=2 new_text={'<obj>' start=2 end=3}
diff --git a/content/test/data/media/session/image_test_page.html b/content/test/data/media/session/image_test_page.html
new file mode 100644
index 0000000..207e02e8
--- /dev/null
+++ b/content/test/data/media/session/image_test_page.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Media Session Image Test Page</title>
+<body>
+<script>
+navigator.mediaSession.metadata = new MediaMetadata({
+  artwork: [
+    {src: "test_image.jpg", sizes: "1x1", type: "image/jpeg"},
+    {src: "test_image.jpg", sizes: "10x10", type: "image/jpeg"}
+  ]
+});
+</script>
+<video id="video" controls>
+  <source src="video-6seconds.webm">
+</video>
+</body>
diff --git a/content/test/data/media/session/test_image.jpg b/content/test/data/media/session/test_image.jpg
new file mode 100644
index 0000000..da757f73
--- /dev/null
+++ b/content/test/data/media/session/test_image.jpg
Binary files differ
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index f215155..af60d4c 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -313,7 +313,6 @@
                            : web_contents->GetMainFrame()->frame_tree_node()),
       request_(nullptr),
       navigation_url_(original_url),
-      socket_address_("2001:db8::1", 80),
       initial_method_("GET"),
       browser_initiated_(browser_initiated),
       transition_(browser_initiated ? ui::PAGE_TRANSITION_TYPED
@@ -321,6 +320,10 @@
       contents_mime_type_("text/html"),
       load_url_params_(nullptr),
       weak_factory_(this) {
+  net::IPAddress address;
+  CHECK(address.AssignFromIPLiteral("2001:db8::1"));
+  remote_endpoint_ = net::IPEndPoint(address, 80);
+
   // For renderer-initiated navigation, the RenderFrame must be initialized. Do
   // it if it hasn't happened yet.
   if (!browser_initiated)
@@ -359,7 +362,7 @@
   CHECK_EQ(frame_tree_node_, request_->frame_tree_node());
   state_ = STARTED;
   navigation_url_ = handle->GetURL();
-  // |socket_address_| cannot be inferred from the request.
+  // |remote_endpoint_| cannot be inferred from the request.
   // |initial_method_| cannot be set after the request has started.
   browser_initiated_ = request_->browser_initiated();
   // |same_document_| should always be false here.
@@ -508,7 +511,7 @@
   if (frame_tree_node_->navigation_request()) {
     static_cast<TestRenderFrameHost*>(frame_tree_node_->current_frame_host())
         ->PrepareForCommitDeprecatedForNavigationSimulator(
-            socket_address_, is_signed_exchange_inner_response_);
+            remote_endpoint_, is_signed_exchange_inner_response_);
   }
 
   // Synchronous failure can cause the navigation to finish here.
@@ -810,10 +813,10 @@
 }
 
 void NavigationSimulatorImpl::SetSocketAddress(
-    const net::HostPortPair& socket_address) {
+    const net::IPEndPoint& remote_endpoint) {
   CHECK_LE(state_, STARTED) << "The socket address cannot be set after the "
                                "navigation has committed or failed";
-  socket_address_ = socket_address;
+  remote_endpoint_ = remote_endpoint;
 }
 
 void NavigationSimulatorImpl::SetIsSignedExchangeInnerResponse(
diff --git a/content/test/navigation_simulator_impl.h b/content/test/navigation_simulator_impl.h
index a436e99..c2b721a3 100644
--- a/content/test/navigation_simulator_impl.h
+++ b/content/test/navigation_simulator_impl.h
@@ -18,6 +18,7 @@
 #include "content/public/test/navigation_simulator.h"
 #include "mojo/public/cpp/bindings/associated_interface_request.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "url/gurl.h"
@@ -78,7 +79,7 @@
   void SetMethod(const std::string& method) override;
   void SetIsFormSubmission(bool is_form_submission) override;
   void SetReferrer(const Referrer& referrer) override;
-  void SetSocketAddress(const net::HostPortPair& socket_address) override;
+  void SetSocketAddress(const net::IPEndPoint& remote_endpoint) override;
   void SetIsSignedExchangeInnerResponse(
       bool is_signed_exchange_inner_response) override;
   void SetInterfaceProviderRequest(
@@ -213,7 +214,7 @@
   // Note: additional parameters to modify the navigation should be properly
   // initialized (if needed) in InitializeFromStartedRequest.
   GURL navigation_url_;
-  net::HostPortPair socket_address_;
+  net::IPEndPoint remote_endpoint_;
   bool is_signed_exchange_inner_response_ = false;
   std::string initial_method_;
   bool is_form_submission_ = false;
diff --git a/content/test/stub_layer_tree_view_delegate.h b/content/test/stub_layer_tree_view_delegate.h
index a75b0eb..eddef3c 100644
--- a/content/test/stub_layer_tree_view_delegate.h
+++ b/content/test/stub_layer_tree_view_delegate.h
@@ -26,6 +26,7 @@
   void SendScrollEndEventFromImplSide(
       cc::ElementId scroll_latched_element_id) override {}
   void BeginMainFrame(base::TimeTicks frame_time) override {}
+  void DidBeginMainFrame() override {}
   void RecordStartOfFrameMetrics() override {}
   void RecordEndOfFrameMetrics(base::TimeTicks) override {}
   void RequestNewLayerTreeFrameSink(
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index 883f2bf8..b7cf360 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -26,6 +26,7 @@
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_render_widget_host.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_response_headers.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -365,26 +366,26 @@
 }
 
 void TestRenderFrameHost::PrepareForCommit() {
-  PrepareForCommitInternal(GURL(), net::HostPortPair(),
+  PrepareForCommitInternal(GURL(), net::IPEndPoint(),
                            /* is_signed_exchange_inner_response=*/false);
 }
 
 void TestRenderFrameHost::PrepareForCommitDeprecatedForNavigationSimulator(
-    const net::HostPortPair& socket_address,
+    const net::IPEndPoint& remote_endpoint,
     bool is_signed_exchange_inner_response) {
-  PrepareForCommitInternal(GURL(), socket_address,
+  PrepareForCommitInternal(GURL(), remote_endpoint,
                            is_signed_exchange_inner_response);
 }
 
 void TestRenderFrameHost::PrepareForCommitWithServerRedirect(
     const GURL& redirect_url) {
-  PrepareForCommitInternal(redirect_url, net::HostPortPair(),
+  PrepareForCommitInternal(redirect_url, net::IPEndPoint(),
                            /* is_signed_exchange_inner_response=*/false);
 }
 
 void TestRenderFrameHost::PrepareForCommitInternal(
     const GURL& redirect_url,
-    const net::HostPortPair& socket_address,
+    const net::IPEndPoint& remote_endpoint,
     bool is_signed_exchange_inner_response) {
   NavigationRequest* request = frame_tree_node_->navigation_request();
   CHECK(request);
@@ -423,7 +424,7 @@
   // Simulate the network stack commit.
   scoped_refptr<network::ResourceResponse> response(
       new network::ResourceResponse);
-  response->head.socket_address = socket_address;
+  response->head.remote_endpoint = remote_endpoint;
   response->head.is_signed_exchange_inner_response =
       is_signed_exchange_inner_response;
   // TODO(carlosk): Ideally, it should be possible someday to
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index 73a0a2d..335a105 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -26,7 +26,7 @@
 struct FrameHostMsg_DidCommitProvisionalLoad_Params;
 
 namespace net {
-class HostPortPair;
+class IPEndPoint;
 }
 
 namespace content {
@@ -146,7 +146,7 @@
   // TODO(clamy): Have NavigationSimulator make the relevant calls directly and
   // remove this function.
   void PrepareForCommitDeprecatedForNavigationSimulator(
-      const net::HostPortPair& socket_address,
+      const net::IPEndPoint& remote_endpoint,
       bool is_signed_exchange_inner_response);
 
   // This method does the same as PrepareForCommit.
@@ -247,7 +247,7 @@
                                   const ModificationCallback& callback);
 
   void PrepareForCommitInternal(const GURL& redirect_url,
-                                const net::HostPortPair& socket_address,
+                                const net::IPEndPoint& remote_endpoint,
                                 bool is_signed_exchange_inner_response);
 
   // Computes the page ID for a pending navigation in this RenderFrameHost;
diff --git a/docs/android_build_instructions.md b/docs/android_build_instructions.md
index b18614b..702d2c9 100644
--- a/docs/android_build_instructions.md
+++ b/docs/android_build_instructions.md
@@ -353,11 +353,6 @@
    * What it does: Uses multiple `.so` files instead of just one (faster links)
  * `is_java_debug = true` *(default=`is_debug`)*
    * What it does: Disables ProGuard (slow build step)
- * `enable_incremental_javac = true` *(default=`false`)*
-   * What it does: Tries to compile only a subset of `.java` files within an
-     `android_library` for subsequent builds.
-   * Can cause infrequent (once a month-ish) failures due to not recompiling a
-     class that should be recompiled.
 
 #### Incremental Install
 "Incremental install" uses reflection and side-loading to speed up the edit
diff --git a/docs/win_cross.md b/docs/win_cross.md
index ffea53d..ef55dc3 100644
--- a/docs/win_cross.md
+++ b/docs/win_cross.md
@@ -6,8 +6,6 @@
 
 What does *not* work:
 
-* goma. Sorry. ([internal bug](http://b/64390790)) You can use the
-  [jumbo build](jumbo.md) for faster build times.
 * 64-bit renderer processes don't use V8 snapshots, slowing down their startup
   ([bug](https://crbug.com/803591))
 * on Mac hosts, building a 32-bit chrome ([bug](https://crbug.com/794838))
@@ -77,6 +75,13 @@
 
     ninja -C out/gnwin base_unittests.exe
 
+## Goma
+
+For now, one needs to use the rbe backend, not the (default) borg backend:
+
+    goma_auth.py login
+    GOMA_STUBBY_PROXY_IP_ADDRESS=rbe-staging1.endpoints.cxx-compiler-service.cloud.goog GOMA_USE_CASE=rbe-staging goma_ctl.py ensure_start
+
 ## Copying and running chrome
 
 A convenient way to copy chrome over to a Windows box is to build the
diff --git a/extensions/browser/api/idle/BUILD.gn b/extensions/browser/api/idle/BUILD.gn
index a635a4f0..edc5714 100644
--- a/extensions/browser/api/idle/BUILD.gn
+++ b/extensions/browser/api/idle/BUILD.gn
@@ -21,7 +21,6 @@
 
   deps = [
     "//extensions/common/api",
-    "//ui/base/idle",
   ]
 
   public_deps = [
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index b1aa4b5..5c32809 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -19,6 +19,7 @@
 #include "extensions/browser/extension_navigation_ui_data.h"
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_data_stream.h"
 #include "net/base/upload_file_element_reader.h"
@@ -346,7 +347,7 @@
     net::URLRequest* url_request) {
   response_code = url_request->GetResponseCode();
   response_headers = url_request->response_headers();
-  response_ip = url_request->GetSocketAddress().host();
+  response_ip = url_request->GetResponseRemoteEndpoint().ToStringWithoutPort();
   response_from_cache = url_request->was_cached();
 }
 
@@ -355,7 +356,7 @@
   response_headers = response.headers;
   if (response_headers)
     response_code = response_headers->response_code();
-  response_ip = response.socket_address.host();
+  response_ip = response.remote_endpoint.ToStringWithoutPort();
   response_from_cache = response.was_fetched_via_cache;
 }
 
diff --git a/extensions/browser/api/web_request/web_request_proxying_websocket.cc b/extensions/browser/api/web_request/web_request_proxying_websocket.cc
index 856d4bb..3aa4ad81 100644
--- a/extensions/browser/api/web_request/web_request_proxying_websocket.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_websocket.cc
@@ -9,6 +9,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_navigation_ui_data.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_util.h"
 
 namespace extensions {
@@ -165,7 +166,7 @@
       response_.headers->AddHeader(header->name + ": " + header->value);
     }
   }
-  response_.socket_address = response->socket_address;
+  response_.remote_endpoint = response->remote_endpoint;
 
   forwarding_client_->OnFinishOpeningHandshake(std::move(response));
 
@@ -232,7 +233,7 @@
 void WebRequestProxyingWebSocket::OnAuthRequired(
     const scoped_refptr<net::AuthChallengeInfo>& auth_info,
     const scoped_refptr<net::HttpResponseHeaders>& headers,
-    const net::HostPortPair& socket_address,
+    const net::IPEndPoint& remote_endpoint,
     OnAuthRequiredCallback callback) {
   if (!auth_info || !callback) {
     OnError(net::ERR_FAILED);
@@ -240,7 +241,7 @@
   }
 
   response_.headers = headers;
-  response_.socket_address = socket_address;
+  response_.remote_endpoint = remote_endpoint;
   auth_required_callback_ = std::move(callback);
 
   auto continuation = base::BindRepeating(
diff --git a/extensions/browser/api/web_request/web_request_proxying_websocket.h b/extensions/browser/api/web_request/web_request_proxying_websocket.h
index 88ff6f3..173a89d 100644
--- a/extensions/browser/api/web_request/web_request_proxying_websocket.h
+++ b/extensions/browser/api/web_request/web_request_proxying_websocket.h
@@ -24,6 +24,10 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+namespace net {
+class IPEndPoint;
+}  // namespace net
+
 namespace extensions {
 
 // A WebRequestProxyingWebSocket proxies a WebSocket connection and dispatches
@@ -81,7 +85,7 @@
   // mojom::AuthenticationHandler method:
   void OnAuthRequired(const scoped_refptr<net::AuthChallengeInfo>& auth_info,
                       const scoped_refptr<net::HttpResponseHeaders>& headers,
-                      const net::HostPortPair& socket_address,
+                      const net::IPEndPoint& remote_endpoint,
                       OnAuthRequiredCallback callback) override;
 
   static void StartProxying(
diff --git a/extensions/browser/guest_view/app_view/app_view_guest.cc b/extensions/browser/guest_view/app_view/app_view_guest.cc
index 918df08..b075617 100644
--- a/extensions/browser/guest_view/app_view/app_view_guest.cc
+++ b/extensions/browser/guest_view/app_view/app_view_guest.cc
@@ -117,7 +117,9 @@
 AppViewGuest::~AppViewGuest() {
 }
 
-bool AppViewGuest::HandleContextMenu(const content::ContextMenuParams& params) {
+bool AppViewGuest::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
+    const content::ContextMenuParams& params) {
   if (app_view_guest_delegate_) {
     return app_view_guest_delegate_->HandleContextMenu(web_contents(), params);
   }
diff --git a/extensions/browser/guest_view/app_view/app_view_guest.h b/extensions/browser/guest_view/app_view/app_view_guest.h
index a44c3b9..0fea3d0 100644
--- a/extensions/browser/guest_view/app_view/app_view_guest.h
+++ b/extensions/browser/guest_view/app_view/app_view_guest.h
@@ -60,7 +60,8 @@
   int GetTaskPrefix() const final;
 
   // content::WebContentsDelegate implementation.
-  bool HandleContextMenu(const content::ContextMenuParams& params) final;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) final;
   void RequestMediaAccessPermission(
       content::WebContents* web_contents,
       const content::MediaStreamRequest& request,
diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.cc b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
index d69bbe9..0890def7 100644
--- a/extensions/browser/guest_view/extension_options/extension_options_guest.cc
+++ b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
@@ -199,6 +199,7 @@
 }
 
 bool ExtensionOptionsGuest::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   if (!extension_options_guest_delegate_)
     return false;
diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.h b/extensions/browser/guest_view/extension_options/extension_options_guest.h
index 7eb02c1..bdeb2d5 100644
--- a/extensions/browser/guest_view/extension_options/extension_options_guest.h
+++ b/extensions/browser/guest_view/extension_options/extension_options_guest.h
@@ -48,7 +48,8 @@
       content::WebContents* source,
       const content::OpenURLParams& params) final;
   void CloseContents(content::WebContents* source) final;
-  bool HandleContextMenu(const content::ContextMenuParams& params) final;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) final;
   bool ShouldCreateWebContents(
       content::WebContents* web_contents,
       content::RenderFrameHost* opener,
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
index f5696fd..7677eb8a 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -286,6 +286,7 @@
 }
 
 bool MimeHandlerViewGuest::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   return delegate_ && delegate_->HandleContextMenu(web_contents(), params);
 }
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
index a448bc2..a0e702b 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h
@@ -125,7 +125,8 @@
       const content::OpenURLParams& params) final;
   void NavigationStateChanged(content::WebContents* source,
                               content::InvalidateTypes changed_flags) final;
-  bool HandleContextMenu(const content::ContextMenuParams& params) final;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) final;
   bool PreHandleGestureEvent(content::WebContents* source,
                              const blink::WebGestureEvent& event) final;
   content::JavaScriptDialogManager* GetJavaScriptDialogManager(
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 884534b..a554840 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -605,6 +605,7 @@
 }
 
 bool WebViewGuest::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   return web_view_guest_delegate_ &&
          web_view_guest_delegate_->HandleContextMenu(params);
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index 691ed1f..f155d67 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -212,7 +212,8 @@
                               int32_t line_no,
                               const base::string16& source_id) final;
   void CloseContents(content::WebContents* source) final;
-  bool HandleContextMenu(const content::ContextMenuParams& params) final;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) final;
   bool HandleKeyboardEvent(content::WebContents* source,
                            const content::NativeWebKeyboardEvent& event) final;
   void LoadProgressChanged(content::WebContents* source, double progress) final;
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index de1c1a1..34cf471f 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -58,8 +58,8 @@
     //               specifies the start of a (sub-)domain of the URL.
     //
     // <b>'^'</b>  : Separator character: This matches anything except a letter, a
-    //               digit or one of the following: _ - . %.
-    //
+    //               digit or one of the following: _ - . %. This can also match
+    //               the end of the URL.
     //
     // Therefore <code>urlFilter</code> is composed of the following parts:
     // (optional Left/Domain name anchor) + pattern + (optional Right anchor).
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.cc b/extensions/shell/browser/shell_desktop_controller_aura.cc
index 621c32a..160283d 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura.cc
@@ -234,12 +234,12 @@
 
 ui::EventDispatchDetails ShellDesktopControllerAura::DispatchKeyEventPostIME(
     ui::KeyEvent* key_event,
-    base::OnceCallback<void(bool)> ack_callback) {
+    DispatchKeyEventPostIMECallback callback) {
   if (key_event->target()) {
     aura::WindowTreeHost* host = static_cast<aura::Window*>(key_event->target())
                                      ->GetRootWindow()
                                      ->GetHost();
-    return host->DispatchKeyEventPostIME(key_event, std::move(ack_callback));
+    return host->DispatchKeyEventPostIME(key_event, std::move(callback));
   }
 
   // Send the key event to the focused window.
@@ -247,11 +247,11 @@
       const_cast<aura::Window*>(focus_controller_->GetActiveWindow());
   if (active_window) {
     return active_window->GetRootWindow()->GetHost()->DispatchKeyEventPostIME(
-        key_event, std::move(ack_callback));
+        key_event, std::move(callback));
   }
 
   return GetPrimaryHost()->DispatchKeyEventPostIME(key_event,
-                                                   std::move(ack_callback));
+                                                   std::move(callback));
 }
 
 void ShellDesktopControllerAura::OnKeepAliveStateChanged(
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.h b/extensions/shell/browser/shell_desktop_controller_aura.h
index bddf4df..3617a47 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.h
+++ b/extensions/shell/browser/shell_desktop_controller_aura.h
@@ -98,7 +98,7 @@
   // ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* key_event,
-      base::OnceCallback<void(bool)> ack_callback) override;
+      DispatchKeyEventPostIMECallback callback) override;
 
   // KeepAliveStateObserver:
   void OnKeepAliveStateChanged(bool is_keeping_alive) override;
diff --git a/fuchsia/engine/browser/frame_impl_browsertest.cc b/fuchsia/engine/browser/frame_impl_browsertest.cc
index a6d70ea..0c8ba13 100644
--- a/fuchsia/engine/browser/frame_impl_browsertest.cc
+++ b/fuchsia/engine/browser/frame_impl_browsertest.cc
@@ -161,6 +161,28 @@
   EXPECT_FALSE(frame);
 }
 
+IN_PROC_BROWSER_TEST_F(FrameImplTest, ContextDeletedBeforeFrameWithView) {
+  chromium::web::FramePtr frame = CreateFrame();
+  EXPECT_TRUE(frame);
+
+  zx::eventpair import_token;
+  fuchsia::ui::gfx::ExportToken export_token;
+  ASSERT_EQ(zx::eventpair::create(0, &import_token, &export_token.value),
+            ZX_OK);
+
+  frame->CreateView(std::move(export_token));
+  base::RunLoop().RunUntilIdle();
+
+  base::RunLoop run_loop;
+  frame.set_error_handler([&run_loop](zx_status_t status) {
+    EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
+    run_loop.Quit();
+  });
+  context().Unbind();
+  run_loop.Run();
+  EXPECT_FALSE(frame);
+}
+
 IN_PROC_BROWSER_TEST_F(FrameImplTest, GoBackAndForward) {
   chromium::web::FramePtr frame = CreateFrame();
   chromium::web::NavigationControllerPtr controller;
diff --git a/fuchsia/runners/BUILD.gn b/fuchsia/runners/BUILD.gn
index e103204..0da5786 100644
--- a/fuchsia/runners/BUILD.gn
+++ b/fuchsia/runners/BUILD.gn
@@ -89,14 +89,11 @@
   sources = [
     "cast/fake_application_config_manager.cc",
     "cast/fake_application_config_manager.h",
-    "cast/test_common.cc",
-    "cast/test_common.h",
   ]
   public_deps = [
     "//base",
     "//fuchsia:cast_fidl",
-    "//net:test_support",
-    "//third_party/fuchsia-sdk/sdk:sys",
+    "//url",
   ]
   visibility = [ ":*" ]
 }
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc
index 0acfaab..45810131 100644
--- a/fuchsia/runners/cast/cast_runner.cc
+++ b/fuchsia/runners/cast/cast_runner.cc
@@ -82,11 +82,6 @@
     chromium::cast::ApplicationConfigPtr app_config) {
   if (!app_config) {
     DLOG(WARNING) << "No ApplicationConfig was found.";
-
-    // For test purposes, we need to call RegisterComponent even if there is no
-    // URL to launch.
-    // TODO: Replace this hack, e.g. with an test-specific callback.
-    RegisterComponent(std::unique_ptr<WebComponent>(nullptr));
     return;
   }
 
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index 1ffcd36e..df0e76c 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -6,10 +6,12 @@
 #include <lib/zx/channel.h>
 
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/scoped_service_binding.h"
 #include "base/fuchsia/service_directory.h"
 #include "base/fuchsia/service_directory_client.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/test_timeouts.h"
 #include "fuchsia/base/fit_adapter.h"
 #include "fuchsia/base/result_receiver.h"
@@ -17,7 +19,6 @@
 #include "fuchsia/fidl/chromium/web/cpp/fidl.h"
 #include "fuchsia/runners/cast/cast_runner.h"
 #include "fuchsia/runners/cast/fake_application_config_manager.h"
-#include "fuchsia/runners/cast/test_common.h"
 #include "fuchsia/runners/common/web_component.h"
 #include "fuchsia/runners/common/web_content_runner.h"
 #include "net/test/embedded_test_server/default_handlers.h"
@@ -31,43 +32,72 @@
     FILE_PATH_LITERAL("fuchsia/runners/cast/testdata");
 
 void ComponentErrorHandler(zx_status_t status) {
-  ZX_LOG(ERROR, status) << "Component launch failed.";
+  ZX_LOG(ERROR, status) << "Component launch failed";
   ADD_FAILURE();
 }
 
+class FakeCastChannel : public chromium::cast::CastChannel {
+ public:
+  FakeCastChannel() = default;
+
+  // chromium::web::CastChannel implementation.
+  void OnOpened(fidl::InterfaceHandle<chromium::web::MessagePort> channel,
+                OnOpenedCallback callback_ignored) override {
+    // |callback_ignored| is dropped because these tests don't exercise multiple
+    // channel lifetimes.
+    connected_channel_ = channel.Bind();
+
+    if (on_channel_connected_)
+      std::move(on_channel_connected_).Run();
+  }
+
+  chromium::web::MessagePort* connected_channel() const {
+    // Return nullptr if |connected_channel_| is not currently bound.
+    return connected_channel_ ? connected_channel_.get() : nullptr;
+  }
+
+  void set_on_channel_connected(base::OnceClosure on_channel_connected) {
+    on_channel_connected_ = std::move(on_channel_connected);
+  }
+
+ protected:
+  // The connected Cast Channel.
+  chromium::web::MessagePortPtr connected_channel_;
+
+  // A pending on-connect callback, to be invoked once a Cast Channel is
+  // received.
+  base::OnceClosure on_channel_connected_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeCastChannel);
+};
+
 }  // namespace
 
-class CastRunnerIntegrationTest : public testing::Test,
-                                  public chromium::cast::CastChannel {
+class CastRunnerIntegrationTest : public testing::Test {
  public:
   CastRunnerIntegrationTest()
       : run_timeout_(TestTimeouts::action_timeout()),
-        cast_channel_binding_(this) {
+        app_config_binding_(&app_config_manager_) {
     // Create a new test ServiceDirectory, and ServiceDirectoryClient connected
     // to it, for tests to use to drive the CastRunner.
     fidl::InterfaceHandle<fuchsia::io::Directory> directory;
-    test_services_ = std::make_unique<base::fuchsia::ServiceDirectory>(
+    public_services_ = std::make_unique<base::fuchsia::ServiceDirectory>(
         directory.NewRequest());
-    test_services_client_ =
-        std::make_unique<base::fuchsia::ServiceDirectoryClient>(
-            std::move(directory));
 
     // Create the AppConfigManager.
-    app_config_binding_ = std::make_unique<
-        fidl::Binding<chromium::cast::ApplicationConfigManager>>(
-        &app_config_manager_);
-    chromium::cast::ApplicationConfigManagerPtr app_config_manager_interface;
-    app_config_binding_->Bind(app_config_manager_interface.NewRequest());
+    chromium::cast::ApplicationConfigManagerPtr app_config_manager_ptr;
+    app_config_binding_.Bind(app_config_manager_ptr.NewRequest());
 
     // Create the CastRunner, published into |test_services_|.
     cast_runner_ = std::make_unique<CastRunner>(
-        test_services_.get(), WebContentRunner::CreateDefaultWebContext(),
-        std::move(app_config_manager_interface),
-        cast_runner_run_loop_.QuitClosure());
+        public_services_.get(), WebContentRunner::CreateDefaultWebContext(),
+        std::move(app_config_manager_ptr), cast_runner_run_loop_.QuitClosure());
 
     // Connect to the CastRunner's fuchsia.sys.Runner interface.
+    base::fuchsia::ServiceDirectoryClient public_directory_client(
+        std::move(directory));
     cast_runner_ptr_ =
-        test_services_client_->ConnectToService<fuchsia::sys::Runner>();
+        public_directory_client.ConnectToService<fuchsia::sys::Runner>();
     cast_runner_ptr_.set_error_handler([this](zx_status_t status) {
       ZX_LOG(ERROR, status) << "CastRunner closed channel.";
       ADD_FAILURE();
@@ -88,23 +118,48 @@
   }
 
   void WaitUntilCastChannelOpened() {
-    if (connected_channel_)
+    if (cast_channel_.connected_channel())
       return;
 
     base::RunLoop run_loop;
-    on_channel_connected_cb_ = run_loop.QuitClosure();
+    cast_channel_.set_on_channel_connected(run_loop.QuitClosure());
     run_loop.Run();
+
+    DCHECK(cast_channel_.connected_channel());
   }
 
-  // chromium::web::CastChannel implementation.
-  void OnOpened(fidl::InterfaceHandle<chromium::web::MessagePort> channel,
-                OnOpenedCallback callback_ignored) override {
-    // |callback_ignored| is dropped because these tests don't exercise multiple
-    // channel lifetimes.
-    connected_channel_ = channel.Bind();
+  fuchsia::sys::ComponentControllerPtr StartCastComponent(
+      base::StringPiece component_url) {
+    DCHECK(!component_services_);
 
-    if (on_channel_connected_cb_)
-      std::move(on_channel_connected_cb_).Run();
+    // Create a ServiceDirectory and publish the CastChannel into it.
+    fidl::InterfaceHandle<fuchsia::io::Directory> directory;
+    component_services_ = std::make_unique<base::fuchsia::ServiceDirectory>(
+        directory.NewRequest());
+    cast_channel_binding_ = std::make_unique<
+        base::fuchsia::ScopedServiceBinding<chromium::cast::CastChannel>>(
+        component_services_.get(), &cast_channel_);
+
+    // Configure the Runner, including a service directory channel to publish
+    // services to.
+    fuchsia::sys::StartupInfo startup_info;
+    startup_info.launch_info.url = component_url.as_string();
+
+    // Place the ServiceDirectory in the |flat_namespace|.
+    startup_info.flat_namespace.paths.emplace_back("/svc");
+    startup_info.flat_namespace.directories.emplace_back(
+        directory.TakeChannel());
+
+    fuchsia::sys::Package package;
+    package.resolved_url = component_url.as_string();
+
+    fuchsia::sys::ComponentControllerPtr component_controller;
+    cast_runner_ptr_->StartComponent(std::move(package),
+                                     std::move(startup_info),
+                                     component_controller.NewRequest());
+
+    EXPECT_TRUE(component_controller.is_bound());
+    return component_controller;
   }
 
  protected:
@@ -114,22 +169,17 @@
 
   // Returns fake Cast application information to the CastRunner.
   FakeApplicationConfigManager app_config_manager_;
-  std::unique_ptr<fidl::Binding<chromium::cast::ApplicationConfigManager>>
-      app_config_binding_;
+  fidl::Binding<chromium::cast::ApplicationConfigManager> app_config_binding_;
 
-  // The connected Cast Channel.
-  chromium::web::MessagePortPtr connected_channel_;
-
-  // A pending on-connect callback, to be invoked once a Cast Channel is
-  // received.
-  base::OnceClosure on_channel_connected_cb_;
-
-  // Receives connected channels from the CastRunner.
-  fidl::Binding<chromium::cast::CastChannel> cast_channel_binding_;
+  // Incoming service directory, CastChannel and service bindings.
+  std::unique_ptr<base::fuchsia::ServiceDirectory> component_services_;
+  FakeCastChannel cast_channel_;
+  std::unique_ptr<
+      base::fuchsia::ScopedServiceBinding<chromium::cast::CastChannel>>
+      cast_channel_binding_;
 
   // ServiceDirectory into which the CastRunner will publish itself.
-  std::unique_ptr<base::fuchsia::ServiceDirectory> test_services_;
-  std::unique_ptr<base::fuchsia::ServiceDirectoryClient> test_services_client_;
+  std::unique_ptr<base::fuchsia::ServiceDirectory> public_services_;
 
   std::unique_ptr<CastRunner> cast_runner_;
   fuchsia::sys::RunnerPtr cast_runner_ptr_;
@@ -147,11 +197,9 @@
                                     test_server_.GetURL(kBlankAppPath));
 
   // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller_ptr;
-  base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent(
-      base::StringPrintf("cast:%s", kBlankAppId), &cast_runner_ptr_,
-      component_controller_ptr.NewRequest(), &cast_channel_binding_));
-  component_controller_ptr.set_error_handler(&ComponentErrorHandler);
+  fuchsia::sys::ComponentControllerPtr component_controller =
+      StartCastComponent(base::StringPrintf("cast:%s", kBlankAppId));
+  component_controller.set_error_handler(&ComponentErrorHandler);
 
   // Access the NavigationController from the WebComponent. The test will hang
   // here if no WebComponent was created.
@@ -179,30 +227,32 @@
     EXPECT_EQ(nav_entry->get()->url, test_server_.GetURL(kBlankAppPath).spec());
   }
 
-  // Verify that the component is torn down when |component_controller_ptr| is
+  // Verify that the component is torn down when |component_controller| is
   // unbound, by observing the destruction of its child Interfaces.
   base::RunLoop destruction_run_loop;
   nav_controller.set_error_handler(
       [&destruction_run_loop](zx_status_t) { destruction_run_loop.Quit(); });
-  component_controller_ptr.Unbind();
+  component_controller.Unbind();
   destruction_run_loop.Run();
 }
 
 TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) {
-  // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller_ptr;
-  base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent(
-      "cast:99999999", &cast_runner_ptr_, component_controller_ptr.NewRequest(),
-      &cast_channel_binding_));
-  component_controller_ptr.set_error_handler(&ComponentErrorHandler);
+  // Launch the a component with an invalid Cast app Id.
+  fuchsia::sys::ComponentControllerPtr component_controller =
+      StartCastComponent("cast:99999999");
 
-  // Ensure no WebComponent was created.
+  // Run the loop until the ComponentController is dropped, or a WebComponent is
+  // created.
   base::RunLoop run_loop;
+  component_controller.set_error_handler([&run_loop](zx_status_t status) {
+    EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
+    run_loop.Quit();
+  });
   cr_fuchsia::ResultReceiver<WebComponent*> web_component(
       run_loop.QuitClosure());
   cast_runner_->GetWebComponentForTest(web_component.GetReceiveCallback());
   run_loop.Run();
-  EXPECT_EQ(*web_component, nullptr);
+  EXPECT_FALSE(web_component.has_value());
 }
 
 TEST_F(CastRunnerIntegrationTest, CastChannel) {
@@ -212,11 +262,9 @@
                                     test_server_.GetURL(kCastChannelAppPath));
 
   // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller_ptr;
-  base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent(
-      base::StringPrintf("cast:%s", kCastChannelAppId), &cast_runner_ptr_,
-      component_controller_ptr.NewRequest(), &cast_channel_binding_));
-  component_controller_ptr.set_error_handler(&ComponentErrorHandler);
+  fuchsia::sys::ComponentControllerPtr component_controller =
+      StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId));
+  component_controller.set_error_handler(&ComponentErrorHandler);
 
   // Access the NavigationController from the WebComponent. The test will hang
   // here if no WebComponent was created.
@@ -252,7 +300,7 @@
     base::RunLoop run_loop;
     cr_fuchsia::ResultReceiver<chromium::web::WebMessage> message(
         run_loop.QuitClosure());
-    connected_channel_->ReceiveMessage(
+    cast_channel_.connected_channel()->ReceiveMessage(
         cr_fuchsia::CallbackToFitFunction(message.GetReceiveCallback()));
     run_loop.Run();
 
@@ -260,10 +308,10 @@
               expected);
   }
 
-  component_controller_ptr.Unbind();
+  // Shutdown the component and wait for the CastChannel to close.
+  component_controller.Unbind();
   base::RunLoop run_loop;
-  cast_channel_binding_.set_error_handler(
-      [&run_loop](zx_status_t) { run_loop.Quit(); });
+  cast_channel_binding_->SetOnLastClientCallback(run_loop.QuitClosure());
   run_loop.Run();
 }
 
@@ -274,20 +322,18 @@
                                     test_server_.GetURL(kCastChannelAppPath));
 
   // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller_ptr;
-  base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent(
-      base::StringPrintf("cast:%s", kCastChannelAppId), &cast_runner_ptr_,
-      component_controller_ptr.NewRequest(), &cast_channel_binding_));
+  fuchsia::sys::ComponentControllerPtr component_controller =
+      StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId));
 
   // Expect that disconnecting the Cast Channel consumer service will trigger
   // the destruction of the Cast Component.
-  cast_channel_binding_.Unbind();
+  cast_channel_binding_.reset();
   base::RunLoop run_loop;
-  component_controller_ptr.set_error_handler(
+  component_controller.set_error_handler(
       [&run_loop](zx_status_t) { run_loop.Quit(); });
   run_loop.Run();
 
-  EXPECT_FALSE(component_controller_ptr.is_bound());
+  EXPECT_FALSE(component_controller.is_bound());
 }
 
 TEST_F(CastRunnerIntegrationTest, CastChannelComponentControllerDropped) {
@@ -297,18 +343,15 @@
                                     test_server_.GetURL(kCastChannelAppPath));
 
   // Launch the test-app component.
-  fuchsia::sys::ComponentControllerPtr component_controller_ptr;
-  base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent(
-      base::StringPrintf("cast:%s", kCastChannelAppId), &cast_runner_ptr_,
-      component_controller_ptr.NewRequest(), &cast_channel_binding_));
+  fuchsia::sys::ComponentControllerPtr component_controller =
+      StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId));
 
   // Expect that disconnecting the ComponentController will kill the Cast
   // Component, which we verify indirectly by listening for the disconnection
   // of one of its CastChannel FIDL client.
-  component_controller_ptr.Unbind();
+  component_controller.Unbind();
   base::RunLoop run_loop;
-  cast_channel_binding_.set_error_handler(
-      [&run_loop](zx_status_t) { run_loop.Quit(); });
+  cast_channel_binding_->SetOnLastClientCallback(run_loop.QuitClosure());
   run_loop.Run();
 }
 
diff --git a/fuchsia/runners/cast/test_common.cc b/fuchsia/runners/cast/test_common.cc
deleted file mode 100644
index db08a55..0000000
--- a/fuchsia/runners/cast/test_common.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fuchsia/runners/cast/test_common.h"
-
-#include <lib/fidl/cpp/binding.h>
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/service_directory.h"
-#include "base/run_loop.h"
-
-fidl::InterfaceHandle<fuchsia::io::Directory> StartCastComponent(
-    const base::StringPiece& cast_url,
-    fuchsia::sys::RunnerPtr* sys_runner,
-    fidl::InterfaceRequest<fuchsia::sys::ComponentController>
-        component_controller_request,
-    fidl::Binding<chromium::cast::CastChannel>* cast_channel_binding) {
-  // Construct, bind, and populate a ServiceDirectory for publishing
-  // the CastChannel service to the CastComponent.
-  fidl::InterfaceHandle<fuchsia::io::Directory> directory;
-  base::fuchsia::ServiceDirectory cast_channel_directory(
-      directory.NewRequest());
-  base::RunLoop service_connect_runloop;
-  cast_channel_directory.AddService(base::BindRepeating(
-      [](base::RepeatingClosure on_connect_cb,
-         fidl::Binding<chromium::cast::CastChannel>* cast_channel_binding_,
-         fidl::InterfaceRequest<chromium::cast::CastChannel> request) {
-        cast_channel_binding_->Bind(std::move(request));
-        on_connect_cb.Run();
-      },
-      base::Passed(service_connect_runloop.QuitClosure()),
-      base::Unretained(cast_channel_binding)));
-
-  // Configure the Runner, including a service directory channel to publish
-  // services to.
-  fuchsia::sys::StartupInfo startup_info;
-  startup_info.launch_info.url = cast_url.as_string();
-
-  fidl::InterfaceHandle<fuchsia::io::Directory> component_services;
-  startup_info.launch_info.directory_request =
-      component_services.NewRequest().TakeChannel();
-
-  // Publish the ServiceDirectory into the FlatNamespace for CastChannel to be
-  // picked up from.
-  startup_info.flat_namespace.paths.emplace_back("/svc");
-  startup_info.flat_namespace.directories.emplace_back(directory.TakeChannel());
-
-  fuchsia::sys::Package package;
-  package.resolved_url = cast_url.as_string();
-
-  sys_runner->get()->StartComponent(std::move(package), std::move(startup_info),
-                                    std::move(component_controller_request));
-
-  // Process the runloop until the CastChannel FIDL service is connected.
-  service_connect_runloop.Run();
-
-  // Prepare the service directory for clean teardown.
-  cast_channel_directory.RemoveAllServices();
-
-  return component_services;
-}
diff --git a/fuchsia/runners/cast/test_common.h b/fuchsia/runners/cast/test_common.h
deleted file mode 100644
index ec1c20fd..0000000
--- a/fuchsia/runners/cast/test_common.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FUCHSIA_RUNNERS_CAST_TEST_COMMON_H_
-#define FUCHSIA_RUNNERS_CAST_TEST_COMMON_H_
-
-#include <fuchsia/sys/cpp/fidl.h>
-#include <lib/fidl/cpp/binding.h>
-
-#include "base/strings/string_piece.h"
-#include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
-
-namespace fuchsia {
-namespace io {
-class Directory;
-}
-}  // namespace fuchsia
-
-// Starts a Cast component from the runner |sys_runner| with the URL |cast_url|
-// and returns the outgoing service directory client channel.
-// The Cast component will connect to the CastChannel FIDL service bound at
-// |cast_channel_binding|.
-// Blocks until |cast_channel_binding| is bound.
-fidl::InterfaceHandle<fuchsia::io::Directory> StartCastComponent(
-    const base::StringPiece& cast_url,
-    fuchsia::sys::RunnerPtr* sys_runner,
-    fidl::InterfaceRequest<fuchsia::sys::ComponentController>
-        component_controller_request,
-    fidl::Binding<chromium::cast::CastChannel>* cast_channel_binding);
-
-#endif  // FUCHSIA_RUNNERS_CAST_TEST_COMMON_H_
diff --git a/fuchsia/runners/common/web_component.cc b/fuchsia/runners/common/web_component.cc
index e72000c9..eeb7cbbc 100644
--- a/fuchsia/runners/common/web_component.cc
+++ b/fuchsia/runners/common/web_component.cc
@@ -61,15 +61,18 @@
   // Create the underlying Frame and get its NavigationController.
   runner_->context()->CreateFrame(frame_.NewRequest());
 
-  // Publish ViewProvider before returning control to the message-loop, to
-  // ensure that it is available before the ServiceDirectory starts processing
-  // requests.
-  view_provider_binding_ = std::make_unique<
-      base::fuchsia::ScopedServiceBinding<fuchsia::ui::app::ViewProvider>>(
-      startup_context()->public_services(), this);
-  legacy_view_provider_binding_ = std::make_unique<
-      base::fuchsia::ScopedServiceBinding<fuchsia::ui::viewsv1::ViewProvider>>(
-      startup_context()->public_services(), this);
+  if (startup_context()->public_services()) {
+    // Publish ViewProvider before returning control to the message-loop, to
+    // ensure that it is available before the ServiceDirectory starts processing
+    // requests.
+    view_provider_binding_ = std::make_unique<
+        base::fuchsia::ScopedServiceBinding<fuchsia::ui::app::ViewProvider>>(
+        startup_context()->public_services(), this);
+    legacy_view_provider_binding_ =
+        std::make_unique<base::fuchsia::ScopedServiceBinding<
+            fuchsia::ui::viewsv1::ViewProvider>>(
+            startup_context()->public_services(), this);
+  }
 }
 
 void WebComponent::Kill() {
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 84330d4..fc00b99 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -223,7 +223,7 @@
     console_ids: "chromium/chromium.mac"
     console_ids: "chromium/chromium.linux"
     console_ids: "chromium/chromium.chromiumos"
-    console_ids: "chromium/chromium.chrome"
+    console_ids: "chrome/chromium.chrome"
     console_ids: "chromium/chromium.memory"
     console_ids: "chromium/chromium.gpu"
   }
@@ -622,22 +622,22 @@
     short_name: "kvn"
   }
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome Win"
+    name: "buildbucket/luci.chrome.ci/Google Chrome Win"
     category: "chromium.chrome"
     short_name: "win"
   }
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome Linux x64"
+    name: "buildbucket/luci.chrome.ci/Google Chrome Linux x64"
     category: "chromium.chrome"
     short_name: "lnx"
   }
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome Mac"
+    name: "buildbucket/luci.chrome.ci/Google Chrome Mac"
     category: "chromium.chrome"
     short_name: "mac"
   }
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome ChromeOS"
+    name: "buildbucket/luci.chrome.ci/Google Chrome ChromeOS"
     category: "chromium.chrome"
     short_name: "cro"
   }
@@ -1057,19 +1057,19 @@
   refs: "refs/heads/master"
   manifest_name: "REVISION"
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome Win"
+    name: "buildbucket/luci.chrome.ci/Google Chrome Win"
     short_name: "win"
   }
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome Linux x64"
+    name: "buildbucket/luci.chrome.ci/Google Chrome Linux x64"
     short_name: "lnx"
   }
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome Mac"
+    name: "buildbucket/luci.chrome.ci/Google Chrome Mac"
     short_name: "mac"
   }
   builders {
-    name: "buildbot/chromium.chrome/Google Chrome ChromeOS"
+    name: "buildbucket/luci.chrome.ci/Google Chrome ChromeOS"
     short_name: "cro"
   }
 }
diff --git a/ios/chrome/browser/main/BUILD.gn b/ios/chrome/browser/main/BUILD.gn
index 9d64d50..297d778 100644
--- a/ios/chrome/browser/main/BUILD.gn
+++ b/ios/chrome/browser/main/BUILD.gn
@@ -11,6 +11,7 @@
   deps = [
     "//base",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/web_state_list",
   ]
diff --git a/ios/chrome/browser/main/browser.h b/ios/chrome/browser/main/browser.h
index 100e738..e846213 100644
--- a/ios/chrome/browser/main/browser.h
+++ b/ios/chrome/browser/main/browser.h
@@ -24,16 +24,15 @@
 // See src/docs/ios/objects.md for more information.
 class Browser : public base::SupportsUserData {
  public:
-  // Creates a new Browser attached to |browser_state|. The |tab_model| must be
-  // non-nil.
-  static std::unique_ptr<Browser> Create(ios::ChromeBrowserState* browser_state,
-                                         TabModel* tab_model);
+  // Creates a new Browser attached to |browser_state|.
+  static std::unique_ptr<Browser> Create(
+      ios::ChromeBrowserState* browser_state);
   ~Browser() override {}
 
   // Accessor for the owning ChromeBrowserState.
   virtual ios::ChromeBrowserState* GetBrowserState() const = 0;
 
-  // Accessor for the TabModel. DEPRECATED: prefer web_state_list() whenever
+  // Accessor for the TabModel. DEPRECATED: prefer GetWebStateList() whenever
   // possible.
   virtual TabModel* GetTabModel() const = 0;
 
diff --git a/ios/chrome/browser/main/browser_impl.h b/ios/chrome/browser/main/browser_impl.h
index b9c8b3f..1d17091 100644
--- a/ios/chrome/browser/main/browser_impl.h
+++ b/ios/chrome/browser/main/browser_impl.h
@@ -7,6 +7,7 @@
 
 #import "ios/chrome/browser/main/browser.h"
 
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 
 @class TabModel;
@@ -23,27 +24,25 @@
 // See src/docs/ios/objects.md for more information.
 class BrowserImpl : public Browser {
  public:
-  // Constructs a BrowserImpl attached to |browser_state|. The |tab_model| must
-  // be non-nil.
-  BrowserImpl(ios::ChromeBrowserState* browser_state, TabModel* tab_model);
+  // Constructs a BrowserImpl attached to |browser_state|.
+  explicit BrowserImpl(ios::ChromeBrowserState* browser_state);
   ~BrowserImpl() override;
 
-  // Accessor for the owning ChromeBrowserState.
+  // Browser.
   ios::ChromeBrowserState* GetBrowserState() const override;
-
-  // Accessor for the TabModel. DEPRECATED: prefer web_state_list() whenever
-  // possible.
   TabModel* GetTabModel() const override;
-
-  // Accessor for the WebStateList.
   WebStateList* GetWebStateList() const override;
 
  private:
+  // Exposed to allow unittests to pass in a mock TabModel.
+  FRIEND_TEST_ALL_PREFIXES(BrowserImplTest, TestAccessors);
+  BrowserImpl(ios::ChromeBrowserState* browser_state, TabModel* tab_model);
+
   ios::ChromeBrowserState* browser_state_;
   __strong TabModel* tab_model_;
   WebStateList* web_state_list_;
 
-  DISALLOW_IMPLICIT_CONSTRUCTORS(BrowserImpl);
+  DISALLOW_COPY_AND_ASSIGN(BrowserImpl);
 };
 
 #endif  // IOS_CHROME_BROWSER_MAIN_BROWSER_IMPL_H_
diff --git a/ios/chrome/browser/main/browser_impl.mm b/ios/chrome/browser/main/browser_impl.mm
index ddba98e..3f0eded3 100644
--- a/ios/chrome/browser/main/browser_impl.mm
+++ b/ios/chrome/browser/main/browser_impl.mm
@@ -6,12 +6,23 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/sessions/session_service_ios.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+BrowserImpl::BrowserImpl(ios::ChromeBrowserState* browser_state)
+    : browser_state_(browser_state) {
+  DCHECK(browser_state_);
+  tab_model_ =
+      [[TabModel alloc] initWithSessionService:[SessionServiceIOS sharedService]
+                                  browserState:browser_state_];
+  web_state_list_ = tab_model_.webStateList;
+}
+
 BrowserImpl::BrowserImpl(ios::ChromeBrowserState* browser_state,
                          TabModel* tab_model)
     : browser_state_(browser_state), tab_model_(tab_model) {
@@ -35,7 +46,7 @@
 }
 
 // static
-std::unique_ptr<Browser> Browser::Create(ios::ChromeBrowserState* browser_state,
-                                         TabModel* tab_model) {
-  return std::make_unique<BrowserImpl>(browser_state, tab_model);
+std::unique_ptr<Browser> Browser::Create(
+    ios::ChromeBrowserState* browser_state) {
+  return std::make_unique<BrowserImpl>(browser_state);
 }
diff --git a/ios/chrome/browser/main/browser_impl_unittest.mm b/ios/chrome/browser/main/browser_impl_unittest.mm
index fc3fe63..2508e26b 100644
--- a/ios/chrome/browser/main/browser_impl_unittest.mm
+++ b/ios/chrome/browser/main/browser_impl_unittest.mm
@@ -29,6 +29,8 @@
   void WebStateDetached(web::WebState* web_state) override {}
 };
 
+}  // namespace
+
 class BrowserImplTest : public PlatformTest {
  protected:
   BrowserImplTest()
@@ -55,5 +57,3 @@
   EXPECT_EQ(tab_model_, browser.GetTabModel());
   EXPECT_EQ(&web_state_list_, browser.GetWebStateList());
 }
-
-}  // namespace
diff --git a/ios/chrome/browser/ui/authentication/cells/account_control_item.mm b/ios/chrome/browser/ui/authentication/cells/account_control_item.mm
index d82029b..b211c8d 100644
--- a/ios/chrome/browser/ui/authentication/cells/account_control_item.mm
+++ b/ios/chrome/browser/ui/authentication/cells/account_control_item.mm
@@ -101,7 +101,7 @@
 
   _detailTextLabel = [[UILabel alloc] init];
   _detailTextLabel.font =
-      [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+      [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
   _detailTextLabel.adjustsFontForContentSizeCategory = YES;
   _detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
   _detailTextLabel.numberOfLines = 0;
diff --git a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
index 66c763d..b2de2ad3 100644
--- a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
+++ b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
@@ -131,7 +131,7 @@
   _detailTextLabel = [[UILabel alloc] init];
   _detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
   _detailTextLabel.font =
-      [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+      [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
   _detailTextLabel.adjustsFontForContentSizeCategory = YES;
   _detailTextLabel.textColor = UIColorFromRGB(kSettingsCellsDetailTextColor);
   [contentView addSubview:_detailTextLabel];
@@ -169,10 +169,10 @@
         constraintEqualToAnchor:contentView.centerYAnchor],
     [_imageView.topAnchor
         constraintGreaterThanOrEqualToAnchor:contentView.topAnchor
-                                    constant:kTableViewVerticalSpacing],
+                                    constant:kTableViewLargeVerticalSpacing],
     [_imageView.bottomAnchor
         constraintLessThanOrEqualToAnchor:contentView.bottomAnchor
-                                 constant:-kTableViewVerticalSpacing],
+                                 constant:-kTableViewLargeVerticalSpacing],
     [_textLabel.topAnchor
         constraintEqualToAnchor:verticalCenteringView.topAnchor],
     [_textLabel.bottomAnchor
@@ -185,12 +185,10 @@
         constraintEqualToAnchor:contentView.centerYAnchor],
     [verticalCenteringView.topAnchor
         constraintGreaterThanOrEqualToAnchor:contentView.topAnchor
-                                    constant:
-                                        kTableViewTwoLabelsCellVerticalSpacing],
+                                    constant:kTableViewVerticalSpacing],
     [verticalCenteringView.bottomAnchor
         constraintLessThanOrEqualToAnchor:contentView.bottomAnchor
-                                 constant:
-                                     kTableViewTwoLabelsCellVerticalSpacing],
+                                 constant:kTableViewVerticalSpacing],
 
     // Set trailing anchors.
     [_errorIcon.trailingAnchor
diff --git a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm b/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm
index 38c1f49..d16f5c3 100644
--- a/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm
+++ b/ios/chrome/browser/ui/autofill/cells/autofill_edit_item.mm
@@ -214,9 +214,9 @@
           constraintEqualToAnchor:_editIconView.heightAnchor],
     ]];
     AddOptionalVerticalPadding(contentView, _textLabel,
-                               kTableViewOneLabelCellVerticalSpacing);
+                               kTableViewLargeVerticalSpacing);
     AddOptionalVerticalPadding(contentView, _textField,
-                               kTableViewOneLabelCellVerticalSpacing);
+                               kTableViewLargeVerticalSpacing);
 
     [self updateForAccessibilityContentSizeCategory:
               UIContentSizeCategoryIsAccessibilityCategory(
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index fb68015..bb668f9 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -643,6 +643,9 @@
 }
 
 - (void)testOpeningNewTab {
+  // TODO(crbug.com/933953): Re-enable this test when grey_tap() issue is fixed.
+  EARL_GREY_TEST_DISABLED(@"Disabled due to full-configs and slimnav failures");
+
   [ChromeEarlGreyUI openNewTab];
 
   // Check that the fake omnibox is here.
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_ui_element.h b/ios/chrome/browser/ui/fullscreen/fullscreen_ui_element.h
index 98b55b9..924e65b2 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_ui_element.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_ui_element.h
@@ -17,9 +17,18 @@
 // value denotes that the toolbar should be completely visible, and a |progress|
 // value of 0.0 denotes that the toolbar should be completely hidden.
 //
-// NOTE: Since this selector is called for every scroll offset, it's not
-// optional, as checking |-respondsToSelector:| for every FullscreenUIElement
-// at every scroll offset can introduce performance issues.
+// This selector is called for every scroll offset, it's not optional, as
+// checking |-respondsToSelector:| for every FullscreenUIElement at every scroll
+// offset can introduce performance issues.
+//
+// If the implementation of this selector uses batched layout updates (e.g.
+// updating the UI in |-layoutSubviews| or by updating constraints), then a
+// layout pass should be forced using |-setNeedsLayout| and |-layoutIfNeeded|.
+// This will ensure that the layout is updated for each scroll position rather
+// than batching multiple fullscreen progress updates together.  This is
+// especially important for FullscreenUIElements that do not implement
+// |-animateFullscreenWithAnimator:|, as this selctor is called by
+// FullscreenUIUpdater in an animation block.
 - (void)updateForFullscreenProgress:(CGFloat)progress;
 
 @optional
diff --git a/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm b/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm
index 2b2c5710..3049228 100644
--- a/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm
@@ -134,6 +134,8 @@
       [(id<FullscreenUIElement>)view updateForFullscreenProgress:progress];
     }
   }
+  [self.view setNeedsLayout];
+  [self.view layoutIfNeeded];
 }
 
 #pragma mark - Private Methods
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler.mm b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
index db900cc..7880fb8 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
@@ -153,14 +153,11 @@
 }
 
 - (void)createMainBrowser {
-  TabModel* tabModel =
-      [[TabModel alloc] initWithSessionService:[SessionServiceIOS sharedService]
-                                  browserState:_browserState];
-  [self setUpTabModel:tabModel
+  _mainBrowser = Browser::Create(_browserState);
+  [self setUpTabModel:_mainBrowser->GetTabModel()
            withBrowserState:_browserState
       restorePersistedState:YES];
 
-  _mainBrowser = Browser::Create(_browserState, tabModel);
   // Follow loaded URLs in the main tab model to send those in case of
   // crashes.
   breakpad::MonitorURLsForTabModel(self.mainBrowser->GetTabModel());
@@ -377,13 +374,12 @@
   ios::ChromeBrowserState* otrBrowserState =
       _browserState->GetOffTheRecordChromeBrowserState();
   DCHECK(otrBrowserState);
-  TabModel* tabModel =
-      [[TabModel alloc] initWithSessionService:[SessionServiceIOS sharedService]
-                                  browserState:otrBrowserState];
-  [self setUpTabModel:tabModel
+
+  std::unique_ptr<Browser> browser = Browser::Create(otrBrowserState);
+  [self setUpTabModel:browser->GetTabModel()
            withBrowserState:otrBrowserState
       restorePersistedState:restorePersistedState];
-  return Browser::Create(otrBrowserState, tabModel);
+  return browser;
 }
 
 - (void)setUpTabModel:(TabModel*)tabModel
diff --git a/ios/chrome/browser/ui/settings/cells/autofill_data_item.mm b/ios/chrome/browser/ui/settings/cells/autofill_data_item.mm
index a08af56..9728170 100644
--- a/ios/chrome/browser/ui/settings/cells/autofill_data_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/autofill_data_item.mm
@@ -20,6 +20,12 @@
 
 @implementation AutofillDataItem
 
+@synthesize deletable = _deletable;
+@synthesize GUID = _GUID;
+@synthesize text = _text;
+@synthesize leadingDetailText = _leadingDetailText;
+@synthesize trailingDetailText = _trailingDetailText;
+
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
   if (self) {
@@ -40,12 +46,6 @@
 
 @end
 
-#pragma mark - AutofillDataCell
-
-@interface AutofillDataCell ()
-@property(nonatomic, strong) UIStackView* mainLabelsContainer;
-@end
-
 @implementation AutofillDataCell
 
 @synthesize textLabel = _textLabel;
@@ -67,13 +67,12 @@
   UIView* contentView = self.contentView;
 
   _textLabel = [[UILabel alloc] init];
-  _leadingDetailTextLabel = [[UILabel alloc] init];
+  _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  [contentView addSubview:_textLabel];
 
-  _mainLabelsContainer = [[UIStackView alloc]
-      initWithArrangedSubviews:@[ _textLabel, _leadingDetailTextLabel ]];
-  _mainLabelsContainer.translatesAutoresizingMaskIntoConstraints = NO;
-  _mainLabelsContainer.axis = UILayoutConstraintAxisVertical;
-  [contentView addSubview:_mainLabelsContainer];
+  _leadingDetailTextLabel = [[UILabel alloc] init];
+  _leadingDetailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  [contentView addSubview:_leadingDetailTextLabel];
 
   _trailingDetailTextLabel = [[UILabel alloc] init];
   _trailingDetailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
@@ -95,7 +94,7 @@
   _leadingDetailTextLabel.numberOfLines = 0;
   _leadingDetailTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
   _leadingDetailTextLabel.font =
-      [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+      [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
   _leadingDetailTextLabel.adjustsFontForContentSizeCategory = YES;
   _leadingDetailTextLabel.textColor =
       UIColorFromRGB(kSettingsCellsDetailTextColor);
@@ -112,12 +111,16 @@
 
   [NSLayoutConstraint activateConstraints:@[
     // Set horizontal anchors.
-    [_mainLabelsContainer.leadingAnchor
+    [_textLabel.leadingAnchor
         constraintEqualToAnchor:contentView.leadingAnchor
                        constant:kTableViewHorizontalSpacing],
-    [_mainLabelsContainer.trailingAnchor
+    [_textLabel.trailingAnchor
         constraintLessThanOrEqualToAnchor:_trailingDetailTextLabel.leadingAnchor
                                  constant:-kTableViewHorizontalSpacing],
+    [_leadingDetailTextLabel.leadingAnchor
+        constraintEqualToAnchor:_textLabel.leadingAnchor],
+    [_leadingDetailTextLabel.trailingAnchor
+        constraintEqualToAnchor:_textLabel.trailingAnchor],
     [_trailingDetailTextLabel.trailingAnchor
         constraintEqualToAnchor:contentView.trailingAnchor
                        constant:-kTableViewHorizontalSpacing],
@@ -128,18 +131,16 @@
                                multiplier:kDetailTextWidthMultiplier],
 
     // Set vertical anchors.
-    [_mainLabelsContainer.centerYAnchor
-        constraintEqualToAnchor:contentView.centerYAnchor],
+    [_leadingDetailTextLabel.topAnchor
+        constraintEqualToAnchor:_textLabel.bottomAnchor],
     [_trailingDetailTextLabel.centerYAnchor
         constraintEqualToAnchor:contentView.centerYAnchor],
   ]];
 
-  AddOptionalVerticalPadding(contentView, _mainLabelsContainer
-
-                             ,
-                             kTableViewTwoLabelsCellVerticalSpacing);
+  AddOptionalVerticalPadding(contentView, _textLabel, _leadingDetailTextLabel,
+                             kTableViewLargeVerticalSpacing);
   AddOptionalVerticalPadding(contentView, _trailingDetailTextLabel,
-                             kTableViewOneLabelCellVerticalSpacing);
+                             kTableViewLargeVerticalSpacing);
 }
 
 #pragma mark - UITableViewCell
diff --git a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
index 5a2dea1..1185dc40 100644
--- a/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.mm
@@ -77,7 +77,7 @@
           constraintEqualToAnchor:_textLabel.firstBaselineAnchor],
     ]];
     AddOptionalVerticalPadding(contentView, _textLabel,
-                               kTableViewOneLabelCellVerticalSpacing);
+                               kTableViewLargeVerticalSpacing);
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/settings/cells/passphrase_error_item.mm b/ios/chrome/browser/ui/settings/cells/passphrase_error_item.mm
index 303cced..db9b230 100644
--- a/ios/chrome/browser/ui/settings/cells/passphrase_error_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/passphrase_error_item.mm
@@ -4,12 +4,15 @@
 
 #import "ios/chrome/browser/ui/settings/cells/passphrase_error_item.h"
 
-#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
-
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Padding used on the leading and trailing edges of the cell.
+const CGFloat kHorizontalPadding = 16;
+}  // namespace
+
 @implementation PassphraseErrorItem
 
 @synthesize text = _text;
@@ -62,22 +65,17 @@
     [NSLayoutConstraint activateConstraints:@[
       [_errorImageView.leadingAnchor
           constraintEqualToAnchor:contentView.leadingAnchor
-                         constant:kTableViewHorizontalSpacing],
+                         constant:kHorizontalPadding],
       [_textLabel.leadingAnchor
           constraintEqualToAnchor:_errorImageView.trailingAnchor
-                         constant:kTableViewHorizontalSpacing],
+                         constant:kHorizontalPadding],
       [_textLabel.trailingAnchor
           constraintEqualToAnchor:contentView.trailingAnchor
-                         constant:-kTableViewHorizontalSpacing],
+                         constant:-kHorizontalPadding],
       [_errorImageView.centerYAnchor
           constraintEqualToAnchor:contentView.centerYAnchor],
-      [_textLabel.topAnchor
-          constraintEqualToAnchor:contentView.topAnchor
-                         constant:kTableViewOneLabelCellVerticalSpacing],
-      [_textLabel.bottomAnchor
-          constraintEqualToAnchor:contentView.bottomAnchor
-                         constant:-kTableViewOneLabelCellVerticalSpacing],
-
+      [_textLabel.centerYAnchor
+          constraintEqualToAnchor:contentView.centerYAnchor],
     ]];
 
     [_errorImageView
diff --git a/ios/chrome/browser/ui/settings/cells/settings_detail_item.mm b/ios/chrome/browser/ui/settings/cells/settings_detail_item.mm
index 42bd261..fc9c768 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_detail_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/settings_detail_item.mm
@@ -7,7 +7,6 @@
 #include <algorithm>
 
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
@@ -18,9 +17,16 @@
 
 namespace {
 
+// Padding used on the leading and trailing edges of the cell and between the
+// two labels.
+const CGFloat kHorizontalPadding = 16;
+
 // Padding used between the icon and the text labels.
 const CGFloat kIconTrailingPadding = 12;
 
+// Padding used on the top and bottom edges of the cell.
+const CGFloat kVerticalPadding = 16;
+
 // Size of the icon image.
 const CGFloat kIconImageSize = 28;
 
@@ -135,7 +141,7 @@
     // these will be active at a time, defaulting to hidden.
     _iconHiddenConstraint = [_labelContainerGuide.leadingAnchor
         constraintEqualToAnchor:contentView.leadingAnchor
-                       constant:kTableViewHorizontalSpacing];
+                       constant:kHorizontalPadding];
     _iconVisibleConstraint = [_labelContainerGuide.leadingAnchor
         constraintEqualToAnchor:_iconImageView.trailingAnchor
                        constant:kIconTrailingPadding];
@@ -154,30 +160,29 @@
     ];
 
     _accessibilityConstraints = @[
-      [_textLabel.topAnchor
-          constraintEqualToAnchor:self.contentView.topAnchor
-                         constant:kTableViewLargeVerticalSpacing],
+      [_textLabel.topAnchor constraintEqualToAnchor:self.contentView.topAnchor
+                                           constant:kVerticalPadding],
       [_detailTextLabel.bottomAnchor
           constraintEqualToAnchor:self.contentView.bottomAnchor
-                         constant:-kTableViewLargeVerticalSpacing],
+                         constant:-kVerticalPadding],
       [_textLabel.bottomAnchor
           constraintEqualToAnchor:_detailTextLabel.topAnchor
-                         constant:-kTableViewLargeVerticalSpacing],
+                         constant:-kVerticalPadding],
       [_textLabel.trailingAnchor
           constraintLessThanOrEqualToAnchor:self.contentView.trailingAnchor
-                                   constant:-kTableViewHorizontalSpacing],
+                                   constant:-kHorizontalPadding],
       [_detailTextLabel.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor
-                         constant:kTableViewHorizontalSpacing],
+                         constant:kHorizontalPadding],
       [_detailTextLabel.trailingAnchor
           constraintLessThanOrEqualToAnchor:_labelContainerGuide.trailingAnchor
-                                   constant:-kTableViewHorizontalSpacing],
+                                   constant:-kHorizontalPadding],
     ];
 
     [NSLayoutConstraint activateConstraints:@[
       [_iconImageView.leadingAnchor
           constraintEqualToAnchor:contentView.leadingAnchor
-                         constant:kTableViewHorizontalSpacing],
+                         constant:kHorizontalPadding],
       [_iconImageView.widthAnchor constraintEqualToConstant:kIconImageSize],
       [_iconImageView.heightAnchor constraintEqualToConstant:kIconImageSize],
 
@@ -186,15 +191,14 @@
           constraintEqualToAnchor:_labelContainerGuide.leadingAnchor],
       [_labelContainerGuide.trailingAnchor
           constraintEqualToAnchor:contentView.trailingAnchor
-                         constant:-kTableViewHorizontalSpacing],
+                         constant:-kHorizontalPadding],
 
       [_iconImageView.centerYAnchor
           constraintEqualToAnchor:contentView.centerYAnchor],
       _iconHiddenConstraint,
     ]];
 
-    AddOptionalVerticalPadding(contentView, _textLabel,
-                               kTableViewOneLabelCellVerticalSpacing);
+    AddOptionalVerticalPadding(contentView, _textLabel, kVerticalPadding);
 
     [self updateForAccessibilityContentSizeCategory:
               UIContentSizeCategoryIsAccessibilityCategory(
@@ -282,8 +286,8 @@
 }
 
 - (CGFloat)textLabelTargetWidth {
-  CGFloat availableWidth = CGRectGetWidth(_labelContainerGuide.layoutFrame) -
-                           kTableViewHorizontalSpacing;
+  CGFloat availableWidth =
+      CGRectGetWidth(_labelContainerGuide.layoutFrame) - kHorizontalPadding;
   CGFloat textLabelWidth = self.textLabel.frame.size.width;
   CGFloat detailTextLabelWidth = self.detailTextLabel.frame.size.width;
 
@@ -296,8 +300,8 @@
 }
 
 - (CGFloat)detailTextLabelTargetWidth {
-  CGFloat availableWidth = CGRectGetWidth(_labelContainerGuide.layoutFrame) -
-                           kTableViewHorizontalSpacing;
+  CGFloat availableWidth =
+      CGRectGetWidth(_labelContainerGuide.layoutFrame) - kHorizontalPadding;
   CGFloat textLabelWidth = self.textLabel.frame.size.width;
   CGFloat detailTextLabelWidth = self.detailTextLabel.frame.size.width;
 
diff --git a/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm b/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm
index 897985c..63effad 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm
+++ b/ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.mm
@@ -95,20 +95,18 @@
         constraintEqualToAnchor:contentView.centerYAnchor],
     [_imageView.topAnchor
         constraintGreaterThanOrEqualToAnchor:contentView.topAnchor
-                                    constant:kTableViewVerticalSpacing],
+                                    constant:kTableViewLargeVerticalSpacing],
     [contentView.bottomAnchor
         constraintGreaterThanOrEqualToAnchor:_imageView.bottomAnchor
-                                    constant:kTableViewVerticalSpacing],
+                                    constant:kTableViewLargeVerticalSpacing],
     [textStackView.centerYAnchor
         constraintEqualToAnchor:contentView.centerYAnchor],
     [textStackView.topAnchor
         constraintGreaterThanOrEqualToAnchor:contentView.topAnchor
-                                    constant:
-                                        kTableViewTwoLabelsCellVerticalSpacing],
+                                    constant:kTableViewLargeVerticalSpacing],
     [contentView.bottomAnchor
         constraintGreaterThanOrEqualToAnchor:textStackView.bottomAnchor
-                                    constant:
-                                        kTableViewTwoLabelsCellVerticalSpacing],
+                                    constant:kTableViewLargeVerticalSpacing],
   ]];
 }
 
diff --git a/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm b/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm
index 1f58e001..2488f97 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.mm
@@ -61,7 +61,7 @@
     _detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
     _detailTextLabel.numberOfLines = 0;
     _detailTextLabel.font =
-        [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+        [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     _detailTextLabel.adjustsFontForContentSizeCategory = YES;
     _detailTextLabel.textColor = UIColorFromRGB(kSettingsCellsDetailTextColor);
     [contentView addSubview:_detailTextLabel];
diff --git a/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm b/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm
index 0b6c1e3..ea54139 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm
+++ b/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm
@@ -19,6 +19,9 @@
 // Padding used between the icon and the text labels.
 const CGFloat kIconTrailingPadding = 12;
 
+// Padding used on the top and bottom edges of the cell.
+const CGFloat kVerticalPadding = 16;
+
 // Size of the icon image.
 const CGFloat kIconImageSize = 28;
 }  // namespace
@@ -113,13 +116,13 @@
     _accessibilityConstraints = @[
       [_switchView.topAnchor
           constraintEqualToAnchor:textLayoutGuide.bottomAnchor
-                         constant:kTableViewLargeVerticalSpacing],
+                         constant:kVerticalPadding],
       [_switchView.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor
                          constant:kTableViewHorizontalSpacing],
       [_switchView.bottomAnchor
           constraintEqualToAnchor:self.contentView.bottomAnchor
-                         constant:-kTableViewLargeVerticalSpacing],
+                         constant:-kVerticalPadding],
       [textLayoutGuide.trailingAnchor
           constraintLessThanOrEqualToAnchor:self.contentView.trailingAnchor
                                    constant:-kTableViewHorizontalSpacing],
@@ -160,7 +163,7 @@
     }
 
     AddOptionalVerticalPadding(self.contentView, textLayoutGuide,
-                               kTableViewOneLabelCellVerticalSpacing);
+                               kVerticalPadding);
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/settings/cells/version_item.mm b/ios/chrome/browser/ui/settings/cells/version_item.mm
index 6c1a69d..40cdfd4 100644
--- a/ios/chrome/browser/ui/settings/cells/version_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/version_item.mm
@@ -75,15 +75,10 @@
         forControlEvents:UIControlEventTouchUpInside];
     [self.contentView addSubview:button];
 
-    NSLayoutConstraint* heightConstraint = [self.contentView.heightAnchor
-        constraintGreaterThanOrEqualToConstant:kChromeTableViewCellHeight];
-    // Don't set the priority to required to avoid clashing with the estimated
-    // height.
-    heightConstraint.priority = UILayoutPriorityRequired - 1;
-
     AddSameConstraints(button, self.contentView);
     [NSLayoutConstraint activateConstraints:@[
-      heightConstraint,
+      [self.contentView.heightAnchor constraintGreaterThanOrEqualToConstant:
+                                         kTableViewHeaderFooterViewHeight],
       [_textLabel.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor],
       [_textLabel.trailingAnchor
diff --git a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
index 6f0127e..40e5bb0 100644
--- a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
@@ -26,7 +26,7 @@
 // |estimatedSection{Header|Footer}Height|.
 const CGFloat kDefaultHeaderFooterHeight = 10;
 // Estimated height of the header/footer, used to speed the constraints.
-const CGFloat kEstimatedHeaderFooterHeight = 50;
+const CGFloat kEstimatedHeaderFooterHeight = 35;
 
 enum SavedBarButtomItemPositionEnum {
   kUndefinedBarButtonItemPosition,
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
index 56f3a42..60032dfe 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_activity_indicator_header_footer_item.mm
@@ -61,7 +61,7 @@
                                         forAxis:UILayoutConstraintAxisVertical];
     self.subtitleLabel = [[UILabel alloc] init];
     self.subtitleLabel.font =
-        [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+        [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     self.subtitleLabel.textColor = [UIColor lightGrayColor];
     [self.subtitleLabel
         setContentCompressionResistancePriority:UILayoutPriorityRequired
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h
index dd37534..5e138dea 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h
@@ -10,18 +10,9 @@
 // The minimum height for a TableViewHeaderFooterView.
 extern const CGFloat kTableViewHeaderFooterViewHeight;
 
-// The minimum height for a TableViewCell.
-extern const CGFloat kChromeTableViewCellHeight;
-
 // The horizontal spacing between views and the container view of a cell.
 extern const CGFloat kTableViewHorizontalSpacing;
 
-// The vertical spacing for a cell containing only one label.
-extern const CGFloat kTableViewOneLabelCellVerticalSpacing;
-
-// The vertical spacing for a cell containing one label and one sub label.
-extern const CGFloat kTableViewTwoLabelsCellVerticalSpacing;
-
 // The vertical spacing between views and the container view of a cell.
 extern const CGFloat kTableViewVerticalSpacing;
 
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm
index 32d75077..6d1976c 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.mm
@@ -8,11 +8,8 @@
 #error "This file requires ARC support."
 #endif
 
-const CGFloat kTableViewHeaderFooterViewHeight = 48.0;
-const CGFloat kChromeTableViewCellHeight = 48.0;
+const CGFloat kTableViewHeaderFooterViewHeight = 44.0;
 const CGFloat kTableViewHorizontalSpacing = 16.0;
-const CGFloat kTableViewOneLabelCellVerticalSpacing = 14.0;
-const CGFloat kTableViewTwoLabelsCellVerticalSpacing = 11.0;
 const CGFloat kTableViewVerticalSpacing = 8.0;
 const CGFloat kTableViewLargeVerticalSpacing = 16.0;
 const CGFloat kTableViewSubViewHorizontalSpacing = 12.0;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm
index 5282354..4a75ea3 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.mm
@@ -13,6 +13,10 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+const CGFloat kMinimalHeight = 48;
+}  // namespace
+
 #pragma mark - TableViewDetailTextItem
 
 @implementation TableViewDetailTextItem
@@ -95,23 +99,20 @@
     _detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
     [containerView addSubview:_detailTextLabel];
 
-    NSLayoutConstraint* heightConstraint = [self.contentView.heightAnchor
-        constraintGreaterThanOrEqualToConstant:kChromeTableViewCellHeight];
-    // Don't set the priority to required to avoid clashing with the estimated
-    // height.
-    heightConstraint.priority = UILayoutPriorityRequired - 1;
+    CGFloat margin = kTableViewHorizontalSpacing;
 
     [NSLayoutConstraint activateConstraints:@[
       // Minimal height.
-      heightConstraint,
+      [self.contentView.heightAnchor
+          constraintGreaterThanOrEqualToConstant:kMinimalHeight],
 
       // Container.
       [containerView.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor
-                         constant:kTableViewHorizontalSpacing],
+                         constant:margin],
       [containerView.trailingAnchor
           constraintEqualToAnchor:self.contentView.trailingAnchor
-                         constant:-kTableViewHorizontalSpacing],
+                         constant:-margin],
       [containerView.centerYAnchor
           constraintEqualToAnchor:self.contentView.centerYAnchor],
 
@@ -132,8 +133,7 @@
     ]];
 
     // Make sure there are top and bottom margins of at least |margin|.
-    AddOptionalVerticalPadding(self.contentView, containerView,
-                               kTableViewTwoLabelsCellVerticalSpacing);
+    AddOptionalVerticalPadding(self.contentView, containerView, margin);
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
index 79368cf..fcfa04b 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
@@ -13,6 +13,11 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Vertical spacing between label and the container view of a cell.
+const CGFloat kLabelCellVerticalSpacing = 11.0;
+}  // namespace
+
 @implementation TableViewImageItem
 
 @synthesize image = _image;
@@ -92,12 +97,6 @@
     horizontalStack.alignment = UIStackViewAlignmentCenter;
 
     [self.contentView addSubview:horizontalStack];
-
-    NSLayoutConstraint* heightConstraint = [self.contentView.heightAnchor
-        constraintGreaterThanOrEqualToConstant:kChromeTableViewCellHeight];
-    // Don't set the priority to required to avoid clashing with the estimated
-    // height.
-    heightConstraint.priority = UILayoutPriorityRequired - 1;
     [NSLayoutConstraint activateConstraints:@[
       // Horizontal Stack constraints.
       [horizontalStack.leadingAnchor
@@ -106,15 +105,12 @@
       [horizontalStack.trailingAnchor
           constraintEqualToAnchor:self.contentView.trailingAnchor
                          constant:-kTableViewHorizontalSpacing],
-      [horizontalStack.centerYAnchor
-          constraintEqualToAnchor:self.contentView.centerYAnchor],
       [horizontalStack.topAnchor
-          constraintGreaterThanOrEqualToAnchor:self.contentView.topAnchor
-                                      constant:kTableViewVerticalSpacing],
+          constraintEqualToAnchor:self.contentView.topAnchor
+                         constant:kLabelCellVerticalSpacing],
       [horizontalStack.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
-                                   constant:-kTableViewVerticalSpacing],
-      heightConstraint,
+          constraintEqualToAnchor:self.contentView.bottomAnchor
+                         constant:-kLabelCellVerticalSpacing],
     ]];
   }
   return self;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm
index 558d546a..59b74fa 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_item.mm
@@ -15,6 +15,11 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Vertical spacing between label and the container view of a cell.
+const CGFloat kLabelCellVerticalSpacing = 11.0;
+}  // namespace
+
 #pragma mark - TableViewTextItem
 
 @implementation TableViewTextItem
@@ -100,12 +105,11 @@
       [_textLabel.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor
                          constant:kTableViewHorizontalSpacing],
-      [_textLabel.topAnchor
-          constraintEqualToAnchor:self.contentView.topAnchor
-                         constant:kTableViewOneLabelCellVerticalSpacing],
+      [_textLabel.topAnchor constraintEqualToAnchor:self.contentView.topAnchor
+                                           constant:kLabelCellVerticalSpacing],
       [_textLabel.bottomAnchor
           constraintEqualToAnchor:self.contentView.bottomAnchor
-                         constant:-kTableViewOneLabelCellVerticalSpacing],
+                         constant:-kLabelCellVerticalSpacing],
       [_textLabel.trailingAnchor
           constraintEqualToAnchor:self.contentView.trailingAnchor
                          constant:-kTableViewHorizontalSpacing]
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
index 4bd9d67..93f7c44 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
@@ -162,13 +162,13 @@
     // Set font sizes using dynamic type.
     _titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
     _titleLabel.adjustsFontForContentSizeCategory = YES;
-    _URLLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+    _URLLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     _URLLabel.adjustsFontForContentSizeCategory = YES;
     _URLLabel.textColor =
         UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
     _URLLabel.hidden = YES;
     _metadataLabel.font =
-        [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+        [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     _metadataLabel.textColor =
         UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
     _metadataLabel.adjustsFontForContentSizeCategory = YES;
@@ -202,22 +202,6 @@
     [contentView addSubview:_faviconBadgeView];
     [contentView addSubview:self.horizontalStack];
 
-    NSLayoutConstraint* heightConstraint = [self.contentView.heightAnchor
-        constraintGreaterThanOrEqualToConstant:kChromeTableViewCellHeight];
-    // Don't set the priority to required to avoid clashing with the estimated
-    // height.
-    heightConstraint.priority = UILayoutPriorityRequired - 1;
-
-    NSLayoutConstraint* topConstraint = [self.horizontalStack.topAnchor
-        constraintGreaterThanOrEqualToAnchor:self.contentView.topAnchor
-                                    constant:
-                                        kTableViewTwoLabelsCellVerticalSpacing];
-    NSLayoutConstraint* bottomConstraint = [self.horizontalStack.bottomAnchor
-        constraintGreaterThanOrEqualToAnchor:self.contentView.bottomAnchor
-                                    constant:
-                                        -
-                                        kTableViewTwoLabelsCellVerticalSpacing];
-
     [NSLayoutConstraint activateConstraints:@[
       // The favicon view is a fixed size, is pinned to the leading edge of the
       // content view, and is centered vertically.
@@ -252,9 +236,12 @@
       [self.horizontalStack.trailingAnchor
           constraintEqualToAnchor:self.contentView.trailingAnchor
                          constant:-kTableViewHorizontalSpacing],
-      [self.horizontalStack.centerYAnchor
-          constraintEqualToAnchor:self.contentView.centerYAnchor],
-      topConstraint, bottomConstraint, heightConstraint
+      [self.horizontalStack.topAnchor
+          constraintEqualToAnchor:self.contentView.topAnchor
+                         constant:kTableViewVerticalSpacing],
+      [self.horizontalStack.bottomAnchor
+          constraintEqualToAnchor:self.contentView.bottomAnchor
+                         constant:-kTableViewVerticalSpacing]
     ]];
   }
   return self;
diff --git a/ios/chrome/browser/ui/tabs/BUILD.gn b/ios/chrome/browser/ui/tabs/BUILD.gn
index 6f5b50d..e87784d 100644
--- a/ios/chrome/browser/ui/tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/tabs/BUILD.gn
@@ -53,6 +53,7 @@
     "//ios/chrome/browser/ui/favicon/resources:default_favicon_incognito",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/image_util",
+    "//ios/chrome/browser/ui/ntp:util",
     "//ios/chrome/browser/ui/popup_menu/public",
     "//ios/chrome/browser/ui/tab_grid/grid/resources:grid_cell_close_button",
     "//ios/chrome/browser/ui/tabs/requirements",
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
index d975e9a..7434e42 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
+++ b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
@@ -33,6 +33,7 @@
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #include "ios/chrome/browser/ui/fullscreen/fullscreen_controller_factory.h"
 #include "ios/chrome/browser/ui/fullscreen/scoped_fullscreen_disabler.h"
+#import "ios/chrome/browser/ui/ntp/ntp_util.h"
 #import "ios/chrome/browser/ui/popup_menu/public/popup_menu_long_press_delegate.h"
 #import "ios/chrome/browser/ui/tabs/requirements/tab_strip_constants.h"
 #import "ios/chrome/browser/ui/tabs/requirements/tab_strip_presentation.h"
@@ -1050,7 +1051,7 @@
       [view setFavicon:favicon.ToUIImage()];
   }
 
-  if (tab.webState->IsLoading())
+  if (tab.webState->IsLoading() && !IsVisibleURLNewTabPage(tab.webState))
     [view startProgressSpinner];
   else
     [view stopProgressSpinner];
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn
index 2764c29..a6420c2 100644
--- a/ios/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -121,6 +121,7 @@
     "//components/bookmarks/browser",
     "//components/bookmarks/test",
     "//components/search_engines",
+    "//ios/chrome/browser",
     "//ios/chrome/browser/bookmarks",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/search_engines",
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm b/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm
index d8774e0..d4b7149 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_mediator.mm
@@ -267,8 +267,11 @@
   DCHECK(self.consumer);
   [self updateConsumerForWebState:self.webState];
 
-  [self.consumer setIsNTP:IsVisibleURLNewTabPage(self.webState)];
-  [self.consumer setLoadingState:self.webState->IsLoading()];
+  BOOL isNTP = IsVisibleURLNewTabPage(self.webState);
+  [self.consumer setIsNTP:isNTP];
+  // Never show the loading UI for an NTP.
+  BOOL isLoading = self.webState->IsLoading() && !isNTP;
+  [self.consumer setLoadingState:isLoading];
   [self updateBookmarksForWebState:self.webState];
   [self updateShareMenuForWebState:self.webState];
 }
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_mediator_unittest.mm b/ios/chrome/browser/ui/toolbar/toolbar_mediator_unittest.mm
index 7ea933d..aab848e 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_mediator_unittest.mm
@@ -13,6 +13,7 @@
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/ui/toolbar/test/toolbar_test_navigation_manager.h"
 #import "ios/chrome/browser/ui/toolbar/test/toolbar_test_web_state.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_consumer.h"
@@ -394,6 +395,19 @@
   [[consumer_ verify] setLoadingState:NO];
 }
 
+// Tests the Toolbar is not updated when the Webstate observer method
+// DidStartLoading is triggered by SetLoading on the NTP.
+TEST_F(ToolbarMediatorTest, TestDidStartLoadingNTP) {
+  mediator_.webStateList = web_state_list_.get();
+  SetUpActiveWebState();
+  mediator_.consumer = consumer_;
+
+  web_state_->SetLoading(false);
+  web_state_->SetVisibleURL(GURL(kChromeUINewTabURL));
+  web_state_->SetLoading(true);
+  [[consumer_ verify] setLoadingState:NO];
+}
+
 // Tests the Toolbar is updated when the Webstate observer method
 // DidLoadPageWithSuccess is triggered by OnPageLoaded.
 TEST_F(ToolbarMediatorTest, TestDidLoadPageWithSucess) {
diff --git a/ios/web/public/web_state/ui/crw_content_view.h b/ios/web/public/web_state/ui/crw_content_view.h
index 3e8a092..ed38357 100644
--- a/ios/web/public/web_state/ui/crw_content_view.h
+++ b/ios/web/public/web_state/ui/crw_content_view.h
@@ -27,10 +27,6 @@
 // bug, where UIScrollView.content inset does not work (rdar://23584409).
 @property(nonatomic, assign) UIEdgeInsets contentInset;
 
-// Returns YES if content is being displayed in the scroll view.
-// TODO(stuartmorgan): See if this can be removed from the public interface.
-- (BOOL)isViewAlive;
-
 @optional
 
 // Whether or not the content view should use the content inset when setting
diff --git a/ios/web/web_state/ui/crw_web_view_content_view.mm b/ios/web/web_state/ui/crw_web_view_content_view.mm
index d9a9cb4..72c35a2 100644
--- a/ios/web/web_state/ui/crw_web_view_content_view.mm
+++ b/ios/web/web_state/ui/crw_web_view_content_view.mm
@@ -90,10 +90,6 @@
   }
 }
 
-- (BOOL)isViewAlive {
-  return YES;
-}
-
 - (void)setContentOffset:(CGPoint)contentOffset {
   if (CGPointEqualToPoint(_contentOffset, contentOffset))
     return;
diff --git a/media/learning/common/learning_task.h b/media/learning/common/learning_task.h
index 4c38e88..a7ca9b9f 100644
--- a/media/learning/common/learning_task.h
+++ b/media/learning/common/learning_task.h
@@ -101,24 +101,16 @@
   // the histogram name.
   std::string uma_hacky_confusion_matrix;
 
-  // RandomTree parameters
-
-  // How RandomTree handles unknown feature values.
-  enum class RTUnknownValueHandling {
-    // Return an empty distribution as the prediction.
-    kEmptyDistribution,
-
-    // Return the sum of the traversal of all splits.
-    kUseAllSplits,
-  };
-  RTUnknownValueHandling rt_unknown_value_handling =
-      RTUnknownValueHandling::kUseAllSplits;
-
   // RandomForest parameters
 
   // Number of trees in the random forest.
   size_t rf_number_of_trees = 100;
 
+  // Should ExtraTrees apply one-hot conversion automatically?  RandomTree has
+  // been modified to support nominals directly, though it isn't exactly the
+  // same as one-hot conversion.  It is, however, much faster.
+  bool use_one_hot_conversion = false;
+
   // Reporting parameters
 
   // This is a hack for the initial media capabilities investigation. It
diff --git a/media/learning/impl/extra_trees_trainer.cc b/media/learning/impl/extra_trees_trainer.cc
index 4e16aae7..b820c6a64 100644
--- a/media/learning/impl/extra_trees_trainer.cc
+++ b/media/learning/impl/extra_trees_trainer.cc
@@ -34,11 +34,16 @@
   if (!tree_trainer_)
     tree_trainer_ = std::make_unique<RandomTreeTrainer>(rng());
 
-  // RandomTree requires one-hot vectors to properly choose split points the way
-  // that ExtraTrees require.
-  // TODO(liberato): Modify it not to need this.  It's slow.
-  converter_ = std::make_unique<OneHotConverter>(task, training_data);
-  converted_training_data_ = converter_->Convert(training_data);
+  // We've modified RandomTree to handle nominals, so we don't need to do one-
+  // hot conversion normally.  It's slow.  However, the changes to RandomTree
+  // are only approximately the same thing.
+  if (task_.use_one_hot_conversion) {
+    converter_ = std::make_unique<OneHotConverter>(task, training_data);
+    converted_training_data_ = converter_->Convert(training_data);
+    task_ = converter_->converted_task();
+  } else {
+    converted_training_data_ = training_data;
+  }
 
   // Start training.  Send in nullptr to start the process.
   OnRandomTreeModel(std::move(model_cb), nullptr);
@@ -52,17 +57,22 @@
 
   // If this is the last tree, then return the finished model.
   if (trees_.size() == task_.rf_number_of_trees) {
-    std::move(model_cb).Run(std::make_unique<ConvertingModel>(
-        std::move(converter_),
-        std::make_unique<VotingEnsemble>(std::move(trees_))));
+    std::unique_ptr<Model> model =
+        std::make_unique<VotingEnsemble>(std::move(trees_));
+    // If we have a converter, then wrap everything in a ConvertingModel.
+    if (converter_) {
+      model = std::make_unique<ConvertingModel>(std::move(converter_),
+                                                std::move(model));
+    }
+
+    std::move(model_cb).Run(std::move(model));
     return;
   }
 
   // Train the next tree.
   auto cb = base::BindOnce(&ExtraTreesTrainer::OnRandomTreeModel, AsWeakPtr(),
                            std::move(model_cb));
-  tree_trainer_->Train(converter_->converted_task(), converted_training_data_,
-                       std::move(cb));
+  tree_trainer_->Train(task_, converted_training_data_, std::move(cb));
 }
 
 }  // namespace learning
diff --git a/media/learning/impl/random_tree_trainer.cc b/media/learning/impl/random_tree_trainer.cc
index 3f5713d1..88e1b97 100644
--- a/media/learning/impl/random_tree_trainer.cc
+++ b/media/learning/impl/random_tree_trainer.cc
@@ -36,7 +36,6 @@
                int split_index,
                FeatureValue split_point)
       : split_index_(split_index),
-        rt_unknown_value_handling_(task.rt_unknown_value_handling),
         ordering_(task.feature_descriptions[split_index].ordering),
         split_point_(split_point) {}
 
@@ -47,8 +46,8 @@
     FeatureValue f;
     switch (ordering_) {
       case LearningTask::Ordering::kUnordered:
-        // Use the nominal value directly.
-        f = features[split_index_];
+        // Use 0 for "!=" and 1 for "==".
+        f = FeatureValue(features[split_index_] == split_point_);
         break;
       case LearningTask::Ordering::kNumeric:
         // Use 0 for "<=" and 1 for ">".
@@ -58,18 +57,9 @@
 
     auto iter = children_.find(f);
 
-    // If we've never seen this feature value, then average all our branches.
-    // This is an attempt to mimic one-hot encoding, where we'll take the zero
-    // branch but it depends on the tree structure which of the one-hot values
-    // we're choosing.
-    if (iter == children_.end()) {
-      switch (rt_unknown_value_handling_) {
-        case LearningTask::RTUnknownValueHandling::kEmptyDistribution:
-          return TargetDistribution();
-        case LearningTask::RTUnknownValueHandling::kUseAllSplits:
-          return PredictDistributionWithMissingValues(features);
-      }
-    }
+    // If we've never seen this feature value, then return nothing.
+    if (iter == children_.end())
+      return TargetDistribution();
 
     return iter->second->PredictDistribution(features);
   }
@@ -98,9 +88,6 @@
   int split_index_ = -1;
   base::flat_map<FeatureValue, std::unique_ptr<Model>> children_;
 
-  // How we handle unknown values.
-  LearningTask::RTUnknownValueHandling rt_unknown_value_handling_;
-
   // How is our feature value ordered?
   LearningTask::Ordering ordering_;
 
@@ -227,6 +214,11 @@
   }
 
   // Select the feature subset to consider at this leaf.
+  // TODO(liberato): For nominals, with one-hot encoding, we'd give an equal
+  // chance to each feature's value.  For example, if F1 has {A, B} and F2 has
+  // {C,D,E,F}, then we would pick uniformly over {A,B,C,D,E,F}.  However, now
+  // we pick between {F1, F2} then pick between either {A,B} or {C,D,E,F}.  We
+  // do this because it's simpler and doesn't seem to hurt anything.
   FeatureSet feature_candidates = new_unused_set;
   // TODO(liberato): Let our caller override this.
   const size_t features_per_split =
@@ -267,17 +259,6 @@
   std::unique_ptr<InteriorNode> node = std::make_unique<InteriorNode>(
       task, best_potential_split.split_index, best_potential_split.split_point);
 
-  // Don't let the subtree use this feature if this is nominal split, since
-  // there's nothing left to split.  For numeric splits, we might want to split
-  // it further.  Note that if there is only one branch for this split, then
-  // we returned a leaf anyway.
-  if (task.feature_descriptions[best_potential_split.split_index].ordering ==
-      LearningTask::Ordering::kUnordered) {
-    DCHECK(new_unused_set.find(best_potential_split.split_index) !=
-           new_unused_set.end());
-    new_unused_set.erase(best_potential_split.split_index);
-  }
-
   for (auto& branch_iter : best_potential_split.branch_infos) {
     node->AddChild(branch_iter.first,
                    Build(task, training_data, branch_iter.second.training_idx,
@@ -297,18 +278,21 @@
   DCHECK_GT(training_idx.size(), 0u);
 
   Split split(split_index);
-  base::Optional<FeatureValue> split_point;
+
+  bool is_numeric = task.feature_descriptions[split_index].ordering ==
+                    LearningTask::Ordering::kNumeric;
 
   // TODO(liberato): Consider removing nominal feature support and RF.  That
   // would make this code somewhat simpler.
 
   // For a numeric split, find the split point.  Otherwise, we'll split on every
   // nominal value that this feature has in |training_data|.
-  if (task.feature_descriptions[split_index].ordering ==
-      LearningTask::Ordering::kNumeric) {
-    split_point =
-        FindNumericSplitPoint(split.split_index, training_data, training_idx);
-    split.split_point = *split_point;
+  if (is_numeric) {
+    split.split_point =
+        FindSplitPoint_Numeric(split.split_index, training_data, training_idx);
+  } else {
+    split.split_point =
+        FindSplitPoint_Nominal(split.split_index, training_data, training_idx);
   }
 
   // Find the split's feature values and construct the training set for each.
@@ -323,13 +307,14 @@
     FeatureValue v_i = example.features[split.split_index];
 
     // Figure out what value this example would use for splitting.  For nominal,
-    // it's just |v_i|.  For numeric, it's whether |v_i| is <= the split point
-    // or not (0 for <=, 1 for >).
+    // it's 1 or 0, based on whether |v_i| is equal to the split or not.  For
+    // numeric, it's whether |v_i| is <= the split point or not (0 for <=, and 1
+    // for >).
     FeatureValue split_feature;
-    if (split_point)
-      split_feature = FeatureValue(v_i > *split_point);
+    if (is_numeric)
+      split_feature = FeatureValue(v_i > split.split_point);
     else
-      split_feature = v_i;
+      split_feature = FeatureValue(v_i == split.split_point);
 
     // Add |v_i| to the right training set.  Remember that emplace will do
     // nothing if the key already exists.
@@ -345,18 +330,18 @@
   // Figure out how good / bad this split is.
   switch (task.target_description.ordering) {
     case LearningTask::Ordering::kUnordered:
-      ComputeNominalSplitScore(&split, total_weight);
+      ComputeSplitScore_Nominal(&split, total_weight);
       break;
     case LearningTask::Ordering::kNumeric:
-      ComputeNumericSplitScore(&split, total_weight);
+      ComputeSplitScore_Numeric(&split, total_weight);
       break;
   }
 
   return split;
 }
 
-void RandomTreeTrainer::ComputeNominalSplitScore(Split* split,
-                                                 double total_weight) {
+void RandomTreeTrainer::ComputeSplitScore_Nominal(Split* split,
+                                                  double total_weight) {
   // Compute the nats given that we're at this node.
   split->nats_remaining = 0;
   for (auto& info_iter : split->branch_infos) {
@@ -374,8 +359,8 @@
   }
 }
 
-void RandomTreeTrainer::ComputeNumericSplitScore(Split* split,
-                                                 double total_weight) {
+void RandomTreeTrainer::ComputeSplitScore_Numeric(Split* split,
+                                                  double total_weight) {
   // Compute the nats given that we're at this node.
   split->nats_remaining = 0;
   for (auto& info_iter : split->branch_infos) {
@@ -402,7 +387,7 @@
   }
 }
 
-FeatureValue RandomTreeTrainer::FindNumericSplitPoint(
+FeatureValue RandomTreeTrainer::FindSplitPoint_Numeric(
     size_t split_index,
     const TrainingData& training_data,
     const std::vector<size_t>& training_idx) {
@@ -444,5 +429,42 @@
   return v_split;
 }
 
+FeatureValue RandomTreeTrainer::FindSplitPoint_Nominal(
+    size_t split_index,
+    const TrainingData& training_data,
+    const std::vector<size_t>& training_idx) {
+  // We should not be given a training set of size 0, since there's no need to
+  // check an empty split.
+  DCHECK_GT(training_idx.size(), 0u);
+
+  // Construct a set of all values for |training_idx|.  We don't care about
+  // their relative frequency, since one-hot encoding doesn't.
+  // For example, if a feature has 10 "yes" instances and 1 "no" instance, then
+  // there's a 50% chance for each to be chosen here.  This is because one-hot
+  // encoding would do roughly the same thing: when choosing features, the
+  // "is_yes" and "is_no" features that come out of one-hot encoding would be
+  // equally likely to be chosen.
+  //
+  // Important but subtle note: we can't choose a value that's been chosen
+  // before for this feature, since that would be like splitting on the same
+  // one-hot feature more than once.  Luckily, we won't be asked to do that.  If
+  // we choose "Yes" at some level in the tree, then the "==" branch will have
+  // trivial features which will be removed from consideration early (we never
+  // consider features with only one value), and the != branch won't have any
+  // "Yes" values for us to pick at a lower level.
+  std::set<FeatureValue> values;
+  for (size_t idx : training_idx) {
+    const LabelledExample& example = training_data[idx];
+    values.insert(example.features[split_index]);
+  }
+
+  // Select one uniformly at random.
+  size_t which = rng()->Generate(values.size());
+  auto it = values.begin();
+  for (; which > 0; it++, which--)
+    ;
+  return *it;
+}
+
 }  // namespace learning
 }  // namespace media
diff --git a/media/learning/impl/random_tree_trainer.h b/media/learning/impl/random_tree_trainer.h
index 0c764bc1..3383e55 100644
--- a/media/learning/impl/random_tree_trainer.h
+++ b/media/learning/impl/random_tree_trainer.h
@@ -160,15 +160,20 @@
 
   // Fill in |nats_remaining| for |split| for a nominal target.  |total_weight|
   // is the total weight of all instances coming into this split.
-  void ComputeNominalSplitScore(Split* split, double total_weight);
+  void ComputeSplitScore_Nominal(Split* split, double total_weight);
 
   // Fill in |nats_remaining| for |split| for a numeric target.
-  void ComputeNumericSplitScore(Split* split, double total_weight);
+  void ComputeSplitScore_Numeric(Split* split, double total_weight);
+
+  // Compute the split point for |training_data| for a nominal feature.
+  FeatureValue FindSplitPoint_Nominal(size_t index,
+                                      const TrainingData& training_data,
+                                      const std::vector<size_t>& training_idx);
 
   // Compute the split point for |training_data| for a numeric feature.
-  FeatureValue FindNumericSplitPoint(size_t index,
-                                     const TrainingData& training_data,
-                                     const std::vector<size_t>& training_idx);
+  FeatureValue FindSplitPoint_Numeric(size_t index,
+                                      const TrainingData& training_data,
+                                      const std::vector<size_t>& training_idx);
 
   DISALLOW_COPY_AND_ASSIGN(RandomTreeTrainer);
 };
diff --git a/media/learning/impl/random_tree_trainer_unittest.cc b/media/learning/impl/random_tree_trainer_unittest.cc
index c090b936..6af9e94 100644
--- a/media/learning/impl/random_tree_trainer_unittest.cc
+++ b/media/learning/impl/random_tree_trainer_unittest.cc
@@ -169,30 +169,17 @@
   training_data.push_back(example_1);
   training_data.push_back(example_2);
 
-  task_.rt_unknown_value_handling =
-      LearningTask::RTUnknownValueHandling::kEmptyDistribution;
-  std::unique_ptr<Model> model = Train(task_, training_data);
-  TargetDistribution distribution =
+  auto model = Train(task_, training_data);
+  auto distribution =
       model->PredictDistribution(FeatureVector({FeatureValue(789)}));
   if (ordering_ == LearningTask::Ordering::kUnordered) {
-    // OOV data should return an empty distribution (nominal).
-    EXPECT_EQ(distribution.size(), 0u);
-  } else {
-    // OOV data should end up in the |example_2| bucket, since the feature is
-    // numerically higher.
+    // OOV data could be split on either feature first, so we don't really know
+    // which to expect.  We assert that there should be exactly one example, but
+    // whether it's |example_1| or |example_2| isn't clear.
     EXPECT_EQ(distribution.size(), 1u);
-    EXPECT_EQ(distribution[example_2.target_value], 1u);
-  }
-
-  task_.rt_unknown_value_handling =
-      LearningTask::RTUnknownValueHandling::kUseAllSplits;
-  model = Train(task_, training_data);
-  distribution = model->PredictDistribution(FeatureVector({FeatureValue(789)}));
-  if (ordering_ == LearningTask::Ordering::kUnordered) {
-    // OOV data should return with the sum of all splits.
-    EXPECT_EQ(distribution.size(), 2u);
-    EXPECT_EQ(distribution[example_1.target_value], 1u);
-    EXPECT_EQ(distribution[example_2.target_value], 1u);
+    EXPECT_EQ(distribution[example_1.target_value] +
+                  distribution[example_2.target_value],
+              1u);
   } else {
     // The unknown feature is numerically higher than |example_2|, so we
     // expect it to fall into that bucket.
@@ -212,8 +199,6 @@
     training_data.push_back(example);
   }
 
-  task_.rt_unknown_value_handling =
-      LearningTask::RTUnknownValueHandling::kEmptyDistribution;
   std::unique_ptr<Model> model = Train(task_, training_data);
   for (size_t i = 0; i < 4; i++) {
     // Get a prediction for the |i|-th feature value.
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 86c0ea48..6cd039e 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -8,7 +8,7 @@
   "//chrome/common/importer/typemaps.gni",
   "//chrome/common/media_router/mojo/typemaps.gni",
   "//chrome/typemaps.gni",
-  "//chromecast/common/mojom/typemaps.gni",
+  "//chromecast/typemaps.gni",
   "//chromeos/typemaps.gni",
   "//chromeos/components/multidevice/mojom/typemaps.gni",
   "//chromeos/services/secure_channel/public/mojom/typemaps.gni",
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index a7d25a1..7469312 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -13,6 +13,7 @@
 # Chrome builds. Ideally we could create some generic knobs here that could be
 # flipped elsewhere though.
 import("//build/config/chrome_build.gni")
+import("//build/config/chromecast_build.gni")
 import("//build/config/nacl/config.gni")
 import("//components/nacl/features.gni")
 import("//third_party/jinja2/jinja2.gni")
@@ -56,7 +57,7 @@
 # check |target_os| explicitly, as it's consistent across all toolchains.
 enable_scrambled_message_ids =
     enable_mojom_message_id_scrambling &&
-    (is_mac || is_win || (is_linux && !is_chromeos) ||
+    (is_mac || is_win || (is_linux && !is_chromeos && !is_chromecast) ||
      ((enable_nacl || is_nacl || is_nacl_nonsfi) && target_os != "chromeos"))
 
 mojom_generator_root = "//mojo/public/tools/bindings"
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 57655c5..e39b8c4 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -261,9 +261,12 @@
 NET_ERROR(TEMPORARILY_THROTTLED, -139)
 
 // A request to create an SSL tunnel connection through the HTTPS proxy
-// received a non-200 (OK) and non-407 (Proxy Auth) response.  The response
-// body might include a description of why the request failed.
-NET_ERROR(HTTPS_PROXY_TUNNEL_RESPONSE, -140)
+// received a 302 (temporary redirect) response.  The response body might
+// include a description of why the request failed.
+//
+// TODO(https://crbug.com/928551): This is deprecated and should not be used by
+// new code.
+NET_ERROR(HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT, -140)
 
 // We were unable to sign the CertificateVerify data of an SSL client auth
 // handshake with the client certificate's private key.
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index 2534f30..7bff65d 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -482,10 +482,12 @@
 }
 
 // start id from 1 to distinguish from NULL RequestHandle
-MockHostResolverBase::MockHostResolverBase(bool use_caching)
+MockHostResolverBase::MockHostResolverBase(bool use_caching,
+                                           int cache_invalidation_num)
     : last_request_priority_(DEFAULT_PRIORITY),
       synchronous_mode_(false),
       ondemand_mode_(false),
+      initial_cache_invalidation_num_(cache_invalidation_num),
       next_request_id_(1),
       num_resolve_(0),
       num_resolve_from_cache_(0),
@@ -497,9 +499,10 @@
   rules_map_[HostResolverSource::MULTICAST_DNS] =
       CreateCatchAllHostResolverProc();
 
-  if (use_caching) {
+  if (use_caching)
     cache_.reset(new HostCache(kMaxCacheEntries));
-  }
+  else
+    DCHECK_GE(0, cache_invalidation_num);
 }
 
 int MockHostResolverBase::Resolve(RequestImpl* request) {
@@ -602,6 +605,18 @@
             AddressList::CopyWithPort(entry->addresses().value(), host.port());
         *out_stale_info = std::move(stale_info);
       }
+
+      auto cache_invalidation_iterator = cache_invalidation_nums_.find(key);
+      if (cache_invalidation_iterator != cache_invalidation_nums_.end()) {
+        DCHECK_LE(1, cache_invalidation_iterator->second);
+        cache_invalidation_iterator->second--;
+        if (cache_invalidation_iterator->second == 0) {
+          HostCache::Entry new_entry(*entry);
+          cache_->Set(key, new_entry, tick_clock_->NowTicks(),
+                      base::TimeDelta());
+          cache_invalidation_nums_.erase(cache_invalidation_iterator);
+        }
+      }
     }
   }
   return rv;
@@ -624,8 +639,11 @@
                        flags, source);
     // Storing a failure with TTL 0 so that it overwrites previous value.
     base::TimeDelta ttl;
-    if (rv == OK)
+    if (rv == OK) {
       ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
+      if (initial_cache_invalidation_num_ > 0)
+        cache_invalidation_nums_[key] = initial_cache_invalidation_num_;
+    }
     cache_->Set(key,
                 HostCache::Entry(rv, addr, HostCache::Entry::SOURCE_UNKNOWN),
                 tick_clock_->NowTicks(), ttl);
diff --git a/net/dns/mock_host_resolver.h b/net/dns/mock_host_resolver.h
index 2927a4b..96630e6 100644
--- a/net/dns/mock_host_resolver.h
+++ b/net/dns/mock_host_resolver.h
@@ -216,12 +216,17 @@
     tick_clock_ = tick_clock;
   }
 
- protected:
-  explicit MockHostResolverBase(bool use_caching);
-
  private:
+  friend class MockHostResolver;
+  friend class MockCachingHostResolver;
+
   typedef std::map<size_t, RequestImpl*> RequestMap;
 
+  // If > 0, |cache_invalidation_num| is the number of times a cached entry can
+  // be read before it invalidates itself. Useful to force cache expiration
+  // scenarios.
+  explicit MockHostResolverBase(bool use_caching, int cache_invalidation_num);
+
   // Handle resolution for |request|. Expected to be called only the RequestImpl
   // object itself.
   int Resolve(RequestImpl* request);
@@ -253,6 +258,9 @@
       rules_map_;
   std::unique_ptr<HostCache> cache_;
 
+  const int initial_cache_invalidation_num_;
+  std::map<HostCache::Key, int> cache_invalidation_nums_;
+
   // Maintain non-owning pointers to outstanding requests and listeners to allow
   // completing/notifying them. The objects are owned by callers, and should be
   // removed from |this| on destruction by calling DetachRequest() or
@@ -274,7 +282,9 @@
 
 class MockHostResolver : public MockHostResolverBase {
  public:
-  MockHostResolver() : MockHostResolverBase(false /*use_caching*/) {}
+  MockHostResolver()
+      : MockHostResolverBase(false /*use_caching*/,
+                             0 /* cache_invalidation_num */) {}
   ~MockHostResolver() override {}
 };
 
@@ -285,7 +295,11 @@
 // operation mode in case that is what you needed from the caching version).
 class MockCachingHostResolver : public MockHostResolverBase {
  public:
-  MockCachingHostResolver() : MockHostResolverBase(true /*use_caching*/) {}
+  // If > 0, |cache_invalidation_num| is the number of times a cached entry can
+  // be read before it invalidates itself. Useful to force cache expiration
+  // scenarios.
+  explicit MockCachingHostResolver(int cache_invalidation_num = 0)
+      : MockHostResolverBase(true /*use_caching*/, cache_invalidation_num) {}
   ~MockCachingHostResolver() override {}
 };
 
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
index 45e80ac..8f4a8622 100644
--- a/net/ftp/ftp_network_transaction.cc
+++ b/net/ftp/ftp_network_transaction.cc
@@ -19,6 +19,7 @@
 #include "net/base/address_list.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/escape.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/parse_number.h"
 #include "net/base/port_util.h"
@@ -691,7 +692,7 @@
     IPEndPoint ip_endpoint;
     result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
     if (result == OK) {
-      response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
+      response_.remote_endpoint = ip_endpoint;
       next_state_ = STATE_CTRL_READ;
 
       if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
diff --git a/net/ftp/ftp_network_transaction_unittest.cc b/net/ftp/ftp_network_transaction_unittest.cc
index 1740da9..ce412717 100644
--- a/net/ftp/ftp_network_transaction_unittest.cc
+++ b/net/ftp/ftp_network_transaction_unittest.cc
@@ -9,8 +9,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "net/base/host_port_pair.h"
 #include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/ftp/ftp_request_info.h"
@@ -907,9 +907,10 @@
 
   EXPECT_TRUE(transaction_->GetResponseInfo()->is_directory_listing);
   EXPECT_EQ(-1, transaction_->GetResponseInfo()->expected_content_size);
-  EXPECT_EQ((GetFamily() == AF_INET) ? "127.0.0.1" : "::1",
-            transaction_->GetResponseInfo()->socket_address.host());
-  EXPECT_EQ(21, transaction_->GetResponseInfo()->socket_address.port());
+  EXPECT_EQ(
+      (GetFamily() == AF_INET) ? "127.0.0.1" : "::1",
+      transaction_->GetResponseInfo()->remote_endpoint.ToStringWithoutPort());
+  EXPECT_EQ(21, transaction_->GetResponseInfo()->remote_endpoint.port());
 }
 
 TEST_P(FtpNetworkTransactionTest, DirectoryTransactionWithPasvFallback) {
@@ -990,9 +991,10 @@
 
   // We pass an artificial value of 18 as a response to the SIZE command.
   EXPECT_EQ(18, transaction_->GetResponseInfo()->expected_content_size);
-  EXPECT_EQ((GetFamily() == AF_INET) ? "127.0.0.1" : "::1",
-            transaction_->GetResponseInfo()->socket_address.host());
-  EXPECT_EQ(21, transaction_->GetResponseInfo()->socket_address.port());
+  EXPECT_EQ(
+      (GetFamily() == AF_INET) ? "127.0.0.1" : "::1",
+      transaction_->GetResponseInfo()->remote_endpoint.ToStringWithoutPort());
+  EXPECT_EQ(21, transaction_->GetResponseInfo()->remote_endpoint.port());
 }
 
 TEST_P(FtpNetworkTransactionTest, DownloadTransactionWithPasvFallback) {
diff --git a/net/ftp/ftp_response_info.h b/net/ftp/ftp_response_info.h
index 70d36536..d20249a0 100644
--- a/net/ftp/ftp_response_info.h
+++ b/net/ftp/ftp_response_info.h
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "base/time/time.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 
 namespace net {
 
@@ -37,7 +37,7 @@
   bool is_directory_listing;
 
   // Remote address of the socket which fetched this resource.
-  HostPortPair socket_address;
+  IPEndPoint remote_endpoint;
 };
 
 }  // namespace net
diff --git a/net/http/bidirectional_stream.cc b/net/http/bidirectional_stream.cc
index 3530487..404ac07 100644
--- a/net/http/bidirectional_stream.cc
+++ b/net/http/bidirectional_stream.cc
@@ -420,14 +420,14 @@
   StartRequest(ssl_config);
 }
 
-void BidirectionalStream::OnHttpsProxyTunnelResponse(
+void BidirectionalStream::OnHttpsProxyTunnelResponseRedirect(
     const HttpResponseInfo& response_info,
     const SSLConfig& used_ssl_config,
     const ProxyInfo& used_proxy_info,
     std::unique_ptr<HttpStream> stream) {
   DCHECK(stream_request_);
 
-  NotifyFailed(ERR_HTTPS_PROXY_TUNNEL_RESPONSE);
+  NotifyFailed(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT);
 }
 
 void BidirectionalStream::OnQuicBroken() {}
diff --git a/net/http/bidirectional_stream.h b/net/http/bidirectional_stream.h
index 61fa08c..2cfeca0 100644
--- a/net/http/bidirectional_stream.h
+++ b/net/http/bidirectional_stream.h
@@ -218,10 +218,11 @@
                         HttpAuthController* auth_controller) override;
   void OnNeedsClientAuth(const SSLConfig& used_ssl_config,
                          SSLCertRequestInfo* cert_info) override;
-  void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info,
-                                  const SSLConfig& used_ssl_config,
-                                  const ProxyInfo& used_proxy_info,
-                                  std::unique_ptr<HttpStream> stream) override;
+  void OnHttpsProxyTunnelResponseRedirect(
+      const HttpResponseInfo& response_info,
+      const SSLConfig& used_ssl_config,
+      const ProxyInfo& used_proxy_info,
+      std::unique_ptr<HttpStream> stream) override;
   void OnQuicBroken() override;
 
   // Helper method to notify delegate if there is an error.
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index bd10e49a..dc68d9ee 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -8381,10 +8381,12 @@
 
 // Tests basic pickling/unpickling of HttpResponseInfo.
 TEST_F(HttpCacheTest, PersistHttpResponseInfo) {
+  const IPEndPoint expected_endpoint =
+      IPEndPoint(net::IPAddress(1, 2, 3, 4), 80);
   // Set some fields (add more if needed.)
   HttpResponseInfo response1;
   response1.was_cached = false;
-  response1.socket_address = HostPortPair("1.2.3.4", 80);
+  response1.remote_endpoint = expected_endpoint;
   response1.headers = new HttpResponseHeaders("HTTP/1.1 200 OK");
 
   // Pickle.
@@ -8399,8 +8401,7 @@
 
   // Verify fields.
   EXPECT_TRUE(response2.was_cached);  // InitFromPickle sets this flag.
-  EXPECT_EQ("1.2.3.4", response2.socket_address.host());
-  EXPECT_EQ(80, response2.socket_address.port());
+  EXPECT_EQ(expected_endpoint, response2.remote_endpoint);
   EXPECT_EQ("HTTP/1.1 200 OK", response2.headers->GetStatusLine());
 }
 
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 397f54b..e20860ca 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -648,7 +648,7 @@
   OnIOComplete(ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
 }
 
-void HttpNetworkTransaction::OnHttpsProxyTunnelResponse(
+void HttpNetworkTransaction::OnHttpsProxyTunnelResponseRedirect(
     const HttpResponseInfo& response_info,
     const SSLConfig& used_ssl_config,
     const ProxyInfo& used_proxy_info,
@@ -668,7 +668,7 @@
   stream_ = std::move(stream);
   stream_->SetRequestHeadersCallback(request_headers_callback_);
   stream_request_.reset();  // we're done with the stream request
-  OnIOComplete(ERR_HTTPS_PROXY_TUNNEL_RESPONSE);
+  OnIOComplete(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT);
 }
 
 void HttpNetworkTransaction::OnQuicBroken() {
@@ -854,11 +854,12 @@
 }
 
 int HttpNetworkTransaction::DoCreateStreamComplete(int result) {
-  // If |result| is ERR_HTTPS_PROXY_TUNNEL_RESPONSE, then
-  // DoCreateStreamComplete is being called from OnHttpsProxyTunnelResponse,
-  // which resets the stream request first. Therefore, we have to grab the
-  // connection attempts in *that* function instead of here in that case.
-  if (result != ERR_HTTPS_PROXY_TUNNEL_RESPONSE)
+  // If |result| is ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT, then
+  // DoCreateStreamComplete is being called from
+  // OnHttpsProxyTunnelResponseRedirect, which resets the stream request first.
+  // Therefore, we have to grab the connection attempts in *that* function
+  // instead of here in that case.
+  if (result != ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT)
     CopyConnectionAttemptsFromStreamRequest();
 
   if (result == OK) {
@@ -866,10 +867,8 @@
     DCHECK(stream_.get());
   } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
     result = HandleCertificateRequest(result);
-  } else if (result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
-    // Return OK and let the caller read the proxy's error page
-    next_state_ = STATE_NONE;
-    return OK;
+  } else if (result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT) {
+    return DoCreateStreamCompletedTunnelResponseRedirect();
   } else if (result == ERR_HTTP_1_1_REQUIRED ||
              result == ERR_PROXY_HTTP_1_1_REQUIRED) {
     return HandleHttp11Required(result);
@@ -1943,4 +1942,45 @@
   return result;
 }
 
+static HttpNetworkTransaction::TunnelRedirectHistogramValue
+GetTunnelRedirectHistogramValue(bool is_main_frame, bool was_auto_detected) {
+  if (!is_main_frame && !was_auto_detected)
+    return HttpNetworkTransaction::kSubresourceByExplicitProxy;
+  if (is_main_frame && !was_auto_detected)
+    return HttpNetworkTransaction::kMainFrameByExplicitProxy;
+  if (!is_main_frame && was_auto_detected)
+    return HttpNetworkTransaction::kSubresourceByAutoDetectedProxy;
+  return HttpNetworkTransaction::kMainFrameByAutoDetectedProxy;
+}
+
+// TODO(https://crbug.com/928551): Support for redirect on CONNECT is
+// deprecated, and support will be removed.
+//
+// The code in this method handles the temporary histogramming and
+// compatibility-mode policy during the phase-out.
+int HttpNetworkTransaction::DoCreateStreamCompletedTunnelResponseRedirect() {
+  bool is_main_frame = (request_->load_flags & LOAD_MAIN_FRAME_DEPRECATED) ==
+                       LOAD_MAIN_FRAME_DEPRECATED;
+  bool was_auto_detected = proxy_info_.did_use_auto_detected_pac_script();
+
+  UMA_HISTOGRAM_ENUMERATION(
+      "Net.Proxy.RedirectDuringConnect",
+      GetTunnelRedirectHistogramValue(is_main_frame, was_auto_detected));
+
+  // For legacy compatibility, the proxy is allowed to redirect CONNECT
+  // if:
+  //      (a) the request was for a top-level frame
+  //      (b) the proxy server was explicitly configured (i.e. not
+  //          auto-detected).
+  if (is_main_frame && !was_auto_detected) {
+    // Return OK and let the caller read the proxy's error page
+    next_state_ = STATE_NONE;
+    return OK;
+  }
+
+  // Otherwise let the request fail.
+  stream_.reset();
+  return ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT;
+}
+
 }  // namespace net
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 7ccc5f9..736c98f 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -50,6 +50,16 @@
     : public HttpTransaction,
       public HttpStreamRequest::Delegate {
  public:
+  // Enumeration used by Net.Proxy.RedirectDuringConnect. Exposed here for
+  // sharing by unit-tests.
+  enum TunnelRedirectHistogramValue {
+    kSubresourceByExplicitProxy = 0,
+    kMainFrameByExplicitProxy = 1,
+    kSubresourceByAutoDetectedProxy = 2,
+    kMainFrameByAutoDetectedProxy = 3,
+    kMaxValue = kMainFrameByAutoDetectedProxy
+  };
+
   HttpNetworkTransaction(RequestPriority priority,
                          HttpNetworkSession* session);
 
@@ -117,10 +127,11 @@
                         HttpAuthController* auth_controller) override;
   void OnNeedsClientAuth(const SSLConfig& used_ssl_config,
                          SSLCertRequestInfo* cert_info) override;
-  void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info,
-                                  const SSLConfig& used_ssl_config,
-                                  const ProxyInfo& used_proxy_info,
-                                  std::unique_ptr<HttpStream> stream) override;
+  void OnHttpsProxyTunnelResponseRedirect(
+      const HttpResponseInfo& response_info,
+      const SSLConfig& used_ssl_config,
+      const ProxyInfo& used_proxy_info,
+      std::unique_ptr<HttpStream> stream) override;
 
   void OnQuicBroken() override;
   void GetConnectionAttempts(ConnectionAttempts* out) const override;
@@ -309,6 +320,10 @@
   // "Accept-Encoding".
   bool ContentEncodingsValid() const;
 
+  // Logic for handling ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT seen during
+  // DoCreateStreamCompletedTunnel().
+  int DoCreateStreamCompletedTunnelResponseRedirect();
+
   scoped_refptr<HttpAuthController>
       auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
 
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 9295d8cdf..b3ac220 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -39,6 +39,7 @@
 #include "net/base/completion_once_callback.h"
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/load_timing_info_test_util.h"
 #include "net/base/net_errors.h"
@@ -51,7 +52,6 @@
 #include "net/base/upload_file_element_reader.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/mock_cert_verifier.h"
-#include "net/dns/host_cache.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_handler_digest.h"
@@ -470,8 +470,8 @@
     }
     out.status_line = response->headers->GetStatusLine();
 
-    EXPECT_EQ("127.0.0.1", response->socket_address.host());
-    EXPECT_EQ(80, response->socket_address.port());
+    EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
+    EXPECT_EQ(80, response->remote_endpoint.port());
 
     bool got_endpoint =
         trans.GetRemoteEndpoint(&out.remote_endpoint_after_start);
@@ -9498,7 +9498,7 @@
                                  CONNECT_TIMING_HAS_SSL_TIMES);
 }
 
-// Test an HTTPS Proxy's ability to redirect a CONNECT request
+// Test that an HTTPS Proxy can redirect a CONNECT request for main frames.
 TEST_F(HttpNetworkTransactionTest, RedirectOfHttpsConnectViaHttpsProxy) {
   session_deps_.proxy_resolution_service =
       ProxyResolutionService::CreateFixedFromPacResult(
@@ -9511,6 +9511,7 @@
   session_deps_.host_resolver->set_ondemand_mode(true);
 
   HttpRequestInfo request;
+  request.load_flags = LOAD_MAIN_FRAME_DEPRECATED;
   request.method = "GET";
   request.url = GURL("https://www.example.org/");
   request.traffic_annotation =
@@ -9582,7 +9583,7 @@
   // name, and negotiate an SSL connection to it (Neither of which are done in
   // this case), which the DNS and SSL times for the proxy are all included in
   // connect_start / connect_end. See
-  // HttpNetworkTransaction::OnHttpsProxyTunnelResponse.
+  // HttpNetworkTransaction::OnHttpsProxyTunnelResponseRedirect
 
   EXPECT_TRUE(load_timing_info.connect_timing.dns_start.is_null());
   EXPECT_TRUE(load_timing_info.connect_timing.dns_end.is_null());
@@ -9600,8 +9601,114 @@
   EXPECT_TRUE(load_timing_info.receive_headers_end.is_null());
 }
 
-// Test an HTTPS (SPDY) Proxy's ability to redirect a CONNECT request
+// Test that an HTTPS Proxy cannot redirect a CONNECT request for subresources.
+TEST_F(HttpNetworkTransactionTest,
+       RedirectOfHttpsConnectSubresourceViaHttpsProxy) {
+  base::HistogramTester histograms;
+  session_deps_.proxy_resolution_service =
+      ProxyResolutionService::CreateFixedFromPacResult(
+          "HTTPS proxy:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestNetLog net_log;
+  session_deps_.net_log = &net_log;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(ASYNC, 0,
+                "CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org:443\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+
+  MockRead data_reads[] = {
+      MockRead(ASYNC, 1, "HTTP/1.1 302 Redirect\r\n"),
+      MockRead(ASYNC, 2, "Location: http://login.example.com/\r\n"),
+      MockRead(ASYNC, 3, "Content-Length: 0\r\n\r\n"),
+  };
+
+  SequencedSocketData data(MockConnect(ASYNC, OK), data_reads, data_writes);
+  SSLSocketDataProvider proxy_ssl(ASYNC, OK);  // SSL to the proxy
+
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&proxy_ssl);
+
+  TestCompletionCallback callback;
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+  HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans.Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsError(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT));
+
+  histograms.ExpectUniqueSample(
+      "Net.Proxy.RedirectDuringConnect",
+      HttpNetworkTransaction::kSubresourceByExplicitProxy, 1);
+}
+
+// Test that an HTTPS Proxy which was auto-detected cannot redirect a CONNECT
+// request for main frames.
+TEST_F(HttpNetworkTransactionTest,
+       RedirectOfHttpsConnectViaAutoDetectedHttpsProxy) {
+  base::HistogramTester histograms;
+  session_deps_.proxy_resolution_service =
+      ProxyResolutionService::CreateFixedFromAutoDetectedPacResult(
+          "HTTPS proxy:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+  TestNetLog net_log;
+  session_deps_.net_log = &net_log;
+
+  HttpRequestInfo request;
+  request.load_flags = LOAD_MAIN_FRAME_DEPRECATED;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  MockWrite data_writes[] = {
+      MockWrite(ASYNC, 0,
+                "CONNECT www.example.org:443 HTTP/1.1\r\n"
+                "Host: www.example.org:443\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+
+  MockRead data_reads[] = {
+      MockRead(ASYNC, 1, "HTTP/1.1 302 Redirect\r\n"),
+      MockRead(ASYNC, 2, "Location: http://login.example.com/\r\n"),
+      MockRead(ASYNC, 3, "Content-Length: 0\r\n\r\n"),
+  };
+
+  SequencedSocketData data(MockConnect(ASYNC, OK), data_reads, data_writes);
+  SSLSocketDataProvider proxy_ssl(ASYNC, OK);  // SSL to the proxy
+
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&proxy_ssl);
+
+  TestCompletionCallback callback;
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+  HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans.Start(&request, callback.callback(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsError(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT));
+
+  histograms.ExpectUniqueSample(
+      "Net.Proxy.RedirectDuringConnect",
+      HttpNetworkTransaction::kMainFrameByAutoDetectedProxy, 1);
+}
+
+// Test an HTTPS (SPDY) Proxy's ability to redirect a CONNECT request for main
+// frames.
 TEST_F(HttpNetworkTransactionTest, RedirectOfHttpsConnectViaSpdyProxy) {
+  base::HistogramTester histograms;
   session_deps_.proxy_resolution_service = ProxyResolutionService::CreateFixed(
       "https://proxy:70", TRAFFIC_ANNOTATION_FOR_TESTS);
   TestNetLog net_log;
@@ -9613,6 +9720,7 @@
 
   HttpRequestInfo request;
   request.method = "GET";
+  request.load_flags = LOAD_MAIN_FRAME_DEPRECATED;
   request.url = GURL("https://www.example.org/");
   request.traffic_annotation =
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
@@ -9692,7 +9800,7 @@
   // name, and negotiate an SSL connection to it (Neither of which are done in
   // this case), which the DNS and SSL times for the proxy are all included in
   // connect_start / connect_end. See
-  // HttpNetworkTransaction::OnHttpsProxyTunnelResponse.
+  // HttpNetworkTransaction::OnHttpsProxyTunnelResponseRedirect.
 
   EXPECT_TRUE(load_timing_info.connect_timing.dns_start.is_null());
   EXPECT_TRUE(load_timing_info.connect_timing.dns_end.is_null());
@@ -9706,6 +9814,10 @@
   EXPECT_TRUE(load_timing_info.send_start.is_null());
   EXPECT_TRUE(load_timing_info.send_end.is_null());
   EXPECT_TRUE(load_timing_info.receive_headers_end.is_null());
+
+  histograms.ExpectUniqueSample(
+      "Net.Proxy.RedirectDuringConnect",
+      HttpNetworkTransaction::kMainFrameByExplicitProxy, 1);
 }
 
 // Test that an HTTPS proxy's response to a CONNECT request is filtered.
@@ -15744,30 +15856,11 @@
   EXPECT_EQ("hello!", response_data);
 }
 
-class OneTimeCachingHostResolver : public MockHostResolverBase {
- public:
-  explicit OneTimeCachingHostResolver(const HostPortPair& host_port)
-      : MockHostResolverBase(/* use_caching = */ true), host_port_(host_port) {}
-  ~OneTimeCachingHostResolver() override = default;
-
-  int ResolveFromCache(const RequestInfo& info,
-                       AddressList* addresses,
-                       const NetLogWithSource& net_log) override {
-    int rv = MockHostResolverBase::ResolveFromCache(info, addresses, net_log);
-    if (rv == OK && info.host_port_pair().Equals(host_port_))
-      GetHostCache()->clear();
-    return rv;
-  }
-
- private:
-  const HostPortPair host_port_;
-};
-
 TEST_F(HttpNetworkTransactionTest,
        UseIPConnectionPoolingWithHostCacheExpiration) {
-  // Set up a special HttpNetworkSession with a OneTimeCachingHostResolver.
-  session_deps_.host_resolver = std::make_unique<OneTimeCachingHostResolver>(
-      HostPortPair("mail.example.com", 443));
+  // Set up HostResolver to invalidate cached entries after 1 cached resolve.
+  session_deps_.host_resolver =
+      std::make_unique<MockCachingHostResolver>(1 /* cache_invalidation_num */);
   std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
 
   AddSSLSocketData();
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index 6c57135..68349676 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -507,7 +507,7 @@
       stream_socket_.reset();
       socket_ = nullptr;
       is_reused_ = false;
-      return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
+      return ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT;
 
     case 407:  // Proxy Authentication Required
       // We need this status code to allow proxy authentication.  Our
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index 457b4a9..9f4509b 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -436,7 +436,7 @@
     EXPECT_FALSE(handle_.socket());
   } else {
     // Expect ProxyClientSocket to return the proxy's response, sanitized.
-    EXPECT_THAT(rv, IsError(ERR_HTTPS_PROXY_TUNNEL_RESPONSE));
+    EXPECT_THAT(rv, IsError(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT));
     EXPECT_TRUE(handle_.is_initialized());
     ASSERT_TRUE(handle_.socket());
 
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index a202d79..a820e4c8 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -279,7 +279,7 @@
     error_response_info_ = client_socket_->GetAdditionalErrorState();
 
   if (result == OK || result == ERR_PROXY_AUTH_REQUESTED ||
-      result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
+      result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT) {
     SetSocket(std::move(client_socket_));
   }
   return result;
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 4764795..0dfb809 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -229,7 +229,13 @@
   uint16_t socket_address_port;
   if (!iter.ReadUInt16(&socket_address_port))
     return false;
-  socket_address = HostPortPair(socket_address_host, socket_address_port);
+
+  IPAddress ip_address;
+  if (ip_address.AssignFromIPLiteral(socket_address_host)) {
+    remote_endpoint = IPEndPoint(ip_address, socket_address_port);
+  } else if (ParseURLHostnameToAddress(socket_address_host, &ip_address)) {
+    remote_endpoint = IPEndPoint(ip_address, socket_address_port);
+  }
 
   // Read protocol-version.
   if (flags & RESPONSE_INFO_HAS_ALPN_NEGOTIATED_PROTOCOL) {
@@ -364,8 +370,8 @@
   if (vary_data.is_valid())
     vary_data.Persist(pickle);
 
-  pickle->WriteString(socket_address.host());
-  pickle->WriteUInt16(socket_address.port());
+  pickle->WriteString(remote_endpoint.ToStringWithoutPort());
+  pickle->WriteUInt16(remote_endpoint.port());
 
   if (was_alpn_negotiated)
     pickle->WriteString(alpn_negotiated_protocol);
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index ca364d7..48fea75 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/time/time.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
 #include "net/base/proxy_server.h"
 #include "net/http/http_vary_data.h"
@@ -170,7 +171,7 @@
   // originally.  This is true even if the response was re-validated using a
   // different remote address, or if some of the content came from a byte-range
   // request to a different address.
-  HostPortPair socket_address;
+  IPEndPoint remote_endpoint;
 
   // Protocol negotiated with the server.
   std::string alpn_negotiated_protocol;
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index f7158e1..4f72e404 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -530,13 +530,13 @@
   // |this| may be deleted after this call.
 }
 
-void HttpStreamFactory::Job::OnHttpsProxyTunnelResponseCallback(
+void HttpStreamFactory::Job::OnHttpsProxyTunnelResponseRedirectCallback(
     const HttpResponseInfo& response_info,
     std::unique_ptr<HttpStream> stream) {
   DCHECK_NE(job_type_, PRECONNECT);
 
-  delegate_->OnHttpsProxyTunnelResponse(this, response_info, server_ssl_config_,
-                                        proxy_info_, std::move(stream));
+  delegate_->OnHttpsProxyTunnelResponseRedirect(
+      this, response_info, server_ssl_config_, proxy_info_, std::move(stream));
   // |this| may be deleted after this call.
 }
 
@@ -639,7 +639,7 @@
                   connection_->ssl_error_response_info().cert_request_info)));
       return;
 
-    case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: {
+    case ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT: {
       DCHECK(connection_.get());
       DCHECK(connection_->socket());
       DCHECK(establishing_tunnel_);
@@ -652,7 +652,7 @@
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE,
           base::BindOnce(
-              &Job::OnHttpsProxyTunnelResponseCallback,
+              &Job::OnHttpsProxyTunnelResponseRedirectCallback,
               ptr_factory_.GetWeakPtr(),
               *proxy_socket->GetConnectResponseInfo(),
               std::make_unique<ProxyConnectRedirectHttpStream>(
@@ -1096,7 +1096,7 @@
   }
 
   if (result == ERR_PROXY_AUTH_REQUESTED ||
-      result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
+      result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT) {
     DCHECK(!ssl_started);
     // Other state (i.e. |using_ssl_|) suggests that |connection_| will have an
     // SSL socket, but there was an error before that could happen.  This
diff --git a/net/http/http_stream_factory_job.h b/net/http/http_stream_factory_job.h
index 4d50b0c7..7dc0399 100644
--- a/net/http/http_stream_factory_job.h
+++ b/net/http/http_stream_factory_job.h
@@ -92,9 +92,9 @@
                                     const SSLConfig& used_ssl_config,
                                     const SSLInfo& ssl_info) = 0;
 
-    // Invoked when |job| has a failure of the CONNECT request through an HTTPS
-    // proxy.
-    virtual void OnHttpsProxyTunnelResponse(
+    // Invoked when |job| has a failure of the CONNECT request (due to a 302
+    // redirect) through an HTTPS proxy.
+    virtual void OnHttpsProxyTunnelResponseRedirect(
         Job* job,
         const HttpResponseInfo& response_info,
         const SSLConfig& used_ssl_config,
@@ -302,8 +302,9 @@
   void OnNeedsProxyAuthCallback(const HttpResponseInfo& response_info,
                                 HttpAuthController* auth_controller);
   void OnNeedsClientAuthCallback(SSLCertRequestInfo* cert_info);
-  void OnHttpsProxyTunnelResponseCallback(const HttpResponseInfo& response_info,
-                                          std::unique_ptr<HttpStream> stream);
+  void OnHttpsProxyTunnelResponseRedirectCallback(
+      const HttpResponseInfo& response_info,
+      std::unique_ptr<HttpStream> stream);
   void OnPreconnectsComplete();
 
   void OnIOComplete(int result);
diff --git a/net/http/http_stream_factory_job_controller.cc b/net/http/http_stream_factory_job_controller.cc
index 333031e..7c53ba1 100644
--- a/net/http/http_stream_factory_job_controller.cc
+++ b/net/http/http_stream_factory_job_controller.cc
@@ -415,7 +415,7 @@
   delegate_->OnCertificateError(status, used_ssl_config, ssl_info);
 }
 
-void HttpStreamFactory::JobController::OnHttpsProxyTunnelResponse(
+void HttpStreamFactory::JobController::OnHttpsProxyTunnelResponseRedirect(
     Job* job,
     const HttpResponseInfo& response_info,
     const SSLConfig& used_ssl_config,
@@ -434,8 +434,8 @@
     BindJob(job);
   if (!request_)
     return;
-  delegate_->OnHttpsProxyTunnelResponse(response_info, used_ssl_config,
-                                        used_proxy_info, std::move(stream));
+  delegate_->OnHttpsProxyTunnelResponseRedirect(
+      response_info, used_ssl_config, used_proxy_info, std::move(stream));
 }
 
 void HttpStreamFactory::JobController::OnNeedsClientAuth(
diff --git a/net/http/http_stream_factory_job_controller.h b/net/http/http_stream_factory_job_controller.h
index 6c4c2b0..5a7b0ff4d 100644
--- a/net/http/http_stream_factory_job_controller.h
+++ b/net/http/http_stream_factory_job_controller.h
@@ -120,13 +120,14 @@
                           const SSLConfig& used_ssl_config,
                           const SSLInfo& ssl_info) override;
 
-  // Invoked when |job| has a failure of the CONNECT request through an HTTPS
-  // proxy.
-  void OnHttpsProxyTunnelResponse(Job* job,
-                                  const HttpResponseInfo& response_info,
-                                  const SSLConfig& used_ssl_config,
-                                  const ProxyInfo& used_proxy_info,
-                                  std::unique_ptr<HttpStream> stream) override;
+  // Invoked when |job| has a failure of the CONNECT request (due to 302
+  // redirect) through an HTTPS proxy.
+  void OnHttpsProxyTunnelResponseRedirect(
+      Job* job,
+      const HttpResponseInfo& response_info,
+      const SSLConfig& used_ssl_config,
+      const ProxyInfo& used_proxy_info,
+      std::unique_ptr<HttpStream> stream) override;
 
   // Invoked when |job| raises failure for SSL Client Auth.
   void OnNeedsClientAuth(Job* job,
diff --git a/net/http/http_stream_factory_test_util.h b/net/http/http_stream_factory_test_util.h
index c186f10..c944019 100644
--- a/net/http/http_stream_factory_test_util.h
+++ b/net/http/http_stream_factory_test_util.h
@@ -93,11 +93,11 @@
                     SSLCertRequestInfo* cert_info));
 
   // std::unique_ptr is not copyable and therefore cannot be mocked.
-  void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info,
-                                  const SSLConfig& used_ssl_config,
-                                  const ProxyInfo& used_proxy_info,
-                                  std::unique_ptr<HttpStream> stream) override {
-  }
+  void OnHttpsProxyTunnelResponseRedirect(
+      const HttpResponseInfo& response_info,
+      const SSLConfig& used_ssl_config,
+      const ProxyInfo& used_proxy_info,
+      std::unique_ptr<HttpStream> stream) override {}
 
   MOCK_METHOD0(OnQuicBroken, void());
 
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc
index 216f10b..83ace9c 100644
--- a/net/http/http_stream_factory_unittest.cc
+++ b/net/http/http_stream_factory_unittest.cc
@@ -265,11 +265,11 @@
   void OnNeedsClientAuth(const SSLConfig& used_ssl_config,
                          SSLCertRequestInfo* cert_info) override {}
 
-  void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info,
-                                  const SSLConfig& used_ssl_config,
-                                  const ProxyInfo& used_proxy_info,
-                                  std::unique_ptr<HttpStream> stream) override {
-  }
+  void OnHttpsProxyTunnelResponseRedirect(
+      const HttpResponseInfo& response_info,
+      const SSLConfig& used_ssl_config,
+      const ProxyInfo& used_proxy_info,
+      std::unique_ptr<HttpStream> stream) override {}
 
   void OnQuicBroken() override {}
 
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 7ceff48..95a2c9f9 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -245,7 +245,7 @@
   int result = stream_socket_->GetPeerAddress(&ip_endpoint);
   if (result != OK)
     return result;
-  response_->socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
+  response_->remote_endpoint = ip_endpoint;
 
   std::string request = request_line + headers.ToString();
   request_headers_length_ = request.size();
diff --git a/net/http/http_stream_request.h b/net/http/http_stream_request.h
index c07e7f6..602854c 100644
--- a/net/http/http_stream_request.h
+++ b/net/http/http_stream_request.h
@@ -125,9 +125,9 @@
     virtual void OnNeedsClientAuth(const SSLConfig& used_ssl_config,
                                    SSLCertRequestInfo* cert_info) = 0;
 
-    // This is the failure of the CONNECT request through an HTTPS proxy.
-    // Headers can be read from |response_info|, while the body can be read
-    // from |stream|.
+    // This is the failure of the CONNECT request through an HTTPS proxy due to
+    // a 302 redirect. Headers can be read from |response_info|, while the body
+    // can be read from |stream|.
     //
     // |used_ssl_config| indicates the actual SSL configuration used for this
     // stream, since the HttpStreamRequest may have modified the configuration
@@ -137,7 +137,7 @@
     // since the HttpStreamRequest performs the proxy resolution.
     //
     // Ownership of |stream| is transferred to the delegate.
-    virtual void OnHttpsProxyTunnelResponse(
+    virtual void OnHttpsProxyTunnelResponseRedirect(
         const HttpResponseInfo& response_info,
         const SSLConfig& used_ssl_config,
         const ProxyInfo& used_proxy_info,
diff --git a/net/nqe/network_quality_estimator_util_unittest.cc b/net/nqe/network_quality_estimator_util_unittest.cc
index 0f464f9..c907e910 100644
--- a/net/nqe/network_quality_estimator_util_unittest.cc
+++ b/net/nqe/network_quality_estimator_util_unittest.cc
@@ -6,7 +6,9 @@
 
 #include <memory>
 
+#include "base/optional.h"
 #include "base/test/scoped_task_environment.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/host_resolver.h"
@@ -46,31 +48,13 @@
 
   EXPECT_EQ(0u, mock_host_resolver.num_resolve());
 
-  {
-    // Resolve example1.com so that the resolution entry is cached.
-    TestCompletionCallback callback;
-    std::unique_ptr<HostResolver::Request> request;
-    AddressList ignored;
-    int rv = mock_host_resolver.Resolve(
-        HostResolver::RequestInfo(HostPortPair("example1.com", 443)),
-        DEFAULT_PRIORITY, &ignored, callback.callback(), &request,
-        NetLogWithSource());
-    EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_EQ(OK, callback.WaitForResult());
-  }
-
-  {
-    // Resolve example2.com so that the resolution entry is cached.
-    TestCompletionCallback callback;
-    std::unique_ptr<HostResolver::Request> request;
-    AddressList ignored;
-    int rv = mock_host_resolver.Resolve(
-        HostResolver::RequestInfo(HostPortPair("example2.com", 443)),
-        DEFAULT_PRIORITY, &ignored, callback.callback(), &request,
-        NetLogWithSource());
-    EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_EQ(OK, callback.WaitForResult());
-  }
+  // Load hostnames into HostResolver cache.
+  int rv = mock_host_resolver.LoadIntoCache(HostPortPair("example1.com", 443),
+                                            base::nullopt);
+  EXPECT_EQ(OK, rv);
+  rv = mock_host_resolver.LoadIntoCache(HostPortPair("example2.com", 443),
+                                        base::nullopt);
+  EXPECT_EQ(OK, rv);
 
   EXPECT_EQ(2u, mock_host_resolver.num_non_local_resolves());
 
@@ -115,19 +99,11 @@
       IsPrivateHost(&mock_host_resolver, HostPortPair("example3.com", 443)));
   EXPECT_EQ(0u, mock_host_resolver.num_non_local_resolves());
 
-  {
-    // Resolve example3.com so that the resolution entry is cached.
-    TestCompletionCallback callback;
-    std::unique_ptr<HostResolver::Request> request;
-    AddressList ignored;
-    int rv = mock_host_resolver.Resolve(
-        HostResolver::RequestInfo(HostPortPair("example3.com", 443)),
-        DEFAULT_PRIORITY, &ignored, callback.callback(), &request,
-        NetLogWithSource());
-    EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ(1u, mock_host_resolver.num_non_local_resolves());
-  }
+  int rv = mock_host_resolver.LoadIntoCache(HostPortPair("example3.com", 443),
+                                            base::nullopt);
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ(1u, mock_host_resolver.num_non_local_resolves());
+
   EXPECT_TRUE(
       IsPrivateHost(&mock_host_resolver, HostPortPair("example3.com", 443)));
 
diff --git a/net/proxy_resolution/pac_file_decider.cc b/net/proxy_resolution/pac_file_decider.cc
index 88fd027..48af28f 100644
--- a/net/proxy_resolution/pac_file_decider.cc
+++ b/net/proxy_resolution/pac_file_decider.cc
@@ -42,8 +42,6 @@
          base::string16::npos;
 }
 
-}  // anonymous namespace
-
 // This is the hard-coded location used by the DNS portion of web proxy
 // auto-discovery.
 //
@@ -56,11 +54,18 @@
 //
 // For more details, also check out this comment:
 // http://code.google.com/p/chromium/issues/detail?id=18575#c20
-namespace {
 const char kWpadUrl[] = "http://wpad/wpad.dat";
 const int kQuickCheckDelayMs = 1000;
+
 }  // namespace
 
+PacFileDataWithSource::PacFileDataWithSource() = default;
+PacFileDataWithSource::~PacFileDataWithSource() = default;
+PacFileDataWithSource::PacFileDataWithSource(const PacFileDataWithSource&) =
+    default;
+PacFileDataWithSource& PacFileDataWithSource::operator=(
+    const PacFileDataWithSource&) = default;
+
 std::unique_ptr<base::Value> PacFileDecider::PacSource::NetLogCallback(
     const GURL* effective_pac_url,
     NetLogCaptureMode /* capture_mode */) const {
@@ -156,7 +161,7 @@
   return effective_config_;
 }
 
-const scoped_refptr<PacFileData>& PacFileDecider::script_data() const {
+const PacFileDataWithSource& PacFileDecider::script_data() const {
   DCHECK_EQ(STATE_NONE, next_state_);
   return script_data_;
 }
@@ -369,12 +374,13 @@
   const PacSource& pac_source = current_pac_source();
 
   // Extract the current script data.
+  script_data_.from_auto_detect = pac_source.type != PacSource::CUSTOM;
   if (fetch_pac_bytes_) {
-    script_data_ = PacFileData::FromUTF16(pac_script_);
+    script_data_.data = PacFileData::FromUTF16(pac_script_);
   } else {
-    script_data_ = pac_source.type == PacSource::CUSTOM
-                       ? PacFileData::FromURL(pac_source.url)
-                       : PacFileData::ForAutoDetect();
+    script_data_.data = pac_source.type == PacSource::CUSTOM
+                            ? PacFileData::FromURL(pac_source.url)
+                            : PacFileData::ForAutoDetect();
   }
 
   // Let the caller know which automatic setting we ended up initializing the
diff --git a/net/proxy_resolution/pac_file_decider.h b/net/proxy_resolution/pac_file_decider.h
index fe4fbe1b..6b91d9d1 100644
--- a/net/proxy_resolution/pac_file_decider.h
+++ b/net/proxy_resolution/pac_file_decider.h
@@ -36,6 +36,24 @@
 class ProxyResolver;
 class PacFileFetcher;
 
+// Structure that encapsulates the result a PacFileData along with an
+// indication of its origin: was it obtained implicitly from auto-detect,
+// or was it read from a more explicitly configured URL.
+//
+// Note that |!from_auto_detect| does NOT imply the script was securely
+// delivered. Most commonly PAC scripts are configured from http:// URLs,
+// both for auto-detect and not.
+struct NET_EXPORT_PRIVATE PacFileDataWithSource {
+  PacFileDataWithSource();
+  explicit PacFileDataWithSource(const PacFileDataWithSource&);
+  ~PacFileDataWithSource();
+
+  PacFileDataWithSource& operator=(const PacFileDataWithSource&);
+
+  scoped_refptr<PacFileData> data;
+  bool from_auto_detect = false;
+};
+
 // PacFileDecider is a helper class used by ProxyResolutionService to
 // determine which PAC script to use given our proxy configuration.
 //
@@ -87,7 +105,7 @@
 
   const ProxyConfigWithAnnotation& effective_config() const;
 
-  const scoped_refptr<PacFileData>& script_data() const;
+  const PacFileDataWithSource& script_data() const;
 
   void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; }
 
@@ -197,7 +215,7 @@
 
   // Results.
   ProxyConfigWithAnnotation effective_config_;
-  scoped_refptr<PacFileData> script_data_;
+  PacFileDataWithSource script_data_;
 
   std::unique_ptr<HostResolver::ResolveHostRequest> resolve_request_;
 
diff --git a/net/proxy_resolution/pac_file_decider_unittest.cc b/net/proxy_resolution/pac_file_decider_unittest.cc
index 216d3ba..c257c2e 100644
--- a/net/proxy_resolution/pac_file_decider_unittest.cc
+++ b/net/proxy_resolution/pac_file_decider_unittest.cc
@@ -215,7 +215,8 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsOk());
-  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
+  EXPECT_EQ(rule.text(), decider.script_data().data->utf16());
+  EXPECT_FALSE(decider.script_data().from_auto_detect);
 
   // Check the NetLog was filled correctly.
   TestNetLogEntry::List entries;
@@ -253,7 +254,7 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsError(kFailedDownloading));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 
   // Check the NetLog was filled correctly.
   TestNetLogEntry::List entries;
@@ -289,7 +290,7 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsError(kFailedParsing));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 }
 
 // Fail downloading the custom PAC script, because the fetcher was NULL.
@@ -306,7 +307,7 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsError(ERR_UNEXPECTED));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 }
 
 // Succeeds in choosing autodetect (WPAD DNS).
@@ -326,7 +327,8 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsOk());
-  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
+  EXPECT_EQ(rule.text(), decider.script_data().data->utf16());
+  EXPECT_TRUE(decider.script_data().from_auto_detect);
 
   EXPECT_TRUE(decider.effective_config().value().has_pac_url());
   EXPECT_EQ(rule.url, decider.effective_config().value().pac_url());
@@ -371,7 +373,8 @@
   resolver_.rules_map()[HostResolverSource::SYSTEM]->AddRule("wpad", "1.2.3.4");
 
   EXPECT_THAT(StartDecider(), IsOk());
-  EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
+  EXPECT_EQ(rule_.text(), decider_->script_data().data->utf16());
+  EXPECT_TRUE(decider_->script_data().from_auto_detect);
 
   EXPECT_TRUE(decider_->effective_config().value().has_pac_url());
   EXPECT_EQ(rule_.url, decider_->effective_config().value().pac_url());
@@ -388,7 +391,8 @@
   resolver_.ResolveAllPending();
   callback_.WaitForResult();
   EXPECT_FALSE(resolver_.has_pending_requests());
-  EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
+  EXPECT_EQ(rule_.text(), decider_->script_data().data->utf16());
+  EXPECT_TRUE(decider_->script_data().from_auto_detect);
   EXPECT_TRUE(decider_->effective_config().value().has_pac_url());
   EXPECT_EQ(rule_.url, decider_->effective_config().value().pac_url());
 }
@@ -500,7 +504,8 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsOk());
-  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
+  EXPECT_EQ(rule.text(), decider.script_data().data->utf16());
+  EXPECT_FALSE(decider.script_data().from_auto_detect);
 
   EXPECT_TRUE(decider.effective_config().value().has_pac_url());
   EXPECT_EQ(rule.url, decider.effective_config().value().pac_url());
@@ -529,7 +534,8 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsOk());
-  EXPECT_EQ(rule.text(), decider.script_data()->utf16());
+  EXPECT_EQ(rule.text(), decider.script_data().data->utf16());
+  EXPECT_FALSE(decider.script_data().from_auto_detect);
 
   // Verify that the effective configuration no longer contains auto detect or
   // any of the manual settings.
@@ -592,7 +598,7 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsError(kFailedDownloading));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 }
 
 // Fails at WPAD (downloading), and fails at custom PAC (parsing).
@@ -614,7 +620,7 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsError(kFailedParsing));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 }
 
 // This is a copy-paste of CustomPacFails1, with the exception that we give it
@@ -642,7 +648,7 @@
       IsError(ERR_IO_PENDING));
 
   EXPECT_THAT(callback.WaitForResult(), IsError(kFailedDownloading));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 
   // Check the NetLog was filled correctly.
   TestNetLogEntry::List entries;
@@ -684,7 +690,7 @@
           ProxyConfigWithAnnotation(config, TRAFFIC_ANNOTATION_FOR_TESTS),
           base::TimeDelta::FromSeconds(-5), true, callback.callback()),
       IsError(kFailedDownloading));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 
   // Check the NetLog was filled correctly.
   TestNetLogEntry::List entries;
@@ -752,7 +758,8 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsOk());
-  EXPECT_EQ(dhcp_fetcher.expected_text(), decider.script_data()->utf16());
+  EXPECT_EQ(dhcp_fetcher.expected_text(), decider.script_data().data->utf16());
+  EXPECT_TRUE(decider.script_data().from_auto_detect);
 
   EXPECT_TRUE(decider.effective_config().value().has_pac_url());
   EXPECT_EQ(GURL("http://dhcppac/"),
@@ -779,7 +786,7 @@
                                 config, TRAFFIC_ANNOTATION_FOR_TESTS),
                             base::TimeDelta(), true, callback.callback()),
               IsError(kFailedDownloading));
-  EXPECT_FALSE(decider.script_data());
+  EXPECT_FALSE(decider.script_data().data);
 
   EXPECT_FALSE(decider.effective_config().value().has_pac_url());
 }
diff --git a/net/proxy_resolution/proxy_info.cc b/net/proxy_resolution/proxy_info.cc
index cb0bf4eb..762756d 100644
--- a/net/proxy_resolution/proxy_info.cc
+++ b/net/proxy_resolution/proxy_info.cc
@@ -8,7 +8,10 @@
 
 namespace net {
 
-ProxyInfo::ProxyInfo() : did_bypass_proxy_(false), did_use_pac_script_(false) {}
+ProxyInfo::ProxyInfo()
+    : did_bypass_proxy_(false),
+      did_use_pac_script_(false),
+      did_use_auto_detected_pac_script_(false) {}
 
 ProxyInfo::ProxyInfo(const ProxyInfo& other) = default;
 
@@ -22,6 +25,7 @@
   traffic_annotation_ = other.traffic_annotation_;
   did_bypass_proxy_ = other.did_bypass_proxy_;
   did_use_pac_script_ = other.did_use_pac_script_;
+  did_use_auto_detected_pac_script_ = other.did_use_auto_detected_pac_script_;
 }
 
 void ProxyInfo::UseDirect() {
@@ -88,6 +92,7 @@
   traffic_annotation_.reset();
   did_bypass_proxy_ = false;
   did_use_pac_script_ = false;
+  did_use_auto_detected_pac_script_ = false;
 }
 
 }  // namespace net
diff --git a/net/proxy_resolution/proxy_info.h b/net/proxy_resolution/proxy_info.h
index e3ea8da6..daa5862 100644
--- a/net/proxy_resolution/proxy_info.h
+++ b/net/proxy_resolution/proxy_info.h
@@ -127,6 +127,12 @@
     return did_use_pac_script_;
   }
 
+  // Returns true if the proxy list was obtained from a PAC script that
+  // was auto-detected.
+  bool did_use_auto_detected_pac_script() const {
+    return did_use_auto_detected_pac_script_;
+  }
+
   // Returns the first valid proxy server. is_empty() must be false to be able
   // to call this function.
   const ProxyServer& proxy_server() const { return proxy_list_.Get(); }
@@ -203,6 +209,7 @@
 
   // Whether we used a PAC script for resolving the proxy.
   bool did_use_pac_script_;
+  bool did_use_auto_detected_pac_script_;
 
   // How long it took to resolve the proxy.  Times are both null if proxy was
   // determined synchronously without running a PAC.
diff --git a/net/proxy_resolution/proxy_resolution_service.cc b/net/proxy_resolution/proxy_resolution_service.cc
index 5aff9e25..a657d1ff 100644
--- a/net/proxy_resolution/proxy_resolution_service.cc
+++ b/net/proxy_resolution/proxy_resolution_service.cc
@@ -432,6 +432,7 @@
   InitProxyResolver()
       : proxy_resolver_factory_(nullptr),
         proxy_resolver_(NULL),
+        resolver_using_auto_detected_script_(nullptr),
         next_state_(STATE_NONE),
         quick_check_enabled_(true) {}
 
@@ -442,8 +443,12 @@
 
   // Begins initializing the proxy resolver; calls |callback| when done. A
   // ProxyResolver instance will be created using |proxy_resolver_factory| and
-  // returned via |proxy_resolver| if the final result is OK.
+  // assigned to |*proxy_resolver| if the final result is OK.
+  // |*resolver_using_auto_detected_script| will be set to true if
+  // |proxy_resolver| was initialized using script data that originates from
+  // proxy auto-detection.
   int Start(std::unique_ptr<ProxyResolver>* proxy_resolver,
+            bool* resolver_using_auto_detected_script,
             ProxyResolverFactory* proxy_resolver_factory,
             PacFileFetcher* pac_file_fetcher,
             DhcpPacFileFetcher* dhcp_pac_file_fetcher,
@@ -453,6 +458,7 @@
             CompletionOnceCallback callback) {
     DCHECK_EQ(STATE_NONE, next_state_);
     proxy_resolver_ = proxy_resolver;
+    resolver_using_auto_detected_script_ = resolver_using_auto_detected_script;
     proxy_resolver_factory_ = proxy_resolver_factory;
 
     decider_.reset(
@@ -469,16 +475,21 @@
   // Similar to Start(), however it skips the PacFileDecider stage. Instead
   // |effective_config|, |decider_result| and |script_data| will be used as the
   // inputs for initializing the ProxyResolver. A ProxyResolver instance will
-  // be created using |proxy_resolver_factory| and returned via
-  // |proxy_resolver| if the final result is OK.
+  // be created using |proxy_resolver_factory| and assigned to
+  // |*proxy_resolver| if the final result is OK.
+  // |*resolver_using_auto_detected_script| will be set to true if
+  // |proxy_resolver| was initialized using script data that originates from
+  // proxy auto-detection.
   int StartSkipDecider(std::unique_ptr<ProxyResolver>* proxy_resolver,
+                       bool* resolver_using_auto_detected_script,
                        ProxyResolverFactory* proxy_resolver_factory,
                        const ProxyConfigWithAnnotation& effective_config,
                        int decider_result,
-                       PacFileData* script_data,
+                       const PacFileDataWithSource& script_data,
                        CompletionOnceCallback callback) {
     DCHECK_EQ(STATE_NONE, next_state_);
     proxy_resolver_ = proxy_resolver;
+    resolver_using_auto_detected_script_ = resolver_using_auto_detected_script;
     proxy_resolver_factory_ = proxy_resolver_factory;
 
     effective_config_ = effective_config;
@@ -501,7 +512,7 @@
 
   // Returns the PAC script data that was selected by PacFileDecider.
   // Should only be called upon completion of the initialization.
-  const scoped_refptr<PacFileData>& script_data() {
+  const PacFileDataWithSource& script_data() {
     DCHECK_EQ(STATE_NONE, next_state_);
     return script_data_;
   }
@@ -583,18 +594,21 @@
   }
 
   int DoCreateResolver() {
-    DCHECK(script_data_.get());
+    DCHECK(script_data_.data);
     // TODO(eroman): Should log this latency to the NetLog.
     next_state_ = STATE_CREATE_RESOLVER_COMPLETE;
     return proxy_resolver_factory_->CreateProxyResolver(
-        script_data_, proxy_resolver_,
+        script_data_.data, proxy_resolver_,
         base::Bind(&InitProxyResolver::OnIOCompletion, base::Unretained(this)),
         &create_resolver_request_);
   }
 
   int DoCreateResolverComplete(int result) {
-    if (result != OK)
+    if (result == OK) {
+      *resolver_using_auto_detected_script_ = script_data_.from_auto_detect;
+    } else {
       proxy_resolver_->reset();
+    }
     return result;
   }
 
@@ -607,12 +621,13 @@
 
   ProxyConfigWithAnnotation config_;
   ProxyConfigWithAnnotation effective_config_;
-  scoped_refptr<PacFileData> script_data_;
+  PacFileDataWithSource script_data_;
   TimeDelta wait_delay_;
   std::unique_ptr<PacFileDecider> decider_;
   ProxyResolverFactory* proxy_resolver_factory_;
   std::unique_ptr<ProxyResolverFactory::Request> create_resolver_request_;
   std::unique_ptr<ProxyResolver>* proxy_resolver_;
+  bool* resolver_using_auto_detected_script_;
   CompletionOnceCallback callback_;
   State next_state_;
   bool quick_check_enabled_;
@@ -629,7 +644,7 @@
 class ProxyResolutionService::PacFileDeciderPoller {
  public:
   typedef base::Callback<
-      void(int, PacFileData*, const ProxyConfigWithAnnotation&)>
+      void(int, const PacFileDataWithSource&, const ProxyConfigWithAnnotation&)>
       ChangeCallback;
 
   // Builds a poller helper, and starts polling for updates. Whenever a change
@@ -658,7 +673,7 @@
                        PacFileFetcher* pac_file_fetcher,
                        DhcpPacFileFetcher* dhcp_pac_file_fetcher,
                        int init_net_error,
-                       const scoped_refptr<PacFileData>& init_script_data,
+                       const PacFileDataWithSource& init_script_data,
                        NetLog* net_log)
       : change_callback_(callback),
         config_(config),
@@ -769,7 +784,7 @@
   }
 
   bool HasScriptDataChanged(int result,
-                            const scoped_refptr<PacFileData>& script_data) {
+                            const PacFileDataWithSource& script_data) {
     if (result != last_error_) {
       // Something changed -- it was failing before and now it succeeded, or
       // conversely it succeeded before and now it failed. Or it failed in
@@ -786,16 +801,17 @@
     // Otherwise if it succeeded both this time and last time, we need to look
     // closer and see if we ended up downloading different content for the PAC
     // script.
-    return !script_data->Equals(last_script_data_.get());
+    return !script_data.data->Equals(last_script_data_.data.get()) ||
+           (script_data.from_auto_detect != last_script_data_.from_auto_detect);
   }
 
   void NotifyProxyResolutionServiceOfChange(
       int result,
-      const scoped_refptr<PacFileData>& script_data,
+      const PacFileDataWithSource& script_data,
       const ProxyConfigWithAnnotation& effective_config) {
     // Note that |this| may be deleted after calling into the
     // ProxyResolutionService.
-    change_callback_.Run(result, script_data.get(), effective_config);
+    change_callback_.Run(result, script_data, effective_config);
   }
 
   ChangeCallback change_callback_;
@@ -805,7 +821,7 @@
   DhcpPacFileFetcher* dhcp_pac_file_fetcher_;
 
   int last_error_;
-  scoped_refptr<PacFileData> last_script_data_;
+  PacFileDataWithSource last_script_data_;
 
   std::unique_ptr<PacFileDecider> decider_;
   TimeDelta next_poll_delay_;
@@ -880,6 +896,7 @@
   // Outstanding requests are cancelled during ~ProxyResolutionService, so this
   // is guaranteed to be valid throughout our lifetime.
   ProxyResolutionService* service_;
+  bool resolver_using_auto_detected_script_;
   CompletionOnceCallback user_callback_;
   ProxyInfo* results_;
   GURL url_;
@@ -938,6 +955,8 @@
   if (service_->ApplyPacBypassRules(url_, results_))
     return OK;
 
+  resolver_using_auto_detected_script_ =
+      service_->resolver_using_auto_detected_script_;
   return resolver()->GetProxyForURL(
       url_, results_,
       base::Bind(&ProxyResolutionService::RequestImpl::QueryComplete,
@@ -975,6 +994,8 @@
   // Make a note in the results which configuration was in use at the
   // time of the resolve.
   results_->did_use_pac_script_ = true;
+  results_->did_use_auto_detected_pac_script_ =
+      resolver_using_auto_detected_script_;
   results_->proxy_resolve_start_time_ = creation_time_;
   results_->proxy_resolve_end_time_ = TimeTicks::Now();
 
@@ -1106,6 +1127,22 @@
   // ProxyResolver dependency we give it will never be used.
   std::unique_ptr<ProxyConfigService> proxy_config_service(
       new ProxyConfigServiceFixed(ProxyConfigWithAnnotation(
+          ProxyConfig::CreateFromCustomPacURL(
+              GURL("https://my-pac-script.invalid/wpad.dat")),
+          traffic_annotation)));
+
+  return std::make_unique<ProxyResolutionService>(
+      std::move(proxy_config_service),
+      std::make_unique<ProxyResolverFactoryForPacResult>(pac_string), nullptr);
+}
+
+// static
+std::unique_ptr<ProxyResolutionService>
+ProxyResolutionService::CreateFixedFromAutoDetectedPacResult(
+    const std::string& pac_string,
+    const NetworkTrafficAnnotationTag& traffic_annotation) {
+  std::unique_ptr<ProxyConfigService> proxy_config_service(
+      new ProxyConfigServiceFixed(ProxyConfigWithAnnotation(
           ProxyConfig::CreateAutoDetect(), traffic_annotation)));
 
   return std::make_unique<ProxyResolutionService>(
@@ -1656,7 +1693,8 @@
   init_proxy_resolver_.reset(new InitProxyResolver());
   init_proxy_resolver_->set_quick_check_enabled(quick_check_enabled_);
   int rv = init_proxy_resolver_->Start(
-      &resolver_, resolver_factory_.get(), pac_file_fetcher_.get(),
+      &resolver_, &resolver_using_auto_detected_script_,
+      resolver_factory_.get(), pac_file_fetcher_.get(),
       dhcp_pac_file_fetcher_.get(), net_log_, fetched_config_.value(),
       wait_delay,
       base::Bind(&ProxyResolutionService::OnInitProxyResolverComplete,
@@ -1668,7 +1706,7 @@
 
 void ProxyResolutionService::InitializeUsingDecidedConfig(
     int decider_result,
-    PacFileData* script_data,
+    const PacFileDataWithSource& script_data,
     const ProxyConfigWithAnnotation& effective_config) {
   DCHECK(fetched_config_);
   DCHECK(fetched_config_->value().HasAutomaticSettings());
@@ -1679,8 +1717,8 @@
 
   init_proxy_resolver_.reset(new InitProxyResolver());
   int rv = init_proxy_resolver_->StartSkipDecider(
-      &resolver_, resolver_factory_.get(), effective_config, decider_result,
-      script_data,
+      &resolver_, &resolver_using_auto_detected_script_,
+      resolver_factory_.get(), effective_config, decider_result, script_data,
       base::Bind(&ProxyResolutionService::OnInitProxyResolverComplete,
                  base::Unretained(this)));
 
diff --git a/net/proxy_resolution/proxy_resolution_service.h b/net/proxy_resolution/proxy_resolution_service.h
index 6d7f7ed..3ead1551 100644
--- a/net/proxy_resolution/proxy_resolution_service.h
+++ b/net/proxy_resolution/proxy_resolution_service.h
@@ -41,10 +41,10 @@
 
 class DhcpPacFileFetcher;
 class NetLog;
+class PacFileFetcher;
 class ProxyDelegate;
 class ProxyResolverFactory;
-class PacFileData;
-class PacFileFetcher;
+struct PacFileDataWithSource;
 
 // This class can be used to resolve the proxy server to use when loading a
 // HTTP(S) URL.  It uses the given ProxyResolver to handle the actual proxy
@@ -270,6 +270,13 @@
       const std::string& pac_string,
       const NetworkTrafficAnnotationTag& traffic_annotation);
 
+  // Same as CreateFixedFromPacResult(), except the resulting ProxyInfo from
+  // resolutions will be tagged as having been auto-detected.
+  static std::unique_ptr<ProxyResolutionService>
+  CreateFixedFromAutoDetectedPacResult(
+      const std::string& pac_string,
+      const NetworkTrafficAnnotationTag& traffic_annotation);
+
   // Creates a config service appropriate for this platform that fetches the
   // system proxy settings. |main_task_runner| is the thread where the consumer
   // of the ProxyConfigService will live.
@@ -374,7 +381,7 @@
   // Start the initialization skipping past the "decision" phase.
   void InitializeUsingDecidedConfig(
       int decider_result,
-      PacFileData* script_data,
+      const PacFileDataWithSource& script_data,
       const ProxyConfigWithAnnotation& effective_config);
 
   // NetworkChangeNotifier::IPAddressObserver
@@ -398,7 +405,12 @@
 
   std::unique_ptr<ProxyConfigService> config_service_;
   std::unique_ptr<ProxyResolverFactory> resolver_factory_;
+
+  // If non-null, the initialized ProxyResolver to use for requests, and a
+  // boolean indicating whether it was initialized using an auto-detected
+  // script.
   std::unique_ptr<ProxyResolver> resolver_;
+  bool resolver_using_auto_detected_script_;
 
   // We store the proxy configuration that was last fetched from the
   // ProxyConfigService, as well as the resulting "effective" configuration.
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index 1be12cc1..bb0e8b4 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_split.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
@@ -680,7 +681,7 @@
   if (rv != OK)
     return rv;
 
-  response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
+  response_info_->remote_endpoint = address;
   response_info_->connection_info =
       ConnectionInfoFromQuicVersion(quic_session()->GetQuicVersion());
   response_info_->vary_data.Init(*request_info_,
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 334b38a..38e8c58 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "net/base/chunked_upload_data_stream.h"
 #include "net/base/completion_once_callback.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/mock_network_change_notifier.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/test_proxy_delegate.h"
@@ -730,7 +731,7 @@
   void CheckResponsePort(HttpNetworkTransaction* trans, uint16_t port) {
     const HttpResponseInfo* response = trans->GetResponseInfo();
     ASSERT_TRUE(response != nullptr);
-    EXPECT_EQ(port, response->socket_address.port());
+    EXPECT_EQ(port, response->remote_endpoint.port());
   }
 
   void CheckWasHttpResponse(HttpNetworkTransaction* trans) {
@@ -2217,13 +2218,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -2231,7 +2225,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
@@ -2309,13 +2303,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -2333,7 +2320,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -2440,13 +2427,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -2464,7 +2444,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -2584,13 +2564,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -2608,7 +2581,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -2725,13 +2698,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -2745,7 +2711,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -2851,13 +2817,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -2871,7 +2830,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -2982,13 +2941,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -3003,7 +2955,7 @@
   auto trans = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         session_.get());
   TestCompletionCallback callback;
-  rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3061,13 +3013,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
 
@@ -3075,7 +3020,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3192,13 +3137,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -3212,7 +3150,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3334,13 +3272,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -3354,7 +3285,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3488,13 +3419,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -3508,7 +3432,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3641,13 +3565,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -3661,7 +3578,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3783,13 +3700,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // Use a TestTaskRunner to avoid waiting in real time for timeouts.
@@ -3804,7 +3714,7 @@
   auto trans = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         session_.get());
   TestCompletionCallback callback;
-  rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3879,13 +3789,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
 
@@ -3893,7 +3796,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -3978,13 +3881,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
 
@@ -3992,7 +3888,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Pump the message loop to get the request started.
@@ -5018,13 +4914,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   AddHangingNonAlternateProtocolSocketData();
   CreateSession();
@@ -5056,13 +4945,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   request_.url = GURL("http://mail.example.org/");
   CreateSession();
@@ -5105,13 +4987,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -5119,7 +4994,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
@@ -5194,11 +5069,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                         CompletionOnceCallback(), &request, net_log_.bound());
 
   AddHangingNonAlternateProtocolSocketData();
   CreateSession();
@@ -5291,11 +5161,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                         CompletionOnceCallback(), &request, net_log_.bound());
 
   AddHangingNonAlternateProtocolSocketData();
   CreateSession();
@@ -5361,13 +5226,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -5375,7 +5233,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
@@ -5428,13 +5286,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -5442,7 +5293,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
@@ -5490,13 +5341,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -5504,7 +5348,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
@@ -5555,13 +5399,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   session_->quic_stream_factory()->set_require_confirmation(true);
@@ -5569,7 +5406,7 @@
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
   TestCompletionCallback callback;
-  rv = trans.Start(&request_, callback.callback(), net_log_.bound());
+  int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
@@ -5930,13 +5767,6 @@
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                            "");
-  HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   // TODO(rch): Check if we need a 0RTT version of ConnectionCloseDuringConnect
@@ -6011,13 +5841,6 @@
   // synchronously.
   host_resolver_.set_synchronous_mode(true);
   host_resolver_.rules()->AddIPLiteralRule("myproxy.org", "192.168.0.1", "");
-  HostResolver::RequestInfo info(HostPortPair("myproxy.org", 443));
-  AddressList address;
-  std::unique_ptr<HostResolver::Request> request;
-  int rv = host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
-                                  CompletionOnceCallback(), &request,
-                                  net_log_.bound());
-  EXPECT_THAT(rv, IsOk());
 
   CreateSession();
   crypto_client_stream_factory_.set_handshake_mode(
@@ -7122,7 +6945,7 @@
     EXPECT_TRUE(response->was_alpn_negotiated);
     EXPECT_EQ(QuicHttpStream::ConnectionInfoFromQuicVersion(version_),
               response->connection_info);
-    EXPECT_EQ(443, response->socket_address.port());
+    EXPECT_EQ(443, response->remote_endpoint.port());
   }
 
   quic::QuicStreamId GetNthClientInitiatedBidirectionalStreamId(int n) {
diff --git a/net/quic/quic_proxy_client_socket.cc b/net/quic/quic_proxy_client_socket.cc
index 48a9b7b..c21ee5c 100644
--- a/net/quic/quic_proxy_client_socket.cc
+++ b/net/quic/quic_proxy_client_socket.cc
@@ -405,7 +405,7 @@
       if (!SanitizeProxyRedirect(&response_))
         return ERR_TUNNEL_CONNECTION_FAILED;
       next_state_ = STATE_DISCONNECTED;
-      return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
+      return ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT;
 
     case 407:  // Proxy Authentication Required
       next_state_ = STATE_CONNECT_COMPLETE;
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index 7034da4..d075d0b 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -674,7 +674,7 @@
 
   Initialize();
 
-  AssertConnectFails(ERR_HTTPS_PROXY_TUNNEL_RESPONSE);
+  AssertConnectFails(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT);
 
   const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
   ASSERT_TRUE(response != nullptr);
diff --git a/net/socket/sequenced_socket_data_unittest.cc b/net/socket/sequenced_socket_data_unittest.cc
index 895d6c4..1d08a05 100644
--- a/net/socket/sequenced_socket_data_unittest.cc
+++ b/net/socket/sequenced_socket_data_unittest.cc
@@ -14,11 +14,7 @@
 #include "net/base/io_buffer.h"
 #include "net/base/test_completion_callback.h"
 #include "net/log/net_log_with_source.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
-#include "net/socket/transport_client_socket_pool.h"
-#include "net/socket/transport_connect_job.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_with_scoped_task_environment.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -227,34 +223,16 @@
   TestCompletionCallback read_callback_;
   scoped_refptr<IOBuffer> read_buf_;
   TestCompletionCallback write_callback_;
-  StreamSocket* sock_;
 
- private:
-  MockConnect connect_data_;
   std::unique_ptr<SequencedSocketData> data_;
 
-  const HostPortPair endpoint_;
-  scoped_refptr<TransportClientSocketPool::SocketParams> tcp_params_;
   MockClientSocketFactory socket_factory_;
-  MockTransportClientSocketPool socket_pool_;
-  ClientSocketHandle connection_;
   bool expect_eof_;
 
-  DISALLOW_COPY_AND_ASSIGN(SequencedSocketDataTest);
+  std::unique_ptr<StreamSocket> sock_;
 };
 
-SequencedSocketDataTest::SequencedSocketDataTest()
-    : sock_(nullptr),
-      connect_data_(SYNCHRONOUS, OK),
-      endpoint_("www.google.com", 443),
-      tcp_params_(TransportClientSocketPool::SocketParams::
-                      CreateFromTransportSocketParams(
-                          base::MakeRefCounted<TransportSocketParams>(
-                              endpoint_,
-                              false,
-                              OnHostResolutionCallback()))),
-      socket_pool_(10, 10, &socket_factory_),
-      expect_eof_(true) {}
+SequencedSocketDataTest::SequencedSocketDataTest() : expect_eof_(true) {}
 
 SequencedSocketDataTest::~SequencedSocketDataTest() {
   // Make sure no unexpected pending tasks will cause a failure.
@@ -267,17 +245,15 @@
 
 void SequencedSocketDataTest::Initialize(base::span<const MockRead> reads,
                                          base::span<const MockWrite> writes) {
-  data_.reset(new SequencedSocketData(reads, writes));
-  data_->set_connect_data(connect_data_);
+  data_ = std::make_unique<SequencedSocketData>(MockConnect(SYNCHRONOUS, OK),
+                                                reads, writes);
   socket_factory_.AddSocketDataProvider(data_.get());
-
-  EXPECT_EQ(OK, connection_.Init(
-                    endpoint_.ToString(), tcp_params_, LOWEST, SocketTag(),
-                    ClientSocketPool::RespectLimits::ENABLED,
-                    CompletionOnceCallback(),
-                    reinterpret_cast<TransportClientSocketPool*>(&socket_pool_),
-                    NetLogWithSource()));
-  sock_ = connection_.socket();
+  sock_ = socket_factory_.CreateTransportClientSocket(
+      AddressList(IPEndPoint(IPAddress::IPv4Localhost(), 443)),
+      nullptr /* socket_performance_watcher */, nullptr /* net_log */,
+      NetLogSource());
+  TestCompletionCallback callback;
+  EXPECT_EQ(OK, sock_->Connect(callback.callback()));
 }
 
 void SequencedSocketDataTest::AssertSyncReadEquals(const char* data, int len) {
@@ -522,17 +498,17 @@
 
   read_buf_ = base::MakeRefCounted<IOBuffer>(kLen4);
 
-  ReentrantHelper helper3(sock_);
+  ReentrantHelper helper3(sock_.get());
   helper3.SetExpectedRead(kMsg3, kLen3);
   helper3.SetInvokeRead(read_buf_, kLen4, ERR_IO_PENDING,
                         read_callback_.callback());
 
-  ReentrantHelper helper2(sock_);
+  ReentrantHelper helper2(sock_.get());
   helper2.SetExpectedRead(kMsg2, kLen2);
   helper2.SetInvokeRead(helper3.read_buf(), kLen3, ERR_IO_PENDING,
                         helper3.callback());
 
-  ReentrantHelper helper(sock_);
+  ReentrantHelper helper(sock_.get());
   helper.SetExpectedRead(kMsg1, kLen1);
   helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING,
                        helper2.callback());
@@ -845,16 +821,16 @@
 
   Initialize(base::span<MockRead>(), writes);
 
-  ReentrantHelper helper3(sock_);
+  ReentrantHelper helper3(sock_.get());
   helper3.SetExpectedWrite(kLen3);
   helper3.SetInvokeWrite(kMsg4, kLen4, ERR_IO_PENDING,
                          write_callback_.callback());
 
-  ReentrantHelper helper2(sock_);
+  ReentrantHelper helper2(sock_.get());
   helper2.SetExpectedWrite(kLen2);
   helper2.SetInvokeWrite(kMsg3, kLen3, ERR_IO_PENDING, helper3.callback());
 
-  ReentrantHelper helper(sock_);
+  ReentrantHelper helper(sock_.get());
   helper.SetExpectedWrite(kLen1);
   helper.SetInvokeWrite(kMsg2, kLen2, ERR_IO_PENDING, helper2.callback());
 
@@ -1050,16 +1026,16 @@
 
   read_buf_ = base::MakeRefCounted<IOBuffer>(kLen4);
 
-  ReentrantHelper helper3(sock_);
+  ReentrantHelper helper3(sock_.get());
   helper3.SetExpectedWrite(kLen3);
   helper3.SetInvokeRead(read_buf_, kLen4, ERR_IO_PENDING,
                         read_callback_.callback());
 
-  ReentrantHelper helper2(sock_);
+  ReentrantHelper helper2(sock_.get());
   helper2.SetExpectedRead(kMsg2, kLen2);
   helper2.SetInvokeWrite(kMsg3, kLen3, ERR_IO_PENDING, helper3.callback());
 
-  ReentrantHelper helper(sock_);
+  ReentrantHelper helper(sock_.get());
   helper.SetExpectedWrite(kLen1);
   helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING,
                        helper2.callback());
@@ -1085,15 +1061,15 @@
 
   read_buf_ = base::MakeRefCounted<IOBuffer>(kLen4);
 
-  ReentrantHelper helper3(sock_);
+  ReentrantHelper helper3(sock_.get());
   helper3.SetExpectedWrite(kLen3);
   helper3.SetInvokeRead(read_buf_, kLen4, kLen4, failing_callback());
 
-  ReentrantHelper helper2(sock_);
+  ReentrantHelper helper2(sock_.get());
   helper2.SetExpectedRead(kMsg2, kLen2);
   helper2.SetInvokeWrite(kMsg3, kLen3, ERR_IO_PENDING, helper3.callback());
 
-  ReentrantHelper helper(sock_);
+  ReentrantHelper helper(sock_.get());
   helper.SetExpectedWrite(kLen1);
   helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING,
                        helper2.callback());
@@ -1122,16 +1098,16 @@
 
   read_buf_ = base::MakeRefCounted<IOBuffer>(kLen4);
 
-  ReentrantHelper helper3(sock_);
+  ReentrantHelper helper3(sock_.get());
   helper3.SetExpectedRead(kMsg3, kLen3);
   helper3.SetInvokeWrite(kMsg4, kLen4, kLen4, failing_callback());
 
-  ReentrantHelper helper2(sock_);
+  ReentrantHelper helper2(sock_.get());
   helper2.SetExpectedWrite(kLen2);
   helper2.SetInvokeRead(helper3.read_buf(), kLen3, ERR_IO_PENDING,
                         helper3.callback());
 
-  ReentrantHelper helper(sock_);
+  ReentrantHelper helper(sock_.get());
   helper.SetExpectedRead(kMsg1, kLen1);
   helper.SetInvokeWrite(kMsg2, kLen2, ERR_IO_PENDING, helper2.callback());
 
diff --git a/net/socket/socks_client_socket_pool_unittest.cc b/net/socket/socks_client_socket_pool_unittest.cc
deleted file mode 100644
index 76cf2a8b..0000000
--- a/net/socket/socks_client_socket_pool_unittest.cc
+++ /dev/null
@@ -1,209 +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 "net/socket/socks_client_socket_pool.h"
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/containers/span.h"
-#include "base/run_loop.h"
-#include "base/stl_util.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "net/base/load_timing_info.h"
-#include "net/base/load_timing_info_test_util.h"
-#include "net/base/net_errors.h"
-#include "net/base/test_completion_callback.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/log/net_log_source.h"
-#include "net/log/net_log_with_source.h"
-#include "net/socket/client_socket_factory.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/socket_tag.h"
-#include "net/socket/socket_test_util.h"
-#include "net/socket/socks_connect_job.h"
-#include "net/socket/transport_connect_job.h"
-#include "net/test/gtest_util.h"
-#include "net/test/test_with_scoped_task_environment.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::test::IsError;
-using net::test::IsOk;
-
-namespace net {
-
-namespace {
-
-const int kMaxSockets = 32;
-const int kMaxSocketsPerGroup = 6;
-
-scoped_refptr<TransportSocketParams> CreateProxyHostParams() {
-  return new TransportSocketParams(HostPortPair("proxy", 80), false,
-                                   OnHostResolutionCallback());
-}
-
-scoped_refptr<SOCKSSocketParams> CreateSOCKSv5Params() {
-  return new SOCKSSocketParams(CreateProxyHostParams(), true /* socks_v5 */,
-                               HostPortPair("host", 80),
-                               TRAFFIC_ANNOTATION_FOR_TESTS);
-}
-
-class SOCKSClientSocketPoolTest : public TestWithScopedTaskEnvironment {
- protected:
-  class SOCKS5MockData {
-   public:
-    explicit SOCKS5MockData(IoMode mode) {
-      writes_.reset(new MockWrite[3]);
-      writes_[0] = MockWrite(mode, kSOCKS5GreetRequest,
-                             kSOCKS5GreetRequestLength);
-      writes_[1] = MockWrite(mode, kSOCKS5OkRequest, kSOCKS5OkRequestLength);
-      writes_[2] = MockWrite(mode, 0);
-
-      reads_.reset(new MockRead[3]);
-      reads_[0] = MockRead(mode, kSOCKS5GreetResponse,
-                           kSOCKS5GreetResponseLength);
-      reads_[1] = MockRead(mode, kSOCKS5OkResponse, kSOCKS5OkResponseLength);
-      reads_[2] = MockRead(mode, 0);
-
-      data_.reset(new StaticSocketDataProvider(
-          base::make_span(reads_.get(), 3), base::make_span(writes_.get(), 3)));
-    }
-
-    SocketDataProvider* data_provider() { return data_.get(); }
-
-   private:
-    std::unique_ptr<StaticSocketDataProvider> data_;
-    std::unique_ptr<MockWrite[]> writes_;
-    std::unique_ptr<MockRead[]> reads_;
-  };
-
-  SOCKSClientSocketPoolTest()
-      : transport_socket_pool_(kMaxSockets,
-                               kMaxSocketsPerGroup,
-                               &transport_client_socket_factory_),
-        pool_(kMaxSockets,
-              kMaxSocketsPerGroup,
-              &host_resolver_,
-              &transport_socket_pool_,
-              NULL,
-              NULL) {}
-
-  ~SOCKSClientSocketPoolTest() override = default;
-
-  int StartRequestV5(const std::string& group_name, RequestPriority priority) {
-    return test_base_.StartRequestUsingPool(
-        &pool_, group_name, priority, ClientSocketPool::RespectLimits::ENABLED,
-        CreateSOCKSv5Params());
-  }
-
-  int GetOrderOfRequest(size_t index) const {
-    return test_base_.GetOrderOfRequest(index);
-  }
-
-  std::vector<std::unique_ptr<TestSocketRequest>>* requests() {
-    return test_base_.requests();
-  }
-
-  MockClientSocketFactory transport_client_socket_factory_;
-  MockTransportClientSocketPool transport_socket_pool_;
-
-  MockHostResolver host_resolver_;
-  SOCKSClientSocketPool pool_;
-  ClientSocketPoolTest test_base_;
-};
-
-TEST_F(SOCKSClientSocketPoolTest, Simple) {
-  SOCKS5MockData data(SYNCHRONOUS);
-  data.data_provider()->set_connect_data(MockConnect(SYNCHRONOUS, OK));
-  transport_client_socket_factory_.AddSocketDataProvider(data.data_provider());
-
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", CreateSOCKSv5Params(), LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       CompletionOnceCallback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-}
-
-// Test that SocketTag passed into SOCKSClientSocketPool is applied to returned
-// sockets.
-#if defined(OS_ANDROID)
-TEST_F(SOCKSClientSocketPoolTest, Tag) {
-  MockTaggingClientSocketFactory socket_factory;
-  MockTransportClientSocketPool transport_socket_pool(
-      kMaxSockets, kMaxSocketsPerGroup, &socket_factory);
-  SOCKSClientSocketPool pool(kMaxSockets, kMaxSocketsPerGroup, &host_resolver_,
-                             &transport_socket_pool, NULL, NULL);
-  SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
-  SocketTag tag2(getuid(), 0x87654321);
-  scoped_refptr<TransportSocketParams> tcp_params(new TransportSocketParams(
-      HostPortPair("proxy", 80), false, OnHostResolutionCallback()));
-  scoped_refptr<SOCKSSocketParams> params(new SOCKSSocketParams(
-      tcp_params, true /* socks_v5 */, HostPortPair("host", 80),
-      TRAFFIC_ANNOTATION_FOR_TESTS));
-
-  // Test socket is tagged when created synchronously.
-  SOCKS5MockData data_sync(SYNCHRONOUS);
-  data_sync.data_provider()->set_connect_data(MockConnect(SYNCHRONOUS, OK));
-  socket_factory.AddSocketDataProvider(data_sync.data_provider());
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", params, LOW, tag1,
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       CompletionOnceCallback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_EQ(socket_factory.GetLastProducedTCPSocket()->tag(), tag1);
-  EXPECT_TRUE(
-      socket_factory.GetLastProducedTCPSocket()->tagged_before_connected());
-
-  // Test socket is tagged when reused synchronously.
-  StreamSocket* socket = handle.socket();
-  handle.Reset();
-  rv = handle.Init("a", params, LOW, tag2,
-                   ClientSocketPool::RespectLimits::ENABLED,
-                   CompletionOnceCallback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_TRUE(handle.socket()->IsConnected());
-  EXPECT_EQ(handle.socket(), socket);
-  EXPECT_EQ(socket_factory.GetLastProducedTCPSocket()->tag(), tag2);
-  handle.socket()->Disconnect();
-  handle.Reset();
-
-  // Test socket is tagged when created asynchronously.
-  SOCKS5MockData data_async(ASYNC);
-  socket_factory.AddSocketDataProvider(data_async.data_provider());
-  TestCompletionCallback callback;
-  rv = handle.Init("a", params, LOW, tag1,
-                   ClientSocketPool::RespectLimits::ENABLED,
-                   callback.callback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_EQ(socket_factory.GetLastProducedTCPSocket()->tag(), tag1);
-  EXPECT_TRUE(
-      socket_factory.GetLastProducedTCPSocket()->tagged_before_connected());
-
-  // Test socket is tagged when reused after being created asynchronously.
-  socket = handle.socket();
-  handle.Reset();
-  rv = handle.Init("a", params, LOW, tag2,
-                   ClientSocketPool::RespectLimits::ENABLED,
-                   CompletionOnceCallback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_TRUE(handle.socket()->IsConnected());
-  EXPECT_EQ(handle.socket(), socket);
-  EXPECT_EQ(socket_factory.GetLastProducedTCPSocket()->tag(), tag2);
-}
-#endif
-
-}  // namespace
-
-}  // namespace net
diff --git a/net/socket/ssl_connect_job.cc b/net/socket/ssl_connect_job.cc
index 8498ea2..0da918b 100644
--- a/net/socket/ssl_connect_job.cc
+++ b/net/socket/ssl_connect_job.cc
@@ -311,7 +311,7 @@
   if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
     error_response_info_ = transport_socket_handle_->ssl_error_response_info();
   } else if (result == ERR_PROXY_AUTH_REQUESTED ||
-             result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
+             result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT) {
     StreamSocket* socket = transport_socket_handle_->socket();
     ProxyClientSocket* tunnel_socket = static_cast<ProxyClientSocket*>(socket);
     error_response_info_ = *tunnel_socket->GetConnectResponseInfo();
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 62e070b..a4ef27f 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -17,7 +17,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/upload_data_stream.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
@@ -322,7 +322,7 @@
   int result = stream_->GetPeerAddress(&address);
   if (result != OK)
     return result;
-  response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
+  response_info_->remote_endpoint = address;
 
   if (stream_->type() == SPDY_PUSH_STREAM) {
     // Pushed streams do not send any data, and should always be
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 16cbd38..36bb9bcb 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -21,6 +21,7 @@
 #include "net/base/chunked_upload_data_stream.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/elements_upload_data_stream.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/proxy_delegate.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
@@ -178,8 +179,8 @@
       EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
       EXPECT_TRUE(response->was_fetched_via_spdy);
       EXPECT_TRUE(response->was_alpn_negotiated);
-      EXPECT_EQ("127.0.0.1", response->socket_address.host());
-      EXPECT_EQ(443, response->socket_address.port());
+      EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
+      EXPECT_EQ(443, response->remote_endpoint.port());
       output_.status_line = response->headers->GetStatusLine();
       output_.response_info = *response;  // Make a copy so we can verify.
       output_.rv = ReadTransaction(trans_.get(), &output_.response_data);
@@ -4865,8 +4866,8 @@
   EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
   EXPECT_TRUE(response->was_fetched_via_spdy);
   EXPECT_TRUE(response->was_alpn_negotiated);
-  EXPECT_EQ("127.0.0.1", response->socket_address.host());
-  EXPECT_EQ(443, response->socket_address.port());
+  EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
+  EXPECT_EQ(443, response->remote_endpoint.port());
   std::string response_data;
   rv = ReadTransaction(&trans2, &response_data);
   EXPECT_THAT(rv, IsOk());
@@ -4998,8 +4999,8 @@
             response->connection_info);
   EXPECT_TRUE(response->was_alpn_negotiated);
   EXPECT_TRUE(request_.url.SchemeIs("https"));
-  EXPECT_EQ("127.0.0.1", response->socket_address.host());
-  EXPECT_EQ(443, response->socket_address.port());
+  EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
+  EXPECT_EQ(443, response->remote_endpoint.port());
   std::string response_data;
   ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk());
   EXPECT_EQ("hello", response_data);
@@ -5088,8 +5089,8 @@
             response->connection_info);
   EXPECT_FALSE(response->was_alpn_negotiated);
   EXPECT_TRUE(request_.url.SchemeIs("https"));
-  EXPECT_EQ("127.0.0.1", response->socket_address.host());
-  EXPECT_EQ(70, response->socket_address.port());
+  EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
+  EXPECT_EQ(70, response->remote_endpoint.port());
   std::string response_data;
   ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk());
   EXPECT_EQ("hello", response_data);
@@ -8126,7 +8127,7 @@
             response->connection_info);
   EXPECT_TRUE(response->was_alpn_negotiated);
   EXPECT_FALSE(response->was_fetched_via_spdy);
-  EXPECT_EQ(70, response->socket_address.port());
+  EXPECT_EQ(70, response->remote_endpoint.port());
   ASSERT_TRUE(response->headers);
   EXPECT_EQ("HTTP/1.1 101 Switching Protocols",
             response->headers->GetStatusLine());
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index 2ed9e12..79247c1 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -408,7 +408,7 @@
       // Note that this triggers a spdy::ERROR_CODE_CANCEL.
       spdy_stream_->DetachDelegate();
       next_state_ = STATE_DISCONNECTED;
-      return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
+      return ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT;
 
     case 407:  // Proxy Authentication Required
       next_state_ = STATE_OPEN;
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index 75d7f9ff..ebcaea5 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -560,7 +560,7 @@
 
   Initialize(reads, writes);
 
-  AssertConnectFails(ERR_HTTPS_PROXY_TUNNEL_RESPONSE);
+  AssertConnectFails(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT);
 
   const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
   ASSERT_TRUE(response != NULL);
diff --git a/net/spdy/spdy_session_pool_unittest.cc b/net/spdy/spdy_session_pool_unittest.cc
index e3f28a96..d294ff9 100644
--- a/net/spdy/spdy_session_pool_unittest.cc
+++ b/net/spdy/spdy_session_pool_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/traced_value.h"
 #include "build/build_config.h"
-#include "net/base/completion_once_callback.h"
 #include "net/dns/host_cache.h"
 #include "net/http/http_network_session.h"
 #include "net/log/net_log_with_source.h"
@@ -345,7 +344,6 @@
     std::string name;
     std::string iplist;
     SpdySessionKey key;
-    AddressList addresses;
   } test_hosts[] = {
       {"http://www.example.org", "www.example.org",
        "192.0.2.33,192.168.0.1,192.168.0.5"},
@@ -362,11 +360,8 @@
 
     // This test requires that the HostResolver cache be populated.  Normal
     // code would have done this already, but we do it manually.
-    HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
-    std::unique_ptr<HostResolver::Request> request;
-    int rv = session_deps_.host_resolver->Resolve(
-        info, DEFAULT_PRIORITY, &test_hosts[i].addresses,
-        CompletionOnceCallback(), &request, NetLogWithSource());
+    int rv = session_deps_.host_resolver->LoadIntoCache(
+        HostPortPair(test_hosts[i].name, kTestPort), base::nullopt);
     EXPECT_THAT(rv, IsOk());
 
     // Setup a SpdySessionKey.
@@ -527,7 +522,6 @@
     std::string name;
     std::string iplist;
     SpdySessionKey key;
-    AddressList addresses;
   } test_hosts[] = {
       {"www.webkit.org", "192.0.2.33,192.168.0.1,192.168.0.5"},
       {"js.webkit.com", "192.168.0.4,192.168.0.1,192.0.2.33"},
@@ -540,11 +534,8 @@
 
     // This test requires that the HostResolver cache be populated.  Normal
     // code would have done this already, but we do it manually.
-    HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
-    std::unique_ptr<HostResolver::Request> request;
-    int rv = session_deps_.host_resolver->Resolve(
-        info, DEFAULT_PRIORITY, &test_hosts[i].addresses,
-        CompletionOnceCallback(), &request, NetLogWithSource());
+    int rv = session_deps_.host_resolver->LoadIntoCache(
+        HostPortPair(test_hosts[i].name, kTestPort), base::nullopt);
     EXPECT_THAT(rv, IsOk());
 
     // Setup a SpdySessionKey
@@ -593,7 +584,6 @@
     std::string name;
     std::string iplist;
     SpdySessionKey key;
-    AddressList addresses;
   } test_hosts[] = {
       {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
   };
@@ -604,11 +594,8 @@
     session_deps_.host_resolver->rules()->AddIPLiteralRule(
         test_hosts[i].name, test_hosts[i].iplist, std::string());
 
-    HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
-    std::unique_ptr<HostResolver::Request> request;
-    int rv = session_deps_.host_resolver->Resolve(
-        info, DEFAULT_PRIORITY, &test_hosts[i].addresses,
-        CompletionOnceCallback(), &request, NetLogWithSource());
+    int rv = session_deps_.host_resolver->LoadIntoCache(
+        HostPortPair(test_hosts[i].name, kTestPort), base::nullopt);
     EXPECT_THAT(rv, IsOk());
 
     test_hosts[i].key =
@@ -675,7 +662,6 @@
     std::string name;
     std::string iplist;
     SpdySessionKey key;
-    AddressList addresses;
   } test_hosts[] = {
       {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
   };
@@ -686,11 +672,8 @@
     session_deps_.host_resolver->rules()->AddIPLiteralRule(
         test_hosts[i].name, test_hosts[i].iplist, std::string());
 
-    HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
-    std::unique_ptr<HostResolver::Request> request;
-    int rv = session_deps_.host_resolver->Resolve(
-        info, DEFAULT_PRIORITY, &test_hosts[i].addresses,
-        CompletionOnceCallback(), &request, NetLogWithSource());
+    int rv = session_deps_.host_resolver->LoadIntoCache(
+        HostPortPair(test_hosts[i].name, kTestPort), base::nullopt);
     EXPECT_THAT(rv, IsOk());
 
     test_hosts[i].key =
@@ -1064,7 +1047,6 @@
     std::string name;
     std::string iplist;
     SpdySessionKey key;
-    AddressList addresses;
   } test_hosts[] = {
       {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
   };
@@ -1075,11 +1057,8 @@
     session_deps_.host_resolver->rules()->AddIPLiteralRule(
         test_hosts[i].name, test_hosts[i].iplist, std::string());
 
-    HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
-    std::unique_ptr<HostResolver::Request> request;
-    int rv = session_deps_.host_resolver->Resolve(
-        info, DEFAULT_PRIORITY, &test_hosts[i].addresses,
-        CompletionOnceCallback(), &request, NetLogWithSource());
+    int rv = session_deps_.host_resolver->LoadIntoCache(
+        HostPortPair(test_hosts[i].name, kTestPort), base::nullopt);
     EXPECT_THAT(rv, IsOk());
 
     test_hosts[i].key =
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index e981f03e..c6eca79 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -3611,14 +3611,10 @@
   SpdySessionKey key2(HostPortPair("mail.example.org", 80),
                       ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
                       SpdySessionKey::IsProxySession::kFalse, SocketTag());
-  HostResolver::RequestInfo info(key2.host_port_pair());
-  AddressList addresses;
-  std::unique_ptr<HostResolver::Request> request;
   // Pre-populate the DNS cache, since a cached entry is required in order to
   // create the alias.
-  int rv = session_deps_.host_resolver->Resolve(
-      info, DEFAULT_PRIORITY, &addresses, CompletionOnceCallback(), &request,
-      NetLogWithSource());
+  int rv = session_deps_.host_resolver->LoadIntoCache(key2.host_port_pair(),
+                                                      base::nullopt);
   EXPECT_THAT(rv, IsOk());
 
   // Get a session for |key2|, which should return the session created earlier.
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc
index ffd641d..f04e303 100644
--- a/net/url_request/http_with_dns_over_https_unittest.cc
+++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -163,11 +163,11 @@
   void OnNeedsClientAuth(const SSLConfig& used_ssl_config,
                          SSLCertRequestInfo* cert_info) override {}
 
-  void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info,
-                                  const SSLConfig& used_ssl_config,
-                                  const ProxyInfo& used_proxy_info,
-                                  std::unique_ptr<HttpStream> stream) override {
-  }
+  void OnHttpsProxyTunnelResponseRedirect(
+      const HttpResponseInfo& response_info,
+      const SSLConfig& used_ssl_config,
+      const ProxyInfo& used_proxy_info,
+      std::unique_ptr<HttpStream> stream) override {}
 
   void OnQuicBroken() override {}
 
diff --git a/net/url_request/test_url_fetcher_factory.cc b/net/url_request/test_url_fetcher_factory.cc
index c1d1ec1..39e1f73 100644
--- a/net/url_request/test_url_fetcher_factory.cc
+++ b/net/url_request/test_url_fetcher_factory.cc
@@ -17,7 +17,6 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "net/base/completion_once_callback.h"
-#include "net/base/host_port_pair.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/base/upload_data_stream.h"
@@ -208,9 +207,9 @@
   return fake_response_headers_.get();
 }
 
-HostPortPair TestURLFetcher::GetSocketAddress() const {
+IPEndPoint TestURLFetcher::GetSocketAddress() const {
   NOTIMPLEMENTED();
-  return HostPortPair();
+  return IPEndPoint();
 }
 
 const ProxyServer& TestURLFetcher::ProxyServerUsed() const {
diff --git a/net/url_request/test_url_fetcher_factory.h b/net/url_request/test_url_fetcher_factory.h
index 305e2f6a..695ad95 100644
--- a/net/url_request/test_url_fetcher_factory.h
+++ b/net/url_request/test_url_fetcher_factory.h
@@ -20,6 +20,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "base/threading/thread_checker.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/proxy_server.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
@@ -137,7 +138,7 @@
   void SaveResponseWithWriter(
       std::unique_ptr<URLFetcherResponseWriter> response_writer) override;
   HttpResponseHeaders* GetResponseHeaders() const override;
-  HostPortPair GetSocketAddress() const override;
+  IPEndPoint GetSocketAddress() const override;
   const ProxyServer& ProxyServerUsed() const override;
   bool WasFetchedViaProxy() const override;
   bool WasCached() const override;
diff --git a/net/url_request/url_fetcher.h b/net/url_request/url_fetcher.h
index 8a037f6..5eefb7d 100644
--- a/net/url_request/url_fetcher.h
+++ b/net/url_request/url_fetcher.h
@@ -14,6 +14,7 @@
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
 #include "base/supports_user_data.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request.h"
@@ -32,7 +33,6 @@
 }
 
 namespace net {
-class HostPortPair;
 class HttpResponseHeaders;
 class URLFetcherDelegate;
 class URLFetcherResponseWriter;
@@ -321,7 +321,7 @@
   // Retrieve the remote socket address from the request.  Must only
   // be called after the OnURLFetchComplete callback has run and if
   // the request has not failed.
-  virtual HostPortPair GetSocketAddress() const = 0;
+  virtual IPEndPoint GetSocketAddress() const = 0;
 
   // Returns the proxy server that proxied the request. Must only be called
   // after the OnURLFetchComplete callback has run and the request has not
diff --git a/net/url_request/url_fetcher_core.cc b/net/url_request/url_fetcher_core.cc
index 1759ff54..98b1c32 100644
--- a/net/url_request/url_fetcher_core.cc
+++ b/net/url_request/url_fetcher_core.cc
@@ -313,11 +313,11 @@
   return response_headers_.get();
 }
 
-// TODO(panayiotis): socket_address_ is written in the IO thread,
+// TODO(panayiotis): remote_endpoint_ is written in the IO thread,
 // if this is accessed in the UI thread, this could result in a race.
 // Same for response_headers_ above and was_fetched_via_proxy_ below.
-HostPortPair URLFetcherCore::GetSocketAddress() const {
-  return socket_address_;
+IPEndPoint URLFetcherCore::GetSocketAddress() const {
+  return remote_endpoint_;
 }
 
 const ProxyServer& URLFetcherCore::ProxyServerUsed() const {
@@ -428,7 +428,7 @@
   if (net_error == OK) {
     response_code_ = request_->GetResponseCode();
     response_headers_ = request_->response_headers();
-    socket_address_ = request_->GetSocketAddress();
+    remote_endpoint_ = request_->GetResponseRemoteEndpoint();
     proxy_server_ = request_->proxy_server();
     was_fetched_via_proxy_ = request_->was_fetched_via_proxy();
     was_cached_ = request_->was_cached();
diff --git a/net/url_request/url_fetcher_core.h b/net/url_request/url_fetcher_core.h
index 89d9fa20..89c805d 100644
--- a/net/url_request/url_fetcher_core.h
+++ b/net/url_request/url_fetcher_core.h
@@ -18,7 +18,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/timer/timer.h"
 #include "net/base/chunked_upload_data_stream.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/proxy_server.h"
 #include "net/http/http_request_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -116,7 +116,7 @@
   void SaveResponseWithWriter(
       std::unique_ptr<URLFetcherResponseWriter> response_writer);
   HttpResponseHeaders* GetResponseHeaders() const;
-  HostPortPair GetSocketAddress() const;
+  IPEndPoint GetSocketAddress() const;
   const ProxyServer& ProxyServerUsed() const;
   bool WasFetchedViaProxy() const;
   bool WasCached() const;
@@ -265,7 +265,7 @@
   bool was_cached_;
   int64_t received_response_content_length_;
   int64_t total_received_bytes_;
-  HostPortPair socket_address_;
+  IPEndPoint remote_endpoint_;
 
   bool upload_content_set_;          // SetUploadData has been called
   std::string upload_content_;       // HTTP POST payload
diff --git a/net/url_request/url_fetcher_impl.cc b/net/url_request/url_fetcher_impl.cc
index 5ffd221..fa72292 100644
--- a/net/url_request/url_fetcher_impl.cc
+++ b/net/url_request/url_fetcher_impl.cc
@@ -154,7 +154,7 @@
   return core_->GetResponseHeaders();
 }
 
-HostPortPair URLFetcherImpl::GetSocketAddress() const {
+IPEndPoint URLFetcherImpl::GetSocketAddress() const {
   return core_->GetSocketAddress();
 }
 
diff --git a/net/url_request/url_fetcher_impl.h b/net/url_request/url_fetcher_impl.h
index a420486..20e6706 100644
--- a/net/url_request/url_fetcher_impl.h
+++ b/net/url_request/url_fetcher_impl.h
@@ -20,6 +20,7 @@
 
 #include "base/macros.h"
 #include "base/sequenced_task_runner.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_fetcher.h"
@@ -83,7 +84,7 @@
   void SaveResponseWithWriter(
       std::unique_ptr<URLFetcherResponseWriter> response_writer) override;
   HttpResponseHeaders* GetResponseHeaders() const override;
-  HostPortPair GetSocketAddress() const override;
+  IPEndPoint GetSocketAddress() const override;
   const ProxyServer& ProxyServerUsed() const override;
   bool WasFetchedViaProxy() const override;
   bool WasCached() const override;
diff --git a/net/url_request/url_fetcher_impl_unittest.cc b/net/url_request/url_fetcher_impl_unittest.cc
index 8c7283fa..eb4250b1 100644
--- a/net/url_request/url_fetcher_impl_unittest.cc
+++ b/net/url_request/url_fetcher_impl_unittest.cc
@@ -1166,7 +1166,7 @@
   EXPECT_EQ(test_server_->host_port_pair().port(),
             delegate.fetcher()->GetSocketAddress().port());
   EXPECT_EQ(test_server_->host_port_pair().host(),
-            delegate.fetcher()->GetSocketAddress().host());
+            delegate.fetcher()->GetSocketAddress().ToStringWithoutPort());
 }
 
 TEST_F(URLFetcherTest, StopOnRedirect) {
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index ed6274e..66a54b3 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -20,7 +20,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "net/base/auth.h"
-#include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/net_errors.h"
@@ -387,9 +386,9 @@
   }
 }
 
-HostPortPair URLRequest::GetSocketAddress() const {
+IPEndPoint URLRequest::GetResponseRemoteEndpoint() const {
   DCHECK(job_.get());
-  return job_->GetSocketAddress();
+  return job_->GetResponseRemoteEndpoint();
 }
 
 HttpResponseHeaders* URLRequest::response_headers() const {
@@ -406,11 +405,11 @@
   return job_->PopulateNetErrorDetails(details);
 }
 
-bool URLRequest::GetRemoteEndpoint(IPEndPoint* endpoint) const {
+bool URLRequest::GetTransactionRemoteEndpoint(IPEndPoint* endpoint) const {
   if (!job_)
     return false;
 
-  return job_->GetRemoteEndpoint(endpoint);
+  return job_->GetTransactionRemoteEndpoint(endpoint);
 }
 
 void URLRequest::GetMimeType(string* mime_type) const {
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 9301dc4..33337d9 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -22,6 +22,7 @@
 #include "base/time/time.h"
 #include "net/base/auth.h"
 #include "net/base/completion_callback.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_states.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/net_error_details.h"
@@ -53,7 +54,6 @@
 namespace net {
 
 class CookieOptions;
-class HostPortPair;
 class IOBuffer;
 struct LoadTimingInfo;
 struct RedirectInfo;
@@ -512,7 +512,7 @@
 
   // Returns the host and port that the content was fetched from.  See
   // http_response_info.h for caveats relating to cached content.
-  HostPortPair GetSocketAddress() const;
+  IPEndPoint GetResponseRemoteEndpoint() const;
 
   // Get all response headers, as a HttpResponseHeaders object.  See comments
   // in HttpResponseHeaders class as to the format of the data.
@@ -536,14 +536,15 @@
   // Gets the remote endpoint of the most recent socket that the network stack
   // used to make this request.
   //
-  // Note that GetSocketAddress returns the |socket_address| field from
+  // Note that GetResponseRemoteEndpoint returns the |socket_address| field from
   // HttpResponseInfo, which is only populated once the response headers are
   // received, and can return cached values for cache revalidation requests.
-  // GetRemoteEndpoint will only return addresses from the current request.
+  // GetTransactionRemoteEndpoint will only return addresses from the current
+  // request.
   //
   // Returns true and fills in |endpoint| if the endpoint is available; returns
   // false and leaves |endpoint| unchanged if it is unavailable.
-  bool GetRemoteEndpoint(IPEndPoint* endpoint) const;
+  bool GetTransactionRemoteEndpoint(IPEndPoint* endpoint) const;
 
   // Get the mime type.  This method may only be called once the delegate's
   // OnResponseStarted method has been called.
diff --git a/net/url_request/url_request_ftp_job.cc b/net/url_request/url_request_ftp_job.cc
index d1fb951..ebfc053 100644
--- a/net/url_request/url_request_ftp_job.cc
+++ b/net/url_request/url_request_ftp_job.cc
@@ -92,15 +92,15 @@
     *info = *http_response_info_;
 }
 
-HostPortPair URLRequestFtpJob::GetSocketAddress() const {
+IPEndPoint URLRequestFtpJob::GetResponseRemoteEndpoint() const {
   if (proxy_info_.is_direct()) {
     if (!ftp_transaction_)
-      return HostPortPair();
-    return ftp_transaction_->GetResponseInfo()->socket_address;
+      return IPEndPoint();
+    return ftp_transaction_->GetResponseInfo()->remote_endpoint;
   } else {
     if (!http_transaction_)
-      return HostPortPair();
-    return http_transaction_->GetResponseInfo()->socket_address;
+      return IPEndPoint();
+    return http_transaction_->GetResponseInfo()->remote_endpoint;
   }
 }
 
diff --git a/net/url_request/url_request_ftp_job.h b/net/url_request/url_request_ftp_job.h
index 1e701c96..cfc27ff 100644
--- a/net/url_request/url_request_ftp_job.h
+++ b/net/url_request/url_request_ftp_job.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "net/base/auth.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
 #include "net/ftp/ftp_request_info.h"
 #include "net/ftp/ftp_transaction.h"
@@ -42,7 +43,7 @@
   bool IsSafeRedirect(const GURL& location) override;
   bool GetMimeType(std::string* mime_type) const override;
   void GetResponseInfo(HttpResponseInfo* info) override;
-  HostPortPair GetSocketAddress() const override;
+  IPEndPoint GetResponseRemoteEndpoint() const override;
   void SetPriority(RequestPriority priority) override;
   void Start() override;
   void Kill() override;
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 6c00978..ad454053 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -1034,7 +1034,8 @@
     load_timing_info->receive_headers_end = receive_headers_end_;
 }
 
-bool URLRequestHttpJob::GetRemoteEndpoint(IPEndPoint* endpoint) const {
+bool URLRequestHttpJob::GetTransactionRemoteEndpoint(
+    IPEndPoint* endpoint) const {
   if (!transaction_)
     return false;
 
@@ -1380,8 +1381,8 @@
   DoneWithRequest(FINISHED);
 }
 
-HostPortPair URLRequestHttpJob::GetSocketAddress() const {
-  return response_info_ ? response_info_->socket_address : HostPortPair();
+IPEndPoint URLRequestHttpJob::GetResponseRemoteEndpoint() const {
+  return response_info_ ? response_info_->remote_endpoint : IPEndPoint();
 }
 
 void URLRequestHttpJob::RecordTimer() {
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index 0159537a..3f16164 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -17,6 +17,7 @@
 #include "base/time/time.h"
 #include "net/base/auth.h"
 #include "net/base/completion_callback.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_error_details.h"
 #include "net/base/net_export.h"
 #include "net/http/http_request_info.h"
@@ -107,7 +108,7 @@
   bool GetCharset(std::string* charset) override;
   void GetResponseInfo(HttpResponseInfo* info) override;
   void GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override;
-  bool GetRemoteEndpoint(IPEndPoint* endpoint) const override;
+  bool GetTransactionRemoteEndpoint(IPEndPoint* endpoint) const override;
   int GetResponseCode() const override;
   void PopulateNetErrorDetails(NetErrorDetails* details) const override;
   bool CopyFragmentOnRedirect(const GURL& location) const override;
@@ -128,7 +129,7 @@
   void DoneReading() override;
   void DoneReadingRedirectResponse() override;
 
-  HostPortPair GetSocketAddress() const override;
+  IPEndPoint GetResponseRemoteEndpoint() const override;
   void NotifyURLRequestDestroyed() override;
 
   void RecordTimer();
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index b24a0bc..85a1889 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -17,7 +17,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "net/base/auth.h"
-#include "net/base/host_port_pair.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_states.h"
@@ -176,7 +175,7 @@
   // Only certain request types return more than just request start times.
 }
 
-bool URLRequestJob::GetRemoteEndpoint(IPEndPoint* endpoint) const {
+bool URLRequestJob::GetTransactionRemoteEndpoint(IPEndPoint* endpoint) const {
   return false;
 }
 
@@ -284,8 +283,8 @@
   return headers->response_code();
 }
 
-HostPortPair URLRequestJob::GetSocketAddress() const {
-  return HostPortPair();
+IPEndPoint URLRequestJob::GetResponseRemoteEndpoint() const {
+  return IPEndPoint();
 }
 
 void URLRequestJob::OnSuspend() {
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index f08e6e8..2b2cd17 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -16,7 +16,7 @@
 #include "base/optional.h"
 #include "base/power_monitor/power_observer.h"
 #include "net/base/completion_once_callback.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_states.h"
 #include "net/base/net_error_details.h"
 #include "net/base/net_export.h"
@@ -138,7 +138,7 @@
   // Gets the remote endpoint that the network stack is currently fetching the
   // URL from. Returns true and fills in |endpoint| if it is available; returns
   // false and leaves |endpoint| unchanged if it is unavailable.
-  virtual bool GetRemoteEndpoint(IPEndPoint* endpoint) const;
+  virtual bool GetTransactionRemoteEndpoint(IPEndPoint* endpoint) const;
 
   // Populates the network error details of the most recent origin that the
   // network stack makes the request to.
@@ -226,7 +226,7 @@
 
   // Returns the socket address for the connection.
   // See url_request.h for details.
-  virtual HostPortPair GetSocketAddress() const;
+  virtual IPEndPoint GetResponseRemoteEndpoint() const;
 
   // base::PowerObserver methods:
   // We invoke URLRequestJob::Kill on suspend (crbug.com/4606).
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 4dda387..5c960a8 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -937,8 +937,8 @@
     EXPECT_TRUE(!r->is_pending());
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_EQ(d.bytes_received(), 0);
-    EXPECT_EQ("", r->GetSocketAddress().host());
-    EXPECT_EQ(0, r->GetSocketAddress().port());
+    EXPECT_TRUE(r->GetResponseRemoteEndpoint().address().empty());
+    EXPECT_EQ(0, r->GetResponseRemoteEndpoint().port());
 
     HttpRequestHeaders headers;
     EXPECT_FALSE(r->GetFullRequestHeaders(&headers));
@@ -980,8 +980,8 @@
     EXPECT_TRUE(!r->is_pending());
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_EQ(d.bytes_received(), 911);
-    EXPECT_EQ("", r->GetSocketAddress().host());
-    EXPECT_EQ(0, r->GetSocketAddress().port());
+    EXPECT_TRUE(r->GetResponseRemoteEndpoint().address().empty());
+    EXPECT_EQ(0, r->GetResponseRemoteEndpoint().port());
 
     HttpRequestHeaders headers;
     EXPECT_FALSE(r->GetFullRequestHeaders(&headers));
@@ -1011,8 +1011,8 @@
     EXPECT_EQ(1, d.response_started_count());
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_EQ(d.bytes_received(), static_cast<int>(sizeof(kTestFileContent)));
-    EXPECT_EQ("", r->GetSocketAddress().host());
-    EXPECT_EQ(0, r->GetSocketAddress().port());
+    EXPECT_TRUE(r->GetResponseRemoteEndpoint().address().empty());
+    EXPECT_EQ(0, r->GetResponseRemoteEndpoint().port());
 
     HttpRequestHeaders headers;
     EXPECT_FALSE(r->GetFullRequestHeaders(&headers));
@@ -5106,9 +5106,9 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_NE(0, d.bytes_received());
     EXPECT_EQ(http_test_server()->host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(http_test_server()->host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
 
     // TODO(eroman): Add back the NetLog tests...
   }
@@ -5174,9 +5174,9 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_NE(0, d.bytes_received());
     EXPECT_EQ(http_test_server()->host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(http_test_server()->host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
   }
 }
 
@@ -5201,9 +5201,9 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_NE(0, d.bytes_received());
     EXPECT_EQ(http_test_server()->host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(http_test_server()->host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
 
     EXPECT_TRUE(d.have_full_request_headers());
     CheckFullRequestHeaders(d.full_request_headers(), test_url);
@@ -5232,9 +5232,9 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_NE(0, d.bytes_received());
     EXPECT_EQ(http_test_server()->host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(http_test_server()->host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
   }
 }
 
@@ -9321,9 +9321,9 @@
     EXPECT_NE(0, d.bytes_received());
     CheckSSLInfo(r->ssl_info());
     EXPECT_EQ(test_server.host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(test_server.host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
   }
 }
 
@@ -11509,9 +11509,9 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_LT(0, d.bytes_received());
     EXPECT_EQ(ftp_test_server_.host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(ftp_test_server_.host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
   }
 }
 
@@ -11533,9 +11533,9 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_EQ(GetTestFileContents(), d.data_received());
     EXPECT_EQ(ftp_test_server_.host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(ftp_test_server_.host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
   }
 }
 
@@ -11586,9 +11586,9 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_EQ(GetTestFileContents(), d.data_received());
     EXPECT_EQ(ftp_test_server_.host_port_pair().host(),
-              r->GetSocketAddress().host());
+              r->GetResponseRemoteEndpoint().ToStringWithoutPort());
     EXPECT_EQ(ftp_test_server_.host_port_pair().port(),
-              r->GetSocketAddress().port());
+              r->GetResponseRemoteEndpoint().port());
 
     LoadTimingInfo load_timing_info;
     r->GetLoadTimingInfo(&load_timing_info);
diff --git a/net/websockets/websocket_basic_handshake_stream.cc b/net/websockets/websocket_basic_handshake_stream.cc
index e91f713..9ec81a6 100644
--- a/net/websockets/websocket_basic_handshake_stream.cc
+++ b/net/websockets/websocket_basic_handshake_stream.cc
@@ -23,6 +23,7 @@
 #include "base/time/time.h"
 #include "crypto/random.h"
 #include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_body_drainer.h"
@@ -426,7 +427,7 @@
   DCHECK(http_response_info_);
   WebSocketDispatchOnFinishOpeningHandshake(
       connect_delegate_, url_, http_response_info_->headers,
-      http_response_info_->socket_address, http_response_info_->response_time);
+      http_response_info_->remote_endpoint, http_response_info_->response_time);
 }
 
 int WebSocketBasicHandshakeStream::ValidateResponse(int rv) {
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
index 5c44c53..f8045bff 100644
--- a/net/websockets/websocket_channel.cc
+++ b/net/websockets/websocket_channel.cc
@@ -25,6 +25,7 @@
 #include "base/time/time.h"
 #include "net/base/auth.h"
 #include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
@@ -206,11 +207,11 @@
 
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
                      scoped_refptr<HttpResponseHeaders> headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& remote_endpoint,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials) override {
     return creator_->OnAuthRequired(std::move(auth_info), std::move(headers),
-                                    host_port_pair, std::move(callback),
+                                    remote_endpoint, std::move(callback),
                                     credentials);
   }
 
@@ -603,11 +604,11 @@
 int WebSocketChannel::OnAuthRequired(
     scoped_refptr<AuthChallengeInfo> auth_info,
     scoped_refptr<HttpResponseHeaders> response_headers,
-    const HostPortPair& host_port_pair,
+    const IPEndPoint& remote_endpoint,
     base::OnceCallback<void(const AuthCredentials*)> callback,
     base::Optional<AuthCredentials>* credentials) {
   return event_interface_->OnAuthRequired(
-      std::move(auth_info), std::move(response_headers), host_port_pair,
+      std::move(auth_info), std::move(response_headers), remote_endpoint,
       std::move(callback), credentials);
 }
 
diff --git a/net/websockets/websocket_channel.h b/net/websockets/websocket_channel.h
index 24a4426..44f475d 100644
--- a/net/websockets/websocket_channel.h
+++ b/net/websockets/websocket_channel.h
@@ -33,6 +33,7 @@
 
 class HttpRequestHeaders;
 class IOBuffer;
+class IPEndPoint;
 class NetLogWithSource;
 class URLRequest;
 class URLRequestContext;
@@ -214,7 +215,7 @@
   // Forwards the request to the event interface.
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
                      scoped_refptr<HttpResponseHeaders> response_headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& remote_endpoint,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials);
 
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc
index b6fa071..7209658 100644
--- a/net/websockets/websocket_channel_test.cc
+++ b/net/websockets/websocket_channel_test.cc
@@ -27,6 +27,7 @@
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/completion_once_callback.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/http/http_request_headers.h"
@@ -203,11 +204,11 @@
   }
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
                      scoped_refptr<HttpResponseHeaders> response_headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& remote_endpoint,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials) override {
     return OnAuthRequiredCalled(std::move(auth_info),
-                                std::move(response_headers), host_port_pair,
+                                std::move(response_headers), remote_endpoint,
                                 credentials);
   }
 
@@ -219,7 +220,7 @@
   MOCK_METHOD4(OnAuthRequiredCalled,
                int(scoped_refptr<AuthChallengeInfo>,
                    scoped_refptr<HttpResponseHeaders>,
-                   const HostPortPair&,
+                   const IPEndPoint&,
                    base::Optional<AuthCredentials>*));
 };
 
@@ -250,7 +251,7 @@
       bool fatal) override {}
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
                      scoped_refptr<HttpResponseHeaders> response_headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& remote_endpoint,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials) override {
     *credentials = base::nullopt;
@@ -1620,7 +1621,7 @@
   auto response_headers =
       base::MakeRefCounted<HttpResponseHeaders>("HTTP/1.1 200 OK");
   auto response_info = std::make_unique<WebSocketHandshakeResponseInfo>(
-      GURL("ws://www.example.com/"), response_headers, HostPortPair(),
+      GURL("ws://www.example.com/"), response_headers, IPEndPoint(),
       base::Time());
   connect_data_.argument_saver.connect_delegate->OnFinishOpeningHandshake(
       std::move(response_info));
@@ -1645,7 +1646,7 @@
   auto response_headers =
       base::MakeRefCounted<HttpResponseHeaders>("HTTP/1.1 200 OK");
   auto response_info = std::make_unique<WebSocketHandshakeResponseInfo>(
-      url, response_headers, HostPortPair(), base::Time());
+      url, response_headers, IPEndPoint(), base::Time());
   connect_delegate->OnStartOpeningHandshake(std::move(request_info));
   connect_delegate->OnFinishOpeningHandshake(std::move(response_info));
 
@@ -2969,7 +2970,7 @@
   base::Optional<AuthCredentials> credentials;
   scoped_refptr<HttpResponseHeaders> response_headers =
       base::MakeRefCounted<HttpResponseHeaders>("HTTP/1.1 200 OK");
-  HostPortPair socket_address("127.0.0.1", 80);
+  IPEndPoint remote_endpoint(net::IPAddress(127, 0, 0, 1), 80);
 
   EXPECT_CALL(
       *event_interface_,
@@ -2978,7 +2979,7 @@
 
   CreateChannelAndConnect();
   connect_data_.argument_saver.connect_delegate->OnAuthRequired(
-      auth_info, response_headers, socket_address, {}, &credentials);
+      auth_info, response_headers, remote_endpoint, {}, &credentials);
 }
 
 // If we receive another frame after Close, it is not valid. It is not
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc
index b1ad596..9622efb 100644
--- a/net/websockets/websocket_end_to_end_test.cc
+++ b/net/websockets/websocket_end_to_end_test.cc
@@ -30,6 +30,7 @@
 #include "build/build_config.h"
 #include "net/base/auth.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/proxy_delegate.h"
 #include "net/base/url_util.h"
 #include "net/http/http_request_headers.h"
@@ -127,7 +128,7 @@
 
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
                      scoped_refptr<HttpResponseHeaders> response_headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& remote_endpoint,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials) override;
 
@@ -210,7 +211,7 @@
 int ConnectTestingEventInterface::OnAuthRequired(
     scoped_refptr<AuthChallengeInfo> auth_info,
     scoped_refptr<HttpResponseHeaders> response_headers,
-    const HostPortPair& host_port_pair,
+    const IPEndPoint& remote_endpoint,
     base::OnceCallback<void(const AuthCredentials*)> callback,
     base::Optional<AuthCredentials>* credentials) {
   *credentials = base::nullopt;
diff --git a/net/websockets/websocket_event_interface.h b/net/websockets/websocket_event_interface.h
index 190d83b..4c3d6e3 100644
--- a/net/websockets/websocket_event_interface.h
+++ b/net/websockets/websocket_event_interface.h
@@ -24,7 +24,7 @@
 
 class AuthChallengeInfo;
 class AuthCredentials;
-class HostPortPair;
+class IPEndPoint;
 class HttpResponseHeaders;
 class IOBuffer;
 class SSLInfo;
@@ -138,7 +138,7 @@
   virtual int OnAuthRequired(
       scoped_refptr<AuthChallengeInfo> auth_info,
       scoped_refptr<HttpResponseHeaders> response_headers,
-      const HostPortPair& host_port_pair,
+      const IPEndPoint& socket_address,
       base::OnceCallback<void(const AuthCredentials*)> callback,
       base::Optional<AuthCredentials>* credentials) = 0;
 
diff --git a/net/websockets/websocket_handshake_response_info.cc b/net/websockets/websocket_handshake_response_info.cc
index d59ab1e4..90ca0e1 100644
--- a/net/websockets/websocket_handshake_response_info.cc
+++ b/net/websockets/websocket_handshake_response_info.cc
@@ -15,11 +15,11 @@
 WebSocketHandshakeResponseInfo::WebSocketHandshakeResponseInfo(
     const GURL& url,
     scoped_refptr<HttpResponseHeaders> headers,
-    const HostPortPair& socket_address,
+    const IPEndPoint& remote_endpoint,
     base::Time response_time)
     : url(url),
       headers(std::move(headers)),
-      socket_address(socket_address),
+      remote_endpoint(remote_endpoint),
       response_time(response_time) {}
 
 WebSocketHandshakeResponseInfo::~WebSocketHandshakeResponseInfo() = default;
diff --git a/net/websockets/websocket_handshake_response_info.h b/net/websockets/websocket_handshake_response_info.h
index 90232da0..42db90b 100644
--- a/net/websockets/websocket_handshake_response_info.h
+++ b/net/websockets/websocket_handshake_response_info.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
 #include "url/gurl.h"
 
@@ -19,7 +19,7 @@
 struct NET_EXPORT WebSocketHandshakeResponseInfo {
   WebSocketHandshakeResponseInfo(const GURL& url,
                                  scoped_refptr<HttpResponseHeaders> headers,
-                                 const HostPortPair& socket_address,
+                                 const IPEndPoint& remote_endpoint,
                                  base::Time response_time);
   ~WebSocketHandshakeResponseInfo();
   // The request URL
@@ -27,7 +27,7 @@
   // HTTP response headers
   scoped_refptr<HttpResponseHeaders> headers;
   // Remote address of the socket.
-  HostPortPair socket_address;
+  IPEndPoint remote_endpoint;
   // The time that this response arrived
   base::Time response_time;
 
diff --git a/net/websockets/websocket_handshake_stream_create_helper_test.cc b/net/websockets/websocket_handshake_stream_create_helper_test.cc
index f8a71c3..a3df7944 100644
--- a/net/websockets/websocket_handshake_stream_create_helper_test.cc
+++ b/net/websockets/websocket_handshake_stream_create_helper_test.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "net/base/completion_once_callback.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/proxy_server.h"
 #include "net/http/http_network_session.h"
@@ -99,7 +100,7 @@
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
 
                      scoped_refptr<HttpResponseHeaders> response_headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& host_port_pair,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials) override {
     *credentials = base::nullopt;
diff --git a/net/websockets/websocket_http2_handshake_stream.cc b/net/websockets/websocket_http2_handshake_stream.cc
index f5e0ab8..343b4cd 100644
--- a/net/websockets/websocket_http2_handshake_stream.cc
+++ b/net/websockets/websocket_http2_handshake_stream.cc
@@ -12,6 +12,7 @@
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
@@ -102,7 +103,7 @@
     OnFailure("Error getting IP address.");
     return result;
   }
-  http_response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
+  http_response_info_->remote_endpoint = address;
 
   auto request = std::make_unique<WebSocketHandshakeRequestInfo>(
       request_info_->url, base::Time::Now());
@@ -378,7 +379,7 @@
   DCHECK(http_response_info_);
   WebSocketDispatchOnFinishOpeningHandshake(
       connect_delegate_, request_info_->url, http_response_info_->headers,
-      http_response_info_->socket_address, http_response_info_->response_time);
+      http_response_info_->remote_endpoint, http_response_info_->response_time);
 }
 
 void WebSocketHttp2HandshakeStream::OnFailure(const std::string& message) {
diff --git a/net/websockets/websocket_stream.cc b/net/websockets/websocket_stream.cc
index e4a3b9d..5e716880 100644
--- a/net/websockets/websocket_stream.cc
+++ b/net/websockets/websocket_stream.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/url_util.h"
 #include "net/http/http_request_headers.h"
@@ -257,7 +258,8 @@
   void OnFinishOpeningHandshake() {
     WebSocketDispatchOnFinishOpeningHandshake(
         connect_delegate(), url_request_->url(),
-        url_request_->response_headers(), url_request_->GetSocketAddress(),
+        url_request_->response_headers(),
+        url_request_->GetResponseRemoteEndpoint(),
         url_request_->response_time());
   }
 
@@ -414,7 +416,7 @@
   // be called called during the opening handshake.
   int rv = owner_->connect_delegate()->OnAuthRequired(
       scoped_refptr<AuthChallengeInfo>(auth_info), request->response_headers(),
-      request->GetSocketAddress(),
+      request->GetResponseRemoteEndpoint(),
       base::BindOnce(&Delegate::OnAuthRequiredComplete, base::Unretained(this),
                      request),
       &credentials);
@@ -511,13 +513,13 @@
     WebSocketStream::ConnectDelegate* connect_delegate,
     const GURL& url,
     const scoped_refptr<HttpResponseHeaders>& headers,
-    const HostPortPair& socket_address,
+    const IPEndPoint& remote_endpoint,
     base::Time response_time) {
   DCHECK(connect_delegate);
   if (headers.get()) {
     connect_delegate->OnFinishOpeningHandshake(
         std::make_unique<WebSocketHandshakeResponseInfo>(
-            url, headers, socket_address, response_time));
+            url, headers, remote_endpoint, response_time));
   }
 }
 
diff --git a/net/websockets/websocket_stream.h b/net/websockets/websocket_stream.h
index 51ebac9b..867b5a9 100644
--- a/net/websockets/websocket_stream.h
+++ b/net/websockets/websocket_stream.h
@@ -34,9 +34,9 @@
 
 class AuthChallengeInfo;
 class AuthCredentials;
-class HostPortPair;
 class HttpRequestHeaders;
 class HttpResponseHeaders;
+class IPEndPoint;
 class NetLogWithSource;
 class URLRequest;
 class URLRequestContext;
@@ -131,7 +131,7 @@
     virtual int OnAuthRequired(
         scoped_refptr<AuthChallengeInfo> auth_info,
         scoped_refptr<HttpResponseHeaders> response_headers,
-        const HostPortPair& host_port_pair,
+        const IPEndPoint& remote_endpoint,
         base::OnceCallback<void(const AuthCredentials*)> callback,
         base::Optional<AuthCredentials>* credentials) = 0;
   };
@@ -275,7 +275,7 @@
     WebSocketStream::ConnectDelegate* connect_delegate,
     const GURL& gurl,
     const scoped_refptr<HttpResponseHeaders>& headers,
-    const HostPortPair& socket_address,
+    const IPEndPoint& remote_endpoint,
     base::Time response_time);
 
 }  // namespace net
diff --git a/net/websockets/websocket_stream_create_test_base.cc b/net/websockets/websocket_stream_create_test_base.cc
index 2a62ffd..9db4381 100644
--- a/net/websockets/websocket_stream_create_test_base.cc
+++ b/net/websockets/websocket_stream_create_test_base.cc
@@ -8,6 +8,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/log/net_log_with_source.h"
@@ -70,7 +71,7 @@
 
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
                      scoped_refptr<HttpResponseHeaders> response_headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& remote_endpoint,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials) override {
     owner_->run_loop_waiting_for_on_auth_required_.Quit();
diff --git a/net/websockets/websocket_test_util.cc b/net/websockets/websocket_test_util.cc
index fafb944..5335404 100644
--- a/net/websockets/websocket_test_util.cc
+++ b/net/websockets/websocket_test_util.cc
@@ -11,6 +11,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "net/base/ip_endpoint.h"
 #include "net/http/http_network_session.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
 #include "net/socket/socket_test_util.h"
@@ -260,7 +261,7 @@
 int DummyConnectDelegate::OnAuthRequired(
     scoped_refptr<AuthChallengeInfo> auth_info,
     scoped_refptr<HttpResponseHeaders> response_headers,
-    const HostPortPair& host_port_pair,
+    const IPEndPoint& host_port_pair,
     base::OnceCallback<void(const AuthCredentials*)> callback,
     base::Optional<AuthCredentials>* credentials) {
   return OK;
diff --git a/net/websockets/websocket_test_util.h b/net/websockets/websocket_test_util.h
index 4ab42ee..7346a657 100644
--- a/net/websockets/websocket_test_util.h
+++ b/net/websockets/websocket_test_util.h
@@ -35,6 +35,7 @@
 class WebSocketBasicHandshakeStream;
 class ProxyResolutionService;
 class SequencedSocketData;
+class IPEndPoint;
 struct SSLSocketDataProvider;
 
 class LinearCongruentialGenerator {
@@ -196,7 +197,7 @@
       bool fatal) override {}
   int OnAuthRequired(scoped_refptr<AuthChallengeInfo> auth_info,
                      scoped_refptr<HttpResponseHeaders> response_headers,
-                     const HostPortPair& host_port_pair,
+                     const IPEndPoint& remote_endpoint,
                      base::OnceCallback<void(const AuthCredentials*)> callback,
                      base::Optional<AuthCredentials>* credentials) override;
 };
diff --git a/remoting/base/fake_oauth_token_getter.cc b/remoting/base/fake_oauth_token_getter.cc
index b1b21c7..4c60e6d 100644
--- a/remoting/base/fake_oauth_token_getter.cc
+++ b/remoting/base/fake_oauth_token_getter.cc
@@ -18,10 +18,10 @@
 
 FakeOAuthTokenGetter::~FakeOAuthTokenGetter() = default;
 
-void FakeOAuthTokenGetter::CallWithToken(const TokenCallback& on_access_token) {
+void FakeOAuthTokenGetter::CallWithToken(TokenCallback on_access_token) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(on_access_token, status_, user_email_, access_token_));
+      FROM_HERE, base::BindOnce(std::move(on_access_token), status_,
+                                user_email_, access_token_));
 }
 
 void FakeOAuthTokenGetter::InvalidateCache() {
diff --git a/remoting/base/fake_oauth_token_getter.h b/remoting/base/fake_oauth_token_getter.h
index 0b70a09..1966f96 100644
--- a/remoting/base/fake_oauth_token_getter.h
+++ b/remoting/base/fake_oauth_token_getter.h
@@ -20,7 +20,7 @@
   ~FakeOAuthTokenGetter() override;
 
   // OAuthTokenGetter interface.
-  void CallWithToken(const TokenCallback& on_access_token) override;
+  void CallWithToken(TokenCallback on_access_token) override;
   void InvalidateCache() override;
 
  private:
diff --git a/remoting/base/oauth_token_getter.h b/remoting/base/oauth_token_getter.h
index cd3eb58..361d01e 100644
--- a/remoting/base/oauth_token_getter.h
+++ b/remoting/base/oauth_token_getter.h
@@ -25,14 +25,13 @@
     AUTH_ERROR,
   };
 
-  // TODO(crbug.com/824488): Change to use base::OnceCallback.
-  typedef base::Callback<void(Status status,
-                              const std::string& user_email,
-                              const std::string& access_token)>
+  typedef base::OnceCallback<void(Status status,
+                                  const std::string& user_email,
+                                  const std::string& access_token)>
       TokenCallback;
 
-  typedef base::Callback<void(const std::string& user_email,
-                              const std::string& refresh_token)>
+  typedef base::RepeatingCallback<void(const std::string& user_email,
+                                       const std::string& refresh_token)>
       CredentialsUpdatedCallback;
 
   // This structure contains information required to perform authorization
@@ -87,7 +86,7 @@
 
   // Call |on_access_token| with an access token, or the failure status.
   virtual void CallWithToken(
-      const OAuthTokenGetter::TokenCallback& on_access_token) = 0;
+      OAuthTokenGetter::TokenCallback on_access_token) = 0;
 
   // Invalidates the cache, so the next CallWithToken() will get a fresh access
   // token.
diff --git a/remoting/base/oauth_token_getter_impl.cc b/remoting/base/oauth_token_getter_impl.cc
index f095ccbe..5bc231b 100644
--- a/remoting/base/oauth_token_getter_impl.cc
+++ b/remoting/base/oauth_token_getter_impl.cc
@@ -148,11 +148,11 @@
     const std::string& user_email,
     const std::string& access_token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::queue<TokenCallback> callbacks(pending_callbacks_);
-  pending_callbacks_ = base::queue<TokenCallback>();
+  base::queue<TokenCallback> callbacks;
+  callbacks.swap(pending_callbacks_);
 
   while (!callbacks.empty()) {
-    callbacks.front().Run(status, user_email, access_token);
+    std::move(callbacks.front()).Run(status, user_email, access_token);
     callbacks.pop();
   }
 }
@@ -189,10 +189,10 @@
                        std::string());
 }
 
-void OAuthTokenGetterImpl::CallWithToken(const TokenCallback& on_access_token) {
+void OAuthTokenGetterImpl::CallWithToken(TokenCallback on_access_token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (intermediate_credentials_) {
-    pending_callbacks_.push(on_access_token);
+    pending_callbacks_.push(std::move(on_access_token));
     if (!response_pending_) {
       GetOauthTokensFromAuthCode();
     }
@@ -203,13 +203,13 @@
         (!authorization_credentials_->is_service_account && !email_verified_);
 
     if (need_new_auth_token) {
-      pending_callbacks_.push(on_access_token);
+      pending_callbacks_.push(std::move(on_access_token));
       if (!response_pending_) {
         RefreshAccessToken();
       }
     } else {
-      on_access_token.Run(SUCCESS, authorization_credentials_->login,
-                          oauth_access_token_);
+      std::move(on_access_token)
+          .Run(SUCCESS, authorization_credentials_->login, oauth_access_token_);
     }
   }
 }
diff --git a/remoting/base/oauth_token_getter_impl.h b/remoting/base/oauth_token_getter_impl.h
index bdb20080..68ef1fec 100644
--- a/remoting/base/oauth_token_getter_impl.h
+++ b/remoting/base/oauth_token_getter_impl.h
@@ -42,8 +42,7 @@
   ~OAuthTokenGetterImpl() override;
 
   // OAuthTokenGetter interface.
-  void CallWithToken(
-      const OAuthTokenGetter::TokenCallback& on_access_token) override;
+  void CallWithToken(OAuthTokenGetter::TokenCallback on_access_token) override;
   void InvalidateCache() override;
 
  private:
diff --git a/remoting/base/telemetry_log_writer.cc b/remoting/base/telemetry_log_writer.cc
index 6b8e0ec..8054f9c 100644
--- a/remoting/base/telemetry_log_writer.cc
+++ b/remoting/base/telemetry_log_writer.cc
@@ -67,7 +67,7 @@
     LOG(ERROR) << "Failed to serialize log to JSON.";
     return;
   }
-  token_getter_->CallWithToken(base::BindRepeating(
+  token_getter_->CallWithToken(base::BindOnce(
       &TelemetryLogWriter::PostJsonToServer, base::Unretained(this), json));
 }
 
diff --git a/remoting/client/jni/jni_oauth_token_getter.cc b/remoting/client/jni/jni_oauth_token_getter.cc
index 3198018..ea6a1e8 100644
--- a/remoting/client/jni/jni_oauth_token_getter.cc
+++ b/remoting/client/jni/jni_oauth_token_getter.cc
@@ -37,9 +37,12 @@
       NOTREACHED();
       return;
   }
-  callback->Run(status,
-                user_email.is_null() ? "" : ConvertJavaStringToUTF8(user_email),
-                token.is_null() ? "" : ConvertJavaStringToUTF8(token));
+
+  std::string utf8_user_email =
+      user_email.is_null() ? "" : ConvertJavaStringToUTF8(user_email);
+  std::string utf8_token =
+      token.is_null() ? "" : ConvertJavaStringToUTF8(token);
+  std::move(*callback).Run(status, utf8_user_email, utf8_token);
   delete callback;
 }
 
@@ -52,12 +55,12 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
-void JniOAuthTokenGetter::CallWithToken(const TokenCallback& on_access_token) {
+void JniOAuthTokenGetter::CallWithToken(TokenCallback on_access_token) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   JNIEnv* env = base::android::AttachCurrentThread();
-  TokenCallback* callback_copy = new TokenCallback(on_access_token);
-  Java_JniOAuthTokenGetter_fetchAuthToken(
-      env, reinterpret_cast<intptr_t>(callback_copy));
+  TokenCallback* callback = new TokenCallback(std::move(on_access_token));
+  Java_JniOAuthTokenGetter_fetchAuthToken(env,
+                                          reinterpret_cast<intptr_t>(callback));
 }
 
 void JniOAuthTokenGetter::InvalidateCache() {
diff --git a/remoting/client/jni/jni_oauth_token_getter.h b/remoting/client/jni/jni_oauth_token_getter.h
index bdb0381..7328032 100644
--- a/remoting/client/jni/jni_oauth_token_getter.h
+++ b/remoting/client/jni/jni_oauth_token_getter.h
@@ -31,7 +31,7 @@
   ~JniOAuthTokenGetter() override;
 
   // OAuthTokenGetter overrides.
-  void CallWithToken(const TokenCallback& on_access_token) override;
+  void CallWithToken(TokenCallback on_access_token) override;
   void InvalidateCache() override;
 
   base::WeakPtr<JniOAuthTokenGetter> GetWeakPtr();
diff --git a/remoting/client/oauth_token_getter_proxy.cc b/remoting/client/oauth_token_getter_proxy.cc
index 361412b..af04ae84 100644
--- a/remoting/client/oauth_token_getter_proxy.cc
+++ b/remoting/client/oauth_token_getter_proxy.cc
@@ -14,19 +14,19 @@
 namespace {
 
 void ResolveCallback(
-    const OAuthTokenGetter::TokenCallback& on_access_token,
+    OAuthTokenGetter::TokenCallback on_access_token,
     scoped_refptr<base::SingleThreadTaskRunner> original_task_runner,
     OAuthTokenGetter::Status status,
     const std::string& user_email,
     const std::string& access_token) {
   if (!original_task_runner->BelongsToCurrentThread()) {
     original_task_runner->PostTask(
-        FROM_HERE,
-        base::BindOnce(on_access_token, status, user_email, access_token));
+        FROM_HERE, base::BindOnce(std::move(on_access_token), status,
+                                  user_email, access_token));
     return;
   }
 
-  on_access_token.Run(status, user_email, access_token);
+  std::move(on_access_token).Run(status, user_email, access_token);
 }
 
 }  // namespace
@@ -39,21 +39,20 @@
 OAuthTokenGetterProxy::~OAuthTokenGetterProxy() {}
 
 void OAuthTokenGetterProxy::CallWithToken(
-    const OAuthTokenGetter::TokenCallback& on_access_token) {
+    OAuthTokenGetter::TokenCallback on_access_token) {
   if (!task_runner_->BelongsToCurrentThread()) {
     auto task_runner_to_reply = base::ThreadTaskRunnerHandle::Get();
 
-    // TODO(crbug.com/824488): Change to base::BindOnce().
-    auto reply_callback = base::BindRepeating(&ResolveCallback, on_access_token,
-                                              task_runner_to_reply);
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(&OAuthTokenGetter::CallWithToken,
-                                          token_getter_, reply_callback));
+    auto reply_callback = base::BindOnce(
+        &ResolveCallback, std::move(on_access_token), task_runner_to_reply);
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&OAuthTokenGetter::CallWithToken,
+                                  token_getter_, std::move(reply_callback)));
     return;
   }
 
   if (token_getter_) {
-    token_getter_->CallWithToken(on_access_token);
+    token_getter_->CallWithToken(std::move(on_access_token));
   }
 }
 
diff --git a/remoting/client/oauth_token_getter_proxy.h b/remoting/client/oauth_token_getter_proxy.h
index 2fe3199a..a246a434 100644
--- a/remoting/client/oauth_token_getter_proxy.h
+++ b/remoting/client/oauth_token_getter_proxy.h
@@ -29,7 +29,7 @@
   ~OAuthTokenGetterProxy() override;
 
   // OAuthTokenGetter overrides.
-  void CallWithToken(const TokenCallback& on_access_token) override;
+  void CallWithToken(TokenCallback on_access_token) override;
   void InvalidateCache() override;
 
  private:
diff --git a/remoting/client/oauth_token_getter_proxy_unittest.cc b/remoting/client/oauth_token_getter_proxy_unittest.cc
index d9f0bdc..58caac0 100644
--- a/remoting/client/oauth_token_getter_proxy_unittest.cc
+++ b/remoting/client/oauth_token_getter_proxy_unittest.cc
@@ -37,7 +37,7 @@
   void ExpectInvalidateCache();
 
   // OAuthTokenGetter overrides.
-  void CallWithToken(const TokenCallback& on_access_token) override;
+  void CallWithToken(TokenCallback on_access_token) override;
   void InvalidateCache() override;
 
   base::WeakPtr<FakeOAuthTokenGetter> GetWeakPtr();
@@ -66,8 +66,7 @@
                                            const std::string& access_token) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!on_access_token_.is_null());
-  on_access_token_.Run(status, user_email, access_token);
-  on_access_token_.Reset();
+  std::move(on_access_token_).Run(status, user_email, access_token);
 }
 
 void FakeOAuthTokenGetter::ExpectInvalidateCache() {
@@ -76,9 +75,9 @@
   invalidate_cache_expected_ = true;
 }
 
-void FakeOAuthTokenGetter::CallWithToken(const TokenCallback& on_access_token) {
+void FakeOAuthTokenGetter::CallWithToken(TokenCallback on_access_token) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  on_access_token_ = on_access_token;
+  on_access_token_ = std::move(on_access_token);
 }
 
 void FakeOAuthTokenGetter::InvalidateCache() {
diff --git a/remoting/host/basic_desktop_environment.cc b/remoting/host/basic_desktop_environment.cc
index 2622454c5..267134a 100644
--- a/remoting/host/basic_desktop_environment.cc
+++ b/remoting/host/basic_desktop_environment.cc
@@ -72,7 +72,7 @@
 
 std::unique_ptr<FileOperations>
 BasicDesktopEnvironment::CreateFileOperations() {
-  return std::make_unique<LocalFileOperations>();
+  return std::make_unique<LocalFileOperations>(ui_task_runner_);
 }
 
 std::string BasicDesktopEnvironment::GetCapabilities() const {
diff --git a/remoting/host/file_transfer/BUILD.gn b/remoting/host/file_transfer/BUILD.gn
index d898537..f542c47 100644
--- a/remoting/host/file_transfer/BUILD.gn
+++ b/remoting/host/file_transfer/BUILD.gn
@@ -8,6 +8,11 @@
   sources = [
     "ensure_user_mac.cc",
     "ensure_user_win.cc",
+    "file_chooser_chromeos.cc",
+    "file_chooser_common_win.h",
+    "file_chooser_mac.mm",
+    "file_chooser_main_win.cc",
+    "file_chooser_win.cc",
     "get_desktop_directory.cc",
     "get_desktop_directory.h",
     "get_desktop_directory_win.cc",
@@ -29,6 +34,11 @@
   if (is_win) {
     sources -= [ "get_desktop_directory.cc" ]
   }
+
+  if (is_desktop_linux) {
+    sources += [ "file_chooser_linux.cc" ]
+    deps += [ "//build/config/linux/gtk" ]
+  }
 }
 
 source_set("common") {
@@ -45,10 +55,6 @@
     "buffered_file_writer.h",
     "ensure_user.h",
     "file_chooser.h",
-    "file_chooser_common_win.h",
-    "file_chooser_mac.mm",
-    "file_chooser_main_win.cc",
-    "file_chooser_win.cc",
     "file_transfer_message_handler.cc",
     "ipc_file_operations.cc",
     "local_file_operations.cc",
@@ -59,11 +65,6 @@
     "//base",
     "//remoting/protocol",
   ]
-
-  if (is_desktop_linux) {
-    sources += [ "file_chooser_linux.cc" ]
-    deps += [ "//build/config/linux/gtk" ]
-  }
 }
 
 source_set("test_support") {
@@ -71,6 +72,8 @@
 
   sources = [
     "ensure_user_no_op.cc",
+    "fake_file_chooser.cc",
+    "fake_file_chooser.h",
     "fake_file_operations.cc",
     "fake_file_operations.h",
     "get_desktop_directory.cc",
diff --git a/remoting/host/file_transfer/fake_file_chooser.cc b/remoting/host/file_transfer/fake_file_chooser.cc
new file mode 100644
index 0000000..f8afd08
--- /dev/null
+++ b/remoting/host/file_transfer/fake_file_chooser.cc
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/file_transfer/fake_file_chooser.h"
+
+#include "base/bind.h"
+#include "base/no_destructor.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "remoting/protocol/file_transfer_helpers.h"
+
+namespace remoting {
+
+namespace {
+
+FileChooser::Result& StaticResult() {
+  static base::NoDestructor<FileChooser::Result> result(
+      protocol::MakeFileTransferError(
+          FROM_HERE, protocol::FileTransfer_Error_Type_CANCELED));
+  return *result;
+}
+
+}  // namespace
+
+std::unique_ptr<FileChooser> FileChooser::Create(
+    scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+    ResultCallback callback) {
+  return std::make_unique<FakeFileChooser>(std::move(callback));
+}
+
+FakeFileChooser::FakeFileChooser(FileChooser::ResultCallback callback)
+    : callback_(std::move(callback)) {}
+
+FakeFileChooser::~FakeFileChooser() = default;
+
+void FakeFileChooser::Show() {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback_), StaticResult()));
+}
+
+void FakeFileChooser::SetResult(FileChooser::Result result) {
+  StaticResult() = std::move(result);
+}
+
+}  // namespace remoting
diff --git a/remoting/host/file_transfer/fake_file_chooser.h b/remoting/host/file_transfer/fake_file_chooser.h
new file mode 100644
index 0000000..a50a902
--- /dev/null
+++ b/remoting/host/file_transfer/fake_file_chooser.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_HOST_FILE_TRANSFER_FAKE_FILE_CHOOSER_H_
+#define REMOTING_HOST_FILE_TRANSFER_FAKE_FILE_CHOOSER_H_
+
+#include "base/macros.h"
+#include "remoting/host/file_transfer/file_chooser.h"
+
+namespace remoting {
+
+class FakeFileChooser : public FileChooser {
+ public:
+  explicit FakeFileChooser(ResultCallback callback);
+
+  ~FakeFileChooser() override;
+
+  // FileChooser implementation.
+  void Show() override;
+
+  // The result that usages of FakeFileChooser should return.
+  static void SetResult(FileChooser::Result result);
+
+ private:
+  ResultCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeFileChooser);
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_FILE_TRANSFER_FAKE_FILE_CHOOSER_H_
diff --git a/remoting/host/file_transfer/fake_file_operations.cc b/remoting/host/file_transfer/fake_file_operations.cc
index 3377ff5..041700cd6 100644
--- a/remoting/host/file_transfer/fake_file_operations.cc
+++ b/remoting/host/file_transfer/fake_file_operations.cc
@@ -83,7 +83,7 @@
       FROM_HERE,
       base::BindOnce(&FakeFileWriter::DoOpen, weak_ptr_factory_.GetWeakPtr(),
                      std::move(callback)));
-};
+}
 
 void FakeFileOperations::FakeFileWriter::WriteChunk(std::string data,
                                                     Callback callback) {
diff --git a/remoting/host/file_transfer/file_chooser_chromeos.cc b/remoting/host/file_transfer/file_chooser_chromeos.cc
new file mode 100644
index 0000000..c15b8fd
--- /dev/null
+++ b/remoting/host/file_transfer/file_chooser_chromeos.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "remoting/host/file_transfer/file_chooser.h"
+
+namespace remoting {
+
+std::unique_ptr<FileChooser> FileChooser::Create(
+    scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+    ResultCallback callback) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/file_transfer/file_chooser_win.cc b/remoting/host/file_transfer/file_chooser_win.cc
index 95481807..c2c6480 100644
--- a/remoting/host/file_transfer/file_chooser_win.cc
+++ b/remoting/host/file_transfer/file_chooser_win.cc
@@ -137,16 +137,16 @@
     // Currently, WaitForExit returns immediately if GetExitCodeProcess fails,
     // so GetLastError should still be relevant.
     PLOG(ERROR) << "Failed to check exit status";
+    process_.Close();
     std::move(callback_).Run(MakeFileTransferError(
         FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
-    process_.Close();
     return;
   }
   if (exit_code != ERROR_SUCCESS) {
     LOG(ERROR) << "Error running dialog process:" << exit_code;
+    process_.Close();
     std::move(callback_).Run(MakeFileTransferError(
         FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
-    process_.Close();
     return;
   }
   process_.Close();
diff --git a/remoting/host/file_transfer/local_file_operations.cc b/remoting/host/file_transfer/local_file_operations.cc
index d840e7a..844f496 100644
--- a/remoting/host/file_transfer/local_file_operations.cc
+++ b/remoting/host/file_transfer/local_file_operations.cc
@@ -22,6 +22,7 @@
 #include "build/build_config.h"
 #include "remoting/base/result.h"
 #include "remoting/host/file_transfer/ensure_user.h"
+#include "remoting/host/file_transfer/file_chooser.h"
 #include "remoting/host/file_transfer/get_desktop_directory.h"
 #include "remoting/protocol/file_transfer_helpers.h"
 
@@ -43,12 +44,70 @@
   }
 }
 
+scoped_refptr<base::SequencedTaskRunner> CreateFileTaskRunner() {
+#if defined(OS_WIN)
+  // On Windows, we use user impersonation to write files as the currently
+  // logged-in user, while the process as a whole runs as SYSTEM. Since user
+  // impersonation is per-thread on Windows, we need a dedicated thread to
+  // ensure that no other code is accidentally run with the wrong privileges.
+  return base::CreateSingleThreadTaskRunnerWithTraits(
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+#else
+  return base::CreateSequencedTaskRunnerWithTraits(
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
+#endif
+}
+
+class LocalFileReader : public FileOperations::Reader {
+ public:
+  explicit LocalFileReader(
+      scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
+  ~LocalFileReader() override;
+
+  // FileOperations::Reader implementation.
+  void Open(OpenCallback callback) override;
+  void ReadChunk(std::size_t size, ReadCallback callback) override;
+  const base::FilePath& filename() const override;
+  std::uint64_t size() const override;
+  FileOperations::State state() const override;
+
+ private:
+  void OnEnsureUserResult(OpenCallback callback,
+                          protocol::FileTransferResult<Monostate> result);
+  void OnFileChooserResult(OpenCallback callback, FileChooser::Result result);
+  void OnOpenResult(OpenCallback callback, base::File::Error error);
+  void OnGetInfoResult(OpenCallback callback,
+                       base::File::Error error,
+                       const base::File::Info& info);
+  void OnReadResult(ReadCallback callback,
+                    base::File::Error error,
+                    const char* data,
+                    int bytes_read);
+
+  void SetState(FileOperations::State state);
+
+  base::FilePath filename_;
+  std::uint64_t size_ = 0;
+  std::uint64_t offset_ = 0;
+  FileOperations::State state_ = FileOperations::kCreated;
+
+  scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
+  std::unique_ptr<FileChooser> file_chooser_;
+  base::Optional<base::FileProxy> file_proxy_;
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<LocalFileReader> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalFileReader);
+};
+
 class LocalFileWriter : public FileOperations::Writer {
  public:
   LocalFileWriter();
   ~LocalFileWriter() override;
 
-  // FileOperations::Writer implementation
+  // FileOperations::Writer implementation.
   void Open(const base::FilePath& filename, Callback callback) override;
   void WriteChunk(std::string data, Callback callback) override;
   void Close(Callback callback) override;
@@ -93,6 +152,150 @@
   DISALLOW_COPY_AND_ASSIGN(LocalFileWriter);
 };
 
+LocalFileReader::LocalFileReader(
+    scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
+    : ui_task_runner_(std::move(ui_task_runner)), weak_ptr_factory_(this) {}
+
+LocalFileReader::~LocalFileReader() = default;
+
+void LocalFileReader::Open(OpenCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(FileOperations::kCreated, state_);
+  SetState(FileOperations::kBusy);
+  file_task_runner_ = CreateFileTaskRunner();
+  file_proxy_.emplace(file_task_runner_.get());
+  base::PostTaskAndReplyWithResult(
+      file_task_runner_.get(), FROM_HERE, base::BindOnce(&EnsureUserContext),
+      base::BindOnce(&LocalFileReader::OnEnsureUserResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void LocalFileReader::ReadChunk(std::size_t size, ReadCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(FileOperations::kReady, state_);
+  SetState(FileOperations::kBusy);
+  file_proxy_->Read(
+      offset_, size,
+      base::BindOnce(&LocalFileReader::OnReadResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+const base::FilePath& LocalFileReader::filename() const {
+  return filename_;
+}
+
+uint64_t LocalFileReader::size() const {
+  return size_;
+}
+
+FileOperations::State LocalFileReader::state() const {
+  return state_;
+}
+
+void LocalFileReader::OnEnsureUserResult(
+    FileOperations::Reader::OpenCallback callback,
+    protocol::FileTransferResult<Monostate> result) {
+  if (!result) {
+    SetState(FileOperations::kFailed);
+    std::move(callback).Run(std::move(result.error()));
+    return;
+  }
+
+  file_chooser_ = FileChooser::Create(
+      ui_task_runner_,
+      base::BindOnce(&LocalFileReader::OnFileChooserResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  file_chooser_->Show();
+}
+
+void LocalFileReader::OnFileChooserResult(OpenCallback callback,
+                                          FileChooser::Result result) {
+  file_chooser_.reset();
+  if (!result) {
+    SetState(FileOperations::kFailed);
+    std::move(callback).Run(std::move(result.error()));
+    return;
+  }
+
+  filename_ = result->BaseName();
+  file_proxy_->CreateOrOpen(
+      *result, base::File::FLAG_OPEN | base::File::FLAG_READ,
+      base::BindOnce(&LocalFileReader::OnOpenResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void LocalFileReader::OnOpenResult(OpenCallback callback,
+                                   base::File::Error error) {
+  if (error != base::File::FILE_OK) {
+    SetState(FileOperations::kFailed);
+    std::move(callback).Run(protocol::MakeFileTransferError(
+        FROM_HERE, FileErrorToResponseErrorType(error), error));
+    return;
+  }
+
+  file_proxy_->GetInfo(base::BindOnce(&LocalFileReader::OnGetInfoResult,
+                                      weak_ptr_factory_.GetWeakPtr(),
+                                      std::move(callback)));
+}
+
+void LocalFileReader::OnGetInfoResult(OpenCallback callback,
+                                      base::File::Error error,
+                                      const base::File::Info& info) {
+  if (error != base::File::FILE_OK) {
+    SetState(FileOperations::kFailed);
+    std::move(callback).Run(protocol::MakeFileTransferError(
+        FROM_HERE, FileErrorToResponseErrorType(error), error));
+    return;
+  }
+
+  size_ = info.size;
+
+  SetState(FileOperations::kReady);
+  std::move(callback).Run(kSuccessTag);
+}
+
+void LocalFileReader::OnReadResult(ReadCallback callback,
+                                   base::File::Error error,
+                                   const char* data,
+                                   int bytes_read) {
+  if (error != base::File::FILE_OK) {
+    SetState(FileOperations::kFailed);
+    std::move(callback).Run(protocol::MakeFileTransferError(
+        FROM_HERE, FileErrorToResponseErrorType(error), error));
+    return;
+  }
+
+  offset_ += bytes_read;
+  SetState(bytes_read > 0 ? FileOperations::kReady : FileOperations::kComplete);
+
+  // The read buffer is provided and owned by FileProxy, so there's no way to
+  // avoid a copy, here.
+  std::move(callback).Run(std::string(data, bytes_read));
+}
+
+void LocalFileReader::SetState(FileOperations::State state) {
+  switch (state) {
+    case FileOperations::kCreated:
+      NOTREACHED();  // Can never return to initial state.
+      break;
+    case FileOperations::kReady:
+      DCHECK_EQ(FileOperations::kBusy, state_);
+      break;
+    case FileOperations::kBusy:
+      DCHECK(state_ == FileOperations::kCreated ||
+             state_ == FileOperations::kReady);
+      break;
+    case FileOperations::kComplete:
+      DCHECK_EQ(FileOperations::kBusy, state_);
+      break;
+    case FileOperations::kFailed:
+      // Any state can change to kFailed.
+      break;
+  }
+
+  state_ = state;
+}
+
 LocalFileWriter::LocalFileWriter() : weak_ptr_factory_(this) {}
 
 LocalFileWriter::~LocalFileWriter() {
@@ -103,18 +306,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(FileOperations::kCreated, state_);
   SetState(FileOperations::kBusy);
-#if defined(OS_WIN)
-  // On Windows, we use user impersonation to write files as the currently
-  // logged-in user, while the process as a whole runs as SYSTEM. Since user
-  // impersonation is per-thread on Windows, we need a dedicated thread to
-  // ensure that no other code is accidentally run with the wrong privileges.
-  file_task_runner_ = base::CreateSingleThreadTaskRunnerWithTraits(
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-      base::SingleThreadTaskRunnerThreadMode::DEDICATED);
-#else
-  file_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
-#endif
+  file_task_runner_ = CreateFileTaskRunner();
   file_proxy_.emplace(file_task_runner_.get());
   base::PostTaskAndReplyWithResult(
       file_task_runner_.get(), FROM_HERE, base::BindOnce([] {
@@ -366,9 +558,14 @@
 
 }  // namespace
 
+LocalFileOperations::LocalFileOperations(
+    scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
+    : ui_task_runner_(std::move(ui_task_runner)) {}
+
+LocalFileOperations::~LocalFileOperations() = default;
+
 std::unique_ptr<FileOperations::Reader> LocalFileOperations::CreateReader() {
-  NOTIMPLEMENTED();
-  return nullptr;
+  return std::make_unique<LocalFileReader>(ui_task_runner_);
 }
 
 std::unique_ptr<FileOperations::Writer> LocalFileOperations::CreateWriter() {
diff --git a/remoting/host/file_transfer/local_file_operations.h b/remoting/host/file_transfer/local_file_operations.h
index bcb20124..d0ce5f1 100644
--- a/remoting/host/file_transfer/local_file_operations.h
+++ b/remoting/host/file_transfer/local_file_operations.h
@@ -8,6 +8,8 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
 #include "remoting/host/file_transfer/file_operations.h"
 
 namespace remoting {
@@ -17,14 +19,17 @@
 
 class LocalFileOperations : public FileOperations {
  public:
-  LocalFileOperations() = default;
-  ~LocalFileOperations() override = default;
+  explicit LocalFileOperations(
+      scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
+  ~LocalFileOperations() override;
 
   // FileOperations implementation.
   std::unique_ptr<Reader> CreateReader() override;
   std::unique_ptr<Writer> CreateWriter() override;
 
  private:
+  scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+
   DISALLOW_COPY_AND_ASSIGN(LocalFileOperations);
 };
 
diff --git a/remoting/host/file_transfer/local_file_operations_unittest.cc b/remoting/host/file_transfer/local_file_operations_unittest.cc
index c382eea..7f6038a1 100644
--- a/remoting/host/file_transfer/local_file_operations_unittest.cc
+++ b/remoting/host/file_transfer/local_file_operations_unittest.cc
@@ -11,10 +11,27 @@
 #include "base/path_service.h"
 #include "base/test/scoped_path_override.h"
 #include "base/test/scoped_task_environment.h"
+#include "remoting/host/file_transfer/fake_file_chooser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace remoting {
 
+namespace {
+
+// BindOnce disallows binding lambdas with captures. This is reasonable in
+// production code, as it requires one to either explicitly pass owned objects
+// or pointers using Owned, Unretained, et cetera. This helps to avoid use-
+// after-free bugs in async code. In test code, though, where the lambda is
+// immediately invoked in the test method using, e.g., RunUntilIdle, the ability
+// to capture can make the code much easier to read and write.
+template <typename T>
+auto BindLambda(T lambda) {
+  return base::BindOnce(&T::operator(),
+                        base::Owned(new auto(std::move(lambda))));
+}
+
+}  // namespace
+
 class LocalFileOperationsTest : public testing::Test {
  public:
   LocalFileOperationsTest();
@@ -56,7 +73,8 @@
           base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
       // Points DIR_USER_DESKTOP at a scoped temporary directory.
       scoped_path_override_(base::DIR_USER_DESKTOP),
-      file_operations_(std::make_unique<LocalFileOperations>()) {}
+      file_operations_(std::make_unique<LocalFileOperations>(
+          scoped_task_environment_.GetMainThreadTaskRunner())) {}
 
 void LocalFileOperationsTest::SetUp() {}
 
@@ -182,4 +200,139 @@
   ASSERT_TRUE(base::IsDirectoryEmpty(TestDir()));
 }
 
+// Verifies that a file can be successfully opened for reading.
+TEST_F(LocalFileOperationsTest, OpensReader) {
+  base::FilePath path = TestDir().Append(kTestFilename);
+  std::string contents = kTestDataOne + kTestDataTwo + kTestDataThree;
+  ASSERT_EQ(static_cast<int>(contents.size()),
+            base::WriteFile(path, contents.data(), contents.size()));
+
+  std::unique_ptr<FileOperations::Reader> reader =
+      file_operations_->CreateReader();
+
+  FakeFileChooser::SetResult(path);
+  base::Optional<FileOperations::Reader::OpenResult> open_result;
+  ASSERT_EQ(FileOperations::kCreated, reader->state());
+  reader->Open(BindLambda([&](FileOperations::Reader::OpenResult result) {
+    open_result = std::move(result);
+  }));
+  ASSERT_EQ(FileOperations::kBusy, reader->state());
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(FileOperations::kReady, reader->state());
+  ASSERT_TRUE(open_result);
+  ASSERT_TRUE(*open_result);
+  EXPECT_EQ(kTestFilename, reader->filename());
+  EXPECT_EQ(contents.size(), reader->size());
+}
+
+// Verifies that a file can be successfully read in three chunks.
+TEST_F(LocalFileOperationsTest, ReadsThreeChunks) {
+  base::FilePath path = TestDir().Append(kTestFilename);
+  std::string contents = kTestDataOne + kTestDataTwo + kTestDataThree;
+  ASSERT_EQ(static_cast<int>(contents.size()),
+            base::WriteFile(path, contents.data(), contents.size()));
+
+  std::unique_ptr<FileOperations::Reader> reader =
+      file_operations_->CreateReader();
+
+  FakeFileChooser::SetResult(path);
+  base::Optional<FileOperations::Reader::OpenResult> open_result;
+  reader->Open(BindLambda([&](FileOperations::Reader::OpenResult result) {
+    open_result = std::move(result);
+  }));
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(open_result && *open_result);
+
+  for (const auto& chunk : {kTestDataOne, kTestDataTwo, kTestDataThree}) {
+    base::Optional<FileOperations::Reader::ReadResult> read_result;
+    reader->ReadChunk(
+        chunk.size(),
+        BindLambda([&](FileOperations::Reader::ReadResult result) {
+          read_result = std::move(result);
+        }));
+    ASSERT_EQ(FileOperations::kBusy, reader->state());
+    scoped_task_environment_.RunUntilIdle();
+    ASSERT_EQ(FileOperations::kReady, reader->state());
+    ASSERT_TRUE(read_result);
+    ASSERT_TRUE(*read_result);
+    EXPECT_EQ(chunk, **read_result);
+  }
+}
+
+// Verifies proper EOF handling.
+TEST_F(LocalFileOperationsTest, ReaderHandlesEof) {
+  base::FilePath path = TestDir().Append(kTestFilename);
+  std::string contents = kTestDataOne + kTestDataTwo + kTestDataThree;
+  ASSERT_EQ(static_cast<int>(contents.size()),
+            base::WriteFile(path, contents.data(), contents.size()));
+
+  std::unique_ptr<FileOperations::Reader> reader =
+      file_operations_->CreateReader();
+
+  FakeFileChooser::SetResult(path);
+  base::Optional<FileOperations::Reader::OpenResult> open_result;
+  reader->Open(BindLambda([&](FileOperations::Reader::OpenResult result) {
+    open_result = std::move(result);
+  }));
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(open_result && *open_result);
+
+  base::Optional<FileOperations::Reader::ReadResult> read_result;
+  reader->ReadChunk(
+      contents.size() + 5,  // Attempt to read more than is in file.
+      BindLambda([&](FileOperations::Reader::ReadResult result) {
+        read_result = std::move(result);
+      }));
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_EQ(FileOperations::kReady, reader->state());
+  ASSERT_TRUE(read_result && *read_result);
+  EXPECT_EQ(contents, **read_result);
+
+  read_result.reset();
+  reader->ReadChunk(5,
+                    BindLambda([&](FileOperations::Reader::ReadResult result) {
+                      read_result = std::move(result);
+                    }));
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(FileOperations::kComplete, reader->state());
+  ASSERT_TRUE(read_result && *read_result);
+  EXPECT_EQ(std::size_t{0}, (*read_result)->size());
+}
+
+// Verifies cancellation is propagated.
+TEST_F(LocalFileOperationsTest, ReaderCancels) {
+  std::unique_ptr<FileOperations::Reader> reader =
+      file_operations_->CreateReader();
+
+  FakeFileChooser::SetResult(protocol::MakeFileTransferError(
+      FROM_HERE, protocol::FileTransfer_Error_Type_CANCELED));
+  base::Optional<FileOperations::Reader::OpenResult> open_result;
+  reader->Open(BindLambda([&](FileOperations::Reader::OpenResult result) {
+    open_result = std::move(result);
+  }));
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(FileOperations::kFailed, reader->state());
+  ASSERT_TRUE(open_result);
+  ASSERT_FALSE(*open_result);
+  EXPECT_EQ(protocol::FileTransfer_Error_Type_CANCELED,
+            open_result->error().type());
+}
+
+// Verifies failure when file doesn't exist.
+TEST_F(LocalFileOperationsTest, FileNotFound) {
+  std::unique_ptr<FileOperations::Reader> reader =
+      file_operations_->CreateReader();
+
+  // Currently non-existent file.
+  FakeFileChooser::SetResult(TestDir().Append(kTestFilename));
+  base::Optional<FileOperations::Reader::OpenResult> open_result;
+  reader->Open(BindLambda([&](FileOperations::Reader::OpenResult result) {
+    open_result = std::move(result);
+  }));
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(FileOperations::kFailed, reader->state());
+  ASSERT_TRUE(open_result);
+  ASSERT_FALSE(*open_result);
+}
+
 }  // namespace remoting
diff --git a/remoting/host/gcd_rest_client.cc b/remoting/host/gcd_rest_client.cc
index 0120d08..cbd3a42 100644
--- a/remoting/host/gcd_rest_client.cc
+++ b/remoting/host/gcd_rest_client.cc
@@ -82,7 +82,7 @@
   resource_request_->method = "POST";
 
   token_getter_->CallWithToken(
-      base::Bind(&GcdRestClient::OnTokenReceived, base::Unretained(this)));
+      base::BindOnce(&GcdRestClient::OnTokenReceived, base::Unretained(this)));
 }
 
 void GcdRestClient::SetClockForTest(base::Clock* clock) {
diff --git a/remoting/host/signaling_connector.cc b/remoting/host/signaling_connector.cc
index 530bfa6..397ea44 100644
--- a/remoting/host/signaling_connector.cc
+++ b/remoting/host/signaling_connector.cc
@@ -175,7 +175,7 @@
 
   if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
     HOST_LOG << "Attempting to connect signaling.";
-    oauth_token_getter_->CallWithToken(base::Bind(
+    oauth_token_getter_->CallWithToken(base::BindOnce(
         &SignalingConnector::OnAccessToken, weak_factory_.GetWeakPtr()));
   }
 }
diff --git a/remoting/ios/facade/ios_oauth_token_getter.h b/remoting/ios/facade/ios_oauth_token_getter.h
index 7ad1fa8..684f25d 100644
--- a/remoting/ios/facade/ios_oauth_token_getter.h
+++ b/remoting/ios/facade/ios_oauth_token_getter.h
@@ -20,7 +20,7 @@
   ~IosOauthTokenGetter() override;
 
   // OAuthTokenGetter overrides.
-  void CallWithToken(const TokenCallback& on_access_token) override;
+  void CallWithToken(TokenCallback on_access_token) override;
   void InvalidateCache() override;
 
   base::WeakPtr<IosOauthTokenGetter> GetWeakPtr();
diff --git a/remoting/ios/facade/ios_oauth_token_getter.mm b/remoting/ios/facade/ios_oauth_token_getter.mm
index 1f60af2..d9d542f 100644
--- a/remoting/ios/facade/ios_oauth_token_getter.mm
+++ b/remoting/ios/facade/ios_oauth_token_getter.mm
@@ -20,9 +20,8 @@
 
 IosOauthTokenGetter::~IosOauthTokenGetter() {}
 
-void IosOauthTokenGetter::CallWithToken(const TokenCallback& on_access_token) {
-  // This forces the block to copy the callback instead of just the reference.
-  TokenCallback on_access_token_copied = on_access_token;
+void IosOauthTokenGetter::CallWithToken(TokenCallback on_access_token) {
+  __block TokenCallback block_callback = std::move(on_access_token);
   [RemotingService.instance.authentication
       callbackWithAccessToken:^(RemotingAuthenticationStatus status,
                                 NSString* userEmail, NSString* accessToken) {
@@ -40,9 +39,9 @@
           default:
             NOTREACHED();
         }
-        on_access_token_copied.Run(oauth_status,
-                                   base::SysNSStringToUTF8(userEmail),
-                                   base::SysNSStringToUTF8(accessToken));
+        std::move(block_callback)
+            .Run(oauth_status, base::SysNSStringToUTF8(userEmail),
+                 base::SysNSStringToUTF8(accessToken));
       }];
 }
 
diff --git a/remoting/ios/facade/remoting_oauth_authentication.mm b/remoting/ios/facade/remoting_oauth_authentication.mm
index 7f25698..a33a7c02 100644
--- a/remoting/ios/facade/remoting_oauth_authentication.mm
+++ b/remoting/ios/facade/remoting_oauth_authentication.mm
@@ -170,7 +170,7 @@
   // Be careful here since a failure to reset onAccessToken will end up with
   // retain cycle and memory leakage.
   if (_tokenGetter) {
-    _tokenGetter->CallWithToken(base::BindRepeating(
+    _tokenGetter->CallWithToken(base::BindOnce(
         ^(remoting::OAuthTokenGetter::Status status,
           const std::string& user_email, const std::string& access_token) {
           onAccessToken(oauthStatusToRemotingAuthenticationStatus(status),
diff --git a/remoting/protocol/http_ice_config_request.cc b/remoting/protocol/http_ice_config_request.cc
index 13f02b5..241b9055 100644
--- a/remoting/protocol/http_ice_config_request.cc
+++ b/remoting/protocol/http_ice_config_request.cc
@@ -75,7 +75,7 @@
   on_ice_config_callback_ = callback;
 
   if (oauth_token_getter_) {
-    oauth_token_getter_->CallWithToken(base::Bind(
+    oauth_token_getter_->CallWithToken(base::BindOnce(
         &HttpIceConfigRequest::OnOAuthToken, weak_factory_.GetWeakPtr()));
   } else {
     SendRequest();
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
index a4124ba..ba2d363 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
@@ -140,9 +140,6 @@
     case __NR_socket:
 #endif
 
-    // Ptrace is allowed so the Breakpad Microdumper can fork in a renderer
-    // and then ptrace the parent.
-    case __NR_ptrace:
       override_and_allow = true;
       break;
   }
@@ -152,6 +149,12 @@
     return Allow();
   }
 
+  // Ptrace is allowed so the crash reporter can fork in a renderer
+  // and then ptrace the parent. https://crbug.com/933418
+  if (sysno == __NR_ptrace) {
+    return RestrictPtrace();
+  }
+
   // https://crbug.com/644759
   if (sysno == __NR_rt_tgsigqueueinfo) {
     const Arg<pid_t> tgid(0);
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
index 5adc1a7a..7514f9a 100644
--- a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -33,6 +33,7 @@
 #define SECCOMP_MESSAGE_IOCTL_CONTENT "ioctl() failure"
 #define SECCOMP_MESSAGE_KILL_CONTENT "(tg)kill() failure"
 #define SECCOMP_MESSAGE_FUTEX_CONTENT "futex() failure"
+#define SECCOMP_MESSAGE_PTRACE_CONTENT "ptrace() failure"
 
 namespace {
 
@@ -305,6 +306,19 @@
     _exit(1);
 }
 
+intptr_t SIGSYSPtraceFailure(const struct arch_seccomp_data& args,
+                             void* /* aux */) {
+  static const char kSeccompPtraceError[] =
+      __FILE__ ":**CRASHING**:" SECCOMP_MESSAGE_PTRACE_CONTENT "\n";
+  WriteToStdErr(kSeccompPtraceError, sizeof(kSeccompPtraceError) - 1);
+  SetSeccompCrashKey(args);
+  volatile int ptrace_op = args.args[0];
+  volatile char* addr = reinterpret_cast<volatile char*>(ptrace_op & 0xFFF);
+  *addr = '\0';
+  for (;;)
+    _exit(1);
+}
+
 intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args,
                             void* aux) {
   switch (args.nr) {
@@ -363,6 +377,10 @@
   return bpf_dsl::Trap(SIGSYSFutexFailure, NULL);
 }
 
+bpf_dsl::ResultExpr CrashSIGSYSPtrace() {
+  return bpf_dsl::Trap(SIGSYSPtraceFailure, NULL);
+}
+
 bpf_dsl::ResultExpr RewriteSchedSIGSYS() {
   return bpf_dsl::Trap(SIGSYSSchedHandler, NULL);
 }
@@ -401,4 +419,8 @@
   return SECCOMP_MESSAGE_FUTEX_CONTENT;
 }
 
+const char* GetPtraceErrorMessageContentForTests() {
+  return SECCOMP_MESSAGE_PTRACE_CONTENT;
+}
+
 }  // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
index b32fd6fe..baac3b6 100644
--- a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
@@ -25,8 +25,8 @@
 SANDBOX_EXPORT intptr_t
     CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux);
 
-// The following three handlers are suitable to report failures with the
-// clone(), prctl() and ioctl() system calls respectively.
+// The following seven handlers are suitable to report failures for specific
+// system calls with additional information.
 
 // The crashing address will be (clone_flags & 0xFFFFFF), where clone_flags is
 // the clone(2) argument, extracted from |args|.
@@ -48,6 +48,10 @@
 // argument.
 SANDBOX_EXPORT intptr_t
     SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (op & 0xFFF), where op is the second
+// argument.
+SANDBOX_EXPORT intptr_t
+SIGSYSPtraceFailure(const struct arch_seccomp_data& args, void* aux);
 // If the syscall is not being called on the current tid, crashes in the same
 // way as CrashSIGSYS_Handler.  Otherwise, returns the result of calling the
 // syscall with the pid argument set to 0 (which for these calls means the
@@ -66,6 +70,7 @@
 SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSIoctl();
 SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill();
 SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPtrace();
 SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS();
 
 // Allocates a crash key so that Seccomp information can be recorded.
@@ -79,6 +84,7 @@
 SANDBOX_EXPORT const char* GetIoctlErrorMessageContentForTests();
 SANDBOX_EXPORT const char* GetKillErrorMessageContentForTests();
 SANDBOX_EXPORT const char* GetFutexErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetPtraceErrorMessageContentForTests();
 
 }  // namespace sandbox.
 
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 2577f02f..ecd6e339 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -6,7 +6,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <fcntl.h>
 #include <linux/net.h>
 #include <sched.h>
 #include <signal.h>
@@ -31,9 +30,10 @@
 #include "sandbox/linux/system_headers/linux_syscalls.h"
 #include "sandbox/linux/system_headers/linux_time.h"
 
-// PNaCl toolchain does not provide sys/ioctl.h header.
+// PNaCl toolchain does not provide sys/ioctl.h and sys/ptrace.h headers.
 #if !defined(OS_NACL_NONSFI)
 #include <sys/ioctl.h>
+#include <sys/ptrace.h>
 #endif
 
 #if defined(OS_ANDROID)
@@ -383,4 +383,25 @@
   return If(AnyOf(pid == 0, pid == target_pid), Allow()).Else(Error(EPERM));
 }
 
+#if !defined(OS_NACL_NONSFI)
+ResultExpr RestrictPtrace() {
+  const Arg<int> request(0);
+  return Switch(request).CASES((
+#if !defined(__aarch64__)
+        PTRACE_GETREGS,
+        PTRACE_GETFPREGS,
+        PTRACE_GET_THREAD_AREA,
+#endif
+#if defined(__arm__)
+        PTRACE_GETVFPREGS,
+#endif
+        PTRACE_GETREGSET,
+        PTRACE_PEEKDATA,
+        PTRACE_ATTACH,
+        PTRACE_DETACH),
+      Allow())
+      .Default(CrashSIGSYSPtrace());
+}
+#endif  // defined(OS_NACL_NONSFI)
+
 }  // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
index 71c56093..cb563df 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -103,6 +103,10 @@
 // gracefully; see crbug.com/160157.
 SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimit(pid_t target_pid);
 
+// Restrict ptrace() to just read operations that are needed for crash
+// reporting. See https://crbug.com/933418 for details.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPtrace();
+
 }  // namespace sandbox.
 
 #endif  // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
index 8fff3f1..073df00 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -5,14 +5,20 @@
 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
 
 #include <errno.h>
+#include <fcntl.h>
+#include <linux/elf.h>
 #include <sched.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
 #include <sys/resource.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <sys/user.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "base/bind.h"
+#include "base/posix/eintr_wrapper.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/system/sys_info.h"
@@ -242,6 +248,231 @@
   getrusage(RUSAGE_CHILDREN, &usage);
 }
 
+// ptace() Tests ///////////////////////////////////////////////////////////////
+
+// Tests for ptrace involve a slightly complex setup in order to properly test
+// ptrace and the variety of ways it is access-checked. The BPF_TEST macro,
+// the body of which already runs in its own process, spawns another process
+// called the "tracee". The "tracee" then spawns another process called the
+// "tracer". The child then traces the parent and performs the test operations.
+// The tracee must be careful to un-stop the tracer if the tracee expects to
+// die.
+
+class RestrictPtracePolicy : public bpf_dsl::Policy {
+ public:
+  RestrictPtracePolicy() = default;
+  ~RestrictPtracePolicy() override = default;
+
+  ResultExpr EvaluateSyscall(int sysno) const override {
+    switch (sysno) {
+      case __NR_ptrace:
+        return RestrictPtrace();
+      default:
+        return Allow();
+    }
+  }
+};
+
+constexpr char kExitPtraceChildClean = '!';
+
+class PtraceTestHarness {
+ public:
+  using PtraceChildTracerFunc = void (*)(pid_t tracee);
+
+  PtraceTestHarness(PtraceChildTracerFunc tracer_func, bool expect_death)
+      : tracer_func_(tracer_func), expect_death_(expect_death) {}
+  ~PtraceTestHarness() = default;
+
+  void Run() {
+    // Fork the tracee process that will be traced by its child.
+    pid_t pid = fork();
+    BPF_ASSERT_GE(pid, 0);
+
+    if (pid == 0) {
+      RunTracee();
+    } else {
+      // The tracee should always exit cleanly.
+      int status = 0;
+      int rv = waitpid(pid, &status, 0);
+      BPF_ASSERT_EQ(pid, rv);
+      BPF_ASSERT_EQ(0, WEXITSTATUS(status));
+    }
+  }
+
+ private:
+  void RunTracee() {
+    // Create a communications pipe between tracer and tracee.
+    int rv = pipe2(pipes_, O_NONBLOCK);
+    BPF_ASSERT_EQ(0, rv);
+
+    // Pipes for redirecting output.
+    int output_pipes[2];
+    BPF_ASSERT_EQ(0, pipe(output_pipes));
+
+    // Create the tracer process.
+    pid_t pid = fork();
+    BPF_ASSERT_GE(pid, 0);
+
+    if (pid == 0) {
+      // Close the pipe read ends and redirect output.
+      close(pipes_[0]);
+      close(output_pipes[0]);
+
+      close(STDOUT_FILENO);
+      dup2(output_pipes[1], STDOUT_FILENO);
+
+      close(STDERR_FILENO);
+      dup2(output_pipes[1], STDERR_FILENO);
+
+      RunTracer();
+
+      close(output_pipes[1]);
+    } else {
+      close(pipes_[1]);
+      close(output_pipes[1]);
+
+      // Ensure the tracer can trace the tracee. This may fail on systems
+      // without YAMA, so the result is not checked.
+      prctl(PR_SET_PTRACER, pid);
+
+      char c = 0;
+      while (c != kExitPtraceChildClean) {
+        // Read from the control channel in a non-blocking fashion.
+        // If no data are present, loop.
+        ignore_result(read(pipes_[0], &c, 1));
+
+        // Poll the exit status of the child.
+        int status = 0;
+        rv = waitpid(pid, &status, WNOHANG);
+        if (rv != 0) {
+          BPF_ASSERT_EQ(pid, rv);
+          CheckTracerStatus(status, output_pipes[0]);
+          _exit(0);
+        }
+      }
+
+      _exit(0);
+    }
+  }
+
+  void RunTracer() {
+    pid_t ppid = getppid();
+    BPF_ASSERT_NE(0, ppid);
+
+    // Attach to the tracee and then call out to the test function.
+    BPF_ASSERT_EQ(0, ptrace(PTRACE_ATTACH, ppid, nullptr, nullptr));
+
+    tracer_func_(ppid);
+
+    BPF_ASSERT_EQ(1, HANDLE_EINTR(write(pipes_[1], &kExitPtraceChildClean, 1)));
+    close(pipes_[1]);
+
+    _exit(0);
+  }
+
+  void CheckTracerStatus(int status, int output_pipe) {
+    // The child has exited. Test that it did so in the way we were
+    // expecting.
+    if (expect_death_) {
+      // This duplicates a bit of what //sandbox/linux/tests/unit_tests.cc does
+      // but that code is not shareable here.
+      std::string output;
+      const size_t kBufferSize = 1024;
+      size_t total_bytes_read = 0;
+      ssize_t read_this_pass = 0;
+      do {
+        output.resize(output.size() + kBufferSize);
+        read_this_pass = HANDLE_EINTR(
+            read(output_pipe, &output[total_bytes_read], kBufferSize));
+        if (read_this_pass >= 0) {
+          total_bytes_read += read_this_pass;
+          output.resize(total_bytes_read);
+        }
+      } while (read_this_pass > 0);
+
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+      const bool subprocess_got_sigsegv =
+          WIFSIGNALED(status) && (SIGSEGV == WTERMSIG(status));
+#else
+      // This hack is required when a signal handler is installed
+      // for SEGV that will _exit(1).
+      const bool subprocess_got_sigsegv =
+          WIFEXITED(status) && (1 == WEXITSTATUS(status));
+#endif
+      BPF_ASSERT(subprocess_got_sigsegv);
+      BPF_ASSERT_NE(output.find(GetPtraceErrorMessageContentForTests()),
+                    std::string::npos);
+    } else {
+      BPF_ASSERT(WIFEXITED(status));
+      BPF_ASSERT_EQ(0, WEXITSTATUS(status));
+    }
+  }
+
+  PtraceChildTracerFunc tracer_func_;
+  bool expect_death_;
+  int pipes_[2];
+
+  DISALLOW_COPY_AND_ASSIGN(PtraceTestHarness);
+};
+
+BPF_TEST_C(ParameterRestrictions,
+           ptrace_getregs_allowed,
+           RestrictPtracePolicy) {
+  auto tracer = [](pid_t pid) {
+#if defined(__arm__)
+    user_regs regs;
+#else
+    user_regs_struct regs;
+#endif
+    iovec iov;
+    iov.iov_base = &regs;
+    iov.iov_len = sizeof(regs);
+    BPF_ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid,
+                            reinterpret_cast<void*>(NT_PRSTATUS), &iov));
+
+    BPF_ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, nullptr, nullptr));
+  };
+  PtraceTestHarness(tracer, false).Run();
+}
+
+BPF_TEST_C(ParameterRestrictions,
+           ptrace_syscall_blocked,
+           RestrictPtracePolicy) {
+  auto tracer = [](pid_t pid) {
+    // The tracer is about to die. Make sure the tracee is not stopped so it
+    // can reap it and inspect its death signal.
+    kill(pid, SIGCONT);
+
+    BPF_ASSERT_NE(0, ptrace(PTRACE_SYSCALL, 0, nullptr, nullptr));
+  };
+  PtraceTestHarness(tracer, true).Run();
+}
+
+BPF_TEST_C(ParameterRestrictions,
+           ptrace_setregs_blocked,
+           RestrictPtracePolicy) {
+  auto tracer = [](pid_t pid) {
+#if defined(__arm__)
+    user_regs regs;
+#else
+    user_regs_struct regs;
+#endif
+    iovec iov;
+    iov.iov_base = &regs;
+    iov.iov_len = sizeof(regs);
+    BPF_ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid,
+                            reinterpret_cast<void*>(NT_PRSTATUS), &iov));
+
+    // The tracer is about to die. Make sure the tracee is not stopped so it
+    // can reap it and inspect its death signal.
+    kill(pid, SIGCONT);
+
+    BPF_ASSERT_NE(0, ptrace(PTRACE_SETREGSET, pid,
+                            reinterpret_cast<void*>(NT_PRSTATUS), &iov));
+  };
+  PtraceTestHarness(tracer, true).Run();
+}
+
 }  // namespace
 
 }  // namespace sandbox
diff --git a/services/content/navigable_contents_impl.cc b/services/content/navigable_contents_impl.cc
index 9095aae..4c27a5e 100644
--- a/services/content/navigable_contents_impl.cc
+++ b/services/content/navigable_contents_impl.cc
@@ -58,12 +58,12 @@
   delegate_->GoBack(std::move(callback));
 }
 
-void NavigableContentsImpl::CreateView(bool in_service_process,
+void NavigableContentsImpl::CreateView(bool use_window_service,
                                        CreateViewCallback callback) {
   DCHECK(native_content_view_);
 
 #if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
-  if (!in_service_process) {
+  if (use_window_service) {
     remote_view_provider_ =
         std::make_unique<views::RemoteViewProvider>(native_content_view_);
     remote_view_provider_->GetEmbedToken(
@@ -72,7 +72,7 @@
     return;
   }
 #else
-  if (!in_service_process) {
+  if (use_window_service) {
     DLOG(ERROR) << "Remote NavigableContentsView clients are not supported on "
                 << "this platform.";
     return;
diff --git a/services/content/public/cpp/navigable_contents.cc b/services/content/public/cpp/navigable_contents.cc
index 6a0e715..5035763 100644
--- a/services/content/public/cpp/navigable_contents.cc
+++ b/services/content/public/cpp/navigable_contents.cc
@@ -36,7 +36,7 @@
   if (!view_) {
     view_ = base::WrapUnique(new NavigableContentsView(this));
     contents_->CreateView(
-        NavigableContentsView::IsClientRunningInServiceProcess(),
+        ShouldUseWindowService(),
         base::BindOnce(&NavigableContents::OnEmbedTokenReceived,
                        base::Unretained(this)));
   }
@@ -65,6 +65,18 @@
   contents_->FocusThroughTabTraversal(reverse);
 }
 
+void NavigableContents::ForceUseWindowService() {
+  // This should only be called before |view_| is created.
+  DCHECK(!view_);
+
+  force_use_window_service_ = true;
+}
+
+bool NavigableContents::ShouldUseWindowService() const {
+  return !NavigableContentsView::IsClientRunningInServiceProcess() ||
+         force_use_window_service_;
+}
+
 void NavigableContents::ClearViewFocus() {
   if (view_)
     view_->ClearNativeFocus();
diff --git a/services/content/public/cpp/navigable_contents.h b/services/content/public/cpp/navigable_contents.h
index ae675b0..b805d4b5 100644
--- a/services/content/public/cpp/navigable_contents.h
+++ b/services/content/public/cpp/navigable_contents.h
@@ -69,6 +69,13 @@
   // being done via Tab-key cycling or a similar mechanism.
   void FocusThroughTabTraversal(bool reverse);
 
+  // Force NavigableContents to use Window Service for embedding. Note this must
+  // be called before its view is created.
+  void ForceUseWindowService();
+
+  // Whether to use Window Service for embedding.
+  bool ShouldUseWindowService() const;
+
  private:
   // mojom::NavigableContentsClient:
   void ClearViewFocus() override;
@@ -94,6 +101,8 @@
 
   ui::AXTreeID content_ax_tree_id_;
 
+  bool force_use_window_service_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(NavigableContents);
 };
 
diff --git a/services/content/public/cpp/navigable_contents_view.cc b/services/content/public/cpp/navigable_contents_view.cc
index 8c1ef02..75b7e99 100644
--- a/services/content/public/cpp/navigable_contents_view.cc
+++ b/services/content/public/cpp/navigable_contents_view.cc
@@ -164,7 +164,7 @@
     : contents_(contents) {
 #if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
 #if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
-  if (!IsClientRunningInServiceProcess()) {
+  if (contents_->ShouldUseWindowService()) {
     RemoteViewManager* manager = GetRemoteViewManager().get();
     if (manager)
       view_ = manager->CreateRemoteViewHost();
@@ -201,7 +201,7 @@
     const base::UnguessableToken& token) {
 #if defined(TOOLKIT_VIEWS)
 #if BUILDFLAG(ENABLE_REMOTE_NAVIGABLE_CONTENTS_VIEW)
-  if (!IsClientRunningInServiceProcess()) {
+  if (contents_->ShouldUseWindowService()) {
     RemoteViewManager* manager = GetRemoteViewManager().get();
     if (manager) {
       manager->EmbedUsingToken(view_.get(), token);
diff --git a/services/content/public/mojom/navigable_contents.mojom b/services/content/public/mojom/navigable_contents.mojom
index d90f305..adc9294 100644
--- a/services/content/public/mojom/navigable_contents.mojom
+++ b/services/content/public/mojom/navigable_contents.mojom
@@ -37,14 +37,13 @@
   // be given to Mus in order to authorize embedding of that visual
   // representation within the client application's own window tree.
   //
-  // |in_service_process| must be true iff the client calling this method is
-  // running in the same process as the Content Service. This can be known in
-  // the client by calling
-  // |NavigableContentsView::IsClientRunningInServiceProcess()|.
+  // |use_window_service| is true when the client calling this method wants to
+  // use Window Service for embedding. This is the case when the client and the
+  // Content Service are using different window trees.
   //
-  // TODO(https://crbug.com/874143): Remove the |in_service_process| flag once
+  // TODO(https://crbug.com/874143): Remove the |use_window_service| flag once
   // we have more widely available UI Service support.
-  CreateView(bool in_service_process)
+  CreateView(bool use_window_service)
       => (mojo_base.mojom.UnguessableToken embed_token);
 
   // Attempts to transfer global input focus to the navigated contents if they
diff --git a/services/media_session/BUILD.gn b/services/media_session/BUILD.gn
index 3228a8b..8646487 100644
--- a/services/media_session/BUILD.gn
+++ b/services/media_session/BUILD.gn
@@ -25,6 +25,7 @@
     "//mojo/public/cpp/bindings",
     "//services/media_session/public/cpp",
     "//services/media_session/public/mojom",
+    "//skia",
   ]
 
   public_deps = [
diff --git a/services/media_session/DEPS b/services/media_session/DEPS
index b8a225f..de074ee 100644
--- a/services/media_session/DEPS
+++ b/services/media_session/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+jni",
   "+ui/gfx",
+  "+third_party/skia/include",
 ]
diff --git a/services/media_session/public/cpp/media_image_manager.cc b/services/media_session/public/cpp/media_image_manager.cc
index b0f312b..b4b825c 100644
--- a/services/media_session/public/cpp/media_image_manager.cc
+++ b/services/media_session/public/cpp/media_image_manager.cc
@@ -37,8 +37,35 @@
   return base::ToLowerASCII(path.substr(pos));
 }
 
+double GetImageDominantSizeScore(int min_size,
+                                 int ideal_size,
+                                 const gfx::Size& size) {
+  int dominant_size = std::max(size.width(), size.height());
+
+  // If the size is "any".
+  if (dominant_size == 0)
+    return 0.8;
+
+  // Ignore images that are too small.
+  if (dominant_size < min_size)
+    return 0;
+
+  if (dominant_size <= ideal_size)
+    return 0.8 * (dominant_size - min_size) / (ideal_size - min_size) + 0.2;
+
+  return 1.0 * ideal_size / dominant_size;
+}
+
 }  // namespace
 
+// static
+double MediaImageManager::GetImageSizeScore(int min_size,
+                                            int ideal_size,
+                                            const gfx::Size& size) {
+  return GetImageDominantSizeScore(min_size, ideal_size, size) *
+         GetImageAspectRatioScore(size);
+}
+
 MediaImageManager::MediaImageManager(int min_size, int ideal_size)
     : min_size_(min_size), ideal_size_(ideal_size) {}
 
@@ -66,8 +93,10 @@
   if (image.sizes.empty()) {
     best_size_score = kDefaultImageSizeScore;
   } else {
-    for (auto& size : image.sizes)
-      best_size_score = std::max(best_size_score, GetImageSizeScore(size));
+    for (auto& size : image.sizes) {
+      best_size_score = std::max(
+          best_size_score, GetImageSizeScore(min_size_, ideal_size_, size));
+    }
   }
 
   double type_score = kDefaultTypeScore;
@@ -81,28 +110,6 @@
   return best_size_score * type_score;
 }
 
-double MediaImageManager::GetImageSizeScore(const gfx::Size& size) const {
-  return GetImageDominantSizeScore(size) * GetImageAspectRatioScore(size);
-}
-
-double MediaImageManager::GetImageDominantSizeScore(
-    const gfx::Size& size) const {
-  int dominant_size = std::max(size.width(), size.height());
-
-  // If the size is "any".
-  if (dominant_size == 0)
-    return 0.8;
-
-  // Ignore images that are too small.
-  if (dominant_size < min_size_)
-    return 0;
-
-  if (dominant_size <= ideal_size_)
-    return 0.8 * (dominant_size - min_size_) / (ideal_size_ - min_size_) + 0.2;
-
-  return 1.0 * ideal_size_ / dominant_size;
-}
-
 // static
 base::Optional<double> MediaImageManager::GetImageExtensionScore(
     const GURL& url) {
diff --git a/services/media_session/public/cpp/media_image_manager.h b/services/media_session/public/cpp/media_image_manager.h
index be2285e..a8bc41d 100644
--- a/services/media_session/public/cpp/media_image_manager.h
+++ b/services/media_session/public/cpp/media_image_manager.h
@@ -45,6 +45,12 @@
 //       the short edge length by the long edge.
 class COMPONENT_EXPORT(MEDIA_SESSION_CPP) MediaImageManager {
  public:
+  // Returns the image size score as a double between 0 and 1. The score will
+  // be calculated using the size scoring algorithm described above.
+  static double GetImageSizeScore(int min_size,
+                                  int ideal_size,
+                                  const gfx::Size& size);
+
   // The |min_size| is the min size of the images to select in px. The
   // |ideal_size| is the ideal size of the images to select in px.
   MediaImageManager(int min_size, int ideal_size);
@@ -62,10 +68,6 @@
 
   double GetImageScore(const MediaImage& image) const;
 
-  double GetImageSizeScore(const gfx::Size& size) const;
-
-  double GetImageDominantSizeScore(const gfx::Size& size) const;
-
   static base::Optional<double> GetImageExtensionScore(const GURL& url);
 
   static base::Optional<double> GetImageTypeScore(const base::string16& type);
diff --git a/services/media_session/public/cpp/media_session.typemap b/services/media_session/public/cpp/media_session.typemap
index 0eec57c..8549fc5 100644
--- a/services/media_session/public/cpp/media_session.typemap
+++ b/services/media_session/public/cpp/media_session.typemap
@@ -6,17 +6,20 @@
 public_headers = [
   "//services/media_session/public/cpp/media_image.h",
   "//services/media_session/public/cpp/media_metadata.h",
+  "//third_party/skia/include/core/SkBitmap.h",
 ]
 traits_headers =
     [ "//services/media_session/public/cpp/media_session_mojom_traits.h" ]
 public_deps = [
   "//services/media_session/public/cpp",
+  "//skia",
 ]
 deps = [
   "//ui/gfx/geometry/mojo:struct_traits",
 ]
 type_mappings = [
   "media_session.mojom.MediaImage=media_session::MediaImage",
+  "media_session.mojom.MediaImageBitmap=SkBitmap[nullable_is_same_type]",
   "media_session.mojom.MediaMetadata=media_session::MediaMetadata",
 ]
 sources = [
diff --git a/services/media_session/public/cpp/media_session_mojom_traits.cc b/services/media_session/public/cpp/media_session_mojom_traits.cc
index ae4d1a4..5ce8c2ef 100644
--- a/services/media_session/public/cpp/media_session_mojom_traits.cc
+++ b/services/media_session/public/cpp/media_session_mojom_traits.cc
@@ -5,6 +5,7 @@
 #include "services/media_session/public/cpp/media_session_mojom_traits.h"
 
 #include "mojo/public/cpp/base/string16_mojom_traits.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
 #include "url/mojom/url_gurl_mojom_traits.h"
 
@@ -45,4 +46,43 @@
   return true;
 }
 
+// static
+const base::span<const uint8_t>
+StructTraits<media_session::mojom::MediaImageBitmapDataView,
+             SkBitmap>::pixel_data(const SkBitmap& r) {
+  const SkImageInfo& info = r.info();
+  DCHECK_EQ(info.colorType(), kRGBA_8888_SkColorType);
+
+  return base::make_span(static_cast<uint8_t*>(r.getPixels()),
+                         r.computeByteSize());
+}
+
+// static
+bool StructTraits<media_session::mojom::MediaImageBitmapDataView, SkBitmap>::
+    Read(media_session::mojom::MediaImageBitmapDataView data, SkBitmap* out) {
+  mojo::ArrayDataView<uint8_t> pixel_data;
+  data.GetPixelDataDataView(&pixel_data);
+
+  SkImageInfo info = SkImageInfo::Make(
+      data.width(), data.height(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+  if (info.computeByteSize(info.minRowBytes()) > pixel_data.size()) {
+    // Insufficient buffer size.
+    return false;
+  }
+
+  // Create the SkBitmap object which wraps the arc bitmap pixels. This
+  // doesn't copy and |data| and |bitmap| share the buffer.
+  SkBitmap bitmap;
+  if (!bitmap.installPixels(info, const_cast<uint8_t*>(pixel_data.data()),
+                            info.minRowBytes())) {
+    // Error in installing pixels.
+    return false;
+  }
+
+  // Copy the pixels with converting color type.
+  SkImageInfo image_info = info.makeColorType(kN32_SkColorType);
+  return out->tryAllocPixels(image_info) &&
+         bitmap.readPixels(image_info, out->getPixels(), out->rowBytes(), 0, 0);
+}
+
 }  // namespace mojo
diff --git a/services/media_session/public/cpp/media_session_mojom_traits.h b/services/media_session/public/cpp/media_session_mojom_traits.h
index 9d82bab..aa9e067 100644
--- a/services/media_session/public/cpp/media_session_mojom_traits.h
+++ b/services/media_session/public/cpp/media_session_mojom_traits.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/containers/span.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 
 namespace mojo {
@@ -58,6 +59,19 @@
                    media_session::MediaMetadata* out);
 };
 
+// TODO(beccahughes): de-dupe this with ArcBitmap.
+template <>
+struct StructTraits<media_session::mojom::MediaImageBitmapDataView, SkBitmap> {
+  static const base::span<const uint8_t> pixel_data(const SkBitmap& r);
+  static int width(const SkBitmap& r) { return r.width(); }
+  static int height(const SkBitmap& r) { return r.height(); }
+
+  static bool Read(media_session::mojom::MediaImageBitmapDataView data,
+                   SkBitmap* out);
+
+  static bool IsNull(const SkBitmap& r) { return r.isNull(); }
+};
+
 }  // namespace mojo
 
 #endif  // SERVICES_MEDIA_SESSION_PUBLIC_CPP_MEDIA_SESSION_MOJOM_TRAITS_H_
diff --git a/services/media_session/public/cpp/test/mock_media_session.h b/services/media_session/public/cpp/test/mock_media_session.h
index 99d46e7..97d408a 100644
--- a/services/media_session/public/cpp/test/mock_media_session.h
+++ b/services/media_session/public/cpp/test/mock_media_session.h
@@ -120,9 +120,12 @@
   void SkipAd() override {}
   void Seek(base::TimeDelta seek_time) override;
   void Stop(SuspendType type) override;
+  void GetMediaImageBitmap(const MediaImage& image,
+                           int minimum_size_px,
+                           int desired_size_px,
+                           GetMediaImageBitmapCallback callback) override {}
 
   void SetIsControllable(bool value);
-
   void SetPreferStop(bool value) { prefer_stop_ = value; }
 
   void AbandonAudioFocusFromClient();
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom
index eb748ae..4be0ea8 100644
--- a/services/media_session/public/mojom/media_session.mojom
+++ b/services/media_session/public/mojom/media_session.mojom
@@ -56,6 +56,15 @@
   mojo_base.mojom.String16 source_title;
 };
 
+// A bitmap image. We use this instead of SkImage or skia.image.Bitmap so we can
+// use it in ARC and we do not use ArcBitmap because we need to use it in the
+// service.
+struct MediaImageBitmap {
+  int32 width;
+  int32 height;
+  array<uint8> pixel_data;  // Must be ARGB_8888
+};
+
 // Contains state information about a MediaSession.
 struct MediaSessionInfo {
   [Extensible]
@@ -129,7 +138,7 @@
 // WebContents or ARC app.
 // TODO(https://crbug.com/875004): migrate media session from content/public
 // to mojo.
-// Next Method ID: 12
+// Next Method ID: 13
 interface MediaSession {
   [Extensible]
   enum SuspendType {
@@ -186,4 +195,12 @@
 
   // Skip ad.
   SkipAd@11();
+
+  // Gets/Downloads the bitmap version of a MediaImage at least
+  // |minimum_size_px| and closest to |desired_size_px|. If the download
+  // failed, was too small or the image did not come from the media session
+  // then returns a null image.
+  GetMediaImageBitmap@12(
+      MediaImage image, int32 minimum_size_px, int32 desired_size_px)
+          => (MediaImageBitmap? image);
 };
diff --git a/services/network/public/cpp/net_ipc_param_traits.cc b/services/network/public/cpp/net_ipc_param_traits.cc
index 2863a92b..7556baa 100644
--- a/services/network/public/cpp/net_ipc_param_traits.cc
+++ b/services/network/public/cpp/net_ipc_param_traits.cc
@@ -164,6 +164,50 @@
   l->append(p.ToString());
 }
 
+void ParamTraits<net::IPEndPoint>::Write(base::Pickle* m, const param_type& p) {
+  WriteParam(m, p.address());
+  WriteParam(m, p.port());
+}
+
+bool ParamTraits<net::IPEndPoint>::Read(const base::Pickle* m,
+                                        base::PickleIterator* iter,
+                                        param_type* p) {
+  net::IPAddress address;
+  uint16_t port;
+  if (!ReadParam(m, iter, &address) || !ReadParam(m, iter, &port))
+    return false;
+
+  *p = net::IPEndPoint(address, port);
+  return true;
+}
+
+void ParamTraits<net::IPEndPoint>::Log(const param_type& p, std::string* l) {
+  LogParam("IPEndPoint:" + p.ToString(), l);
+}
+
+void ParamTraits<net::IPAddress>::Write(base::Pickle* m, const param_type& p) {
+  base::StackVector<uint8_t, 16> bytes;
+  for (uint8_t byte : p.bytes())
+    bytes->push_back(byte);
+  WriteParam(m, bytes);
+}
+
+bool ParamTraits<net::IPAddress>::Read(const base::Pickle* m,
+                                       base::PickleIterator* iter,
+                                       param_type* p) {
+  base::StackVector<uint8_t, 16> bytes;
+  if (!ReadParam(m, iter, &bytes))
+    return false;
+  if (bytes->size() > 16)
+    return false;
+  *p = net::IPAddress(bytes->data(), bytes->size());
+  return true;
+}
+
+void ParamTraits<net::IPAddress>::Log(const param_type& p, std::string* l) {
+  LogParam("IPAddress:" + (p.empty() ? "(empty)" : p.ToString()), l);
+}
+
 void ParamTraits<net::HttpRequestHeaders>::Write(base::Pickle* m,
                                                  const param_type& p) {
   WriteParam(m, static_cast<int>(p.GetHeaderVector().size()));
diff --git a/services/network/public/cpp/net_ipc_param_traits.h b/services/network/public/cpp/net_ipc_param_traits.h
index c52e6035..8db5f62c 100644
--- a/services/network/public/cpp/net_ipc_param_traits.h
+++ b/services/network/public/cpp/net_ipc_param_traits.h
@@ -13,6 +13,8 @@
 #include "ipc/param_traits_macros.h"
 #include "net/base/auth.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
 #include "net/cert/cert_verify_result.h"
@@ -104,6 +106,26 @@
 };
 
 template <>
+struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::IPEndPoint> {
+  typedef net::IPEndPoint param_type;
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* p);
+  static void Log(const param_type& p, std::string* l);
+};
+
+template <>
+struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::IPAddress> {
+  typedef net::IPAddress param_type;
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* p);
+  static void Log(const param_type& p, std::string* l);
+};
+
+template <>
 struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::HttpRequestHeaders> {
   typedef net::HttpRequestHeaders param_type;
   static void Write(base::Pickle* m, const param_type& p);
diff --git a/services/network/public/cpp/network_ipc_param_traits.h b/services/network/public/cpp/network_ipc_param_traits.h
index a6e7c30..059d735 100644
--- a/services/network/public/cpp/network_ipc_param_traits.h
+++ b/services/network/public/cpp/network_ipc_param_traits.h
@@ -12,7 +12,7 @@
 #include "ipc/ipc_param_traits.h"
 #include "ipc/param_traits_macros.h"
 #include "net/base/auth.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
 #include "net/cert/cert_verify_result.h"
@@ -158,7 +158,7 @@
   IPC_STRUCT_TRAITS_MEMBER(was_alternate_protocol_available)
   IPC_STRUCT_TRAITS_MEMBER(connection_info)
   IPC_STRUCT_TRAITS_MEMBER(alpn_negotiated_protocol)
-  IPC_STRUCT_TRAITS_MEMBER(socket_address)
+  IPC_STRUCT_TRAITS_MEMBER(remote_endpoint)
   IPC_STRUCT_TRAITS_MEMBER(was_fetched_via_cache)
   IPC_STRUCT_TRAITS_MEMBER(proxy_server)
   IPC_STRUCT_TRAITS_MEMBER(was_fetched_via_service_worker)
diff --git a/services/network/public/cpp/p2p_param_traits.cc b/services/network/public/cpp/p2p_param_traits.cc
index 37c6c5c..54990d1 100644
--- a/services/network/public/cpp/p2p_param_traits.cc
+++ b/services/network/public/cpp/p2p_param_traits.cc
@@ -5,60 +5,7 @@
 #include "services/network/public/cpp/p2p_param_traits.h"
 
 #include "ipc/ipc_message_utils.h"
-#include "net/base/ip_address.h"
-#include "net/base/ip_endpoint.h"
-
-namespace IPC {
-
-void ParamTraits<net::IPEndPoint>::Write(base::Pickle* m, const param_type& p) {
-  WriteParam(m, p.address());
-  WriteParam(m, p.port());
-}
-
-bool ParamTraits<net::IPEndPoint>::Read(const base::Pickle* m,
-                                        base::PickleIterator* iter,
-                                        param_type* p) {
-  net::IPAddress address;
-  uint16_t port;
-  if (!ReadParam(m, iter, &address) || !ReadParam(m, iter, &port))
-    return false;
-  if (!address.empty() && !address.IsValid())
-    return false;
-
-  *p = net::IPEndPoint(address, port);
-  return true;
-}
-
-void ParamTraits<net::IPEndPoint>::Log(const param_type& p, std::string* l) {
-  LogParam("IPEndPoint:" + p.ToString(), l);
-}
-
-void ParamTraits<net::IPAddress>::Write(base::Pickle* m, const param_type& p) {
-  base::StackVector<uint8_t, 16> bytes;
-  for (uint8_t byte : p.bytes())
-    bytes->push_back(byte);
-  WriteParam(m, bytes);
-}
-
-bool ParamTraits<net::IPAddress>::Read(const base::Pickle* m,
-                                       base::PickleIterator* iter,
-                                       param_type* p) {
-  base::StackVector<uint8_t, 16> bytes;
-  if (!ReadParam(m, iter, &bytes))
-    return false;
-  if (bytes->size() && bytes->size() != net::IPAddress::kIPv4AddressSize &&
-      bytes->size() != net::IPAddress::kIPv6AddressSize) {
-    return false;
-  }
-  *p = net::IPAddress(bytes->data(), bytes->size());
-  return true;
-}
-
-void ParamTraits<net::IPAddress>::Log(const param_type& p, std::string* l) {
-  LogParam("IPAddress:" + (p.empty() ? "(empty)" : p.ToString()), l);
-}
-
-}  // namespace IPC
+#include "services/network/public/cpp/net_ipc_param_traits.h"
 
 // Generation of IPC definitions.
 
diff --git a/services/network/public/cpp/p2p_param_traits.h b/services/network/public/cpp/p2p_param_traits.h
index 7d1fe868..e236d37 100644
--- a/services/network/public/cpp/p2p_param_traits.h
+++ b/services/network/public/cpp/p2p_param_traits.h
@@ -25,35 +25,6 @@
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT COMPONENT_EXPORT(NETWORK_CPP_BASE)
 
-namespace net {
-class IPAddress;
-class IPEndPoint;
-}  // namespace net
-
-namespace IPC {
-
-template <>
-struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::IPEndPoint> {
-  typedef net::IPEndPoint param_type;
-  static void Write(base::Pickle* m, const param_type& p);
-  static bool Read(const base::Pickle* m,
-                   base::PickleIterator* iter,
-                   param_type* p);
-  static void Log(const param_type& p, std::string* l);
-};
-
-template <>
-struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::IPAddress> {
-  typedef net::IPAddress param_type;
-  static void Write(base::Pickle* m, const param_type& p);
-  static bool Read(const base::Pickle* m,
-                   base::PickleIterator* iter,
-                   param_type* p);
-  static void Log(const param_type& p, std::string* l);
-};
-
-}  // namespace IPC
-
 #endif  // INTERNAL_SERVICES_NETWORK_PUBLIC_CPP_P2P_PARAM_TRAITS_H_
 
 IPC_ENUM_TRAITS_MAX_VALUE(network::P2PSocketType, network::P2P_SOCKET_TYPE_LAST)
diff --git a/services/network/public/cpp/resource_response.cc b/services/network/public/cpp/resource_response.cc
index e39fbb9e..3178a62 100644
--- a/services/network/public/cpp/resource_response.cc
+++ b/services/network/public/cpp/resource_response.cc
@@ -36,7 +36,7 @@
       head.was_alternate_protocol_available;
   new_response->head.connection_info = head.connection_info;
   new_response->head.alpn_negotiated_protocol = head.alpn_negotiated_protocol;
-  new_response->head.socket_address = head.socket_address;
+  new_response->head.remote_endpoint = head.remote_endpoint;
   new_response->head.was_fetched_via_cache = head.was_fetched_via_cache;
   new_response->head.proxy_server = head.proxy_server;
   new_response->head.was_fetched_via_service_worker =
diff --git a/services/network/public/cpp/resource_response_info.h b/services/network/public/cpp/resource_response_info.h
index 0f4df4b..344a6fa 100644
--- a/services/network/public/cpp/resource_response_info.h
+++ b/services/network/public/cpp/resource_response_info.h
@@ -13,7 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
-#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/proxy_server.h"
 #include "net/cert/ct_policy_status.h"
@@ -103,7 +103,7 @@
   std::string alpn_negotiated_protocol;
 
   // Remote address of the socket which fetched this resource.
-  net::HostPortPair socket_address;
+  net::IPEndPoint remote_endpoint;
 
   // True if the response came from cache.
   bool was_fetched_via_cache = false;
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index d128bcec..ab4ceb8 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -58,6 +58,7 @@
   ]
 
   public_deps = [
+    ":mojom_ip_address",
     "//url/mojom:url_mojom_gurl",
     "//url/mojom:url_mojom_origin",
   ]
diff --git a/services/network/public/mojom/websocket.mojom b/services/network/public/mojom/websocket.mojom
index bc9bde7..b1e6952 100644
--- a/services/network/public/mojom/websocket.mojom
+++ b/services/network/public/mojom/websocket.mojom
@@ -6,6 +6,7 @@
 
 import "url/mojom/url.mojom";
 import "services/network/public/mojom/network_param.mojom";
+import "services/network/public/mojom/ip_endpoint.mojom";
 
 enum WebSocketMessageType {
   CONTINUATION,
@@ -33,7 +34,7 @@
   HttpVersion http_version;
   int32 status_code;
   string status_text;
-  HostPortPair socket_address;
+  IPEndPoint remote_endpoint;
   array<HttpHeader> headers;
   string headers_text;
 };
@@ -45,7 +46,7 @@
   // authentication.
   OnAuthRequired(AuthChallengeInfo info,
                  HttpResponseHeaders headers,
-                 HostPortPair socket_address) => (AuthCredentials? credentials);
+                 IPEndPoint remote_endpoint) => (AuthCredentials? credentials);
 };
 
 interface WebSocketClient {
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 703072d4..06909a0 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -22,6 +22,7 @@
 #include "base/time/time.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/base/elements_upload_data_stream.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/mime_sniffer.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_file_element_reader.h"
@@ -73,7 +74,7 @@
   response->head.alpn_negotiated_protocol =
       response_info.alpn_negotiated_protocol;
   response->head.connection_info = response_info.connection_info;
-  response->head.socket_address = response_info.socket_address;
+  response->head.remote_endpoint = response_info.remote_endpoint;
   response->head.was_fetched_via_cache = request->was_cached();
   response->head.proxy_server = request->proxy_server();
   response->head.network_accessed = response_info.network_accessed;
diff --git a/services/network/websocket.cc b/services/network/websocket.cc
index 16c598c..218637a 100644
--- a/services/network/websocket.cc
+++ b/services/network/websocket.cc
@@ -20,6 +20,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/auth.h"
 #include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
@@ -104,7 +105,7 @@
   int OnAuthRequired(
       scoped_refptr<net::AuthChallengeInfo> auth_info,
       scoped_refptr<net::HttpResponseHeaders> response_headers,
-      const net::HostPortPair& host_port_pair,
+      const net::IPEndPoint& remote_endpoint,
       base::OnceCallback<void(const net::AuthCredentials*)> callback,
       base::Optional<net::AuthCredentials>* credentials) override;
 
@@ -250,7 +251,7 @@
   response_to_pass->status_code = response->headers->response_code();
   response_to_pass->status_text = response->headers->GetStatusText();
   response_to_pass->http_version = response->headers->GetHttpVersion();
-  response_to_pass->socket_address = response->socket_address;
+  response_to_pass->remote_endpoint = response->remote_endpoint;
   size_t iter = 0;
   std::string name, value;
   std::string headers_text =
@@ -286,7 +287,7 @@
 int WebSocket::WebSocketEventHandler::OnAuthRequired(
     scoped_refptr<net::AuthChallengeInfo> auth_info,
     scoped_refptr<net::HttpResponseHeaders> response_headers,
-    const net::HostPortPair& host_port_pair,
+    const net::IPEndPoint& remote_endpoint,
     base::OnceCallback<void(const net::AuthCredentials*)> callback,
     base::Optional<net::AuthCredentials>* credentials) {
   DVLOG(3) << "WebSocketEventHandler::OnAuthRequired"
@@ -297,7 +298,7 @@
   }
 
   impl_->auth_handler_->OnAuthRequired(
-      std::move(auth_info), std::move(response_headers), host_port_pair,
+      std::move(auth_info), std::move(response_headers), remote_endpoint,
       base::BindOnce(&WebSocket::OnAuthRequiredComplete,
                      impl_->weak_ptr_factory_.GetWeakPtr(),
                      std::move(callback)));
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
index 5f98984..4b104adf 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
@@ -7,7 +7,7 @@
 #include <stdint.h>
 #include <memory>
 
-#include "base/debug/elf_reader_linux.h"
+#include "base/debug/elf_reader.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
 #include "base/format_macros.h"
@@ -69,11 +69,12 @@
   ModuleData module_data;
   Dl_info dl_info;
   if (dladdr(&__ehdr_start, &dl_info)) {
-    base::Optional<std::string> build_id =
-        base::debug::ReadElfBuildId(&__ehdr_start);
-    if (build_id) {
+    base::debug::ElfBuildIdBuffer build_id;
+    size_t build_id_length =
+        base::debug::ReadElfBuildId(&__ehdr_start, true, build_id);
+    if (build_id_length) {
       module_data.path = dl_info.dli_fname;
-      module_data.build_id = *build_id;
+      module_data.build_id = std::string(build_id, build_id_length);
     }
   }
   return module_data;
diff --git a/services/tracing/agent_registry.cc b/services/tracing/agent_registry.cc
index 759aacc7..f1e4764 100644
--- a/services/tracing/agent_registry.cc
+++ b/services/tracing/agent_registry.cc
@@ -16,6 +16,8 @@
 
 namespace tracing {
 
+const int32_t kAgentResponseTimeoutInSeconds = 10;
+
 AgentRegistry::AgentEntry::AgentEntry(size_t id,
                                       AgentRegistry* agent_registry,
                                       mojom::AgentPtr agent,
@@ -40,10 +42,17 @@
     base::OnceClosure closure) {
   DCHECK_EQ(0u, closures_.count(closure_name));
   closures_[closure_name] = std::move(closure);
+
+  // Adding a disconnect closure means we're waiting for a response from the
+  // agent. If the client becomes unresponsive, we disconnect it.
+  timer_.Start(FROM_HERE,
+               base::TimeDelta::FromSeconds(kAgentResponseTimeoutInSeconds),
+               this, &AgentRegistry::AgentEntry::OnConnectionError);
 }
 
 bool AgentRegistry::AgentEntry::RemoveDisconnectClosure(
     const void* closure_name) {
+  timer_.Stop();
   return closures_.erase(closure_name) > 0;
 }
 
diff --git a/services/tracing/agent_registry.h b/services/tracing/agent_registry.h
index c2b4150..cb701f3 100644
--- a/services/tracing/agent_registry.h
+++ b/services/tracing/agent_registry.h
@@ -12,6 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/service_manager/public/cpp/identity.h"
 #include "services/tracing/public/mojom/tracing.mojom.h"
@@ -53,6 +54,7 @@
     const mojom::TraceDataType type_;
     const base::ProcessId pid_;
     std::map<const void*, base::OnceClosure> closures_;
+    base::RepeatingTimer timer_;
 
     DISALLOW_COPY_AND_ASSIGN(AgentEntry);
   };
diff --git a/services/tracing/coordinator.cc b/services/tracing/coordinator.cc
index 821f0de4..b426897 100644
--- a/services/tracing/coordinator.cc
+++ b/services/tracing/coordinator.cc
@@ -37,11 +37,18 @@
 const char kMetadataTraceLabel[] = "metadata";
 
 const char kRequestBufferUsageClosureName[] = "RequestBufferUsageClosure";
+const char kStartTracingClosureName[] = "StartTracingClosure";
+const int32_t kBeginTracingTimeoutInSeconds = 10;
 
 }  // namespace
 
 namespace tracing {
 
+// static
+const void* Coordinator::GetStartTracingClosureName() {
+  return &kStartTracingClosureName;
+}
+
 class Coordinator::TraceStreamer : public base::SupportsWeakPtr<TraceStreamer> {
  public:
   // Constructed on |main_task_runner_|.
@@ -269,9 +276,9 @@
 
 Coordinator::Coordinator(AgentRegistry* agent_registry,
                          const base::RepeatingClosure& on_disconnect_callback)
-    : on_disconnect_callback_(std::move(on_disconnect_callback)),
+    : task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      on_disconnect_callback_(std::move(on_disconnect_callback)),
       binding_(this),
-      task_runner_(base::SequencedTaskRunnerHandle::Get()),
       // USER_VISIBLE because the task posted from StopAndFlushInternal() is
       // required to stop tracing from the UI.
       // TODO(fdoray): Once we have support for dynamic priorities
@@ -294,10 +301,14 @@
 }
 
 void Coordinator::Reset() {
+  start_tracing_callback_timer_.Stop();
+
   if (!stop_and_flush_callback_.is_null()) {
     base::ResetAndReturn(&stop_and_flush_callback_)
         .Run(base::Value(base::Value::Type::DICTIONARY));
   }
+  if (!start_tracing_callback_.is_null())
+    base::ResetAndReturn(&start_tracing_callback_).Run(false);
   if (!request_buffer_usage_callback_.is_null())
     base::ResetAndReturn(&request_buffer_usage_callback_).Run(false, 0, 0);
 
@@ -326,8 +337,11 @@
       &Coordinator::OnClientConnectionError, base::Unretained(this)));
 }
 
-void Coordinator::StartTracing(const std::string& config) {
-  if ((is_tracing_ && config == config_)) {
+void Coordinator::StartTracing(const std::string& config,
+                               StartTracingCallback callback) {
+  bool is_initializing = !start_tracing_callback_.is_null();
+  if (is_initializing || (is_tracing_ && config == config_)) {
+    std::move(callback).Run(config == config_);
     return;
   }
 
@@ -338,13 +352,113 @@
       base::BindRepeating(&Coordinator::SendStartTracingToAgent,
                           weak_ptr_factory_.GetWeakPtr()),
       false /* call_on_new_agents_only */);
+
+  SetStartTracingCallback(std::move(callback));
+}
+
+void Coordinator::SetStartTracingCallback(StartTracingCallback callback) {
+  start_tracing_callback_ = std::move(callback);
+  CallStartTracingCallbackIfNeeded();
+
+  if (!start_tracing_callback_)
+    return;
+
+  // We can't know for sure whether all processses we request
+  // to connect to the tracing service will connect back, or
+  // if all the connected services will ACK our BeginTracing
+  // request eventually, so we'll add a timeout for that case.
+  start_tracing_callback_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromSeconds(kBeginTracingTimeoutInSeconds),
+      this, &Coordinator::OnBeginTracingTimeout);
+}
+
+void Coordinator::OnBeginTracingTimeout() {
+  if (start_tracing_callback_)
+    std::move(start_tracing_callback_).Run(true);
+}
+
+void Coordinator::CallStartTracingCallbackIfNeeded() {
+  // We still have processes we've asked to begin tracing and
+  // need ACKs from.
+  if (agent_registry_->HasDisconnectClosure(&kStartTracingClosureName))
+    return;
+
+  // We're still waiting for the list of PIDs of the currently
+  // running services.
+  if (pending_currently_running_pids_)
+    return;
+
+  // We're still waiting for processes to connect to the
+  // service.
+  for (auto& pid : pending_connected_pids_) {
+    if (parsed_config_.process_filter_config().IsEnabled(pid))
+      return;
+  }
+
+  if (start_tracing_callback_)
+    std::move(start_tracing_callback_).Run(true);
+
+  start_tracing_callback_timer_.Stop();
+}
+
+void Coordinator::AddExpectedPID(base::ProcessId pid) {
+  if (!pid)
+    return;
+
+  bool pid_is_connected = false;
+  agent_registry_->ForAllAgents(
+      [&pid_is_connected, pid](AgentRegistry::AgentEntry* agent_entry) {
+        if (pid == agent_entry->pid())
+          pid_is_connected = true;
+      });
+
+  if (!pid_is_connected)
+    pending_connected_pids_.insert(pid);
+}
+
+void Coordinator::RemoveExpectedPID(base::ProcessId pid) {
+  pending_connected_pids_.erase(pid);
+  CallStartTracingCallbackIfNeeded();
+}
+
+void Coordinator::FinishedReceivingRunningPIDs() {
+  pending_currently_running_pids_ = false;
+  CallStartTracingCallbackIfNeeded();
+}
+
+void Coordinator::ClearConnectedPIDs() {
+  if (!pending_connected_pids_.empty()) {
+    pending_connected_pids_.clear();
+    CallStartTracingCallbackIfNeeded();
+  }
 }
 
 void Coordinator::SendStartTracingToAgent(
     AgentRegistry::AgentEntry* agent_entry) {
+  if (agent_entry->HasDisconnectClosure(&kStartTracingClosureName))
+    return;
   if (!parsed_config_.process_filter_config().IsEnabled(agent_entry->pid()))
     return;
-  agent_entry->agent()->StartTracing(config_, TRACE_TIME_TICKS_NOW());
+  agent_entry->AddDisconnectClosure(
+      &kStartTracingClosureName,
+      base::BindOnce(&Coordinator::OnTracingStarted,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     base::Unretained(agent_entry), false));
+  RemoveExpectedPID(agent_entry->pid());
+
+  agent_entry->agent()->StartTracing(
+      config_, TRACE_TIME_TICKS_NOW(),
+      base::BindRepeating(&Coordinator::OnTracingStarted,
+                          weak_ptr_factory_.GetWeakPtr(),
+                          base::Unretained(agent_entry)));
+}
+
+void Coordinator::OnTracingStarted(AgentRegistry::AgentEntry* agent_entry,
+                                   bool success) {
+  bool removed =
+      agent_entry->RemoveDisconnectClosure(&kStartTracingClosureName);
+  DCHECK(removed);
+  CallStartTracingCallbackIfNeeded();
 }
 
 void Coordinator::StopAndFlush(mojo::ScopedDataPipeProducerHandle stream,
@@ -363,6 +477,12 @@
   DCHECK(!trace_streamer_);
   DCHECK(stream.is_valid());
   is_tracing_ = false;
+  // If we get a Stop call and this is non-empty, it means we either
+  // hit a timeout waiting for processes to connect, or the trace
+  // client sent a stop without waiting for the BeginTracing callback
+  // to happen. In either case, we don't want/need to wait for additional
+  // processes to connect anymore.
+  ClearConnectedPIDs();
 
   trace_streamer_.reset(new Coordinator::TraceStreamer(
       std::move(stream), agent_label, task_runner_,
@@ -372,6 +492,18 @@
 }
 
 void Coordinator::StopAndFlushInternal() {
+  if (start_tracing_callback_) {
+    // We received a |StopAndFlush| command before receiving |StartTracing| acks
+    // from all agents. Let's retry after a delay.
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::BindRepeating(&Coordinator::StopAndFlushInternal,
+                            weak_ptr_factory_.GetWeakPtr()),
+        base::TimeDelta::FromMilliseconds(
+            mojom::kStopTracingRetryTimeMilliseconds));
+    return;
+  }
+
   size_t num_initialized_agents =
       agent_registry_->SetAgentInitializationCallback(
           base::BindRepeating(&Coordinator::SendStopTracingToAgent,
diff --git a/services/tracing/coordinator.h b/services/tracing/coordinator.h
index 1bd8890..50b5381 100644
--- a/services/tracing/coordinator.h
+++ b/services/tracing/coordinator.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
+#include "base/timer/timer.h"
 #include "base/trace_event/trace_config.h"
 #include "base/values.h"
 #include "mojo/public/cpp/system/data_pipe.h"
@@ -50,10 +51,24 @@
 
   bool IsConnected();
 
+  void AddExpectedPID(base::ProcessId pid);
+  void RemoveExpectedPID(base::ProcessId pid);
+  void FinishedReceivingRunningPIDs();
+
  protected:
   ~Coordinator() override;
 
+  static const void* GetStartTracingClosureName();
+
   virtual void OnClientConnectionError();
+  void CallStartTracingCallbackIfNeeded();
+  void OnBeginTracingTimeout();
+  void SetStartTracingCallback(StartTracingCallback callback);
+  void ClearConnectedPIDs();
+
+  base::trace_event::TraceConfig parsed_config_;
+  StartTracingCallback start_tracing_callback_;
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
  private:
   friend std::default_delete<Coordinator>;
@@ -64,7 +79,8 @@
   void Reset();
 
   // mojom::Coordinator
-  void StartTracing(const std::string& config) override;
+  void StartTracing(const std::string& config,
+                    StartTracingCallback callback) override;
   void StopAndFlush(mojo::ScopedDataPipeProducerHandle stream,
                     StopAndFlushCallback callback) override;
   void StopAndFlushAgent(mojo::ScopedDataPipeProducerHandle stream,
@@ -75,6 +91,7 @@
 
   // Internal methods for collecting events from agents.
   void SendStartTracingToAgent(AgentRegistry::AgentEntry* agent_entry);
+  void OnTracingStarted(AgentRegistry::AgentEntry* agent_entry, bool success);
   void StopAndFlushInternal();
   void SendStopTracingToAgent(AgentRegistry::AgentEntry* agent_entry);
   void SendStopTracingWithNoOpRecorderToAgent(
@@ -89,14 +106,15 @@
 
   base::RepeatingClosure on_disconnect_callback_;
   mojo::Binding<mojom::Coordinator> binding_;
-  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
   const scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
   AgentRegistry* agent_registry_;
   std::string config_;
-  base::trace_event::TraceConfig parsed_config_;
   bool is_tracing_ = false;
 
   std::unique_ptr<TraceStreamer> trace_streamer_;
+  std::set<base::ProcessId> pending_connected_pids_;
+  bool pending_currently_running_pids_ = true;
+  base::OneShotTimer start_tracing_callback_timer_;
   StopAndFlushCallback stop_and_flush_callback_;
 
   // For computing trace buffer usage.
diff --git a/services/tracing/coordinator_test_util.cc b/services/tracing/coordinator_test_util.cc
index edac52b..41bdee5 100644
--- a/services/tracing/coordinator_test_util.cc
+++ b/services/tracing/coordinator_test_util.cc
@@ -24,6 +24,7 @@
 void CoordinatorTestUtil::SetUp() {
   agent_registry_ = std::make_unique<AgentRegistry>();
   output_ = "";
+  tracing_begin_callback_received_ = false;
 }
 
 // testing::Test
@@ -73,9 +74,18 @@
   return agents_.back().get();
 }
 
-void CoordinatorTestUtil::StartTracing(std::string config) {
+void CoordinatorTestUtil::StartTracing(std::string config,
+                                       bool expected_response) {
   wait_for_data_closure_ = tracing_loop_.QuitClosure();
-  coordinator_->StartTracing(config);
+
+  coordinator_->StartTracing(
+      config, base::BindRepeating(
+                  [](bool expected, bool* tracing_begin_callback_received,
+                     bool actual) {
+                    EXPECT_EQ(expected, actual);
+                    *tracing_begin_callback_received = true;
+                  },
+                  expected_response, &tracing_begin_callback_received_));
 }
 
 std::string CoordinatorTestUtil::StopAndFlush() {
diff --git a/services/tracing/coordinator_test_util.h b/services/tracing/coordinator_test_util.h
index 5aaf9ff..21c08625 100644
--- a/services/tracing/coordinator_test_util.h
+++ b/services/tracing/coordinator_test_util.h
@@ -39,7 +39,7 @@
   MockAgent* AddObjectAgent();
   MockAgent* AddStringAgent();
 
-  void StartTracing(std::string config);
+  void StartTracing(std::string config, bool expected_response);
   std::string StopAndFlush();
 
   void IsTracing(bool expected_response);
@@ -49,6 +49,9 @@
   void CheckDisconnectClosures(size_t num_agents);
 
   AgentRegistry* agent_registry() { return agent_registry_.get(); }
+  bool tracing_begin_callback_received() const {
+    return tracing_begin_callback_received_;
+  }
 
  protected:
   std::unique_ptr<Coordinator> coordinator_;
@@ -63,6 +66,7 @@
   base::RunLoop tracing_loop_;
   base::RepeatingClosure wait_for_data_closure_;
   std::string output_;
+  bool tracing_begin_callback_received_ = false;
 };
 
 }  // namespace tracing
diff --git a/services/tracing/coordinator_unittest.cc b/services/tracing/coordinator_unittest.cc
index 9d91e910..beb9bca 100644
--- a/services/tracing/coordinator_unittest.cc
+++ b/services/tracing/coordinator_unittest.cc
@@ -19,6 +19,7 @@
     CoordinatorTestUtil::SetUp();
     coordinator_ = std::make_unique<Coordinator>(agent_registry(),
                                                  base::RepeatingClosure());
+    coordinator_->FinishedReceivingRunningPIDs();
   }
   void TearDown() override { CoordinatorTestUtil::TearDown(); }
 };
@@ -26,7 +27,7 @@
 TEST_F(CoordinatorTest, StartTracingSimple) {
   base::RunLoop run_loop;
   auto* agent = AddArrayAgent();
-  StartTracing("*");
+  StartTracing("*", true);
   run_loop.RunUntilIdle();
 
   // The agent should have received exactly one call from the coordinator.
@@ -37,7 +38,7 @@
 TEST_F(CoordinatorTest, StartTracingTwoAgents) {
   base::RunLoop run_loop;
   auto* agent1 = AddArrayAgent();
-  StartTracing("*");
+  StartTracing("*", true);
   auto* agent2 = AddStringAgent();
   run_loop.RunUntilIdle();
 
@@ -52,13 +53,13 @@
   base::RunLoop run_loop1;
   auto* agent1 = AddArrayAgent(static_cast<base::ProcessId>(1));
   auto* agent2 = AddArrayAgent(static_cast<base::ProcessId>(2));
-  StartTracing("{\"included_process_ids\":[2,4]}");
+  StartTracing("{\"included_process_ids\":[2,4]}", true);
   run_loop1.RunUntilIdle();
 
   base::RunLoop run_loop2;
   auto* agent3 = AddArrayAgent(static_cast<base::ProcessId>(3));
   auto* agent4 = AddArrayAgent(static_cast<base::ProcessId>(4));
-  StartTracing("{\"included_process_ids\":[4,6]}");
+  StartTracing("{\"included_process_ids\":[4,6]}", true);
   run_loop2.RunUntilIdle();
 
   base::RunLoop run_loop3;
@@ -67,27 +68,38 @@
   run_loop3.RunUntilIdle();
 
   // StartTracing should only be received by agents 2, 4, and 6.
-  // Agent 4 should receive StartTracing twice, as it's
-  // included in both configs.
   EXPECT_EQ(0u, agent1->call_stat().size());
   EXPECT_EQ(1u, agent2->call_stat().size());
   EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
   EXPECT_EQ(0u, agent3->call_stat().size());
-  EXPECT_EQ(2u, agent4->call_stat().size());
+  EXPECT_EQ(1u, agent4->call_stat().size());
   EXPECT_EQ("StartTracing", agent4->call_stat()[0]);
-  EXPECT_EQ("StartTracing", agent4->call_stat()[1]);
   EXPECT_EQ(0u, agent5->call_stat().size());
   EXPECT_EQ(1u, agent6->call_stat().size());
   EXPECT_EQ("StartTracing", agent6->call_stat()[0]);
 }
 
+TEST_F(CoordinatorTest, StartTracingWithDifferentConfigs) {
+  base::RunLoop run_loop;
+  auto* agent = AddArrayAgent();
+  StartTracing("config 1", true);
+  // The 2nd |StartTracing| should return false.
+  StartTracing("config 2", false);
+  run_loop.RunUntilIdle();
+
+  // The agent should have received exactly one call from the coordinator
+  // because the 2nd |StartTracing| was aborted.
+  EXPECT_EQ(1u, agent->call_stat().size());
+  EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+}
+
 TEST_F(CoordinatorTest, StartTracingWithSameConfigs) {
   base::RunLoop run_loop;
   auto* agent = AddArrayAgent();
-  StartTracing("config");
-  // The 2nd |StartTracing| should succeed when we are not trying to change
+  StartTracing("config", true);
+  // The 2nd |StartTracing| should return true when we are not trying to change
   // the config.
-  StartTracing("config");
+  StartTracing("config", true);
   run_loop.RunUntilIdle();
 
   // The agent should have received exactly one call from the coordinator
@@ -101,7 +113,7 @@
   agent->data_.push_back("\"content\":{\"a\":1}");
   agent->data_.push_back("\"name\":\"etw\"");
 
-  StartTracing("config");
+  StartTracing("config", true);
   std::string output = StopAndFlush();
 
   EXPECT_EQ("{\"systemTraceEvents\":{\"content\":{\"a\":1},\"name\":\"etw\"}}",
@@ -122,7 +134,7 @@
   agent2->data_.push_back("e3");
   agent2->data_.push_back("e4");
 
-  StartTracing("config");
+  StartTracing("config", true);
   std::string output = StopAndFlush();
 
   // |output| should be of the form {"traceEvents":[ei,ej,ek,el]}, where
@@ -156,7 +168,7 @@
   agent2->data_.push_back("e3");
   agent2->data_.push_back("e4");
 
-  StartTracing("config");
+  StartTracing("config", true);
   std::string output = StopAndFlush();
 
   EXPECT_TRUE(output == "{\"traceEvents\":[e1,e2],\"power\":\"e3e4\"}" ||
@@ -177,7 +189,7 @@
   agent->data_.push_back("event");
   agent->metadata_.SetString("key", "value");
 
-  StartTracing("config");
+  StartTracing("config", true);
   std::string output = StopAndFlush();
 
   // Metadata is written at after trace data.
@@ -191,7 +203,7 @@
 TEST_F(CoordinatorTest, IsTracing) {
   base::RunLoop run_loop;
   AddArrayAgent();
-  StartTracing("config");
+  StartTracing("config", true);
   IsTracing(true);
   run_loop.RunUntilIdle();
 }
@@ -238,7 +250,8 @@
 
 TEST_F(CoordinatorTest, LateAgents) {
   auto* agent1 = AddArrayAgent();
-  StartTracing("config");
+
+  StartTracing("config", true);
   StopAndFlush();
 
   base::RunLoop run_loop;
@@ -256,4 +269,31 @@
   EXPECT_EQ("StopAndFlush", agent2->call_stat()[0]);
 }
 
+TEST_F(CoordinatorTest, WaitForSpecificPIDs) {
+  coordinator_->AddExpectedPID(42);
+  coordinator_->AddExpectedPID(4242);
+
+  auto* agent1 = AddArrayAgent(42);
+  StartTracing("config", true);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(tracing_begin_callback_received());
+
+  auto* agent2 = AddArrayAgent(4242);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(tracing_begin_callback_received());
+
+  StopAndFlush();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2u, agent1->call_stat().size());
+  EXPECT_EQ("StartTracing", agent1->call_stat()[0]);
+  EXPECT_EQ("StopAndFlush", agent1->call_stat()[1]);
+
+  EXPECT_EQ(2u, agent2->call_stat().size());
+  EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
+  EXPECT_EQ("StopAndFlush", agent2->call_stat()[1]);
+}
+
 }  // namespace tracing
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.cc b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
index f159631..cacdcd1 100644
--- a/services/tracing/perfetto/perfetto_tracing_coordinator.cc
+++ b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
@@ -14,6 +14,7 @@
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "services/tracing/perfetto/json_trace_exporter.h"
 #include "services/tracing/perfetto/perfetto_service.h"
+#include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/consumer.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
@@ -197,6 +198,11 @@
       binding_(this),
       weak_factory_(this) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
+
+  agent_registry->SetAgentInitializationCallback(
+      base::BindRepeating(&PerfettoTracingCoordinator::OnNewAgentConnected,
+                          weak_factory_.GetWeakPtr()),
+      false /* call_on_new_agents_only */);
 }
 
 PerfettoTracingCoordinator::~PerfettoTracingCoordinator() {
@@ -219,11 +225,46 @@
                      base::Unretained(this)));
 }
 
-void PerfettoTracingCoordinator::StartTracing(const std::string& config) {
+void PerfettoTracingCoordinator::StartTracing(const std::string& config,
+                                              StartTracingCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  parsed_config_ = base::trace_event::TraceConfig(config);
   tracing_session_ = std::make_unique<TracingSession>(
       config, base::BindOnce(&PerfettoTracingCoordinator::OnTracingOverCallback,
                              weak_factory_.GetWeakPtr()));
+
+  SetStartTracingCallback(std::move(callback));
+}
+
+void PerfettoTracingCoordinator::OnNewAgentConnected(
+    AgentRegistry::AgentEntry* agent_entry) {
+  // TODO(oysteine): While we're still using the Agent
+  // system as a fallback when using Perfetto, rather than
+  // the browser directly using a Consumer interface, we have to
+  // attempt to linearize with newly connected agents so we only
+  // call the BeginTracing callback when we can be fairly sure
+  // that all current agents have registered with Perfetto and
+  // started tracing if requested. We do this linearization
+  // by calling RequestBufferStatus but just throwing away the
+  // result.
+  auto closure = base::BindRepeating(
+      [](base::WeakPtr<PerfettoTracingCoordinator> coordinator,
+         AgentRegistry::AgentEntry* agent_entry, uint32_t capacity,
+         uint32_t count) {
+        bool removed =
+            agent_entry->RemoveDisconnectClosure(GetStartTracingClosureName());
+        DCHECK(removed);
+
+        if (coordinator) {
+          coordinator->RemoveExpectedPID(agent_entry->pid());
+        }
+      },
+      weak_factory_.GetWeakPtr(), base::Unretained(agent_entry));
+
+  agent_entry->AddDisconnectClosure(GetStartTracingClosureName(),
+                                    base::BindOnce(closure, 0, 0));
+
+  agent_entry->agent()->RequestBufferStatus(closure);
 }
 
 void PerfettoTracingCoordinator::OnTracingOverCallback() {
@@ -235,8 +276,25 @@
 void PerfettoTracingCoordinator::StopAndFlush(
     mojo::ScopedDataPipeProducerHandle stream,
     StopAndFlushCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(tracing_session_);
+  StopAndFlushAgent(std::move(stream), "", std::move(callback));
+}
+
+void PerfettoTracingCoordinator::StopAndFlushInternal(
+    mojo::ScopedDataPipeProducerHandle stream,
+    StopAndFlushCallback callback) {
+  if (start_tracing_callback_) {
+    // We received a |StopAndFlush| command before receiving |StartTracing| acks
+    // from all agents. Let's retry after a delay.
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&PerfettoTracingCoordinator::StopAndFlushInternal,
+                       weak_factory_.GetWeakPtr(), std::move(stream),
+                       std::move(callback)),
+        base::TimeDelta::FromMilliseconds(
+            mojom::kStopTracingRetryTimeMilliseconds));
+    return;
+  }
+
   tracing_session_->StopAndFlush(std::move(stream), std::move(callback));
 }
 
@@ -244,7 +302,11 @@
     mojo::ScopedDataPipeProducerHandle stream,
     const std::string& agent_label,
     StopAndFlushCallback callback) {
-  NOTREACHED();
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(tracing_session_);
+
+  ClearConnectedPIDs();
+  StopAndFlushInternal(std::move(stream), std::move(callback));
 }
 
 void PerfettoTracingCoordinator::IsTracing(IsTracingCallback callback) {
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.h b/services/tracing/perfetto/perfetto_tracing_coordinator.h
index 6db0495..554346b 100644
--- a/services/tracing/perfetto/perfetto_tracing_coordinator.h
+++ b/services/tracing/perfetto/perfetto_tracing_coordinator.h
@@ -37,7 +37,8 @@
 
   // mojom::Coordinator implementation.
   // Called by the tracing controller.
-  void StartTracing(const std::string& config) override;
+  void StartTracing(const std::string& config,
+                    StartTracingCallback callback) override;
   void StopAndFlush(mojo::ScopedDataPipeProducerHandle stream,
                     StopAndFlushCallback callback) override;
   void StopAndFlushAgent(mojo::ScopedDataPipeProducerHandle stream,
@@ -49,6 +50,9 @@
  private:
   void OnTracingOverCallback();
   void OnClientConnectionError() override;
+  void OnNewAgentConnected(AgentRegistry::AgentEntry* agent_entry);
+  void StopAndFlushInternal(mojo::ScopedDataPipeProducerHandle stream,
+                            StopAndFlushCallback callback);
 
   mojo::Binding<mojom::Coordinator> binding_;
 
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator_unittest.cc b/services/tracing/perfetto/perfetto_tracing_coordinator_unittest.cc
index 6f6f4e7..ac471fd 100644
--- a/services/tracing/perfetto/perfetto_tracing_coordinator_unittest.cc
+++ b/services/tracing/perfetto/perfetto_tracing_coordinator_unittest.cc
@@ -18,6 +18,7 @@
     CoordinatorTestUtil::SetUp();
     coordinator_ = std::make_unique<PerfettoTracingCoordinator>(
         agent_registry(), base::RepeatingClosure());
+    coordinator_->FinishedReceivingRunningPIDs();
   }
   void TearDown() override { CoordinatorTestUtil::TearDown(); }
 };
@@ -26,7 +27,7 @@
   auto* agent = AddArrayAgent();
   agent->data_.push_back("e1");
 
-  StartTracing("{\"trace_buffer_size_in_kb\":4}");
+  StartTracing("{\"trace_buffer_size_in_kb\":4}", true);
   std::string output = StopAndFlush();
 
   auto json_value = base::JSONReader::Read(output);
diff --git a/services/tracing/public/cpp/base_agent.cc b/services/tracing/public/cpp/base_agent.cc
index 0e14ac9..d31c1a9b2 100644
--- a/services/tracing/public/cpp/base_agent.cc
+++ b/services/tracing/public/cpp/base_agent.cc
@@ -39,7 +39,10 @@
 }
 
 void BaseAgent::StartTracing(const std::string& config,
-                             base::TimeTicks coordinator_time) {}
+                             base::TimeTicks coordinator_time,
+                             Agent::StartTracingCallback callback) {
+  std::move(callback).Run(true /* success */);
+}
 
 void BaseAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) {}
 
diff --git a/services/tracing/public/cpp/base_agent.h b/services/tracing/public/cpp/base_agent.h
index dc1325d..8be2cf54 100644
--- a/services/tracing/public/cpp/base_agent.h
+++ b/services/tracing/public/cpp/base_agent.h
@@ -35,7 +35,8 @@
 
   // tracing::mojom::Agent:
   void StartTracing(const std::string& config,
-                    base::TimeTicks coordinator_time) override;
+                    base::TimeTicks coordinator_time,
+                    Agent::StartTracingCallback callback) override;
   void StopAndFlush(tracing::mojom::RecorderPtr recorder) override;
   void RequestBufferStatus(
       Agent::RequestBufferStatusCallback callback) override;
diff --git a/services/tracing/public/cpp/trace_event_agent.cc b/services/tracing/public/cpp/trace_event_agent.cc
index 88e25d8..94cc70a 100644
--- a/services/tracing/public/cpp/trace_event_agent.cc
+++ b/services/tracing/public/cpp/trace_event_agent.cc
@@ -72,7 +72,8 @@
 }
 
 void TraceEventAgent::StartTracing(const std::string& config,
-                                   base::TimeTicks coordinator_time) {
+                                   base::TimeTicks coordinator_time,
+                                   StartTracingCallback callback) {
   DCHECK(!recorder_);
 #if defined(__native_client__)
   // NaCl and system times are offset by a bit, so subtract some time from
@@ -87,6 +88,7 @@
     enabled_tracing_modes_ |= base::trace_event::TraceLog::FILTERING_MODE;
   base::trace_event::TraceLog::GetInstance()->SetEnabled(
       trace_config, enabled_tracing_modes_);
+  std::move(callback).Run(true);
 }
 
 void TraceEventAgent::StopAndFlush(mojom::RecorderPtr recorder) {
diff --git a/services/tracing/public/cpp/trace_event_agent.h b/services/tracing/public/cpp/trace_event_agent.h
index 1a7c8915..70063d2 100644
--- a/services/tracing/public/cpp/trace_event_agent.h
+++ b/services/tracing/public/cpp/trace_event_agent.h
@@ -49,7 +49,8 @@
 
   // mojom::Agent
   void StartTracing(const std::string& config,
-                    base::TimeTicks coordinator_time) override;
+                    base::TimeTicks coordinator_time,
+                    StartTracingCallback callback) override;
   void StopAndFlush(mojom::RecorderPtr recorder) override;
 
   void RequestBufferStatus(RequestBufferStatusCallback callback) override;
diff --git a/services/tracing/public/cpp/trace_event_agent_unittest.cc b/services/tracing/public/cpp/trace_event_agent_unittest.cc
index 443dd11..9749aac3 100644
--- a/services/tracing/public/cpp/trace_event_agent_unittest.cc
+++ b/services/tracing/public/cpp/trace_event_agent_unittest.cc
@@ -90,7 +90,8 @@
   void StartTracing(const std::string& categories) {
     TraceEventAgent::GetInstance()->StartTracing(
         base::trace_event::TraceConfig(categories, "").ToString(),
-        base::TimeTicks::Now());
+        base::TimeTicks::Now(),
+        base::BindRepeating([](bool success) { EXPECT_TRUE(success); }));
   }
 
   void StopAndFlush(base::Closure quit_closure) {
diff --git a/services/tracing/public/mojom/constants.mojom b/services/tracing/public/mojom/constants.mojom
index d258406d..f62a072 100644
--- a/services/tracing/public/mojom/constants.mojom
+++ b/services/tracing/public/mojom/constants.mojom
@@ -4,6 +4,8 @@
 
 module tracing.mojom;
 
+const uint32 kStopTracingRetryTimeMilliseconds = 100;
+
 const string kServiceName = "tracing";
 
 // The label of agents that provide trace data of the format explained in
diff --git a/services/tracing/public/mojom/tracing.mojom b/services/tracing/public/mojom/tracing.mojom
index 8c081774..3c282c4 100644
--- a/services/tracing/public/mojom/tracing.mojom
+++ b/services/tracing/public/mojom/tracing.mojom
@@ -39,7 +39,8 @@
 // close the recorder connection to signal the tracing service that no more data
 // will be sent.
 interface Agent {
-  StartTracing(string config, mojo_base.mojom.TimeTicks coordinator_time);
+  StartTracing(string config, mojo_base.mojom.TimeTicks coordinator_time)
+      => (bool success);
   StopAndFlush(Recorder recorder);
   RequestBufferStatus() => (uint32 capacity, uint32 count);
 };
@@ -60,7 +61,7 @@
   // The return value is false if tracing is already enabled with a different
   // config. Otherwise, true is returned as soon as the service receives acks
   // from all existing agents and agents that connect during |StartTracing|.
-  StartTracing(string config);
+  StartTracing(string config) => (bool success);
   StopAndFlush(handle<data_pipe_producer> stream)
       => (mojo_base.mojom.DictionaryValue metadata);
   // Same as |StopAndFlush| but only write data from a certain |agent_label| to
diff --git a/services/tracing/test_util.cc b/services/tracing/test_util.cc
index e514302..1bce1291 100644
--- a/services/tracing/test_util.cc
+++ b/services/tracing/test_util.cc
@@ -21,8 +21,10 @@
 }
 
 void MockAgent::StartTracing(const std::string& config,
-                             base::TimeTicks coordinator_time) {
+                             base::TimeTicks coordinator_time,
+                             StartTracingCallback cb) {
   call_stat_.push_back("StartTracing");
+  std::move(cb).Run(true);
 }
 
 void MockAgent::StopAndFlush(mojom::RecorderPtr recorder) {
diff --git a/services/tracing/test_util.h b/services/tracing/test_util.h
index 948f30c..821a7eb0 100644
--- a/services/tracing/test_util.h
+++ b/services/tracing/test_util.h
@@ -37,7 +37,8 @@
  private:
   // mojom::Agent
   void StartTracing(const std::string& config,
-                    base::TimeTicks coordinator_time) override;
+                    base::TimeTicks coordinator_time,
+                    StartTracingCallback cb) override;
   void StopAndFlush(mojom::RecorderPtr recorder) override;
   void RequestBufferStatus(RequestBufferStatusCallback cb) override;
 
diff --git a/services/tracing/tracing_service.cc b/services/tracing/tracing_service.cc
index 1a08557..d277c63 100644
--- a/services/tracing/tracing_service.cc
+++ b/services/tracing/tracing_service.cc
@@ -4,6 +4,7 @@
 
 #include "services/tracing/tracing_service.h"
 
+#include <map>
 #include <utility>
 #include <vector>
 
@@ -25,10 +26,12 @@
 class ServiceListener : public service_manager::mojom::ServiceManagerListener {
  public:
   ServiceListener(service_manager::Connector* connector,
-                  AgentRegistry* agent_registry)
+                  AgentRegistry* agent_registry,
+                  Coordinator* coordinator)
       : binding_(this),
         connector_(connector),
-        agent_registry_(agent_registry) {
+        agent_registry_(agent_registry),
+        coordinator_(coordinator) {
     service_manager::mojom::ServiceManagerPtr service_manager;
     connector_->BindInterface(service_manager::mojom::kServiceName,
                               &service_manager);
@@ -58,31 +61,75 @@
     traced_process->ConnectToTracingService(std::move(new_connection_request));
   }
 
+  size_t CountServicesWithPID(uint32_t pid) {
+    return std::count_if(service_pid_map_.begin(), service_pid_map_.end(),
+                         [pid](decltype(service_pid_map_)::value_type p) {
+                           return p.second == pid;
+                         });
+  }
+
+  void ServiceAddedWithPID(const service_manager::Identity& identity,
+                           uint32_t pid) {
+    service_pid_map_[identity] = pid;
+    // First service with this PID added; expect a connection from it.
+    if (CountServicesWithPID(pid) == 1) {
+      coordinator_->AddExpectedPID(pid);
+      ConnectProcessToTracingService(identity);
+    }
+  }
+
+  void ServiceRemoved(const service_manager::Identity& identity) {
+    auto entry = service_pid_map_.find(identity);
+    if (entry != service_pid_map_.end()) {
+      uint32_t pid = entry->second;
+      service_pid_map_.erase(entry);
+      // Last entry with this PID removed; stop expecting it
+      // to connect to the tracing service.
+      if (CountServicesWithPID(pid) == 0) {
+        coordinator_->RemoveExpectedPID(pid);
+      }
+    }
+  }
+
   // service_manager::mojom::ServiceManagerListener implementation.
   void OnInit(std::vector<service_manager::mojom::RunningServiceInfoPtr>
                   running_services) override {
     for (auto& service : running_services) {
-      ConnectProcessToTracingService(service->identity);
+      if (service->pid) {
+        ServiceAddedWithPID(service->identity, service->pid);
+      }
     }
+
+    coordinator_->FinishedReceivingRunningPIDs();
+  }
+
+  void OnServicePIDReceived(const service_manager::Identity& identity,
+                            uint32_t pid) override {
+    ServiceAddedWithPID(identity, pid);
+  }
+
+  void OnServiceFailedToStart(
+      const service_manager::Identity& identity) override {
+    ServiceRemoved(identity);
+  }
+
+  void OnServiceStopped(const service_manager::Identity& identity) override {
+    ServiceRemoved(identity);
   }
 
   void OnServiceStarted(const service_manager::Identity& identity,
                         uint32_t pid) override {
-    ConnectProcessToTracingService(identity);
   }
 
   void OnServiceCreated(
       service_manager::mojom::RunningServiceInfoPtr service) override {}
-  void OnServicePIDReceived(const service_manager::Identity& identity,
-                            uint32_t pid) override {}
-  void OnServiceFailedToStart(
-      const service_manager::Identity& identity) override {}
-  void OnServiceStopped(const service_manager::Identity& identity) override {}
 
  private:
   mojo::Binding<service_manager::mojom::ServiceManagerListener> binding_;
   service_manager::Connector* connector_;
   AgentRegistry* agent_registry_;
+  Coordinator* coordinator_;
+  std::map<service_manager::Identity, uint32_t> service_pid_map_;
 };
 
 TracingService::TracingService(service_manager::mojom::ServiceRequest request)
@@ -105,7 +152,7 @@
     registry_.AddInterface(
         base::BindRepeating(&PerfettoTracingCoordinator::BindCoordinatorRequest,
                             base::Unretained(perfetto_coordinator.get())));
-    perfetto_tracing_coordinator_ = std::move(perfetto_coordinator);
+    tracing_coordinator_ = std::move(perfetto_coordinator);
   } else {
     auto tracing_coordinator = std::make_unique<Coordinator>(
         tracing_agent_registry_.get(),
@@ -118,7 +165,8 @@
   }
 
   service_listener_ = std::make_unique<ServiceListener>(
-      service_binding_.GetConnector(), tracing_agent_registry_.get());
+      service_binding_.GetConnector(), tracing_agent_registry_.get(),
+      tracing_coordinator_.get());
 }
 
 void TracingService::OnBindInterface(
diff --git a/services/tracing/tracing_service.h b/services/tracing/tracing_service.h
index 298c504..0ff7bbfa3 100644
--- a/services/tracing/tracing_service.h
+++ b/services/tracing/tracing_service.h
@@ -22,7 +22,6 @@
 namespace tracing {
 
 class ServiceListener;
-class PerfettoTracingCoordinator;
 
 class TracingService : public service_manager::Service {
  public:
@@ -47,7 +46,6 @@
       registry_;
   std::unique_ptr<tracing::AgentRegistry> tracing_agent_registry_;
   std::unique_ptr<Coordinator> tracing_coordinator_;
-  std::unique_ptr<PerfettoTracingCoordinator> perfetto_tracing_coordinator_;
 
   std::unique_ptr<ServiceListener> service_listener_;
 
diff --git a/services/ws/ime/ime_unittest.cc b/services/ws/ime/ime_unittest.cc
index 4c59e2cb..bc03cf6 100644
--- a/services/ws/ime/ime_unittest.cc
+++ b/services/ws/ime/ime_unittest.cc
@@ -50,7 +50,7 @@
   void DispatchKeyEventPostIME(
       std::unique_ptr<ui::Event> event,
       DispatchKeyEventPostIMECallback callback) override {
-    std::move(callback).Run(false);
+    std::move(callback).Run(false, false);
   }
   void EnsureCaretNotInRect(const gfx::Rect& rect) override {}
   void SetEditableSelectionRange(const gfx::Range& range) override {}
diff --git a/services/ws/ime/test_ime_driver/test_ime_driver.cc b/services/ws/ime/test_ime_driver/test_ime_driver.cc
index 2ad7862d..bc946a9 100644
--- a/services/ws/ime/test_ime_driver/test_ime_driver.cc
+++ b/services/ws/ime/test_ime_driver/test_ime_driver.cc
@@ -41,16 +41,17 @@
     // Using base::Unretained is safe because |client_| is owned by this class.
     client_->DispatchKeyEventPostIME(
         std::move(key_event),
-        base::BindOnce(&TestInputMethod::PostProcssKeyEvent,
+        base::BindOnce(&TestInputMethod::PostProcessKeyEvent,
                        base::Unretained(this), std::move(cloned_event),
                        std::move(callback)));
   }
   void CancelComposition() override { NOTIMPLEMENTED_LOG_ONCE(); }
   void ShowVirtualKeyboardIfEnabled() override { NOTIMPLEMENTED_LOG_ONCE(); }
 
-  void PostProcssKeyEvent(std::unique_ptr<ui::Event> key_event,
-                          ProcessKeyEventCallback callback,
-                          bool stopped_propagation) {
+  void PostProcessKeyEvent(std::unique_ptr<ui::Event> key_event,
+                           ProcessKeyEventCallback callback,
+                           bool handled,
+                           bool stopped_propagation) {
     // Ignore any events with modifiers set. This is useful for running things
     // like ash_shell_with_content and having accelerators (such as control-n)
     // work.
diff --git a/services/ws/public/mojom/ime/ime.mojom b/services/ws/public/mojom/ime/ime.mojom
index 8028b6d..b8bb278 100644
--- a/services/ws/public/mojom/ime/ime.mojom
+++ b/services/ws/public/mojom/ime/ime.mojom
@@ -235,8 +235,13 @@
   // for more details.
   InsertChar(ui.mojom.Event event);
 
-  // Dispatch a key event skipping IME. Returns true if event was consumed.
-  DispatchKeyEventPostIME(ui.mojom.Event event) => (bool stopped_propagation);
+  // Dispatch a key event after minimal processing by the IME. The results of
+  // the callback indicated whether the event was handled, and whether any
+  // further processing should be performed. That is, if |stopped_propagation|
+  // is true, IME does no further processing.
+  DispatchKeyEventPostIME(ui.mojom.Event event) => (
+      bool handled,
+      bool stopped_propagation);
 
   // Ensure the caret is not in |rect|.  |rect| is in dip screen coordinates
   // and may extend beyond the bounds of this TextInputClient.
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index cab2430..1c48acc 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -170,10 +170,6 @@
 #define SK_SUPPORT_LEGACY_AAA_CHOICE
 #endif
 
-#ifndef SK_SUPPORT_LEGACY_TEXTBLOBCACHEDIFFCANVAS_CONSTRUCTOR
-#define SK_SUPPORT_LEGACY_TEXTBLOBCACHEDIFFCANVAS_CONSTRUCTOR
-#endif
-
 // We're turning this off indefinitely,
 // until we can figure out some fundamental problems with its approach.
 //
diff --git a/styleguide/web/web.md b/styleguide/web/web.md
index 9724ebf..62e86bf 100644
--- a/styleguide/web/web.md
+++ b/styleguide/web/web.md
@@ -248,7 +248,7 @@
   readability.
 
 * Prefer `rgb()` or `rgba()` with decimal values instead of hex notation
-  (`#rrggbb`) because alpha can be more easily added.
+  (`#rrggbb`).
     * Exception: shades of gray (i.e. `#333`)
 
 * If the hex value is `#rrggbb`, use the shorthand notation `#rgb`.
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 9d2e2bd..386f1e3 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -10054,6 +10054,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "scripts": [
@@ -10928,6 +10935,16 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ]
   },
@@ -11538,6 +11555,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ]
   },
@@ -12151,6 +12175,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "scripts": [
@@ -12735,6 +12766,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ]
   },
@@ -13348,6 +13386,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ]
   },
@@ -13964,6 +14009,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ]
   },
@@ -22804,6 +22856,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 3fa3edc2..fe02df0 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -6588,6 +6588,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "isolated_scripts": [
@@ -10789,6 +10796,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "isolated_scripts": [
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 9d4ac2c9..2f54501 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -1660,6 +1660,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "isolated_scripts": [
@@ -2459,6 +2466,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "isolated_scripts": [
@@ -3187,6 +3201,13 @@
           "can_use_on_swarming_builders": true
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "isolated_scripts": [
@@ -4378,6 +4399,18 @@
           ]
         },
         "test": "wtf_unittests"
+      },
+      {
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "xr_browser_tests"
       }
     ],
     "isolated_scripts": [
diff --git a/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter
index bca197b..eaa6a2a 100644
--- a/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/chromeos.mash.fyi.interactive_ui_tests.filter
@@ -95,6 +95,10 @@
 # MouseEventsTest
 -MouseEventsTest.ClickAndDoubleClick
 -MouseEventsTest.ContextMenu
+-MouseEventsTest.MouseOver
 
 # CrashInFlightChange::ChangeFailed()
 -FlashFullscreenInteractiveBrowserTest.FullscreenWithinTab_EscapeKeyExitsFullscreen
+
+#Flaky https://crbug.com/933847
+-LocationIconViewTest.HideOnSecondClick
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index ac9b571a..a19d5df 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3755,6 +3755,12 @@
       },
     },
 
+    'linux_specific_xr_gtests': {
+      'xr_browser_tests': {
+        'test': 'xr_browser_tests',
+      }
+    },
+
     'mac_specific_chromium_gtests': {
       'sandbox_mac_unittests': {},
     },
@@ -4564,6 +4570,7 @@
       'chromium_gtests_for_linux_and_chromeos_only',
       'chromium_gtests_for_win_and_linux_only',
       'linux_flavor_specific_chromium_gtests',
+      'linux_specific_xr_gtests',
       'network_service_gtests',
       'non_android_chromium_gtests',
       'non_android_and_cast_and_chromeos_chromium_gtests',
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index f4f9ce2..b3787a4 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -27,7 +27,6 @@
     "//base",
     "//courgette:courgette_lib",
   ]
-  additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ]
 }
 
 fuzzer_test("language_detection_fuzzer") {
diff --git a/testing/trigger_scripts/base_test_triggerer.py b/testing/trigger_scripts/base_test_triggerer.py
index b95c9dc1..e576e338 100755
--- a/testing/trigger_scripts/base_test_triggerer.py
+++ b/testing/trigger_scripts/base_test_triggerer.py
@@ -206,10 +206,11 @@
     pass
 
   def select_config_indices(self, args, verbose):
-    # Main implementation for base class to determine what
-    # configs to trigger jobs on from self._bot_configs.
-    # Returns a list of indices into the self._bot_configs and
-    # len(self.indices_to_trigger(args)) == len(selected_indices).
+    # Main implementation for base class to determine which bot config to
+    # trigger for each shard.
+    #
+    # Returns a list of tuples (shard_index, bot_config_index).
+    # bot_config_index is an index into self._bot_configs
     pass
 
   def indices_to_trigger(self, args):
@@ -250,25 +251,23 @@
     merged_json = {}
 
     # Choose selected configs for this run of the test suite.
-    selected_configs = self.select_config_indices(args, verbose)
-    for i in self.indices_to_trigger(args):
+    for shard_index, bot_index in self.select_config_indices(args, verbose):
       # For each shard that we're going to distribute, do the following:
       # 1. Pick which bot configuration to use.
       # 2. Insert that bot configuration's dimensions as command line
       #    arguments, and invoke "swarming.py trigger".
-      bot_index = selected_configs[i]
       # Holds the results of the swarming.py trigger call.
       try:
         json_temp = self.make_temp_file(prefix='base_trigger_dimensions',
                                         suffix='.json')
-        args_to_pass = self.modify_args(filtered_remaining_args, bot_index, i,
-                                        args.shards, json_temp)
+        args_to_pass = self.modify_args(filtered_remaining_args, bot_index,
+                                        shard_index, args.shards, json_temp)
         ret = self.run_swarming(args_to_pass, verbose)
         if ret:
           sys.stderr.write('Failed to trigger a task, aborting\n')
           return ret
         result_json = self.read_json_from_temp_file(json_temp)
-        if i == 0:
+        if shard_index == 0:
           # Copy the entire JSON -- in particular, the "request"
           # dictionary -- from shard 0. "swarming.py collect" uses
           # some keys from this dictionary, in particular related to
@@ -278,8 +277,8 @@
           # which will be handled specially.
           merged_json['tasks'] = {}
         for k, v in result_json['tasks'].items():
-          v['shard_index'] = i
-          merged_json['tasks'][k + ':%d:%d' % (i, args.shards)] = v
+          v['shard_index'] = shard_index
+          merged_json['tasks'][k + ':%d:%d' % (shard_index, args.shards)] = v
       finally:
         self.delete_temp_file(json_temp)
     self.write_json_to_file(merged_json, args.dump_json)
diff --git a/testing/trigger_scripts/perf_device_trigger.py b/testing/trigger_scripts/perf_device_trigger.py
index e890acb..964167c4 100755
--- a/testing/trigger_scripts/perf_device_trigger.py
+++ b/testing/trigger_scripts/perf_device_trigger.py
@@ -115,10 +115,12 @@
 
   def select_config_indices(self, args, verbose):
     if args.multiple_trigger_configs:
+      configs = []
       # If specific bot ids were passed in, we want to trigger a job for
       # every valid config regardless of health status since
       # each config represents exactly one bot in the perf swarming pool.
-      return range(len(self.indices_to_trigger(args)))
+      for index in range(len(self.indices_to_trigger(args))):
+        configs.append((index, index))
     return self._select_config_indices_with_soft_affinity(args, verbose)
 
   def _select_config_indices_with_soft_affinity(self, args, verbose):
@@ -176,8 +178,8 @@
     # Now populate the indices into the bot_configs array
     selected_configs = []
     for shard_index in self.indices_to_trigger(args):
-      selected_configs.append(self._find_bot_config_index(
-          shard_to_bot_assignment_map[shard_index].id()))
+      selected_configs.append((shard_index, self._find_bot_config_index(
+          shard_to_bot_assignment_map[shard_index].id())))
     if verbose:
       self._print_device_affinity_info(
         shard_to_bot_assignment_map,
diff --git a/testing/trigger_scripts/trigger_multiple_dimensions.py b/testing/trigger_scripts/trigger_multiple_dimensions.py
index 4fd765a..d4d26f5 100755
--- a/testing/trigger_scripts/trigger_multiple_dimensions.py
+++ b/testing/trigger_scripts/trigger_multiple_dimensions.py
@@ -113,8 +113,9 @@
 
   def select_config_indices(self, args, verbose):
     selected_indices = []
-    for _ in self.indices_to_trigger(args):
-      selected_indices.append(self.pick_bot_configuration(verbose))
+    for shard_index in self.indices_to_trigger(args):
+      selected_indices.append(
+          (shard_index, self.pick_bot_configuration(verbose)))
     return selected_indices
 
   def prune_test_specific_configs(self, args, verbose):
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 99748c1f..01171e5 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -539,6 +539,7 @@
   aar_path = "libs/com_google_ar_core/core-1.6.0.aar"
   info_path = "libs/com_google_ar_core/com_google_ar_core.info"
   extract_native_libraries = true
+  split_compat_class_names = [ "com/google/ar/core/InstallActivity" ]
 }
 
 java_prebuilt("com_google_dagger_dagger_java") {
diff --git a/third_party/blink/common/frame/user_activation_state.cc b/third_party/blink/common/frame/user_activation_state.cc
index ace8e45..88686ae 100644
--- a/third_party/blink/common/frame/user_activation_state.cc
+++ b/third_party/blink/common/frame/user_activation_state.cc
@@ -9,7 +9,7 @@
 // The expiry time should be long enough to allow network round trips even in a
 // very slow connection (to support xhr-like calls with user activation), yet
 // not too long to make an "unattneded" page feel activated.
-constexpr base::TimeDelta kActivationLifespan = base::TimeDelta::FromSeconds(1);
+constexpr base::TimeDelta kActivationLifespan = base::TimeDelta::FromSeconds(5);
 
 void UserActivationState::Activate() {
   has_been_active_ = true;
diff --git a/third_party/blink/common/frame/user_activation_state_unittest.cc b/third_party/blink/common/frame/user_activation_state_unittest.cc
index 30bc7b47..16780b4 100644
--- a/third_party/blink/common/frame/user_activation_state_unittest.cc
+++ b/third_party/blink/common/frame/user_activation_state_unittest.cc
@@ -65,7 +65,7 @@
   user_activation_state.Activate();
 
   // Right before activation expiry, both bits remain set.
-  AdvanceClock(base::TimeDelta::FromMilliseconds(999));
+  AdvanceClock(base::TimeDelta::FromMilliseconds(4999));
   EXPECT_TRUE(user_activation_state.HasBeenActive());
   EXPECT_TRUE(user_activation_state.IsActive());
 
@@ -99,7 +99,7 @@
 
   // An activation is not consumable after expiry.
   user_activation_state.Activate();
-  AdvanceClock(base::TimeDelta::FromSeconds(1));
+  AdvanceClock(base::TimeDelta::FromSeconds(5));
   EXPECT_FALSE(user_activation_state.ConsumeIfActive());
 
   // Consecutive activations within expiry is consumable only once.
diff --git a/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom b/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom
index 19485a1..fdfcf38 100644
--- a/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom
+++ b/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom
@@ -20,4 +20,7 @@
 // not delivered to a certain DocumentInterfaceBroker implementation.
 interface FrameHostTestInterface {
   Ping(url.mojom.Url source_url, string source_event);
+  // Used in tests to distinguish between the different implementations
+  // and verify that interface requests are routed to the proper override.
+  GetName() => (string name);
 };
diff --git a/third_party/blink/public/platform/web_rtc_rtp_receiver.h b/third_party/blink/public/platform/web_rtc_rtp_receiver.h
index 816c9950..0307271 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_receiver.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_receiver.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/webrtc/api/dtls_transport_interface.h"
 #include "third_party/webrtc/api/rtp_parameters.h"
 
 namespace blink {
@@ -29,6 +30,11 @@
   // Two |WebRTCRtpReceiver|s referencing the same WebRTC-layer receiver have
   // the same |id|.
   virtual uintptr_t Id() const = 0;
+  virtual rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+  DtlsTransport() = 0;
+  // Note: For convenience, DtlsTransportInformation always returns a value.
+  // The information is only interesting if DtlsTransport() is non-null.
+  virtual webrtc::DtlsTransportInformation DtlsTransportInformation() = 0;
   virtual const WebMediaStreamTrack& Track() const = 0;
   virtual WebVector<WebString> StreamIds() const = 0;
   virtual WebVector<std::unique_ptr<WebRTCRtpSource>> GetSources() = 0;
diff --git a/third_party/blink/public/platform/web_rtc_rtp_sender.h b/third_party/blink/public/platform/web_rtc_rtp_sender.h
index 1be1836a..4b17890 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_sender.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_sender.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_rtc_void_request.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/webrtc/api/dtls_transport_interface.h"
 #include "third_party/webrtc/api/rtp_parameters.h"
 
 namespace blink {
@@ -29,6 +30,11 @@
   // same |id|. IDs are guaranteed to be unique amongst senders but they are
   // allowed to be reused after a sender is destroyed.
   virtual uintptr_t Id() const = 0;
+  virtual rtc::scoped_refptr<webrtc::DtlsTransportInterface>
+  DtlsTransport() = 0;
+  // Note: For convenience, DtlsTransportInformation always returns a value.
+  // The information is only interesting if DtlsTransport() is non-null.
+  virtual webrtc::DtlsTransportInformation DtlsTransportInformation() = 0;
   virtual WebMediaStreamTrack Track() const = 0;
   virtual WebVector<WebString> StreamIds() const = 0;
   // TODO(hbos): Replace WebRTCVoidRequest by something resolving promises based
diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h
index f8e4ad8b..88b836a 100644
--- a/third_party/blink/public/web/web_document_loader.h
+++ b/third_party/blink/public/web/web_document_loader.h
@@ -93,10 +93,6 @@
   virtual bool HasUnreachableURL() const = 0;
   virtual WebURL UnreachableURL() const = 0;
 
-  // Allows the embedder to append redirects to the chain as a navigation
-  // is starting, in case it is being transferred from another process.
-  virtual void AppendRedirect(const WebURL&) = 0;
-
   // Returns all redirects that occurred (both client and server) before
   // at last committing the current page.  This will contain one entry
   // for each intermediate URL, and one entry for the last URL (so if
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index 42de1cd7..a7da4679 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -101,6 +101,9 @@
   virtual void BeginFrame(base::TimeTicks last_frame_time,
                           bool record_main_frame_metrics) {}
 
+  // Called after UpdateAllLifecyclePhases has run in response to a BeginFrame.
+  virtual void DidBeginFrame() {}
+
   // Called when main frame metrics are desired. The local frame's UKM
   // aggregator must be informed that collection is starting for the
   // frame.
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 1301cca..54df489 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -41,6 +41,7 @@
 #include "services/resource_coordinator/public/mojom/coordination_unit.mojom-blink.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/modules/insecure_input/insecure_input_service.mojom-blink.h"
@@ -7500,6 +7501,13 @@
   return is_secure;
 }
 
+mojo::ScopedMessagePipeHandle Document::SetDocumentInterfaceBrokerForTesting(
+    mojo::ScopedMessagePipeHandle blink_handle) {
+  DCHECK(GetFrame());
+  return GetFrame()->SetDocumentInterfaceBrokerForTesting(
+      std::move(blink_handle));
+}
+
 void Document::DidEnforceInsecureRequestPolicy() {
   if (!GetFrame())
     return;
@@ -7597,6 +7605,20 @@
   return &GetFrame()->GetInterfaceProvider();
 }
 
+mojom::blink::DocumentInterfaceBroker* Document::GetDocumentInterfaceBroker() {
+  if (!GetFrame())
+    return nullptr;
+
+  return &GetFrame()->GetDocumentInterfaceBroker();
+}
+
+void Document::BindDocumentInterfaceBroker(
+    mojo::ScopedMessagePipeHandle js_handle) {
+  if (!GetFrame())
+    return;
+  GetFrame()->BindDocumentInterfaceBroker(std::move(js_handle));
+}
+
 FrameOrWorkerScheduler* Document::GetScheduler() {
   DCHECK(IsMainThread());
 
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index fa6f25f..2576389 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1320,6 +1320,11 @@
     secure_context_state_ = state;
   }
 
+  void BindDocumentInterfaceBroker(mojo::ScopedMessagePipeHandle js_handle);
+
+  mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
+      mojo::ScopedMessagePipeHandle blink_handle);
+
   CanvasFontCache* GetCanvasFontCache();
 
   // Used by unit tests so that all parsing will be main thread for
@@ -1386,6 +1391,7 @@
 
   CoreProbeSink* GetProbeSink() final;
   service_manager::InterfaceProvider* GetInterfaceProvider() final;
+  mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() final;
 
   // Set an explicit feature policy on this document in response to an HTTP
   // Feature-Policy header. This will be relayed to the embedder through the
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index 01728a3..bf229e57 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -61,6 +61,12 @@
 
 namespace blink {
 
+namespace mojom {
+namespace blink {
+class DocumentInterfaceBroker;
+}  // namespace blink
+}  // namespace mojom
+
 class ContentSecurityPolicy;
 class ContentSecurityPolicyDelegate;
 class CoreProbeSink;
@@ -261,6 +267,10 @@
     return nullptr;
   }
 
+  virtual mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() {
+    return nullptr;
+  }
+
   virtual FrameOrWorkerScheduler* GetScheduler() = 0;
   virtual scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
       TaskType) = 0;
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 038c3479..8da56d9 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -1087,6 +1087,30 @@
   return document_interface_broker_.get();
 }
 
+void LocalFrameClientImpl::BindDocumentInterfaceBroker(
+    mojo::ScopedMessagePipeHandle js_handle) {
+  document_interface_broker_bindings_.AddBinding(
+      this, mojom::blink::DocumentInterfaceBrokerRequest(std::move(js_handle)));
+}
+
+mojo::ScopedMessagePipeHandle
+LocalFrameClientImpl::SetDocumentInterfaceBrokerForTesting(
+    mojo::ScopedMessagePipeHandle blink_handle) {
+  // Ensure all pending calls get dispatched before the implementation swap
+  document_interface_broker_bindings_.FlushForTesting();
+
+  mojom::blink::DocumentInterfaceBrokerPtr test_broker(
+      mojom::blink::DocumentInterfaceBrokerPtrInfo(
+          std::move(blink_handle),
+          mojom::blink::DocumentInterfaceBroker::Version_));
+
+  mojo::ScopedMessagePipeHandle real_handle =
+      document_interface_broker_.PassInterface().PassHandle();
+  document_interface_broker_ = std::move(test_broker);
+
+  return real_handle;
+}
+
 AssociatedInterfaceProvider*
 LocalFrameClientImpl::GetRemoteNavigationAssociatedInterfaces() {
   return web_frame_->Client()->GetRemoteNavigationAssociatedInterfaces();
@@ -1185,7 +1209,7 @@
     const {
   DCHECK(web_frame_->GetFrame()->IsMainFrame());
   return web_frame_->OpenerFeatureState();
-};
+}
 
 STATIC_ASSERT_ENUM(DownloadCrossOriginRedirects::kFollow,
                    WebLocalFrameClient::CrossOriginRedirects::kFollow);
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index 58e1a06..2943e35 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -259,8 +259,21 @@
 
   service_manager::InterfaceProvider* GetInterfaceProvider() override;
 
+  // Binds |js_handle| to the current implementation bound to
+  // |document_interface_broker_| to share the same broker between C++ and
+  // JavaScript clients.
+  void BindDocumentInterfaceBroker(
+      mojo::ScopedMessagePipeHandle js_handle) override;
+
   mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() override;
 
+  // Binds |document_interface_broker_| to |blink_handle|. Used in tests to set
+  // a custom override for DocumentInterfaceBroker methods. Returns the handle
+  // to the previously bound 'production' implementation, which will be used to
+  // forward the calls to methods that have not been overridden.
+  mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
+      mojo::ScopedMessagePipeHandle blink_handle) override;
+
   AssociatedInterfaceProvider* GetRemoteNavigationAssociatedInterfaces()
       override;
 
@@ -304,6 +317,18 @@
   const FeaturePolicy::FeatureState& GetOpenerFeatureState() const override;
 
  private:
+  struct DocumentInterfaceBrokerForwarderTraits {
+    using Interface = mojom::blink::DocumentInterfaceBroker;
+    using PointerType = WeakPersistent<LocalFrameClientImpl>;
+    static bool IsNull(PointerType ptr) {
+      return !ptr || !ptr->document_interface_broker_;
+    }
+    static Interface* GetRawPointer(PointerType* ptr) {
+      return (*ptr)->GetDocumentInterfaceBroker();
+    }
+  };
+  friend struct DocumentInterfaceBrokerForwarderTraits;
+
   bool IsLocalFrameClientImpl() const override { return true; }
   WebDevToolsAgentImpl* DevToolsAgent();
 
@@ -315,6 +340,19 @@
   blink::UserAgentMetadata user_agent_metadata_;
 
   mojom::blink::DocumentInterfaceBrokerPtr document_interface_broker_;
+
+  // |document_interface_broker_bindings_| basically just forwards the broker
+  // methods to GetDocumentInterfaceBroker()
+  // via DocumentInterfaceBrokerForwarderTraits.
+  // Used to connect JavaScript clients of DocumentInterfaceBroker with the same
+  // implementation that |document_interface_broker_| is bound to.
+  using DocumentInterfaceBrokerBinding =
+      mojo::Binding<mojom::blink::DocumentInterfaceBroker,
+                    DocumentInterfaceBrokerForwarderTraits>;
+  mojo::BindingSetBase<mojom::blink::DocumentInterfaceBroker,
+                       DocumentInterfaceBrokerBinding,
+                       void>
+      document_interface_broker_bindings_;
 };
 
 DEFINE_TYPE_CASTS(LocalFrameClientImpl,
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
index 074d8bc9..b8c8d9b 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
@@ -91,10 +91,6 @@
   return DocumentLoader::UnreachableURL();
 }
 
-void WebDocumentLoaderImpl::AppendRedirect(const WebURL& url) {
-  DocumentLoader::AppendRedirect(url);
-}
-
 void WebDocumentLoaderImpl::RedirectChain(WebVector<WebURL>& result) const {
   result.Assign(redirect_chain_);
 }
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.h b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
index ff9185c5..067d6af3 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.h
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
@@ -67,7 +67,6 @@
   const WebURLResponse& GetResponse() const override;
   bool HasUnreachableURL() const override;
   WebURL UnreachableURL() const override;
-  void AppendRedirect(const WebURL&) override;
   void RedirectChain(WebVector<WebURL>&) const override;
   bool IsClientRedirect() const override;
   bool ReplacesCurrentHistoryItem() const override;
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index e9544783..87d0dccc 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -4551,9 +4551,8 @@
             KURL(document_loader->GetUrl()));
 }
 
-TEST_F(WebFrameTest, AppendRedirects) {
-  const std::string first_url = "about:blank";
-  const std::string second_url = "http://internal.test";
+TEST_F(WebFrameTest, RedirectChainContainsInitialUrl) {
+  const std::string first_url = "data:text/html,foo";
 
   frame_test_helpers::WebViewHelper web_view_helper;
   web_view_helper.InitializeAndLoad(first_url);
@@ -4561,13 +4560,11 @@
   WebDocumentLoader* document_loader =
       web_view_helper.LocalMainFrame()->GetDocumentLoader();
   ASSERT_TRUE(document_loader);
-  document_loader->AppendRedirect(ToKURL(second_url));
 
   WebVector<WebURL> redirects;
   document_loader->RedirectChain(redirects);
-  ASSERT_EQ(2U, redirects.size());
+  ASSERT_EQ(1U, redirects.size());
   EXPECT_EQ(ToKURL(first_url), KURL(redirects[0]));
-  EXPECT_EQ(ToKURL(second_url), KURL(redirects[1]));
 }
 
 TEST_F(WebFrameTest, IframeRedirect) {
@@ -10689,8 +10686,11 @@
 
   void ExecuteScriptOnMainFrame(const WebScriptSource& script) {
     MainFrame()->ExecuteScript(script);
+    MainFrame()->View()->MainFrameWidget()->BeginFrame(base::TimeTicks::Now(),
+                                                       false);
     MainFrame()->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
         WebWidget::LifecycleUpdateReason::kTest);
+    MainFrame()->View()->MainFrameWidget()->DidBeginFrame();
     RunPendingTasks();
   }
 
@@ -10780,8 +10780,11 @@
 
   void ExecuteScriptOnMainFrame(const WebScriptSource& script) {
     MainFrame()->ExecuteScript(script);
+    MainFrame()->View()->MainFrameWidget()->BeginFrame(base::TimeTicks::Now(),
+                                                       false);
     MainFrame()->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
         WebWidget::LifecycleUpdateReason::kTest);
+    MainFrame()->View()->MainFrameWidget()->DidBeginFrame();
     RunPendingTasks();
   }
 
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 6674869..4321830 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1521,6 +1521,13 @@
   PageWidgetDelegate::Animate(*AsView().page, last_frame_time);
 }
 
+void WebViewImpl::DidBeginFrame() {
+  DCHECK(MainFrameImpl()->GetFrame());
+  DocumentLifecycle::AllowThrottlingScope throttling_scope(
+      MainFrameImpl()->GetFrame()->GetDocument()->Lifecycle());
+  PageWidgetDelegate::DidBeginFrame(*MainFrameImpl()->GetFrame());
+}
+
 void WebViewImpl::BeginRafAlignedInput() {
   raf_aligned_input_start_time_ = CurrentTimeTicks();
 }
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 46a12c4..0a44b85 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -432,6 +432,7 @@
   void SetSuppressFrameRequestsWorkaroundFor704763Only(bool) override;
   void BeginFrame(base::TimeTicks last_frame_time,
                   bool record_main_frame_metrics) override;
+  void DidBeginFrame() override;
   void BeginRafAlignedInput() override;
   void EndRafAlignedInput() override;
   void RecordStartOfFrameMetrics() override;
@@ -484,6 +485,7 @@
   friend class WebView;  // So WebView::Create can call our constructor
   friend class WebViewFrameWidget;
   friend class WTF::RefCounted<WebViewImpl>;
+  friend class SimCompositor;
 
   WebViewImpl(WebViewClient*,
               bool is_hidden,
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index fb56f4d..c6fbff59 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1288,12 +1288,25 @@
   return *Client()->GetInterfaceProvider();
 }
 
+void LocalFrame::BindDocumentInterfaceBroker(
+    mojo::ScopedMessagePipeHandle js_handle) {
+  DCHECK(Client());
+  Client()->BindDocumentInterfaceBroker(std::move(js_handle));
+}
+
 mojom::blink::DocumentInterfaceBroker&
 LocalFrame::GetDocumentInterfaceBroker() {
   DCHECK(Client());
   return *Client()->GetDocumentInterfaceBroker();
 }
 
+mojo::ScopedMessagePipeHandle LocalFrame::SetDocumentInterfaceBrokerForTesting(
+    mojo::ScopedMessagePipeHandle blink_handle) {
+  DCHECK(Client());
+  return Client()->SetDocumentInterfaceBrokerForTesting(
+      std::move(blink_handle));
+}
+
 AssociatedInterfaceProvider*
 LocalFrame::GetRemoteNavigationAssociatedInterfaces() {
   DCHECK(Client());
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 0c22e2b..21ed7af 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -303,7 +303,10 @@
   bool CanNavigate(const Frame&, const KURL& destination_url = KURL());
 
   service_manager::InterfaceProvider& GetInterfaceProvider();
+  void BindDocumentInterfaceBroker(mojo::ScopedMessagePipeHandle js_handle);
   mojom::blink::DocumentInterfaceBroker& GetDocumentInterfaceBroker();
+  mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
+      mojo::ScopedMessagePipeHandle blink_handle);
   InterfaceRegistry* GetInterfaceRegistry() { return interface_registry_; }
 
   // Returns an AssociatedInterfaceProvider the frame can use to request
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index bf43fca..0e269f0 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -381,10 +381,26 @@
     return nullptr;
   }
 
+  // Binds |js_handle| to the currently bound implementation of
+  // DocumentInterfaceBroker to share the same broker between C++ and JavaScript
+  // clients.
+  virtual void BindDocumentInterfaceBroker(
+      mojo::ScopedMessagePipeHandle js_handle) {}
+
   virtual mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() {
     return nullptr;
   }
 
+  // Used in tests to set a custom override for DocumentInterfaceBroker methods.
+  // |blink_handle| is bound to the test implementation on the caller side.
+  // Returns the handle to the previously bound 'production' implementation,
+  // which will be used to forward the calls to methods that have not been
+  // overridden.
+  virtual mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
+      mojo::ScopedMessagePipeHandle blink_handle) {
+    return mojo::ScopedMessagePipeHandle();
+  }
+
   virtual AssociatedInterfaceProvider*
   GetRemoteNavigationAssociatedInterfaces() {
     return nullptr;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index f5ce434..2612a2d 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1037,6 +1037,29 @@
   return frame_->GetDocument()->Lifecycle();
 }
 
+void LocalFrameView::RunPostLifecycleSteps() {
+  RunIntersectionObserverSteps();
+  UpdateThrottlingStatusForSubtree();
+}
+
+void LocalFrameView::RunIntersectionObserverSteps() {
+#if DCHECK_IS_ON()
+  bool was_dirty = NeedsLayout();
+#endif
+  if (ShouldThrottleRendering() || Lifecycle().LifecyclePostponed() ||
+      !frame_->GetDocument()->IsActive()) {
+    return;
+  }
+  TRACE_EVENT0("blink,benchmark",
+               "LocalFrameView::UpdateViewportIntersectionsForSubtree");
+  SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
+                           LocalFrameUkmAggregator::kIntersectionObservation);
+  UpdateViewportIntersectionsForSubtree();
+#if DCHECK_IS_ON()
+  DCHECK(was_dirty || !NeedsLayout());
+#endif
+}
+
 LayoutSVGRoot* LocalFrameView::EmbeddedReplacedContent() const {
   auto* layout_view = this->GetLayoutView();
   if (!layout_view)
@@ -2187,17 +2210,6 @@
   // Run the lifecycle updates.
   UpdateLifecyclePhasesInternal(target_state);
 
-  // Update intersection observations if needed.
-  if (target_state == DocumentLifecycle::kPaintClean) {
-    TRACE_EVENT0("blink,benchmark",
-                 "LocalFrameView::UpdateViewportIntersectionsForSubtree");
-    SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
-                             LocalFrameUkmAggregator::kIntersectionObservation);
-    UpdateViewportIntersectionsForSubtree();
-  }
-
-  UpdateThrottlingStatusForSubtree();
-
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
     for (auto& observer : frame_view.lifecycle_observers_)
       observer->DidFinishLifecycleUpdate();
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 4eac4650..b4db161 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -370,6 +370,12 @@
   // desired state.
   bool UpdateLifecycleToLayoutClean();
 
+  // This for doing work that needs to run synchronously at the end of lifecyle
+  // updates, but needs to happen outside of the lifecycle code. It's OK to
+  // schedule another animation frame here, but the layout tree should not be
+  // invalidated.
+  void RunPostLifecycleSteps();
+
   void ScheduleVisualUpdateForPaintInvalidationIfNeeded();
 
   bool InvalidateViewportConstrainedObjects();
@@ -811,6 +817,8 @@
 
   DocumentLifecycle& Lifecycle() const;
 
+  void RunIntersectionObserverSteps();
+
   // Methods to do point conversion via layoutObjects, in order to take
   // transforms into account.
   IntRect ConvertToContainingEmbeddedContentView(const IntRect&) const;
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.h b/third_party/blink/renderer/core/frame/root_frame_viewport.h
index a18c1ed..6fcf6ff 100644
--- a/third_party/blink/renderer/core/frame/root_frame_viewport.h
+++ b/third_party/blink/renderer/core/frame/root_frame_viewport.h
@@ -8,6 +8,7 @@
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 8fab72df..1b9051f 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -300,6 +300,11 @@
     GetPage()->GetValidationMessageClient().LayoutOverlay();
 }
 
+void WebFrameWidgetImpl::DidBeginFrame() {
+  DCHECK(LocalRootImpl()->GetFrame());
+  PageWidgetDelegate::DidBeginFrame(*LocalRootImpl()->GetFrame());
+}
+
 void WebFrameWidgetImpl::BeginRafAlignedInput() {
   raf_aligned_input_start_time_ = CurrentTimeTicks();
 }
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index 0ae71d3..8c6b4ea 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -85,6 +85,7 @@
   void SetSuppressFrameRequestsWorkaroundFor704763Only(bool) final;
   void BeginFrame(base::TimeTicks last_frame_time,
                   bool record_main_frame_metrics) override;
+  void DidBeginFrame() override;
   void BeginRafAlignedInput() override;
   void EndRafAlignedInput() override;
   void RecordStartOfFrameMetrics() override;
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index a688b04b..2d35cf4 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -61,6 +61,10 @@
   web_view_->BeginFrame(last_frame_time, record_main_frame_metrics);
 }
 
+void WebViewFrameWidget::DidBeginFrame() {
+  web_view_->DidBeginFrame();
+}
+
 void WebViewFrameWidget::BeginRafAlignedInput() {
   web_view_->BeginRafAlignedInput();
 }
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.h b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
index f14a81cd..5ea0099 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.h
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
@@ -50,6 +50,7 @@
   void SetSuppressFrameRequestsWorkaroundFor704763Only(bool) final;
   void BeginFrame(base::TimeTicks last_frame_time,
                   bool record_main_frame_metrics) override;
+  void DidBeginFrame() override;
   void BeginRafAlignedInput() override;
   void EndRafAlignedInput() override;
   void RecordStartOfFrameMetrics() override;
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index fff9cac..f41de290 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -994,44 +994,47 @@
 }
 
 bool HTMLCanvasElement::ShouldAccelerate(AccelerationCriteria criteria) const {
-  if (base::FeatureList::IsEnabled(features::kAlwaysAccelerateCanvas))
-    return true;
-
-  if (context_ && !Is2d())
-    return false;
-
-  // The following is necessary for handling the special case of canvases in the
-  // dev tools overlay, which run in a process that supports accelerated 2d
-  // canvas but in a special compositing context that does not.
-  if (GetLayoutBox() && !GetLayoutBox()->HasAcceleratedCompositing())
-    return false;
-
-  base::CheckedNumeric<int> checked_canvas_pixel_count = Size().Width();
-  checked_canvas_pixel_count *= Size().Height();
-  if (!checked_canvas_pixel_count.IsValid())
-    return false;
-  int canvas_pixel_count = checked_canvas_pixel_count.ValueOrDie();
-
-  // Do not use acceleration for small canvas.
-  if (criteria != kIgnoreResourceLimitCriteria) {
-    Settings* settings = GetDocument().GetSettings();
-    if (!settings ||
-        canvas_pixel_count < settings->GetMinimumAccelerated2dCanvasSize()) {
+  // With this feature enabled we want to accelerate canvases whenever we can.
+  // This does not include when the context_provider CANNOT accelerated
+  // canvases.
+  if (!base::FeatureList::IsEnabled(features::kAlwaysAccelerateCanvas)) {
+    if (context_ && !Is2d())
       return false;
+
+    // The following is necessary for handling the special case of canvases in
+    // the dev tools overlay, which run in a process that supports accelerated
+    // 2d canvas but in a special compositing context that does not.
+    if (GetLayoutBox() && !GetLayoutBox()->HasAcceleratedCompositing())
+      return false;
+
+    base::CheckedNumeric<int> checked_canvas_pixel_count = Size().Width();
+    checked_canvas_pixel_count *= Size().Height();
+    if (!checked_canvas_pixel_count.IsValid())
+      return false;
+    int canvas_pixel_count = checked_canvas_pixel_count.ValueOrDie();
+
+    // Do not use acceleration for small canvas.
+    if (criteria != kIgnoreResourceLimitCriteria) {
+      Settings* settings = GetDocument().GetSettings();
+      if (!settings ||
+          canvas_pixel_count < settings->GetMinimumAccelerated2dCanvasSize()) {
+        return false;
+      }
+
+      // When GPU allocated memory runs low (due to having created too many
+      // accelerated canvases), the compositor starves and browser becomes
+      // laggy. Thus, we should stop allocating more GPU memory to new canvases
+      // created when the current memory usage exceeds the threshold.
+      if (global_gpu_memory_usage_ >= kMaxGlobalGPUMemoryUsage)
+        return false;
+
+      // Allocating too many GPU resources can makes us run into the driver's
+      // resource limits. So we need to keep the number of texture resources
+      // under tight control
+      if (global_accelerated_context_count_ >=
+          kMaxGlobalAcceleratedResourceCount)
+        return false;
     }
-
-    // When GPU allocated memory runs low (due to having created too many
-    // accelerated canvases), the compositor starves and browser becomes laggy.
-    // Thus, we should stop allocating more GPU memory to new canvases created
-    // when the current memory usage exceeds the threshold.
-    if (global_gpu_memory_usage_ >= kMaxGlobalGPUMemoryUsage)
-      return false;
-
-    // Allocating too many GPU resources can makes us run into the driver's
-    // resource limits. So we need to keep the number of texture resources
-    // under tight control
-    if (global_accelerated_context_count_ >= kMaxGlobalAcceleratedResourceCount)
-      return false;
   }
 
   // Avoid creating |contextProvider| until we're sure we want to try use it,
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 9eb9c7c..2175d3a 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -365,12 +365,15 @@
 
   UpdateContainerPolicy();
 
+  KURL url_to_request = url.IsNull() ? BlankURL() : url;
   if (ContentFrame()) {
     // TODO(sclittle): Support lazily loading frame navigations.
     WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
     if (replace_current_item)
       frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
-    ContentFrame()->ScheduleNavigation(GetDocument(), url, frame_load_type,
+
+    ContentFrame()->ScheduleNavigation(GetDocument(), url_to_request,
+                                       frame_load_type,
                                        UserGestureStatus::kNone);
     return true;
   }
@@ -388,7 +391,7 @@
   if (!child_frame)
     return false;
 
-  ResourceRequest request(url.IsNull() ? BlankURL() : url);
+  ResourceRequest request(url_to_request);
   network::mojom::ReferrerPolicy policy = ReferrerPolicyAttribute();
   request.SetReferrerPolicy(policy);
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index 95ab1b98..a3a6006 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -351,7 +351,6 @@
 }
 
 void InspectorEmulationAgent::PrepareRequest(
-    ExecutionContext* execution_context,
     DocumentLoader* loader,
     ResourceRequest& request,
     const FetchInitiatorInfo& initiator_info,
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
index 0adea50e..739bf8c0 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
@@ -17,7 +17,6 @@
 namespace blink {
 
 class DocumentLoader;
-class ExecutionContext;
 class ResourceRequest;
 class WebLocalFrameImpl;
 class WebViewImpl;
@@ -81,8 +80,7 @@
   void ApplyAcceptLanguageOverride(String* accept_lang);
   void ApplyUserAgentOverride(String* user_agent);
   void FrameStartedLoading(LocalFrame*);
-  void PrepareRequest(ExecutionContext*,
-                      DocumentLoader*,
+  void PrepareRequest(DocumentLoader*,
                       ResourceRequest&,
                       const FetchInitiatorInfo&,
                       ResourceType);
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 26a5180..0c59b00 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -711,9 +711,9 @@
 }
 
 void InspectorNetworkAgent::DidBlockRequest(
-    ExecutionContext* execution_context,
     const ResourceRequest& request,
     DocumentLoader* loader,
+    const KURL& fetch_context_url,
     const FetchInitiatorInfo& initiator_info,
     ResourceRequestBlockedReason reason,
     ResourceType resource_type) {
@@ -721,7 +721,7 @@
   InspectorPageAgent::ResourceType type =
       InspectorPageAgent::ToResourceType(resource_type);
 
-  WillSendRequestInternal(execution_context, identifier, loader, request,
+  WillSendRequestInternal(identifier, loader, fetch_context_url, request,
                           ResourceResponse(), initiator_info, type);
 
   String request_id = IdentifiersFactory::RequestId(loader, identifier);
@@ -744,9 +744,9 @@
 }
 
 void InspectorNetworkAgent::WillSendRequestInternal(
-    ExecutionContext* execution_context,
     unsigned long identifier,
     DocumentLoader* loader,
+    const KURL& fetch_context_url,
     const ResourceRequest& request,
     const ResourceResponse& redirect_response,
     const FetchInitiatorInfo& initiator_info,
@@ -762,8 +762,8 @@
   else if (request.HttpBody())
     post_data = request.HttpBody()->DeepCopy();
 
-  resources_data_->ResourceCreated(execution_context, request_id, loader_id,
-                                   request.Url(), post_data);
+  resources_data_->ResourceCreated(request_id, loader_id, request.Url(),
+                                   post_data);
   if (initiator_info.name == fetch_initiator_type_names::kXmlhttprequest)
     type = InspectorPageAgent::kXHRResource;
   else if (initiator_info.name == fetch_initiator_type_names::kFetch)
@@ -800,9 +800,9 @@
     request_info->setIsLinkPreload(true);
 
   String resource_type = InspectorPageAgent::ResourceTypeJson(type);
-  String documentURL =
-      loader ? UrlWithoutFragment(loader->Url()).GetString()
-             : UrlWithoutFragment(execution_context->Url()).GetString();
+  String documentURL = loader
+                           ? UrlWithoutFragment(loader->Url()).GetString()
+                           : UrlWithoutFragment(fetch_context_url).GetString();
   Maybe<String> maybe_frame_id;
   if (!frame_id.IsEmpty())
     maybe_frame_id = frame_id;
@@ -823,7 +823,6 @@
 }
 
 void InspectorNetworkAgent::WillSendNavigationRequest(
-    ExecutionContext* execution_context,
     unsigned long identifier,
     DocumentLoader* loader,
     const KURL& url,
@@ -839,14 +838,12 @@
     post_data = data->PostData();
   else if (http_body)
     post_data = http_body->DeepCopy();
-  resources_data_->ResourceCreated(execution_context, request_id, loader_id,
-                                   url, post_data);
+  resources_data_->ResourceCreated(request_id, loader_id, url, post_data);
   resources_data_->SetResourceType(request_id,
                                    InspectorPageAgent::kDocumentResource);
 }
 
 void InspectorNetworkAgent::PrepareRequest(
-    ExecutionContext* execution_context,
     DocumentLoader* loader,
     ResourceRequest& request,
     const FetchInitiatorInfo& initiator_info,
@@ -892,9 +889,9 @@
 }
 
 void InspectorNetworkAgent::WillSendRequest(
-    ExecutionContext* execution_context,
     unsigned long identifier,
     DocumentLoader* loader,
+    const KURL& fetch_context_url,
     const ResourceRequest& request,
     const ResourceResponse& redirect_response,
     const FetchInitiatorInfo& initiator_info,
@@ -906,7 +903,7 @@
   InspectorPageAgent::ResourceType type =
       InspectorPageAgent::ToResourceType(resource_type);
 
-  WillSendRequestInternal(execution_context, identifier, loader, request,
+  WillSendRequestInternal(identifier, loader, fetch_context_url, request,
                           redirect_response, initiator_info, type);
 }
 
@@ -1111,18 +1108,16 @@
   return loader && loader->MainResourceIdentifier() == identifier;
 }
 
-void InspectorNetworkAgent::WillLoadXHR(XMLHttpRequest* xhr,
-                                        ThreadableLoaderClient* client,
+void InspectorNetworkAgent::WillLoadXHR(ExecutionContext* execution_context,
                                         const AtomicString& method,
                                         const KURL& url,
                                         bool async,
                                         EncodedFormData* form_data,
                                         const HTTPHeaderMap& headers,
                                         bool include_credentials) {
-  DCHECK(xhr);
   DCHECK(!pending_request_);
   pending_xhr_replay_data_ = XHRReplayData::Create(
-      method, UrlWithoutFragment(url), async,
+      execution_context, method, UrlWithoutFragment(url), async,
       form_data ? form_data->DeepCopy() : nullptr, include_credentials);
   for (const auto& header : headers)
     pending_xhr_replay_data_->AddHeader(header.key, header.value);
@@ -1431,8 +1426,8 @@
   if (!xhr_replay_data || !data)
     return Response::Error("Given id does not correspond to XHR");
 
-  ExecutionContext* execution_context = data->GetExecutionContext();
-  if (execution_context->IsContextDestroyed()) {
+  ExecutionContext* execution_context = xhr_replay_data->GetExecutionContext();
+  if (!execution_context || execution_context->IsContextDestroyed()) {
     resources_data_->SetXHRReplayData(request_id, nullptr);
     return Response::Error("Document is already detached");
   }
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index 0c316a4..eb734dc 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -85,29 +85,27 @@
   void Restore() override;
 
   // Probes.
-  void DidBlockRequest(ExecutionContext*,
-                       const ResourceRequest&,
+  void DidBlockRequest(const ResourceRequest&,
                        DocumentLoader*,
+                       const KURL& fetch_context_url,
                        const FetchInitiatorInfo&,
                        ResourceRequestBlockedReason,
                        ResourceType);
   void DidChangeResourcePriority(DocumentLoader*,
                                  unsigned long identifier,
                                  ResourceLoadPriority);
-  void PrepareRequest(ExecutionContext*,
-                      DocumentLoader*,
+  void PrepareRequest(DocumentLoader*,
                       ResourceRequest&,
                       const FetchInitiatorInfo&,
                       ResourceType);
-  void WillSendRequest(ExecutionContext*,
-                       unsigned long identifier,
+  void WillSendRequest(unsigned long identifier,
                        DocumentLoader*,
+                       const KURL& fetch_context_url,
                        const ResourceRequest&,
                        const ResourceResponse& redirect_response,
                        const FetchInitiatorInfo&,
                        ResourceType);
-  void WillSendNavigationRequest(ExecutionContext*,
-                                 unsigned long identifier,
+  void WillSendNavigationRequest(unsigned long identifier,
                                  DocumentLoader*,
                                  const KURL&,
                                  const AtomicString& http_method,
@@ -147,8 +145,7 @@
   void ShouldBlockRequest(const KURL&, bool* result);
   void ShouldBypassServiceWorker(bool* result);
 
-  void WillLoadXHR(XMLHttpRequest*,
-                   ThreadableLoaderClient*,
+  void WillLoadXHR(ExecutionContext*,
                    const AtomicString& method,
                    const KURL&,
                    bool async,
@@ -249,9 +246,9 @@
 
  private:
   void Enable();
-  void WillSendRequestInternal(ExecutionContext*,
-                               unsigned long identifier,
+  void WillSendRequestInternal(unsigned long identifier,
                                DocumentLoader*,
+                               const KURL& fetch_context_url,
                                const ResourceRequest&,
                                const ResourceResponse& redirect_response,
                                const FetchInitiatorInfo&,
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index cac3881c..4ab01fa 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -102,9 +102,9 @@
 }
 
 void InspectorTraceEvents::WillSendRequest(
-    ExecutionContext*,
     unsigned long identifier,
     DocumentLoader* loader,
+    const KURL& fetch_context_url,
     const ResourceRequest& request,
     const ResourceResponse& redirect_response,
     const FetchInitiatorInfo&,
@@ -117,7 +117,6 @@
 }
 
 void InspectorTraceEvents::WillSendNavigationRequest(
-    ExecutionContext*,
     unsigned long identifier,
     DocumentLoader* loader,
     const KURL& url,
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.h b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
index 0d766d2..c197b83 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.h
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
@@ -83,15 +83,14 @@
  public:
   InspectorTraceEvents() = default;
 
-  void WillSendRequest(ExecutionContext*,
-                       unsigned long identifier,
+  void WillSendRequest(unsigned long identifier,
                        DocumentLoader*,
+                       const KURL& fetch_context_url,
                        const ResourceRequest&,
                        const ResourceResponse& redirect_response,
                        const FetchInitiatorInfo&,
                        ResourceType);
-  void WillSendNavigationRequest(ExecutionContext*,
-                                 unsigned long identifier,
+  void WillSendNavigationRequest(unsigned long identifier,
                                  DocumentLoader*,
                                  const KURL&,
                                  const AtomicString& http_method,
diff --git a/third_party/blink/renderer/core/inspector/network_resources_data.cc b/third_party/blink/renderer/core/inspector/network_resources_data.cc
index 838eae7..93c79f1 100644
--- a/third_party/blink/renderer/core/inspector/network_resources_data.cc
+++ b/third_party/blink/renderer/core/inspector/network_resources_data.cc
@@ -42,13 +42,15 @@
 }
 
 // static
-XHRReplayData* XHRReplayData::Create(const AtomicString& method,
+XHRReplayData* XHRReplayData::Create(ExecutionContext* execution_context,
+                                     const AtomicString& method,
                                      const KURL& url,
                                      bool async,
                                      scoped_refptr<EncodedFormData> form_data,
                                      bool include_credentials) {
-  return MakeGarbageCollected<XHRReplayData>(
-      method, url, async, std::move(form_data), include_credentials);
+  return MakeGarbageCollected<XHRReplayData>(execution_context, method, url,
+                                             async, std::move(form_data),
+                                             include_credentials);
 }
 
 void XHRReplayData::AddHeader(const AtomicString& key,
@@ -56,12 +58,14 @@
   headers_.Set(key, value);
 }
 
-XHRReplayData::XHRReplayData(const AtomicString& method,
+XHRReplayData::XHRReplayData(ExecutionContext* execution_context,
+                             const AtomicString& method,
                              const KURL& url,
                              bool async,
                              scoped_refptr<EncodedFormData> form_data,
                              bool include_credentials)
-    : method_(method),
+    : execution_context_(execution_context),
+      method_(method),
       url_(url),
       async_(async),
       form_data_(form_data),
@@ -70,7 +74,6 @@
 // ResourceData
 NetworkResourcesData::ResourceData::ResourceData(
     NetworkResourcesData* network_resources_data,
-    ExecutionContext* execution_context,
     const String& request_id,
     const String& loader_id,
     const KURL& requested_url)
@@ -84,8 +87,7 @@
       http_status_code_(0),
       raw_header_size_(0),
       pending_encoded_data_length_(0),
-      cached_resource_(nullptr),
-      execution_context_(execution_context) {}
+      cached_resource_(nullptr) {}
 
 void NetworkResourcesData::ResourceData::Trace(blink::Visitor* visitor) {
   visitor->Trace(network_resources_data_);
@@ -93,7 +95,6 @@
   visitor->template RegisterWeakMembers<
       NetworkResourcesData::ResourceData,
       &NetworkResourcesData::ResourceData::ClearWeakMembers>(this);
-  visitor->Trace(execution_context_);
 }
 
 void NetworkResourcesData::ResourceData::SetContent(const String& content,
@@ -200,14 +201,13 @@
 }
 
 void NetworkResourcesData::ResourceCreated(
-    ExecutionContext* context,
     const String& request_id,
     const String& loader_id,
     const KURL& requested_url,
     scoped_refptr<EncodedFormData> post_data) {
   EnsureNoDataForRequestId(request_id);
   ResourceData* data = MakeGarbageCollected<ResourceData>(
-      this, context, request_id, loader_id, requested_url);
+      this, request_id, loader_id, requested_url);
   request_id_to_resource_data_map_.Set(request_id, data);
   if (post_data &&
       PrepareToAddResourceData(request_id, post_data->SizeInBytes())) {
diff --git a/third_party/blink/renderer/core/inspector/network_resources_data.h b/third_party/blink/renderer/core/inspector/network_resources_data.h
index 3ae0ffe..7c6e373 100644
--- a/third_party/blink/renderer/core/inspector/network_resources_data.h
+++ b/third_party/blink/renderer/core/inspector/network_resources_data.h
@@ -51,19 +51,23 @@
 
 class XHRReplayData final : public GarbageCollectedFinalized<XHRReplayData> {
  public:
-  static XHRReplayData* Create(const AtomicString& method,
+  static XHRReplayData* Create(ExecutionContext*,
+                               const AtomicString& method,
                                const KURL&,
                                bool async,
                                scoped_refptr<EncodedFormData>,
                                bool include_credentials);
 
-  XHRReplayData(const AtomicString& method,
+  XHRReplayData(ExecutionContext*,
+                const AtomicString& method,
                 const KURL&,
                 bool async,
                 scoped_refptr<EncodedFormData>,
                 bool include_credentials);
 
   void AddHeader(const AtomicString& key, const AtomicString& value);
+
+  ExecutionContext* GetExecutionContext() const { return execution_context_; }
   const AtomicString& Method() const { return method_; }
   const KURL& Url() const { return url_; }
   bool Async() const { return async_; }
@@ -71,9 +75,12 @@
   const HTTPHeaderMap& Headers() const { return headers_; }
   bool IncludeCredentials() const { return include_credentials_; }
 
-  virtual void Trace(blink::Visitor*) {}
+  virtual void Trace(blink::Visitor* visitor) {
+    visitor->Trace(execution_context_);
+  }
 
  private:
+  WeakMember<ExecutionContext> execution_context_;
   AtomicString method_;
   KURL url_;
   bool async_;
@@ -90,7 +97,6 @@
 
    public:
     ResourceData(NetworkResourcesData*,
-                 ExecutionContext*,
                  const String& request_id,
                  const String& loader_id,
                  const KURL&);
@@ -167,7 +173,6 @@
       post_data_ = post_data;
     }
     EncodedFormData* PostData() const { return post_data_.get(); }
-    ExecutionContext* GetExecutionContext() const { return execution_context_; }
     void Trace(blink::Visitor*);
 
    private:
@@ -200,7 +205,6 @@
     scoped_refptr<BlobDataHandle> downloaded_file_blob_;
     Vector<AtomicString> certificate_;
     scoped_refptr<EncodedFormData> post_data_;
-    Member<ExecutionContext> execution_context_;
   };
 
   static NetworkResourcesData* Create(size_t total_buffer_size,
@@ -212,8 +216,7 @@
   NetworkResourcesData(size_t total_buffer_size, size_t resource_buffer_size);
   ~NetworkResourcesData();
 
-  void ResourceCreated(ExecutionContext*,
-                       const String& request_id,
+  void ResourceCreated(const String& request_id,
                        const String& loader_id,
                        const KURL&,
                        scoped_refptr<EncodedFormData>);
diff --git a/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc b/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc
index 441e05e..179d0296 100644
--- a/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc
+++ b/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc
@@ -53,18 +53,20 @@
 // static
 WorkerInspectorController* WorkerInspectorController::Create(
     WorkerThread* thread,
+    const KURL& url,
     scoped_refptr<InspectorTaskRunner> inspector_task_runner,
     std::unique_ptr<WorkerDevToolsParams> devtools_params) {
   WorkerThreadDebugger* debugger =
       WorkerThreadDebugger::From(thread->GetIsolate());
   return debugger ? MakeGarbageCollected<WorkerInspectorController>(
-                        thread, debugger, std::move(inspector_task_runner),
+                        thread, url, debugger, std::move(inspector_task_runner),
                         std::move(devtools_params))
                   : nullptr;
 }
 
 WorkerInspectorController::WorkerInspectorController(
     WorkerThread* thread,
+    const KURL& url,
     WorkerThreadDebugger* debugger,
     scoped_refptr<InspectorTaskRunner> inspector_task_runner,
     std::unique_ptr<WorkerDevToolsParams> devtools_params)
@@ -77,7 +79,7 @@
   if (auto* scope = DynamicTo<WorkerGlobalScope>(thread->GlobalScope())) {
     worker_devtools_token_ = devtools_params->devtools_worker_token;
     parent_devtools_token_ = scope->GetParentDevToolsToken();
-    url_ = scope->Url();
+    url_ = url;
     worker_thread_id_ = thread->GetPlatformThreadId();
   }
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
diff --git a/third_party/blink/renderer/core/inspector/worker_inspector_controller.h b/third_party/blink/renderer/core/inspector/worker_inspector_controller.h
index a154fe38..b9e5efc 100644
--- a/third_party/blink/renderer/core/inspector/worker_inspector_controller.h
+++ b/third_party/blink/renderer/core/inspector/worker_inspector_controller.h
@@ -60,10 +60,12 @@
  public:
   static WorkerInspectorController* Create(
       WorkerThread*,
+      const KURL&,
       scoped_refptr<InspectorTaskRunner>,
       std::unique_ptr<WorkerDevToolsParams>);
 
   WorkerInspectorController(WorkerThread*,
+                            const KURL&,
                             WorkerThreadDebugger*,
                             scoped_refptr<InspectorTaskRunner>,
                             std::unique_ptr<WorkerDevToolsParams>);
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 39c6896..bfa6a13 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -74,7 +74,6 @@
 
 struct SameSizeAsLayoutBlock : public LayoutBox {
   LayoutObjectChildList children;
-  std::unique_ptr<NGConstraintSpace> cached_constraint_space_;
   uint32_t bitfields;
 };
 
@@ -471,7 +470,6 @@
     ClearLayoutOverflow();
 
   height_available_to_children_changed_ = false;
-  cached_constraint_space_.reset();
   NotifyDisplayLockDidLayout();
 }
 
@@ -2114,14 +2112,6 @@
   return layout_block;
 }
 
-const NGConstraintSpace* LayoutBlock::CachedConstraintSpace() const {
-  return cached_constraint_space_.get();
-}
-
-void LayoutBlock::SetCachedConstraintSpace(const NGConstraintSpace& space) {
-  cached_constraint_space_.reset(new NGConstraintSpace(space));
-}
-
 bool LayoutBlock::RecalcNormalFlowChildLayoutOverflowIfNeeded(
     LayoutObject* layout_object) {
   if (layout_object->IsOutOfFlowPositioned())
diff --git a/third_party/blink/renderer/core/layout/layout_block.h b/third_party/blink/renderer/core/layout/layout_block.h
index 5422b712..12d6d78 100644
--- a/third_party/blink/renderer/core/layout/layout_block.h
+++ b/third_party/blink/renderer/core/layout/layout_block.h
@@ -33,7 +33,6 @@
 
 struct PaintInfo;
 class LineLayoutBox;
-class NGConstraintSpace;
 class WordMeasurement;
 
 typedef WTF::ListHashSet<LayoutBox*, 16> TrackedLayoutBoxListHashSet;
@@ -320,9 +319,6 @@
   LayoutUnit AvailableLogicalHeightForPercentageComputation() const;
   bool HasDefiniteLogicalHeight() const;
 
-  const NGConstraintSpace* CachedConstraintSpace() const;
-  void SetCachedConstraintSpace(const NGConstraintSpace& space);
-
  protected:
   bool RecalcNormalFlowChildLayoutOverflowIfNeeded(LayoutObject*);
   void RecalcNormalFlowChildVisualOverflowIfNeeded(LayoutObject*);
@@ -555,7 +551,6 @@
   virtual bool UpdateLogicalWidthAndColumnWidth();
 
   LayoutObjectChildList children_;
-  std::unique_ptr<NGConstraintSpace> cached_constraint_space_;
 
   unsigned
       has_margin_before_quirk_ : 1;  // Note these quirk values can't be put
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index e9898db..39a2a07 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -2545,16 +2545,6 @@
   return nullptr;
 }
 
-scoped_refptr<const NGLayoutResult>
-LayoutBlockFlow::CachedLayoutResultForTesting() {
-  return nullptr;
-}
-
-void LayoutBlockFlow::SetCachedLayoutResult(const NGLayoutResult&,
-                                            const NGBreakToken*) {}
-
-void LayoutBlockFlow::ClearCachedLayoutResult() {}
-
 bool LayoutBlockFlow::AreCachedLinesValidFor(const NGConstraintSpace&) const {
   return false;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.h b/third_party/blink/renderer/core/layout/layout_block_flow.h
index dfab3b9..ddd9f6f4 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.h
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.h
@@ -461,10 +461,6 @@
   virtual scoped_refptr<const NGLayoutResult> CachedLayoutResult(
       const NGConstraintSpace&,
       const NGBreakToken*);
-  virtual scoped_refptr<const NGLayoutResult> CachedLayoutResultForTesting();
-  virtual void SetCachedLayoutResult(const NGLayoutResult&,
-                                     const NGBreakToken*);
-  virtual void ClearCachedLayoutResult();
   virtual bool AreCachedLinesValidFor(const NGConstraintSpace&) const;
   virtual void WillCollectInlines() {}
   virtual void SetPaintFragment(const NGBlockBreakToken*,
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index eb1e0dd2..f87b471 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -62,6 +62,7 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
@@ -97,7 +98,7 @@
   LayoutUnit intrinsic_content_logical_height;
   LayoutRectOutsets margin_box_outsets;
   LayoutUnit preferred_logical_width[2];
-  void* pointers[4];
+  void* pointers[5];
 };
 
 static_assert(sizeof(LayoutBox) == sizeof(SameSizeAsLayoutBox),
@@ -758,6 +759,17 @@
     Layer()->UpdateTransformationMatrix();
     Layer()->UpdateSizeAndScrollingAfterLayout();
   }
+
+  // When we've finished layout, if we aren't a LayoutNG object, we need to
+  // reset our cached layout result. LayoutNG inside of
+  // |NGBlockNode::RunOldLayout| will call |LayoutBox::SetCachedLayoutResult|
+  // with a new synthesized layout result.
+  //
+  // We also want to make sure that if our entrance point into layout changes,
+  // e.g. an OOF-positioned object is laid out by an NG containing block, then
+  // Legacy, then NG again, NG won't use a stale layout result.
+  if (!IsLayoutNGObject())
+    cached_layout_result_.reset();
 }
 
 LayoutUnit LayoutBox::LogicalHeightWithVisibleOverflow() const {
@@ -2294,6 +2306,20 @@
   DCHECK(new_value ? !first_paint_fragment_ : !inline_box_wrapper_);
 }
 
+void LayoutBox::SetCachedLayoutResult(const NGLayoutResult& layout_result,
+                                      const NGBreakToken* break_token) {
+  if (break_token)
+    return;
+  if (layout_result.Status() != NGLayoutResult::kSuccess)
+    return;
+  if (!layout_result.HasValidConstraintSpaceForCaching())
+    return;
+  if (layout_result.GetConstraintSpaceForCaching().IsIntermediateLayout())
+    return;
+
+  cached_layout_result_ = &layout_result;
+}
+
 void LayoutBox::PositionLineBox(InlineBox* box) {
   if (IsOutOfFlowPositioned()) {
     // Cache the x position only if we were an INLINE type originally.
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 4cbb7949..c994e8f 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -31,7 +31,7 @@
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/core/layout/min_max_size.h"
 #include "third_party/blink/renderer/core/layout/overflow_model.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/wtf/compiler.h"
 
 namespace blink {
@@ -40,6 +40,8 @@
 class LayoutBlockFlow;
 class LayoutMultiColumnSpannerPlaceholder;
 struct BoxLayoutExtraInput;
+class NGBreakToken;
+class NGLayoutResult;
 struct NGPhysicalBoxStrut;
 class ShapeOutsideInfo;
 
@@ -904,6 +906,11 @@
   NGPaintFragment* FirstInlineFragment() const final;
   void SetFirstInlineFragment(NGPaintFragment*) final;
 
+  void SetCachedLayoutResult(const NGLayoutResult&, const NGBreakToken*);
+  const NGLayoutResult* GetCachedLayoutResult() const {
+    return cached_layout_result_.get();
+  }
+
   void SetSpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder&);
   void ClearSpannerPlaceholder();
   LayoutMultiColumnSpannerPlaceholder* SpannerPlaceholder() const final {
@@ -1836,6 +1843,7 @@
   };
 
   std::unique_ptr<LayoutBoxRareData> rare_data_;
+  scoped_refptr<const NGLayoutResult> cached_layout_result_;
 };
 
 DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutBox, IsBox());
diff --git a/third_party/blink/renderer/core/layout/layout_file_upload_control.h b/third_party/blink/renderer/core/layout/layout_file_upload_control.h
index 3e84094..c98f4553 100644
--- a/third_party/blink/renderer/core/layout/layout_file_upload_control.h
+++ b/third_party/blink/renderer/core/layout/layout_file_upload_control.h
@@ -23,6 +23,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index 9f66a53..f790c756 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -776,29 +776,31 @@
   intrinsic_size_along_main_axis_.erase(&child);
 }
 
-bool LayoutFlexibleBox::CanAvoidLayoutForNGChild(
-    const LayoutBox& child_box) const {
-  if (!child_box.IsLayoutNGMixin())
+bool LayoutFlexibleBox::CanAvoidLayoutForNGChild(const LayoutBox& child) const {
+  if (!child.IsLayoutNGMixin())
     return false;
-  const LayoutBlockFlow& child(ToLayoutBlockFlow(child_box));
-  // If the last layout was done with a different override size,
-  // or different definite-ness, we need to force-relayout so
-  // that percentage sizes are resolved correctly.
-  const NGConstraintSpace* old_space = child.CachedConstraintSpace();
-  if (!old_space)
+
+  // If the last layout was done with a different override size, or different
+  // definite-ness, we need to force-relayout so that percentage sizes are
+  // resolved correctly.
+  const NGLayoutResult* cached_layout_result = child.GetCachedLayoutResult();
+  if (!cached_layout_result)
     return false;
-  if (old_space->IsFixedSizeInline() != child.HasOverrideLogicalWidth())
+
+  const NGConstraintSpace& old_space =
+      cached_layout_result->GetConstraintSpaceForCaching();
+  if (old_space.IsFixedSizeInline() != child.HasOverrideLogicalWidth())
     return false;
-  if (old_space->IsFixedSizeBlock() != child.HasOverrideLogicalHeight())
+  if (old_space.IsFixedSizeBlock() != child.HasOverrideLogicalHeight())
     return false;
-  if (old_space->FixedSizeBlockIsDefinite() !=
+  if (old_space.FixedSizeBlockIsDefinite() !=
       UseOverrideLogicalHeightForPerentageResolution(child))
     return false;
   if (child.HasOverrideLogicalWidth() &&
-      old_space->AvailableSize().inline_size != child.OverrideLogicalWidth())
+      old_space.AvailableSize().inline_size != child.OverrideLogicalWidth())
     return false;
   if (child.HasOverrideLogicalHeight() &&
-      old_space->AvailableSize().block_size != child.OverrideLogicalHeight())
+      old_space.AvailableSize().block_size != child.OverrideLogicalHeight())
     return false;
   return true;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 702635a..c483bbde 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1544,10 +1544,17 @@
 }
 
 bool LayoutObject::HasNonZeroEffectiveOpacity() const {
-  PropertyTreeState paint_properties = EnclosingLayer()
-                                           ->GetLayoutObject()
-                                           .FirstFragment()
-                                           .LocalBorderBoxProperties();
+  const FragmentData& fragment =
+      EnclosingLayer()->GetLayoutObject().FirstFragment();
+
+  // This can happen for an iframe element which is outside the viewport and has
+  // therefore never been painted. In that case, we do the safe thing -- report
+  // it as having non-zero opacity -- since this method is used by
+  // IntersectionObserver to detect occlusion.
+  if (!fragment.HasLocalBorderBoxProperties())
+    return true;
+
+  PropertyTreeState paint_properties = fragment.LocalBorderBoxProperties();
 
   for (const auto* effect = &paint_properties.Effect().Unalias(); effect;
        effect = SafeUnalias(effect->Parent())) {
diff --git a/third_party/blink/renderer/core/layout/layout_table.h b/third_party/blink/renderer/core/layout/layout_table.h
index f8b75d1..6b13951 100644
--- a/third_party/blink/renderer/core/layout/layout_table.h
+++ b/third_party/blink/renderer/core/layout/layout_table.h
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index ac72bdf..7a99216 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_state.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index c3fb581..0031829c 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -68,9 +68,11 @@
 // cache evolves.
 template <typename Base>
 const NGPhysicalBoxFragment* LayoutNGMixin<Base>::CurrentFragment() const {
-  if (cached_result_)
-    return ToNGPhysicalBoxFragment(cached_result_->PhysicalFragment());
-  return nullptr;
+  const NGLayoutResult* cached_layout_result = Base::GetCachedLayoutResult();
+  if (!cached_layout_result)
+    return nullptr;
+
+  return ToNGPhysicalBoxFragment(cached_layout_result->PhysicalFragment());
 }
 
 template <typename Base>
@@ -276,37 +278,27 @@
   if (Base::NeedsLayout() && !NeedsRelativePositionedLayoutOnly())
     return nullptr;
 
-  if (!cached_result_)
-    return nullptr;
-
-  const NGConstraintSpace& old_space =
-      cached_result_->GetConstraintSpaceForCaching();
-  if (!new_space.MaySkipLayout(old_space))
+  const NGLayoutResult* cached_layout_result = Base::GetCachedLayoutResult();
+  if (!cached_layout_result)
     return nullptr;
 
   // If we have an orthogonal flow root descendant, we don't attempt to cache
   // our layout result. This is because the initial containing block size may
   // have changed, having a high likelihood of changing the size of the
   // orthogonal flow root.
-  if (cached_result_->HasOrthogonalFlowRoots())
+  if (cached_layout_result->HasOrthogonalFlowRoots())
     return nullptr;
 
-  if (!new_space.AreSizesEqual(old_space)) {
-    // We need to descend all the way down into BODY if we're in quirks mode,
-    // since it magically follows the viewport size.
-    if (NGBlockNode(this).IsQuirkyAndFillsViewport())
-      return nullptr;
+  if (!MaySkipLayout(NGBlockNode(this), *cached_layout_result, new_space))
+    return nullptr;
 
-    // If the available / percentage sizes have changed in a way that may affect
-    // layout, we cannot re-use the previous result.
-    if (SizeMayChange(Base::StyleRef(), new_space, old_space, *cached_result_))
-      return nullptr;
-  }
+  const NGConstraintSpace& old_space =
+      cached_layout_result->GetConstraintSpaceForCaching();
 
   // Check BFC block offset. Even if they don't match, there're some cases we
   // can still reuse the fragment.
   base::Optional<LayoutUnit> bfc_block_offset =
-      cached_result_->BfcBlockOffset();
+      cached_layout_result->BfcBlockOffset();
   if (new_space.BfcOffset().block_offset !=
       old_space.BfcOffset().block_offset) {
     // Earlier floats may affect this box if block offset changes.
@@ -336,45 +328,20 @@
 
   // The checks above should be enough to bail if layout is incomplete, but
   // let's verify:
-  DCHECK(IsBlockLayoutComplete(old_space, *cached_result_));
-  return base::AdoptRef(new NGLayoutResult(*cached_result_, bfc_block_offset));
-}
-
-template <typename Base>
-void LayoutNGMixin<Base>::SetCachedLayoutResult(
-    const NGLayoutResult& layout_result,
-    const NGBreakToken* break_token) {
-  if (break_token)
-    return;
-  if (layout_result.Status() != NGLayoutResult::kSuccess)
-    return;
-  if (!layout_result.HasValidConstraintSpaceForCaching())
-    return;
-  if (layout_result.GetConstraintSpaceForCaching().IsIntermediateLayout())
-    return;
-
-  cached_result_ = &layout_result;
-}
-
-template <typename Base>
-void LayoutNGMixin<Base>::ClearCachedLayoutResult() {
-  cached_result_.reset();
-}
-
-template <typename Base>
-scoped_refptr<const NGLayoutResult>
-LayoutNGMixin<Base>::CachedLayoutResultForTesting() {
-  return cached_result_;
+  DCHECK(IsBlockLayoutComplete(old_space, *cached_layout_result));
+  return base::AdoptRef(
+      new NGLayoutResult(*cached_layout_result, bfc_block_offset));
 }
 
 template <typename Base>
 bool LayoutNGMixin<Base>::AreCachedLinesValidFor(
     const NGConstraintSpace& new_space) const {
-  if (!cached_result_)
+  const NGLayoutResult* cached_layout_result = Base::GetCachedLayoutResult();
+  if (!cached_layout_result)
     return false;
 
   const NGConstraintSpace& old_space =
-      cached_result_->GetConstraintSpaceForCaching();
+      cached_layout_result->GetConstraintSpaceForCaching();
 
   if (new_space.AvailableSize().inline_size !=
       old_space.AvailableSize().inline_size)
@@ -387,11 +354,11 @@
 
   // Any floats might need to move, causing lines to wrap differently, needing
   // re-layout.
-  if (!cached_result_->ExclusionSpace().IsEmpty())
+  if (!cached_layout_result->ExclusionSpace().IsEmpty())
     return false;
 
   // Propagating OOF needs re-layout.
-  if (!cached_result_->OutOfFlowPositionedDescendants().IsEmpty())
+  if (!cached_layout_result->OutOfFlowPositionedDescendants().IsEmpty())
     return false;
 
   return true;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index 8c4b8a3..286ad73 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -65,13 +65,8 @@
       const NGConstraintSpace&,
       const NGBreakToken*) final;
 
-  void SetCachedLayoutResult(const NGLayoutResult&, const NGBreakToken*) final;
-  void ClearCachedLayoutResult() final;
   bool AreCachedLinesValidFor(const NGConstraintSpace&) const final;
 
-  // For testing only.
-  scoped_refptr<const NGLayoutResult> CachedLayoutResultForTesting() final;
-
   NGPaintFragment* PaintFragment() const final { return paint_fragment_.get(); }
   void SetPaintFragment(const NGBlockBreakToken*,
                         scoped_refptr<const NGPhysicalFragment>) final;
@@ -100,8 +95,6 @@
                                   MarkingBehavior marking_behavior) final;
 
   std::unique_ptr<NGInlineNodeData> ng_inline_node_data_;
-
-  scoped_refptr<const NGLayoutResult> cached_result_;
   scoped_refptr<NGPaintFragment> paint_fragment_;
 
   friend class NGBaseLayoutAlgorithmTest;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 17ead23..6f4eb40 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -238,9 +238,8 @@
     const NGConstraintSpace& constraint_space,
     const NGBreakToken* break_token) {
   // Use the old layout code and synthesize a fragment.
-  if (!CanUseNewLayout()) {
+  if (!CanUseNewLayout())
     return RunOldLayout(constraint_space);
-  }
 
   LayoutBlockFlow* block_flow =
       box_->IsLayoutNGMixin() ? ToLayoutBlockFlow(box_) : nullptr;
@@ -895,17 +894,22 @@
   DCHECK(!box_->IsLayoutBlock() ||
          ToLayoutBlock(box_)->CreatesNewFormattingContext());
 
-  WritingMode writing_mode = Style().GetWritingMode();
-  LayoutBlock* block = box_->IsLayoutBlock() ? ToLayoutBlock(box_) : nullptr;
-  const NGConstraintSpace* old_space =
-      block ? block->CachedConstraintSpace() : nullptr;
-  if (!old_space || box_->NeedsLayout() || *old_space != constraint_space) {
+  scoped_refptr<const NGLayoutResult> layout_result =
+      box_->GetCachedLayoutResult();
+
+  // We need to force a layout on the child if the constraint space given will
+  // change the layout.
+  bool needs_force_relayout =
+      layout_result && !MaySkipLayout(*this, *layout_result, constraint_space);
+
+  if (box_->NeedsLayout() || !layout_result || needs_force_relayout) {
     BoxLayoutExtraInput input(*box_);
     input.containing_block_content_inline_size =
         CalculateAvailableInlineSizeForLegacy(*box_, constraint_space);
     input.containing_block_content_block_size =
         CalculateAvailableBlockSizeForLegacy(*box_, constraint_space);
 
+    WritingMode writing_mode = Style().GetWritingMode();
     if (LayoutObject* containing_block = box_->ContainingBlock()) {
       if (!IsParallelWritingMode(containing_block->StyleRef().GetWritingMode(),
                                  writing_mode)) {
@@ -925,32 +929,34 @@
     // Using |LayoutObject::LayoutIfNeeded| save us a little bit of overhead,
     // compared to |LayoutObject::ForceChildLayout|.
     DCHECK(!box_->IsLayoutNGMixin());
-    if (box_->NeedsLayout())
+    if (box_->NeedsLayout() && !needs_force_relayout)
       box_->LayoutIfNeeded();
     else
       box_->ForceChildLayout();
 
-    if (block)
-      block->SetCachedConstraintSpace(constraint_space);
+    // Synthesize a new layout result.
+    NGLogicalSize box_size(box_->LogicalWidth(), box_->LogicalHeight());
+    // TODO(kojii): Implement use_first_line_style.
+    NGBoxFragmentBuilder builder(*this, box_->Style(), &constraint_space,
+                                 writing_mode, box_->StyleRef().Direction());
+    builder.SetIsNewFormattingContext(
+        constraint_space.IsNewFormattingContext());
+    builder.SetIsOldLayoutRoot();
+    builder.SetInlineSize(box_size.inline_size);
+    builder.SetBlockSize(box_size.block_size);
+    NGBoxStrut borders(box_->BorderStart(), box_->BorderEnd(),
+                       box_->BorderBefore(), box_->BorderAfter());
+    builder.SetBorders(borders);
+    NGBoxStrut padding(box_->PaddingStart(), box_->PaddingEnd(),
+                       box_->PaddingBefore(), box_->PaddingAfter());
+    builder.SetPadding(padding);
+
+    CopyBaselinesFromOldLayout(constraint_space, &builder);
+    layout_result = builder.ToBoxFragment();
+
+    box_->SetCachedLayoutResult(*layout_result, /* break_token */ nullptr);
   }
-  NGLogicalSize box_size(box_->LogicalWidth(), box_->LogicalHeight());
-  // TODO(kojii): Implement use_first_line_style.
-  NGBoxFragmentBuilder builder(*this, box_->Style(), &constraint_space,
-                               writing_mode, box_->StyleRef().Direction());
-  builder.SetIsNewFormattingContext(constraint_space.IsNewFormattingContext());
-  builder.SetIsOldLayoutRoot();
-  builder.SetInlineSize(box_size.inline_size);
-  builder.SetBlockSize(box_size.block_size);
-  NGBoxStrut borders(box_->BorderStart(), box_->BorderEnd(),
-                     box_->BorderBefore(), box_->BorderAfter());
-  builder.SetBorders(borders);
-  NGBoxStrut padding(box_->PaddingStart(), box_->PaddingEnd(),
-                     box_->PaddingBefore(), box_->PaddingAfter());
-  builder.SetPadding(padding);
 
-  CopyBaselinesFromOldLayout(constraint_space, &builder);
-
-  scoped_refptr<const NGLayoutResult> layout_result = builder.ToBoxFragment();
   UpdateShapeOutsideInfoIfNeeded(
       *layout_result, constraint_space.PercentageResolutionInlineSize());
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc
index 34d0d0b6..0804510 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc
@@ -6,9 +6,146 @@
 
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
 
 namespace blink {
 
+namespace {
+
+inline bool InlineLengthMayChange(const Length& length,
+                                  LengthResolveType type,
+                                  const NGConstraintSpace& new_space,
+                                  const NGConstraintSpace& old_space) {
+  // Percentage inline margins will affect the size if the size is unspecified
+  // (auto and similar). So we need to check both available size and the
+  // percentage resolution size in that case.
+  bool is_unspecified =
+      (length.IsAuto() && type != LengthResolveType::kMinSize) ||
+      length.IsFitContent() || length.IsFillAvailable();
+  if (is_unspecified) {
+    if (new_space.AvailableSize().inline_size !=
+        old_space.AvailableSize().inline_size)
+      return true;
+  }
+  if (is_unspecified || length.IsPercentOrCalc()) {
+    if (new_space.PercentageResolutionInlineSize() !=
+        old_space.PercentageResolutionInlineSize())
+      return true;
+  }
+  return false;
+}
+
+inline bool BlockLengthMayChange(const Length& length,
+                                 const NGConstraintSpace& new_space,
+                                 const NGConstraintSpace& old_space) {
+  if (length.IsFillAvailable()) {
+    if (new_space.AvailableSize().block_size !=
+        old_space.AvailableSize().block_size)
+      return true;
+  }
+  return false;
+}
+
+// Return true if it's possible (but not necessarily guaranteed) that the new
+// constraint space will give a different size compared to the old one, when
+// computed style and child content remain unchanged.
+bool SizeMayChange(const ComputedStyle& style,
+                   const NGConstraintSpace& new_space,
+                   const NGConstraintSpace& old_space,
+                   const NGLayoutResult& layout_result) {
+  DCHECK_EQ(new_space.IsFixedSizeInline(), old_space.IsFixedSizeInline());
+  DCHECK_EQ(new_space.IsFixedSizeBlock(), old_space.IsFixedSizeBlock());
+
+  // Go through all length properties, and, depending on length type
+  // (percentages, auto, etc.), check whether the constraint spaces differ in
+  // such a way that the resulting size *may* change. There are currently many
+  // possible false-positive situations here, as we don't rule out length
+  // changes that won't have any effect on the final size (e.g. if inline-size
+  // is 100px, max-inline-size is 50%, and percentage resolution inline size
+  // changes from 1000px to 500px). If the constraint space has "fixed" size in
+  // a dimension, we can skip checking properties in that dimension and just
+  // look for available size changes, since that's how a "fixed" constraint
+  // space works.
+  if (new_space.IsFixedSizeInline()) {
+    if (new_space.AvailableSize().inline_size !=
+        old_space.AvailableSize().inline_size)
+      return true;
+  } else {
+    if (InlineLengthMayChange(style.LogicalWidth(),
+                              LengthResolveType::kContentSize, new_space,
+                              old_space) ||
+        InlineLengthMayChange(style.LogicalMinWidth(),
+                              LengthResolveType::kMinSize, new_space,
+                              old_space) ||
+        InlineLengthMayChange(style.LogicalMaxWidth(),
+                              LengthResolveType::kMaxSize, new_space,
+                              old_space))
+      return true;
+  }
+
+  if (new_space.IsFixedSizeBlock()) {
+    if (new_space.AvailableSize().block_size !=
+        old_space.AvailableSize().block_size)
+      return true;
+  } else {
+    if (BlockLengthMayChange(style.LogicalHeight(), new_space, old_space) ||
+        BlockLengthMayChange(style.LogicalMinHeight(), new_space, old_space) ||
+        BlockLengthMayChange(style.LogicalMaxHeight(), new_space, old_space))
+      return true;
+    // We only need to check if the PercentageResolutionBlockSizes match if the
+    // layout result has explicitly marked itself as dependent.
+    if (layout_result.DependsOnPercentageBlockSize()) {
+      if (new_space.PercentageResolutionBlockSize() !=
+          old_space.PercentageResolutionBlockSize())
+        return true;
+      if (new_space.ReplacedPercentageResolutionBlockSize() !=
+          old_space.ReplacedPercentageResolutionBlockSize())
+        return true;
+    }
+  }
+
+  if (new_space.PercentageResolutionInlineSize() !=
+      old_space.PercentageResolutionInlineSize()) {
+    // Percentage-based padding is resolved against the inline content box size
+    // of the containing block.
+    if (style.PaddingTop().IsPercentOrCalc() ||
+        style.PaddingRight().IsPercentOrCalc() ||
+        style.PaddingBottom().IsPercentOrCalc() ||
+        style.PaddingLeft().IsPercentOrCalc())
+      return true;
+  }
+
+  return false;
+}
+
+}  // namespace
+
+bool MaySkipLayout(const NGBlockNode node,
+                   const NGLayoutResult& cached_layout_result,
+                   const NGConstraintSpace& new_space) {
+  DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess);
+  DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching());
+
+  const NGConstraintSpace& old_space =
+      cached_layout_result.GetConstraintSpaceForCaching();
+  if (!new_space.MaySkipLayout(old_space))
+    return false;
+
+  if (!new_space.AreSizesEqual(old_space)) {
+    // We need to descend all the way down into BODY if we're in quirks mode,
+    // since it magically follows the viewport size.
+    if (node.IsQuirkyAndFillsViewport())
+      return false;
+
+    // If the available / percentage sizes have changed in a way that may affect
+    // layout, we cannot re-use the previous result.
+    if (SizeMayChange(node.Style(), new_space, old_space, cached_layout_result))
+      return false;
+  }
+
+  return true;
+}
+
 bool IsBlockLayoutComplete(const NGConstraintSpace& space,
                            const NGLayoutResult& result) {
   if (result.Status() != NGLayoutResult::kSuccess)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
index f93fc5e..1a880d9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
@@ -5,15 +5,24 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_UTILS_H_
 
+#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
+
 namespace blink {
 
 class NGConstraintSpace;
 class NGLayoutResult;
 
+// Returns true if for a given |new_space|, the |node| will provide the same
+// |NGLayoutResult| as |cached_layout_result|, and therefore might be able to
+// skip layout.
+bool MaySkipLayout(const NGBlockNode node,
+                   const NGLayoutResult& cached_layout_result,
+                   const NGConstraintSpace& new_space);
+
 // Return true if layout is considered complete. In some cases we require more
 // than one layout pass.
-// This function never considers intermediate layouts
-// (space,IsIntermediateLayout()) to be complete.
+// This function never considers intermediate layouts with
+// |NGConstraintSpace::IsIntermediateLayout| to be complete.
 bool IsBlockLayoutComplete(const NGConstraintSpace&, const NGLayoutResult&);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index a5df118a..61fb484 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
@@ -52,40 +51,6 @@
   }
 }
 
-inline bool InlineLengthMayChange(const Length& length,
-                                  LengthResolveType type,
-                                  const NGConstraintSpace& new_space,
-                                  const NGConstraintSpace& old_space) {
-  // Percentage inline margins will affect the size if the size is unspecified
-  // (auto and similar). So we need to check both available size and the
-  // percentage resolution size in that case.
-  bool is_unspecified =
-      (length.IsAuto() && type != LengthResolveType::kMinSize) ||
-      length.IsFitContent() || length.IsFillAvailable();
-  if (is_unspecified) {
-    if (new_space.AvailableSize().inline_size !=
-        old_space.AvailableSize().inline_size)
-      return true;
-  }
-  if (is_unspecified || length.IsPercentOrCalc()) {
-    if (new_space.PercentageResolutionInlineSize() !=
-        old_space.PercentageResolutionInlineSize())
-      return true;
-  }
-  return false;
-}
-
-inline bool BlockLengthMayChange(const Length& length,
-                                 const NGConstraintSpace& new_space,
-                                 const NGConstraintSpace& old_space) {
-  if (length.IsFillAvailable()) {
-    if (new_space.AvailableSize().block_size !=
-        old_space.AvailableSize().block_size)
-      return true;
-  }
-  return false;
-}
-
 }  // anonymous namespace
 
 bool NeedMinMaxSizeForContentContribution(WritingMode mode,
@@ -750,75 +715,6 @@
   return NGLogicalSize(*replaced_inline, *replaced_block);
 }
 
-bool SizeMayChange(const ComputedStyle& style,
-                   const NGConstraintSpace& new_space,
-                   const NGConstraintSpace& old_space,
-                   const NGLayoutResult& layout_result) {
-  DCHECK_EQ(new_space.IsFixedSizeInline(), old_space.IsFixedSizeInline());
-  DCHECK_EQ(new_space.IsFixedSizeBlock(), old_space.IsFixedSizeBlock());
-
-  // Go through all length properties, and, depending on length type
-  // (percentages, auto, etc.), check whether the constraint spaces differ in
-  // such a way that the resulting size *may* change. There are currently many
-  // possible false-positive situations here, as we don't rule out length
-  // changes that won't have any effect on the final size (e.g. if inline-size
-  // is 100px, max-inline-size is 50%, and percentage resolution inline size
-  // changes from 1000px to 500px). If the constraint space has "fixed" size in
-  // a dimension, we can skip checking properties in that dimension and just
-  // look for available size changes, since that's how a "fixed" constraint
-  // space works.
-  if (new_space.IsFixedSizeInline()) {
-    if (new_space.AvailableSize().inline_size !=
-        old_space.AvailableSize().inline_size)
-      return true;
-  } else {
-    if (InlineLengthMayChange(style.LogicalWidth(),
-                              LengthResolveType::kContentSize, new_space,
-                              old_space) ||
-        InlineLengthMayChange(style.LogicalMinWidth(),
-                              LengthResolveType::kMinSize, new_space,
-                              old_space) ||
-        InlineLengthMayChange(style.LogicalMaxWidth(),
-                              LengthResolveType::kMaxSize, new_space,
-                              old_space))
-      return true;
-  }
-
-  if (new_space.IsFixedSizeBlock()) {
-    if (new_space.AvailableSize().block_size !=
-        old_space.AvailableSize().block_size)
-      return true;
-  } else {
-    if (BlockLengthMayChange(style.LogicalHeight(), new_space, old_space) ||
-        BlockLengthMayChange(style.LogicalMinHeight(), new_space, old_space) ||
-        BlockLengthMayChange(style.LogicalMaxHeight(), new_space, old_space))
-      return true;
-    // We only need to check if the PercentageResolutionBlockSizes match if the
-    // layout result has explicitly marked itself as dependent.
-    if (layout_result.DependsOnPercentageBlockSize()) {
-      if (new_space.PercentageResolutionBlockSize() !=
-          old_space.PercentageResolutionBlockSize())
-        return true;
-      if (new_space.ReplacedPercentageResolutionBlockSize() !=
-          old_space.ReplacedPercentageResolutionBlockSize())
-        return true;
-    }
-  }
-
-  if (new_space.PercentageResolutionInlineSize() !=
-      old_space.PercentageResolutionInlineSize()) {
-    // Percentage-based padding is resolved against the inline content box size
-    // of the containing block.
-    if (style.PaddingTop().IsPercentOrCalc() ||
-        style.PaddingRight().IsPercentOrCalc() ||
-        style.PaddingBottom().IsPercentOrCalc() ||
-        style.PaddingLeft().IsPercentOrCalc())
-      return true;
-  }
-
-  return false;
-}
-
 int ResolveUsedColumnCount(int computed_count,
                            LayoutUnit computed_size,
                            LayoutUnit used_gap,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
index 5d5b7be..4e43161 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
@@ -24,7 +24,6 @@
 class NGConstraintSpace;
 class NGBlockNode;
 class NGLayoutInputNode;
-class NGLayoutResult;
 
 // LengthResolvePhase indicates what type of layout pass we are currently in.
 // This changes how lengths are resolved. kIntrinsic must be used during the
@@ -159,14 +158,6 @@
                     const NGConstraintSpace&,
                     const base::Optional<MinMaxSize>&);
 
-// Return true if it's possible (but not necessarily guaranteed) that the new
-// constraint space will give a different size compared to the old one, when
-// computed style and child content remain unchanged.
-bool SizeMayChange(const ComputedStyle&,
-                   const NGConstraintSpace& new_space,
-                   const NGConstraintSpace& old_space,
-                   const NGLayoutResult& layout_result);
-
 // Based on available inline size, CSS computed column-width, CSS computed
 // column-count and CSS used column-gap, return CSS used column-count.
 CORE_EXPORT int ResolveUsedColumnCount(int computed_count,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
index ee842b08..b24174f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
@@ -59,7 +59,7 @@
   Element* rel = GetDocument().getElementById("rel");
   LayoutBlockFlow* block_flow = ToLayoutBlockFlow(rel->GetLayoutObject());
   scoped_refptr<const NGLayoutResult> result =
-      block_flow->CachedLayoutResultForTesting();
+      block_flow->GetCachedLayoutResult();
   EXPECT_TRUE(result);
   EXPECT_EQ(result->OutOfFlowPositionedDescendants().size(), (size_t)2);
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index 9eaf65e..cd599b5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index d2defcc..b14d85c 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -162,6 +162,7 @@
   unreachable_url_ = params_->unreachable_url;
   previews_state_ = params_->previews_state;
 
+  // See WebNavigationParams for special case explanations.
   if (!params_->is_static_data && url_.IsAboutSrcdocURL()) {
     loading_srcdoc_ = true;
     // TODO(dgozman): instead of reaching to the owner here, we could instead:
@@ -184,6 +185,21 @@
     WebNavigationParams::FillStaticResponse(
         params_.get(), "text/html", "UTF-8",
         base::make_span(encoded_srcdoc.data(), encoded_srcdoc.length()));
+  } else if (!params_->is_static_data && fetcher_->Archive()) {
+    // If we have an archive loaded in some ancestor frame, we should
+    // retrieve document content from that archive. This is different from
+    // loading an archive into this frame, which will be handled separately
+    // once we load the body and parse it as an archive.
+    params_->body_loader.reset();
+    ArchiveResource* archive_resource =
+        fetcher_->Archive()->SubresourceForURL(url_);
+    if (archive_resource) {
+      SharedBuffer* archive_data = archive_resource->Data();
+      WebNavigationParams::FillStaticResponse(
+          params_.get(), archive_resource->MimeType(),
+          archive_resource->TextEncoding(),
+          base::make_span(archive_data->Data(), archive_data->size()));
+    }
   }
 
   WebNavigationTimings& timings = params_->navigation_timings;
@@ -218,11 +234,36 @@
   // The document URL needs to be added to the head of the list as that is
   // where the redirects originated.
   if (is_client_redirect_)
-    AppendRedirect(frame_->GetDocument()->Url());
+    redirect_chain_.push_back(frame_->GetDocument()->Url());
 
   if (!params_->origin_to_commit.IsNull())
     origin_to_commit_ = params_->origin_to_commit.Get()->IsolatedCopy();
 
+  loading_url_as_empty_document_ =
+      !params_->is_static_data && WillLoadUrlAsEmpty(url_);
+
+  if (!loading_url_as_empty_document_) {
+    content_security_policy_ =
+        CreateCSP(params_->response.ToResourceResponse(), origin_policy_);
+    if (!content_security_policy_) {
+      // Loading the document was blocked by the CSP check. Pretend that
+      // this was an empty document instead and don't reuse the
+      // original URL (https://crbug.com/622385).
+      // TODO(mkwst):  Remove this once XFO moves to the browser.
+      // https://crbug.com/555418.
+      was_blocked_after_csp_ = true;
+      KURL blocked_url = SecurityOrigin::UrlWithUniqueOpaqueOrigin();
+      original_url_ = blocked_url;
+      url_ = blocked_url;
+      params_->url = blocked_url;
+      WebNavigationParams::FillStaticResponse(params_.get(), "text/html",
+                                              "UTF-8", "");
+    }
+  }
+
+  if (!GetFrameLoader().StateMachine()->CreatingInitialEmptyDocument())
+    redirect_chain_.push_back(url_);
+
   probe::lifecycleEvent(frame_, this, "init", CurrentTimeTicksInSeconds());
 }
 
@@ -368,8 +409,8 @@
   }
   redirect_chain_.clear();
   if (is_client_redirect_)
-    AppendRedirect(old_url);
-  AppendRedirect(new_url);
+    redirect_chain_.push_back(old_url);
+  redirect_chain_.push_back(new_url);
 
   SetHistoryItemStateForCommit(
       history_item_.Get(), type,
@@ -620,7 +661,7 @@
   CHECK(SecurityOrigin::Create(current_request_url)->CanDisplay(url_));
 
   DCHECK(!GetTiming().FetchStart().is_null());
-  AppendRedirect(url_);
+  redirect_chain_.push_back(url_);
   GetTiming().AddRedirect(current_request_url, url_);
 
   // If a redirection happens during a back/forward navigation, don't restore
@@ -683,64 +724,35 @@
   return true;
 }
 
-void DocumentLoader::CancelLoadAfterCSPDenied(
-    const ResourceResponse& response) {
-  was_blocked_after_csp_ = true;
-
-  // Pretend that this was an empty HTTP 200 response.  Don't reuse the original
-  // URL for the empty page (https://crbug.com/622385).
-  //
-  // TODO(mkwst):  Remove this once XFO moves to the browser.
-  // https://crbug.com/555418.
-  content_security_policy_.Clear();
-  KURL blocked_url = SecurityOrigin::UrlWithUniqueOpaqueOrigin();
-  original_url_ = blocked_url;
-  url_ = blocked_url;
-  redirect_chain_.pop_back();
-  AppendRedirect(blocked_url);
-  response_ = ResourceResponse(blocked_url);
-  response_.SetMimeType("text/html");
-  FinishedLoading(CurrentTimeTicks());
-}
-
-bool DocumentLoader::HandleResponse(const ResourceResponse& response) {
-  DCHECK(frame_);
-  application_cache_host_->DidReceiveResponseForMainResource(response);
-
-  content_security_policy_ = ContentSecurityPolicy::Create();
-  content_security_policy_->SetOverrideURLForSelf(response.CurrentRequestUrl());
-
-  AtomicString mixed_content_header = response.HttpHeaderField("mixed-content");
-  if (EqualIgnoringASCIICase(mixed_content_header, "noupgrade")) {
-    frame_->GetDocument()->SetMixedAutoupgradeOptOut(true);
-  }
+ContentSecurityPolicy* DocumentLoader::CreateCSP(
+    const ResourceResponse& response,
+    const String& origin_policy_string) {
+  ContentSecurityPolicy* csp = ContentSecurityPolicy::Create();
+  csp->SetOverrideURLForSelf(response.CurrentRequestUrl());
 
   if (!frame_->GetSettings()->BypassCSP()) {
-    content_security_policy_->DidReceiveHeaders(
-        ContentSecurityPolicyResponseHeaders(response));
+    csp->DidReceiveHeaders(ContentSecurityPolicyResponseHeaders(response));
 
     // Handle OriginPolicy. We can skip the entire block if the OP policies have
     // already been passed down.
-    if (!content_security_policy_->HasPolicyFromSource(
+    if (!csp->HasPolicyFromSource(
             kContentSecurityPolicyHeaderSourceOriginPolicy)) {
-      std::unique_ptr<OriginPolicy> origin_policy =
-          OriginPolicy::From(StringUTF8Adaptor(origin_policy_).AsStringPiece());
+      std::unique_ptr<OriginPolicy> origin_policy = OriginPolicy::From(
+          StringUTF8Adaptor(origin_policy_string).AsStringPiece());
       if (origin_policy) {
-        for (auto csp : origin_policy->GetContentSecurityPolicies()) {
-          content_security_policy_->DidReceiveHeader(
-              WTF::String::FromUTF8(csp.policy.data(), csp.policy.length()),
-              csp.report_only ? kContentSecurityPolicyHeaderTypeReport
-                              : kContentSecurityPolicyHeaderTypeEnforce,
-              kContentSecurityPolicyHeaderSourceOriginPolicy);
+        for (auto policy : origin_policy->GetContentSecurityPolicies()) {
+          csp->DidReceiveHeader(WTF::String::FromUTF8(policy.policy.data(),
+                                                      policy.policy.length()),
+                                policy.report_only
+                                    ? kContentSecurityPolicyHeaderTypeReport
+                                    : kContentSecurityPolicyHeaderTypeEnforce,
+                                kContentSecurityPolicyHeaderSourceOriginPolicy);
         }
       }
     }
   }
-  if (!content_security_policy_->AllowAncestors(frame_,
-                                                response.CurrentRequestUrl())) {
-    CancelLoadAfterCSPDenied(response);
-    return false;
-  }
+  if (!csp->AllowAncestors(frame_, response.CurrentRequestUrl()))
+    return nullptr;
 
   if (!frame_->GetSettings()->BypassCSP() &&
       !GetFrameLoader().RequiredCSP().IsEmpty()) {
@@ -748,17 +760,16 @@
         frame_->Tree().Parent()->GetSecurityContext()->GetSecurityOrigin();
     if (ContentSecurityPolicy::ShouldEnforceEmbeddersPolicy(
             response, parent_security_origin)) {
-      content_security_policy_->AddPolicyFromHeaderValue(
-          GetFrameLoader().RequiredCSP(),
-          kContentSecurityPolicyHeaderTypeEnforce,
-          kContentSecurityPolicyHeaderSourceHTTP);
+      csp->AddPolicyFromHeaderValue(GetFrameLoader().RequiredCSP(),
+                                    kContentSecurityPolicyHeaderTypeEnforce,
+                                    kContentSecurityPolicyHeaderSourceHTTP);
     } else {
       ContentSecurityPolicy* required_csp = ContentSecurityPolicy::Create();
       required_csp->AddPolicyFromHeaderValue(
           GetFrameLoader().RequiredCSP(),
           kContentSecurityPolicyHeaderTypeEnforce,
           kContentSecurityPolicyHeaderSourceHTTP);
-      if (!required_csp->Subsumes(*content_security_policy_)) {
+      if (!required_csp->Subsumes(*csp)) {
         String message = "Refused to display '" +
                          response.CurrentRequestUrl().ElidedString() +
                          "' because it has not opted-into the following policy "
@@ -768,11 +779,21 @@
             kSecurityMessageSource, kErrorMessageLevel, message,
             response.CurrentRequestUrl(), this, MainResourceIdentifier());
         frame_->GetDocument()->AddConsoleMessage(console_message);
-        CancelLoadAfterCSPDenied(response);
-        return false;
+        return nullptr;
       }
     }
   }
+  return csp;
+}
+
+bool DocumentLoader::HandleResponse(const ResourceResponse& response) {
+  DCHECK(frame_);
+  application_cache_host_->DidReceiveResponseForMainResource(response);
+
+  AtomicString mixed_content_header = response.HttpHeaderField("mixed-content");
+  if (EqualIgnoringASCIICase(mixed_content_header, "noupgrade")) {
+    frame_->GetDocument()->SetMixedAutoupgradeOptOut(true);
+  }
 
   // Pre-commit state, count usage the use counter associated with "this"
   // (provisional document loader) instead of frame_'s document loader.
@@ -849,7 +870,7 @@
 
   InstallNewDocument(
       Url(), initiator_origin, owner_document,
-      frame_->ShouldReuseDefaultView(Url(), GetContentSecurityPolicy())
+      frame_->ShouldReuseDefaultView(Url(), content_security_policy_.Get())
           ? WebGlobalObjectReusePolicy::kUseExisting
           : WebGlobalObjectReusePolicy::kCreateNew,
       mime_type, encoding, InstallNewDocumentReason::kNavigation,
@@ -927,10 +948,6 @@
   data_buffer_->Clear();
 }
 
-void DocumentLoader::AppendRedirect(const KURL& url) {
-  redirect_chain_.push_back(url);
-}
-
 void DocumentLoader::StopLoading() {
   fetcher_->StopFetching();
   body_loader_.reset();
@@ -1020,31 +1037,13 @@
   DCHECK(params_);
   state_ = kProvisional;
 
-  if (!params_->is_static_data && WillLoadUrlAsEmpty(url_)) {
+  if (loading_url_as_empty_document_) {
     LoadEmpty();
     return;
   }
 
-  // See WebNavigationParams for special case explanations.
-  if (params_->is_static_data) {
+  if (params_->is_static_data)
     has_substitute_data_ = true;
-  } else if (fetcher_->Archive()) {
-    // If we have an archive loaded in some ancestor frame, we should
-    // retrieve document content from that archive. This is different from
-    // loading an archive into this frame, which will be handled separately
-    // once we load the body and parse it as an archive.
-    params_->body_loader.reset();
-    ArchiveResource* archive_resource =
-        fetcher_->Archive()->SubresourceForURL(url_);
-    if (archive_resource) {
-      SharedBuffer* archive_data = archive_resource->Data();
-      WebNavigationParams::FillStaticResponse(
-          params_.get(), archive_resource->MimeType(),
-          archive_resource->TextEncoding(),
-          base::make_span(archive_data->Data(), archive_data->size()));
-    }
-  }
-
   body_loader_ = std::move(params_->body_loader);
   if (!body_loader_) {
     // TODO(dgozman): we should try to get rid of this case.
@@ -1088,7 +1087,7 @@
 
   GetFrameLoader().Progress().WillStartLoading(main_resource_identifier_,
                                                ResourceLoadPriority::kVeryHigh);
-  probe::willSendNavigationRequest(GetFrame()->GetDocument(),
+  probe::willSendNavigationRequest(probe::ToCoreProbeSink(GetFrame()),
                                    main_resource_identifier_, this, url_,
                                    http_method_, http_body_.get());
 
@@ -1109,7 +1108,7 @@
     http_content_type_ = g_null_atom;
     // TODO(dgozman): check whether clearing origin policy is intended behavior.
     origin_policy_ = String();
-    probe::willSendNavigationRequest(GetFrame()->GetDocument(),
+    probe::willSendNavigationRequest(probe::ToCoreProbeSink(GetFrame()),
                                      main_resource_identifier_, this, url_,
                                      http_method_, http_body_.get());
     ResourceResponse redirect_response =
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 8960d67..1aab175 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -187,8 +187,6 @@
     return application_cache_host_.Get();
   }
 
-  void AppendRedirect(const KURL&);
-
   ClientHintsPreferences& GetClientHintsPreferences() {
     return client_hints_preferences_;
   }
@@ -291,6 +289,8 @@
 
   void CommitData(const char* bytes, size_t length);
 
+  ContentSecurityPolicy* CreateCSP(const ResourceResponse&,
+                                   const String& origin_policy_string);
   void StartLoadingInternal();
   void FinishedLoading(TimeTicks finish_time);
   void CancelLoadAfterCSPDenied(const ResourceResponse&);
@@ -429,6 +429,7 @@
   bool listing_ftp_directory_ = false;
   bool loading_mhtml_archive_ = false;
   bool loading_srcdoc_ = false;
+  bool loading_url_as_empty_document_ = false;
   unsigned long main_resource_identifier_ = 0;
   scoped_refptr<ResourceTimingInfo> navigation_timing_info_;
   bool report_timing_info_to_parent_ = false;
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 00d219e..694085f 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -429,8 +429,8 @@
         WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
   }
 
-  probe::prepareRequest(GetFrame()->GetDocument(), MasterDocumentLoader(),
-                        request, initiator_info, resource_type);
+  probe::prepareRequest(Probe(), MasterDocumentLoader(), request,
+                        initiator_info, resource_type);
 
   // ServiceWorker hook ups.
   if (MasterDocumentLoader()->GetServiceWorkerNetworkProvider()) {
@@ -462,9 +462,9 @@
     GetFrame()->Loader().Progress().WillStartLoading(identifier,
                                                      request.Priority());
   }
-  probe::willSendRequest(GetFrame()->GetDocument(), identifier,
-                         MasterDocumentLoader(), request, redirect_response,
-                         initiator_info, resource_type);
+  probe::willSendRequest(Probe(), identifier, MasterDocumentLoader(), Url(),
+                         request, redirect_response, initiator_info,
+                         resource_type);
   if (IdlenessDetector* idleness_detector = GetFrame()->GetIdlenessDetector())
     idleness_detector->OnWillSendRequest(MasterDocumentLoader()->Fetcher());
   if (frame_or_imported_document_->GetDocument()) {
@@ -664,8 +664,7 @@
     }
   }
 
-  if (resource->IsLoadEventBlockingResourceType())
-    frame_or_imported_document_->GetDocument()->CheckCompleted();
+  frame_or_imported_document_->GetDocument()->CheckCompleted();
 }
 
 void FrameFetchContext::DidObserveLoadingBehavior(
@@ -957,9 +956,9 @@
     ResourceType resource_type) const {
   if (GetResourceFetcherProperties().IsDetached())
     return;
-  probe::didBlockRequest(GetFrame()->GetDocument(), resource_request,
-                         MasterDocumentLoader(), fetch_initiator_info,
-                         blocked_reason, resource_type);
+  probe::didBlockRequest(Probe(), resource_request, MasterDocumentLoader(),
+                         Url(), fetch_initiator_info, blocked_reason,
+                         resource_type);
 }
 
 bool FrameFetchContext::ShouldBypassMainWorldCSP() const {
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 3c083f9..1d8f9e8 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1019,8 +1019,6 @@
   provisional_document_loader_ = Client()->CreateDocumentLoader(
       frame_, navigation_type, std::move(navigation_params),
       std::move(extra_data));
-  provisional_document_loader_->AppendRedirect(
-      provisional_document_loader_->Url());
   if (history_item)
     provisional_document_loader_->SetItemForHistoryNavigation(history_item);
 
@@ -1101,8 +1099,6 @@
   provisional_document_loader_ = Client()->CreateDocumentLoader(
       frame_, info.navigation_type, std::move(navigation_params),
       std::move(extra_data));
-  provisional_document_loader_->AppendRedirect(
-      provisional_document_loader_->Url());
   frame_->GetFrameScheduler()->DidStartProvisionalLoad(frame_->IsMainFrame());
   probe::didStartProvisionalLoad(frame_);
   virtual_time_pauser_.PauseVirtualTime();
diff --git a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
index e286ce62..2a508a0 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
@@ -79,6 +79,8 @@
     }
 
     // Step 13.3. "Set worker global scope's url to response's url." [spec text]
+    global_scope_->InitializeURL(response_url);
+
     // Step 13.4. "Set worker global scope's HTTPS state to response's HTTPS
     // state." [spec text]
 
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
index a686f1c..aabef2993 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
@@ -20,7 +20,9 @@
 #include "third_party/blink/renderer/core/workers/worker_content_settings_client.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
 #include "third_party/blink/renderer/platform/network/network_utils.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -93,7 +95,7 @@
     const FetchInitiatorInfo& fetch_initiator_info,
     ResourceRequestBlockedReason blocked_reason,
     ResourceType resource_type) const {
-  probe::didBlockRequest(global_scope_, resource_request, nullptr,
+  probe::didBlockRequest(Probe(), resource_request, nullptr, Url(),
                          fetch_initiator_info, blocked_reason, resource_type);
 }
 
@@ -164,7 +166,9 @@
 }
 
 const KURL& WorkerFetchContext::Url() const {
-  return global_scope_->Url();
+  return GetResourceFetcherProperties()
+      .GetFetchClientSettingsObject()
+      .GlobalObjectUrl();
 }
 
 const SecurityOrigin* WorkerFetchContext::GetParentSecurityOrigin() const {
@@ -198,7 +202,7 @@
   WrappedResourceRequest webreq(request);
   web_context_->WillSendRequest(webreq);
 
-  probe::prepareRequest(global_scope_, nullptr, request, initiator_info,
+  probe::prepareRequest(Probe(), nullptr, request, initiator_info,
                         resource_type);
 }
 
@@ -219,7 +223,7 @@
     const ResourceResponse& redirect_response,
     ResourceType resource_type,
     const FetchInitiatorInfo& initiator_info) {
-  probe::willSendRequest(global_scope_, identifier, nullptr, request,
+  probe::willSendRequest(Probe(), identifier, nullptr, Url(), request,
                          redirect_response, initiator_info, resource_type);
 }
 
diff --git a/third_party/blink/renderer/core/mojo/mojo.cc b/third_party/blink/renderer/core/mojo/mojo.cc
index 2cb32825..d57a82e 100644
--- a/third_party/blink/renderer/core/mojo/mojo.cc
+++ b/third_party/blink/renderer/core/mojo/mojo.cc
@@ -5,9 +5,11 @@
 #include "third_party/blink/renderer/core/mojo/mojo.h"
 
 #include <string>
+#include <utility>
 
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -114,4 +116,31 @@
   }
 }
 
+// static
+MojoHandle* Mojo::getDocumentInterfaceBrokerHandle(ScriptState* script_state) {
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  DCHECK(execution_context);
+  Document* document = static_cast<Document*>(execution_context);
+  DCHECK(document);
+
+  mojo::MessagePipe pipe;
+  document->BindDocumentInterfaceBroker(std::move(pipe.handle0));
+  return MojoHandle::Create(mojo::ScopedHandle::From(std::move(pipe.handle1)));
+}
+
+// static
+MojoHandle* Mojo::replaceDocumentInterfaceBrokerForTesting(
+    ScriptState* script_state,
+    MojoHandle* test_broker_handle) {
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  DCHECK(execution_context);
+  Document* document = static_cast<Document*>(execution_context);
+  DCHECK(document);
+
+  return MojoHandle::Create(
+      mojo::ScopedHandle::From(document->SetDocumentInterfaceBrokerForTesting(
+          mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(
+              test_broker_handle->TakeHandle().release().value())))));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/mojo/mojo.h b/third_party/blink/renderer/core/mojo/mojo.h
index a81831c..dc38c5a 100644
--- a/third_party/blink/renderer/core/mojo/mojo.h
+++ b/third_party/blink/renderer/core/mojo/mojo.h
@@ -55,6 +55,9 @@
                             const String& interface_name,
                             MojoHandle*,
                             const String& scope);
+  static MojoHandle* getDocumentInterfaceBrokerHandle(ScriptState*);
+  static MojoHandle* replaceDocumentInterfaceBrokerForTesting(ScriptState*,
+                                                              MojoHandle*);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/mojo/mojo.idl b/third_party/blink/renderer/core/mojo/mojo.idl
index 294f0b8..162c2fe 100644
--- a/third_party/blink/renderer/core/mojo/mojo.idl
+++ b/third_party/blink/renderer/core/mojo/mojo.idl
@@ -47,4 +47,6 @@
     static MojoCreateSharedBufferResult createSharedBuffer(unsigned long numBytes);
 
     [CallWith=ScriptState] static void bindInterface(DOMString interfaceName, MojoHandle request_handle, optional MojoScope scope = "context");
+    [CallWith=ScriptState] static MojoHandle getDocumentInterfaceBrokerHandle();
+    [CallWith=ScriptState] static MojoHandle replaceDocumentInterfaceBrokerForTesting(MojoHandle test_broker_handle);
 };
diff --git a/third_party/blink/renderer/core/page/page_widget_delegate.cc b/third_party/blink/renderer/core/page/page_widget_delegate.cc
index 50d836f5..4d98a13c 100644
--- a/third_party/blink/renderer/core/page/page_widget_delegate.cc
+++ b/third_party/blink/renderer/core/page/page_widget_delegate.cc
@@ -74,6 +74,11 @@
   }
 }
 
+void PageWidgetDelegate::DidBeginFrame(LocalFrame& root) {
+  if (LocalFrameView* frame_view = root.View())
+    frame_view->RunPostLifecycleSteps();
+}
+
 void PageWidgetDelegate::PaintContent(cc::PaintCanvas* canvas,
                                       const WebRect& rect,
                                       LocalFrame& root) {
diff --git a/third_party/blink/renderer/core/page/page_widget_delegate.h b/third_party/blink/renderer/core/page/page_widget_delegate.h
index 926e97e..774b64a 100644
--- a/third_party/blink/renderer/core/page/page_widget_delegate.h
+++ b/third_party/blink/renderer/core/page/page_widget_delegate.h
@@ -34,6 +34,7 @@
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/web/web_widget.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace cc {
 class PaintCanvas;
@@ -73,6 +74,8 @@
 
 // Common implementation of WebViewImpl and WebPagePopupImpl.
 class CORE_EXPORT PageWidgetDelegate {
+  STATIC_ONLY(PageWidgetDelegate);
+
  public:
   static void Animate(Page&, base::TimeTicks monotonic_frame_begin_time);
 
@@ -85,6 +88,9 @@
                               WebWidget::LifecycleUpdate requested_update,
                               WebWidget::LifecycleUpdateReason reason);
 
+  // See comment of WebWidget::DidBeginFrame
+  static void DidBeginFrame(LocalFrame& root);
+
   // See documents of methods with the same names in FrameView class.
   static void PaintContent(cc::PaintCanvas*, const WebRect&, LocalFrame& root);
   // See FIXME in the function body about nullptr |root|.
@@ -92,9 +98,6 @@
       PageWidgetEventHandler&,
       const WebCoalescedInputEvent& coalesced_event,
       LocalFrame* root);
-
- private:
-  PageWidgetDelegate() {}
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h
index 8d77719..ca37e5c2 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
 namespace cc {
@@ -20,6 +21,8 @@
 // TODO(kenrb): This class could be temporary depending on how
 // https://crbug.com/680606 is resolved.
 class CORE_EXPORT ScrollingCoordinatorContext final {
+  USING_FAST_MALLOC(ScrollingCoordinatorContext);
+
  public:
   ScrollingCoordinatorContext() {}
   virtual ~ScrollingCoordinatorContext() {}
diff --git a/third_party/blink/renderer/core/page/slot_scoped_traversal.h b/third_party/blink/renderer/core/page/slot_scoped_traversal.h
index 88c2bfc..9b8728b 100644
--- a/third_party/blink/renderer/core/page/slot_scoped_traversal.h
+++ b/third_party/blink/renderer/core/page/slot_scoped_traversal.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SLOT_SCOPED_TRAVERSAL_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -13,6 +14,8 @@
 class HTMLSlotElement;
 
 class CORE_EXPORT SlotScopedTraversal {
+  STATIC_ONLY(SlotScopedTraversal);
+
  public:
   static HTMLSlotElement* FindScopeOwnerSlot(const Element&);
   static Element* NearestInclusiveAncestorAssignedToSlot(const Element&);
diff --git a/third_party/blink/renderer/core/paint/clip_rects_cache.h b/third_party/blink/renderer/core/paint/clip_rects_cache.h
index 65f97d3..833c66d2 100644
--- a/third_party/blink/renderer/core/paint/clip_rects_cache.h
+++ b/third_party/blink/renderer/core/paint/clip_rects_cache.h
@@ -8,7 +8,7 @@
 #include "third_party/blink/renderer/core/paint/clip_rects.h"
 
 #if DCHECK_IS_ON()
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"  // For OverlayScrollbarClipBehavior.
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"  // For OverlayScrollbarClipBehavior.
 #endif
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 0e82d5a..2b90f50 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -60,6 +60,7 @@
 #include "third_party/blink/renderer/core/paint/paint_result.h"
 #include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_clipper.h b/third_party/blink/renderer/core/paint/paint_layer_clipper.h
index e566327..86f9cc8 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_clipper.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_clipper.h
@@ -48,7 +48,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/paint/clip_rects_cache.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 02651d0..de833994 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -51,9 +51,9 @@
 #include "third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index e1dc05f..f69506cf 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -24,19 +24,19 @@
 static constexpr TimeDelta kTimerDelay = TimeDelta::FromSeconds(1);
 constexpr size_t kTextNodeNumberLimit = 5000;
 
-static bool LargeTextOnTop(const std::unique_ptr<TextRecord>& a,
-                           const std::unique_ptr<TextRecord>& b) {
-  return a->first_size < b->first_size;
+static bool LargeTextFirst(const base::WeakPtr<TextRecord>& a,
+                           const base::WeakPtr<TextRecord>& b) {
+  return a->first_size > b->first_size;
 }
 
-static bool LateTextOnTop(const std::unique_ptr<TextRecord>& a,
-                          const std::unique_ptr<TextRecord>& b) {
-  return a->first_paint_time < b->first_paint_time;
+static bool LateTextFirst(const base::WeakPtr<TextRecord>& a,
+                          const base::WeakPtr<TextRecord>& b) {
+  return a->first_paint_time > b->first_paint_time;
 }
 
 TextPaintTimingDetector::TextPaintTimingDetector(LocalFrameView* frame_view)
-    : largest_text_heap_(&LargeTextOnTop),
-      latest_text_heap_(&LateTextOnTop),
+    : size_ordered_set_(&LargeTextFirst),
+      time_ordered_set_(&LateTextFirst),
       timer_(frame_view->GetFrame().GetTaskRunner(TaskType::kInternalDefault),
              this,
              &TextPaintTimingDetector::TimerFired),
@@ -127,18 +127,10 @@
 void TextPaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
   if (!is_recording_)
     return;
-  for (TextRecord& record : texts_to_record_swap_time_) {
-    if (record.node_id == node_id)
-      record.node_id = kInvalidDOMNodeId;
-  }
-  if (recorded_text_node_ids_.find(node_id) == recorded_text_node_ids_.end())
+  if (id_record_map_.find(node_id) == id_record_map_.end())
     return;
-  // We assume that removed nodes' id would not be recycled, and it's expensive
-  // to remove records from largest_text_heap_ and latest_text_heap_, so we
-  // intentionally keep the records of removed nodes in largest_text_heap_ and
-  // latest_text_heap_.
-  recorded_text_node_ids_.erase(node_id);
-  if (recorded_text_node_ids_.size() == 0) {
+  detached_ids_.insert(node_id);
+  if (id_record_map_.size() - detached_ids_.size() == 0) {
     const bool largest_text_paint_invalidated =
         largest_text_paint_ != base::TimeTicks();
     const bool last_text_paint_invalidated =
@@ -174,15 +166,16 @@
   // consumed in a callback earlier than this one. That violates the assumption
   // that only one or zero callback will be called after one OnPaintFinished.
   DCHECK_GT(texts_to_record_swap_time_.size(), 0UL);
-  for (TextRecord& record : texts_to_record_swap_time_) {
-    if (record.node_id == kInvalidDOMNodeId)
-      continue;
-    record.first_paint_time = timestamp;
-    recorded_text_node_ids_.insert(record.node_id);
-    largest_text_heap_.push(std::make_unique<TextRecord>(record));
-    latest_text_heap_.push(std::make_unique<TextRecord>(record));
+  while (texts_to_record_swap_time_.size() > 0) {
+    DOMNodeId node_id = texts_to_record_swap_time_.front();
+    DCHECK(id_record_map_.Contains(node_id));
+    TextRecord* record = id_record_map_.at(node_id);
+    record->first_paint_time = timestamp;
+    size_ordered_set_.insert(record->AsWeakPtr());
+    time_ordered_set_.insert(record->AsWeakPtr());
+
+    texts_to_record_swap_time_.pop();
   }
-  texts_to_record_swap_time_.clear();
   awaiting_swap_promise_ = false;
 }
 
@@ -201,9 +194,12 @@
   // early-returns if the text has been recorded.
   if (size_zero_node_ids_.find(node_id) != size_zero_node_ids_.end())
     return;
-  if (recorded_text_node_ids_.find(node_id) != recorded_text_node_ids_.end())
+  // The node is reattached.
+  if (id_record_map_.Contains(node_id) && detached_ids_.Contains(node_id))
+    detached_ids_.erase(node_id);
+  if (id_record_map_.find(node_id) != id_record_map_.end())
     return;
-  // When node_id is not found in recorded_text_node_ids_, this invalidation is
+  // When node_id is not found in id_record_map_, this invalidation is
   // the text's first invalidation.
 
   uint64_t rect_size = 0;
@@ -221,27 +217,27 @@
     size_zero_node_ids_.insert(node_id);
   } else {
     // Non-trivial text is found.
-    TextRecord record;
-    record.node_id = node_id;
-    record.first_size = rect_size;
+    std::unique_ptr<TextRecord> record = std::make_unique<TextRecord>();
+    record->node_id = node_id;
+    record->first_size = rect_size;
 #ifndef NDEBUG
     if (object.IsText()) {
-      record.text = ToLayoutText(&object)->GetText();
+      record->text = ToLayoutText(&object)->GetText();
     } else if (object.IsFileUploadControl()) {
-      record.text = ToLayoutFileUploadControl(&object)->FileTextValue();
+      record->text = ToLayoutFileUploadControl(&object)->FileTextValue();
     } else {
-      record.text = String("NON-TEXT_OBJECT");
+      record->text = String("NON-TEXT_OBJECT");
     }
 #endif
-    texts_to_record_swap_time_.push_back(record);
+    id_record_map_.insert(node_id, std::move(record));
+    texts_to_record_swap_time_.push(node_id);
   }
 
-  if (recorded_text_node_ids_.size() + size_zero_node_ids_.size() +
-          texts_to_record_swap_time_.size() >=
+  if (id_record_map_.size() + size_zero_node_ids_.size() >=
       kTextNodeNumberLimit) {
     TRACE_EVENT_INSTANT2("loading", "TextPaintTimingDetector::OverNodeLimit",
                          TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
-                         recorded_text_node_ids_.size(), "size_zero_node_count",
+                         id_record_map_.size(), "size_zero_node_count",
                          size_zero_node_ids_.size());
     StopRecordEntries();
   }
@@ -253,22 +249,22 @@
 }
 
 TextRecord* TextPaintTimingDetector::FindLargestPaintCandidate() {
-  return FindCandidate(largest_text_heap_);
+  return FindCandidate(size_ordered_set_);
 }
 
 TextRecord* TextPaintTimingDetector::FindLastPaintCandidate() {
-  return FindCandidate(latest_text_heap_);
+  return FindCandidate(time_ordered_set_);
 }
 
-TextRecord* TextPaintTimingDetector::FindCandidate(TextRecordHeap& heap) {
-  while (!heap.empty() &&
-         !recorded_text_node_ids_.Contains(heap.top()->node_id)) {
-    // If recorded_text_node_ids_ doesn't have record.node_id, the node has
-    // been deleted. We discard the records of deleted node.
-    heap.pop();
+TextRecord* TextPaintTimingDetector::FindCandidate(
+    const TextRecordSet& ordered_set) {
+  for (auto it = ordered_set.begin(); it != ordered_set.end(); ++it) {
+    if (detached_ids_.Contains((*it)->node_id) ||
+        (*it)->first_paint_time.is_null())
+      continue;
+    DCHECK(id_record_map_.Contains((*it)->node_id));
+    return (*it).get();
   }
-  if (!heap.empty())
-    return heap.top().get();
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index 0ef8460..e349e49 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -18,9 +18,11 @@
 class LocalFrameView;
 class PropertyTreeState;
 
-struct TextRecord {
+class TextRecord : public base::SupportsWeakPtr<TextRecord> {
+ public:
   DOMNodeId node_id = kInvalidDOMNodeId;
   uint64_t first_size = 0;
+  // This is treated as unset.
   base::TimeTicks first_paint_time = base::TimeTicks();
 #ifndef NDEBUG
   String text = "";
@@ -53,12 +55,10 @@
   using ReportTimeCallback =
       WTF::CrossThreadFunction<void(WebLayerTreeView::SwapResult,
                                     base::TimeTicks)>;
-  using TextRecordHeapComparator = bool (*)(const std::unique_ptr<TextRecord>&,
-                                            const std::unique_ptr<TextRecord>&);
-  using TextRecordHeap =
-      std::priority_queue<std::unique_ptr<TextRecord>,
-                          std::vector<std::unique_ptr<TextRecord>>,
-                          TextRecordHeapComparator>;
+  using TextRecordSetComparator = bool (*)(const base::WeakPtr<TextRecord>&,
+                                           const base::WeakPtr<TextRecord>&);
+  using TextRecordSet =
+      std::set<base::WeakPtr<TextRecord>, TextRecordSetComparator>;
   friend class TextPaintTimingDetectorTest;
 
  public:
@@ -87,15 +87,16 @@
   void ReportSwapTime(WebLayerTreeView::SwapResult result,
                       base::TimeTicks timestamp);
   void RegisterNotifySwapTime(ReportTimeCallback callback);
-  TextRecord* FindCandidate(TextRecordHeap& heap);
   void OnLargestTextDetected(const TextRecord&);
   void OnLastTextDetected(const TextRecord&);
+  TextRecord* FindCandidate(const TextRecordSet& ordered_set);
 
-  HashSet<DOMNodeId> recorded_text_node_ids_;
+  HashMap<DOMNodeId, std::unique_ptr<TextRecord>> id_record_map_;
   HashSet<DOMNodeId> size_zero_node_ids_;
-  TextRecordHeap largest_text_heap_;
-  TextRecordHeap latest_text_heap_;
-  std::vector<TextRecord> texts_to_record_swap_time_;
+  HashSet<DOMNodeId> detached_ids_;
+  TextRecordSet size_ordered_set_;
+  TextRecordSet time_ordered_set_;
+  std::queue<DOMNodeId> texts_to_record_swap_time_;
 
   // Make sure that at most one swap promise is ongoing.
   bool awaiting_swap_promise_ = false;
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
index b78708c..047f613 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
@@ -28,10 +28,20 @@
     return GetFrameView().GetPaintTimingDetector();
   }
 
-  unsigned CountRecords() {
+  unsigned CountVisibleTexts() {
+    return GetPaintTimingDetector()
+               .GetTextPaintTimingDetector()
+               .id_record_map_.size() -
+           GetPaintTimingDetector()
+               .GetTextPaintTimingDetector()
+               .detached_ids_.size();
+    ;
+  }
+
+  unsigned CountDetachedTexts() {
     return GetPaintTimingDetector()
         .GetTextPaintTimingDetector()
-        .recorded_text_node_ids_.size();
+        .detached_ids_.size();
   }
 
   void InvokeCallback() {
@@ -145,7 +155,8 @@
   GetDocument().getElementById("parent")->RemoveChild(
       GetDocument().getElementById("remove"));
   InvokeCallback();
-  EXPECT_EQ(CountRecords(), 0u);
+  EXPECT_EQ(CountVisibleTexts(), 0u);
+  EXPECT_EQ(CountDetachedTexts(), 1u);
 }
 
 TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_LargestText) {
@@ -191,6 +202,35 @@
   EXPECT_GE(time3, second_last);
 }
 
+// There is a risk that a text that is just recorded is selected to be the
+// metric candidate. The algorithm should skip the text record if its paint time
+// hasn't been recorded yet.
+TEST_F(TextPaintTimingDetectorTest, PendingTextIsLargest) {
+  SetBodyInnerHTML(R"HTML(
+  )HTML");
+  AppendDivElementToBody("text");
+  GetFrameView().UpdateAllLifecyclePhases(
+      DocumentLifecycle::LifecycleUpdateReason::kTest);
+  // We do not call swap-time callback here in order to not set the paint time.
+  EXPECT_FALSE(TextRecordOfLargestTextPaint());
+}
+
+// The same node may be visited by recordText for twice before the paint time
+// is set. In some previous design, this caused the node to be recorded twice.
+TEST_F(TextPaintTimingDetectorTest, VisitSameNodeTwiceBeforePaintTimeIsSet) {
+  SetBodyInnerHTML(R"HTML(
+  )HTML");
+  Element* text = AppendDivElementToBody("text");
+  GetFrameView().UpdateAllLifecyclePhases(
+      DocumentLifecycle::LifecycleUpdateReason::kTest);
+  // Change a property of the text to trigger repaint.
+  text->setAttribute(html_names::kStyleAttr, AtomicString("color:red;"));
+  GetFrameView().UpdateAllLifecyclePhases(
+      DocumentLifecycle::LifecycleUpdateReason::kTest);
+  InvokeCallback();
+  EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id, NodeIdOfText(text));
+}
+
 TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_ReportFirstPaintTime) {
   TimeTicks time1 = CurrentTimeTicks();
   SetBodyInnerHTML(R"HTML(
@@ -365,10 +405,10 @@
   // tracking node while layout ng is using the layout text as the tracking
   // node.
   if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
-    EXPECT_EQ(CountRecords(), 1u);
+    EXPECT_EQ(CountVisibleTexts(), 1u);
   } else {
     // The text and the elllipsis are recorded.
-    EXPECT_EQ(CountRecords(), 2u);
+    EXPECT_EQ(CountVisibleTexts(), 2u);
   }
 }
 
@@ -377,7 +417,7 @@
   Element* element = GetDocument().QuerySelector("input");
   UpdateAllLifecyclePhasesAndSimulateSwapTime();
 
-  EXPECT_EQ(CountRecords(), 1u);
+  EXPECT_EQ(CountVisibleTexts(), 1u);
   EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id,
             DOMNodeIds::IdForNode(element));
 }
@@ -393,7 +433,7 @@
   )HTML");
   UpdateAllLifecyclePhasesAndSimulateSwapTime();
 
-  EXPECT_EQ(CountRecords(), 0u);
+  EXPECT_EQ(CountVisibleTexts(), 0u);
 }
 
 TEST_F(TextPaintTimingDetectorTest, CaptureSVGText) {
@@ -407,7 +447,7 @@
       ToSVGTextContentElement(GetDocument().QuerySelector("text"));
   UpdateAllLifecyclePhasesAndSimulateSwapTime();
 
-  EXPECT_EQ(CountRecords(), 1u);
+  EXPECT_EQ(CountVisibleTexts(), 1u);
   EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id, NodeIdOfText(elem));
 }
 
diff --git a/third_party/blink/renderer/core/probe/core_probes.pidl b/third_party/blink/renderer/core/probe/core_probes.pidl
index 92ca4aa0..63f2a98 100644
--- a/third_party/blink/renderer/core/probe/core_probes.pidl
+++ b/third_party/blink/renderer/core/probe/core_probes.pidl
@@ -89,11 +89,11 @@
   void didPaint(LocalFrame*, const cc::Layer*, const LayoutRect&);
   void applyAcceptLanguageOverride(ExecutionContext*, String* acceptLanguage);
   void applyUserAgentOverride(CoreProbeSink*, String* userAgent);
-  void didBlockRequest([Keep] ExecutionContext*, const ResourceRequest&, DocumentLoader*, const FetchInitiatorInfo&, ResourceRequestBlockedReason, ResourceType);
+  void didBlockRequest(CoreProbeSink*, const ResourceRequest&, DocumentLoader*, const KURL& fetch_context_url, const FetchInitiatorInfo&, ResourceRequestBlockedReason, ResourceType);
   void didChangeResourcePriority(LocalFrame*, DocumentLoader*, unsigned long identifier, ResourceLoadPriority loadPriority);
-  void prepareRequest([Keep] ExecutionContext*, DocumentLoader*, ResourceRequest&, const FetchInitiatorInfo&, ResourceType);
-  void willSendRequest([Keep] ExecutionContext*, unsigned long identifier, DocumentLoader*, const ResourceRequest&, const ResourceResponse& redirectResponse, const FetchInitiatorInfo&, ResourceType);
-  void willSendNavigationRequest([Keep] ExecutionContext*, unsigned long identifier, DocumentLoader*, const KURL&, const AtomicString& http_method, EncodedFormData*);
+  void prepareRequest(CoreProbeSink*, DocumentLoader*, ResourceRequest&, const FetchInitiatorInfo&, ResourceType);
+  void willSendRequest(CoreProbeSink*, unsigned long identifier, DocumentLoader*, const KURL& fetch_context_url, const ResourceRequest&, const ResourceResponse& redirectResponse, const FetchInitiatorInfo&, ResourceType);
+  void willSendNavigationRequest(CoreProbeSink*, unsigned long identifier, DocumentLoader*, const KURL&, const AtomicString& http_method, EncodedFormData*);
   void markResourceAsCached(LocalFrame*, DocumentLoader*, unsigned long identifier);
   void didReceiveResourceResponse(CoreProbeSink*, unsigned long identifier, DocumentLoader*, const ResourceResponse&, Resource*);
   void didReceiveData(CoreProbeSink*, unsigned long identifier, DocumentLoader*, const char* data, uint64_t dataLength);
@@ -104,7 +104,7 @@
   void didFailLoading(CoreProbeSink*, unsigned long identifier, DocumentLoader*, const ResourceError&);
   void willSendEventSourceRequest(ExecutionContext*, ThreadableLoaderClient* eventSource);
   void willDispatchEventSourceEvent(ExecutionContext*, unsigned long identifier, const AtomicString& eventName, const AtomicString& eventId, const String& data);
-  void willLoadXHR(ExecutionContext*, XMLHttpRequest* xhr, ThreadableLoaderClient* client, const AtomicString& method, const KURL& url, bool async, EncodedFormData* form_data, const HTTPHeaderMap& headers, bool includeCredentials);
+  void willLoadXHR([Keep] ExecutionContext*, const AtomicString& method, const KURL& url, bool async, EncodedFormData* form_data, const HTTPHeaderMap& headers, bool includeCredentials);
   void didFinishXHR(ExecutionContext*, XMLHttpRequest* xhr);
   void scriptImported(ExecutionContext*, unsigned long identifier, const String& sourceString);
   void scriptExecutionBlockedByCSP(ExecutionContext*, const String& directiveText);
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index 084ee25..5b483fa1 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -32,9 +32,9 @@
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
diff --git a/third_party/blink/renderer/core/testing/page_test_base.cc b/third_party/blink/renderer/core/testing/page_test_base.cc
index 5698d8f..8c8e8b25 100644
--- a/third_party/blink/renderer/core/testing/page_test_base.cc
+++ b/third_party/blink/renderer/core/testing/page_test_base.cc
@@ -117,6 +117,7 @@
 void PageTestBase::UpdateAllLifecyclePhasesForTest() {
   GetDocument().View()->UpdateAllLifecyclePhases(
       DocumentLifecycle::LifecycleUpdateReason::kTest);
+  GetDocument().View()->RunPostLifecycleSteps();
 }
 
 StyleEngine& PageTestBase::GetStyleEngine() {
diff --git a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
index e06c443..ec460ab 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
@@ -60,14 +60,9 @@
 
   last_frame_time_ += base::TimeDelta::FromSecondsD(time_delta_in_seconds);
 
-  SimCanvas::Commands commands;
-  paint_commands_ = &commands;
-
   layer_tree_view_->layer_tree_host()->Composite(last_frame_time_,
                                                  /*raster=*/false);
-
-  paint_commands_ = nullptr;
-  return commands;
+  return PaintFrame();
 }
 
 SimCanvas::Commands SimCompositor::PaintFrame() {
@@ -104,7 +99,6 @@
   web_view_->MainFrameWidget()->BeginFrame(last_frame_time_, false);
   web_view_->MainFrameWidget()->UpdateAllLifecyclePhases(
       WebWidget::LifecycleUpdateReason::kTest);
-  *paint_commands_ = PaintFrame();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/sim/sim_compositor.h b/third_party/blink/renderer/core/testing/sim/sim_compositor.h
index 466733e..259e28e 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_compositor.h
+++ b/third_party/blink/renderer/core/testing/sim/sim_compositor.h
@@ -98,6 +98,7 @@
   void RequestNewLayerTreeFrameSink(
       LayerTreeFrameSinkCallback callback) override;
   void BeginMainFrame(base::TimeTicks frame_time) override;
+  void DidBeginMainFrame() override { web_view_->DidBeginFrame(); }
 
   WebViewImpl* web_view_ = nullptr;
   content::LayerTreeView* layer_tree_view_ = nullptr;
@@ -106,10 +107,6 @@
 
   base::TimeTicks last_frame_time_;
 
-  // During BeginFrame(), painting is done, and the result is stored here to
-  // be returned from BeginFrame().
-  SimCanvas::Commands* paint_commands_;
-
   std::unique_ptr<cc::ScopedDeferMainFrameUpdate>
       scoped_defer_main_frame_update_;
 };
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 926ed473..8834dae 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -95,6 +95,11 @@
       InstanceCounters::kWorkerGlobalScopeCounter);
 }
 
+NOINLINE const KURL& WorkerGlobalScope::Url() const {
+  CHECK(url_.IsValid());
+  return url_;
+}
+
 KURL WorkerGlobalScope::CompleteURL(const String& url) const {
   // Always return a null URL when passed a null string.
   // FIXME: Should we change the KURL constructor to have this behavior?
@@ -104,6 +109,22 @@
   return KURL(BaseURL(), url);
 }
 
+const KURL& WorkerGlobalScope::BaseURL() const {
+  return Url();
+}
+
+NOINLINE void WorkerGlobalScope::InitializeURL(const KURL& url) {
+  CHECK(url_.IsNull());
+  DCHECK(url.IsValid());
+  if (GetSecurityOrigin()->IsOpaque()) {
+    DCHECK(SecurityOrigin::Create(url)->IsOpaque());
+  } else {
+    DCHECK(GetSecurityOrigin()->IsSameSchemeHostPort(
+        SecurityOrigin::Create(url).get()));
+  }
+  url_ = url;
+}
+
 void WorkerGlobalScope::Dispose() {
   DCHECK(IsContextThread());
   closing_ = true;
@@ -120,7 +141,7 @@
 
 WorkerLocation* WorkerGlobalScope::location() const {
   if (!location_)
-    location_ = WorkerLocation::Create(url_);
+    location_ = WorkerLocation::Create(Url());
   return location_.Get();
 }
 
@@ -424,9 +445,11 @@
                         classic_script_loader->SourceText());
 
   // Step 12.3. "Set worker global scope's url to response's url."
+  InitializeURL(classic_script_loader->ResponseURL());
+
   // Step 12.4. "Set worker global scope's HTTPS state to response's HTTPS
   // state."
-  // These are done in the constructor of WorkerGlobalScope.
+  // This is done in the constructor of WorkerGlobalScope.
 
   // Step 12.5. "Set worker global scope's referrer policy to the result of
   // parsing the `Referrer-Policy` header of response."
@@ -501,7 +524,6 @@
           creation_params->worker_clients,
           std::move(creation_params->web_worker_fetch_context),
           thread->GetWorkerReportingProxy()),
-      url_(creation_params->script_url),
       script_type_(creation_params->script_type),
       user_agent_(creation_params->user_agent),
       parent_devtools_token_(creation_params->parent_devtools_token),
@@ -517,7 +539,8 @@
                             : creation_params->agent_cluster_id) {
   InstanceCounters::IncrementCounter(
       InstanceCounters::kWorkerGlobalScopeCounter);
-  scoped_refptr<SecurityOrigin> security_origin = SecurityOrigin::Create(url_);
+  scoped_refptr<SecurityOrigin> security_origin =
+      SecurityOrigin::Create(creation_params->script_url);
   if (creation_params->starter_origin) {
     security_origin->TransferPrivilegesFrom(
         creation_params->starter_origin->CreatePrivilegeData());
@@ -535,12 +558,14 @@
   BindContentSecurityPolicyToExecutionContext();
   SetWorkerSettings(std::move(creation_params->worker_settings));
 
-  // Set the referrer policy here for workers whose script is fetched on the
-  // main thread. For off-the-main-thread fetches, it is instead set after the
-  // script is fetched.
+  // Set the URL and referrer policy here for workers whose script is fetched
+  // on the main thread. For off-the-main-thread fetches, they are instead set
+  // after the script is fetched.
   if (creation_params->off_main_thread_fetch_option ==
-      OffMainThreadWorkerScriptFetchOption::kDisabled)
+      OffMainThreadWorkerScriptFetchOption::kDisabled) {
+    InitializeURL(creation_params->script_url);
     SetReferrerPolicy(creation_params->referrer_policy);
+  }
 
   SetAddressSpace(creation_params->address_space);
   OriginTrialContext::AddTokens(this,
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index 192a99e..a581a4d0 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -112,17 +112,19 @@
                              ExceptionState&);
 
   // ExecutionContext
-  const KURL& Url() const final { return url_; }
+  const KURL& Url() const final;
   KURL CompleteURL(const String&) const final;
   bool IsWorkerGlobalScope() const final { return true; }
   bool IsContextThread() const final;
-  const KURL& BaseURL() const final { return url_; }
+  const KURL& BaseURL() const final;
   String UserAgent() const final { return user_agent_; }
   HttpsState GetHttpsState() const override { return https_state_; }
   const base::UnguessableToken& GetAgentClusterID() const final {
     return agent_cluster_id_;
   }
 
+  void InitializeURL(const KURL& url);
+
   DOMTimerCoordinator* Timers() final { return &timers_; }
   SecurityContext& GetSecurityContext() final { return *this; }
   void AddConsoleMessage(ConsoleMessage*) final;
@@ -239,7 +241,7 @@
   // ExecutionContext
   EventTarget* ErrorEventTarget() final { return this; }
 
-  const KURL url_;
+  KURL url_;
   const mojom::ScriptType script_type_;
   const String user_agent_;
   const base::UnguessableToken parent_devtools_token_;
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index 56e3b3c..b832979 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -158,7 +158,7 @@
   const Member<WorkerOrWorkletGlobalScope> global_scope_for_logging_;
 };
 
-};  // namespace
+}  // namespace
 
 WorkerOrWorkletGlobalScope::WorkerOrWorkletGlobalScope(
     v8::Isolate* isolate,
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
index 184e25a1..e55b9984 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -447,6 +447,9 @@
     }
     GetWorkerBackingThread().BackingThread().AddTaskObserver(this);
 
+    // TODO(crbug.com/866666): Ideally this URL should be the response URL of
+    // the worker top-level script, while currently can be the request URL
+    // for off-the-main-thread top-level script fetch cases.
     const KURL url_for_debugger = global_scope_creation_params->script_url;
 
     console_message_storage_ = MakeGarbageCollected<ConsoleMessageStorage>();
@@ -455,7 +458,8 @@
     worker_reporting_proxy_.DidCreateWorkerGlobalScope(GlobalScope());
 
     worker_inspector_controller_ = WorkerInspectorController::Create(
-        this, inspector_task_runner_, std::move(devtools_params));
+        this, url_for_debugger, inspector_task_runner_,
+        std::move(devtools_params));
 
     // Since context initialization below may fail, we should notify debugger
     // about the new worker thread separately, so that it can resolve it by id
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 13fad22..fe5a798 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -1069,8 +1069,8 @@
   request.SetExternalRequestStateFromRequestorAddressSpace(
       execution_context.GetSecurityContext().AddressSpace());
 
-  probe::willLoadXHR(&execution_context, this, this, method_, url_, async_,
-                     http_body.get(), request_headers_, with_credentials_);
+  probe::willLoadXHR(&execution_context, method_, url_, async_, http_body.get(),
+                     request_headers_, with_credentials_);
 
   if (http_body) {
     DCHECK_NE(method_, http_names::kGET);
diff --git a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
index 365d09a..5cbb2c7f 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
@@ -383,19 +383,19 @@
     background-image: url(Images/popoverArrows.png);
 }
 
-.force-white-icons [is=ui-icon].spritesheet-smallicons, [is=ui-icon].force-white-icons.spritesheet-smallicons, -theme-preserve {
+:host-context(.force-white-icons) [is=ui-icon].spritesheet-smallicons, .force-white-icons [is=ui-icon].spritesheet-smallicons, [is=ui-icon].force-white-icons.spritesheet-smallicons, -theme-preserve {
     -webkit-mask-image: -webkit-image-set(url(Images/smallIcons.png) 1x, url(Images/smallIcons_2x.png) 2x);
     -webkit-mask-position: var(--spritesheet-position);
     background: #fafafa !important;
 }
 
-.force-white-icons [is=ui-icon].spritesheet-largeicons, [is=ui-icon].force-white-icons.spritesheet-largeicons, -theme-preserve {
+:host-context(.force-white-icons) [is=ui-icon].spritesheet-largeicons, .force-white-icons [is=ui-icon].spritesheet-largeicons, [is=ui-icon].force-white-icons.spritesheet-largeicons, -theme-preserve {
     -webkit-mask-image: -webkit-image-set(url(Images/largeIcons.png) 1x, url(Images/largeIcons_2x.png) 2x);
     -webkit-mask-position: var(--spritesheet-position);
     background: #fafafa !important;
 }
 
-.force-white-icons [is=ui-icon].spritesheet-mediumicons, [is=ui-icon].force-white-icons.spritesheet-mediumicons, -theme-preserve {
+:host-context(.force-white-icons) [is=ui-icon].spritesheet-mediumicon, .force-white-icons [is=ui-icon].spritesheet-mediumicons, [is=ui-icon].force-white-icons.spritesheet-mediumicons, -theme-preserve {
     -webkit-mask-image: -webkit-image-set(url(Images/mediumIcons.png) 1x, url(Images/mediumIcons_2x.png) 2x);
     -webkit-mask-position: var(--spritesheet-position);
     background: #fafafa !important;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
index 059ad04..f09d9ff 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
@@ -100,6 +100,11 @@
   return native_transport_.get();
 }
 
+void RTCDtlsTransport::ChangeState(webrtc::DtlsTransportInformation info) {
+  DCHECK(current_state_.state() != webrtc::DtlsTransportState::kClosed);
+  current_state_ = info;
+}
+
 // Implementation of DtlsTransportProxy::Delegate
 void RTCDtlsTransport::OnStartCompleted(webrtc::DtlsTransportInformation info) {
   current_state_ = info;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h
index 523aa04..c8093e5 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h
@@ -64,6 +64,8 @@
   ExecutionContext* GetExecutionContext() const override;
   // For garbage collection.
   void Trace(blink::Visitor* visitor) override;
+  // Others
+  void ChangeState(webrtc::DtlsTransportInformation info);
   webrtc::DtlsTransportInterface* native_transport();
 
  private:
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 0b1230d..132466f 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -1171,49 +1171,6 @@
          user_media_controller->HasRequestedUserMedia();
 }
 
-RTCDtlsTransport* RTCPeerConnection::LookupDtlsTransportByMid(String mid) {
-  if (mid.IsNull())
-    return nullptr;
-  webrtc::PeerConnectionInterface* native_pc =
-      peer_handler_->NativePeerConnection();
-  if (!native_pc)
-    return nullptr;
-  auto native_transport =
-      native_pc->LookupDtlsTransportByMid(std::string(mid.Utf8().data()));
-  if (!native_transport)
-    return nullptr;
-  // Check for previously created RTCDtlsTransport objects referencing
-  // this transport.
-  auto transport_lookup_result = dtls_transports_by_mid_.find(mid);
-  if (transport_lookup_result != dtls_transports_by_mid_.end()) {
-    if (transport_lookup_result->value->native_transport() !=
-        native_transport.get()) {
-      // The mid's transport has changed. Erase the reference to
-      // the old transport, and continue.
-      dtls_transports_by_mid_.erase(transport_lookup_result);
-    } else {
-      return transport_lookup_result->value;
-    }
-  }
-
-  // Check if the same transport has been returned for another mid.
-  for (auto const& transport_iterator : dtls_transports_by_mid_) {
-    if (transport_iterator.value->native_transport() ==
-        native_transport.get()) {
-      // insert might invalidate the iterator (or not), so be safe.
-      RTCDtlsTransport* transport = transport_iterator.value;
-      dtls_transports_by_mid_.insert(mid, transport);
-      return transport;
-    }
-  }
-  // The transport is previously unseen. Create object and hold on to
-  // a reference to it.
-  RTCDtlsTransport* transport = MakeGarbageCollected<RTCDtlsTransport>(
-      GetExecutionContext(), native_transport);
-  dtls_transports_by_mid_.insert(mid, transport);
-  return transport;
-}
-
 void RTCPeerConnection::ReportSetSdpUsage(
     SetSdpOperationType operation_type,
     const RTCSessionDescriptionInit* session_description_init) const {
@@ -2346,7 +2303,7 @@
 HeapVector<Member<RTCRtpReceiver>>::iterator RTCPeerConnection::FindReceiver(
     const WebRTCRtpReceiver& web_receiver) {
   for (auto* it = rtp_receivers_.begin(); it != rtp_receivers_.end(); ++it) {
-    if ((*it)->web_receiver().Id() == web_receiver.Id())
+    if ((*it)->web_receiver()->Id() == web_receiver.Id())
       return it;
   }
   return rtp_receivers_.end();
@@ -2391,6 +2348,9 @@
     DCHECK_EQ(sender->web_sender()->Id(), web_sender->Id());
     sender->SetTrack(track);
   }
+  sender->set_transport(CreateOrUpdateDtlsTransport(
+      sender->web_sender()->DtlsTransport(),
+      sender->web_sender()->DtlsTransportInformation()));
   return sender;
 }
 
@@ -2425,9 +2385,12 @@
   } else {
     // Update existing receiver is a no-op.
     receiver = *receiver_it;
-    DCHECK_EQ(receiver->web_receiver().Id(), web_receiver->Id());
+    DCHECK_EQ(receiver->web_receiver()->Id(), web_receiver->Id());
     DCHECK_EQ(receiver->track(), track);  // Its track should never change.
   }
+  receiver->set_transport(CreateOrUpdateDtlsTransport(
+      receiver->web_receiver()->DtlsTransport(),
+      receiver->web_receiver()->DtlsTransportInformation()));
   return receiver;
 }
 
@@ -2459,6 +2422,27 @@
   return transceiver;
 }
 
+RTCDtlsTransport* RTCPeerConnection::CreateOrUpdateDtlsTransport(
+    rtc::scoped_refptr<webrtc::DtlsTransportInterface> native_transport,
+    const webrtc::DtlsTransportInformation& information) {
+  if (!native_transport.get()) {
+    return nullptr;
+  }
+  auto transport_locator =
+      dtls_transports_by_native_transport_.find(native_transport);
+  if (transport_locator != dtls_transports_by_native_transport_.end()) {
+    auto transport = transport_locator->value;
+    transport->ChangeState(information);
+    return transport;
+  }
+  RTCDtlsTransport* transport = MakeGarbageCollected<RTCDtlsTransport>(
+      GetExecutionContext(), native_transport);
+  dtls_transports_by_native_transport_.insert(native_transport.get(),
+                                              transport);
+  transport->ChangeState(information);
+  return transport;
+}
+
 RTCDTMFSender* RTCPeerConnection::createDTMFSender(
     MediaStreamTrack* track,
     ExceptionState& exception_state) {
@@ -3076,7 +3060,7 @@
   visitor->Trace(rtp_receivers_);
   visitor->Trace(transceivers_);
   visitor->Trace(scheduled_events_);
-  visitor->Trace(dtls_transports_by_mid_);
+  visitor->Trace(dtls_transports_by_native_transport_);
   EventTargetWithInlineData::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
   MediaStreamObserver::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index 5d72237..a4d24017 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -404,6 +404,12 @@
   RTCRtpTransceiver* CreateOrUpdateTransceiver(
       std::unique_ptr<WebRTCRtpTransceiver>);
 
+  // Creates or updates the RTCDtlsTransport object corresponding to the
+  // given webrtc::DtlsTransportInterface object.
+  RTCDtlsTransport* CreateOrUpdateDtlsTransport(
+      rtc::scoped_refptr<webrtc::DtlsTransportInterface>,
+      const webrtc::DtlsTransportInformation& info);
+
   // Update the |receiver->streams()| to the streams indicated by |stream_ids|,
   // adding to |remove_list| and |add_list| accordingly.
   // https://w3c.github.io/webrtc-pc/#set-associated-remote-streams
@@ -481,10 +487,11 @@
   HeapVector<Member<RTCRtpReceiver>> rtp_receivers_;
   HeapVector<Member<RTCRtpTransceiver>> transceivers_;
 
-  // A map of all transports that have been looked up by MID.
-  // A transport may be referenced by more than one mid, so may
-  // be present multiple times in the table.
-  HeapHashMap<String, Member<RTCDtlsTransport>> dtls_transports_by_mid_;
+  // A map of all webrtc::DtlsTransports that have a corresponding
+  // RTCDtlsTransport object. Garbage collection will remove map entries
+  // when they are no longer in use.
+  HeapHashMap<webrtc::DtlsTransportInterface*, WeakMember<RTCDtlsTransport>>
+      dtls_transports_by_native_transport_;
 
   std::unique_ptr<WebRTCPeerConnectionHandler> peer_handler_;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
index b540e08..960c004 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_source.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_capabilities.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
@@ -36,9 +37,7 @@
 }
 
 RTCDtlsTransport* RTCRtpReceiver::transport() {
-  if (!transceiver_)
-    return nullptr;
-  return pc_->LookupDtlsTransportByMid(transceiver_->mid());
+  return transport_;
 }
 
 RTCDtlsTransport* RTCRtpReceiver::rtcp_transport() {
@@ -90,8 +89,8 @@
   return promise;
 }
 
-const WebRTCRtpReceiver& RTCRtpReceiver::web_receiver() const {
-  return *receiver_;
+WebRTCRtpReceiver* RTCRtpReceiver::web_receiver() {
+  return receiver_.get();
 }
 
 MediaStreamVector RTCRtpReceiver::streams() const {
@@ -106,6 +105,10 @@
   transceiver_ = transceiver;
 }
 
+void RTCRtpReceiver::set_transport(RTCDtlsTransport* transport) {
+  transport_ = transport;
+}
+
 void RTCRtpReceiver::UpdateSourcesIfNeeded() {
   if (!web_sources_needs_updating_)
     return;
@@ -128,6 +131,7 @@
 void RTCRtpReceiver::Trace(blink::Visitor* visitor) {
   visitor->Trace(pc_);
   visitor->Trace(track_);
+  visitor->Trace(transport_);
   visitor->Trace(streams_);
   visitor->Trace(transceiver_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
index 60a17bf..ea1d6a7 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
@@ -48,10 +48,11 @@
   HeapVector<Member<RTCRtpContributingSource>> getContributingSources();
   ScriptPromise getStats(ScriptState*);
 
-  const WebRTCRtpReceiver& web_receiver() const;
+  WebRTCRtpReceiver* web_receiver();
   MediaStreamVector streams() const;
   void set_streams(MediaStreamVector streams);
   void set_transceiver(RTCRtpTransceiver*);
+  void set_transport(RTCDtlsTransport*);
   void UpdateSourcesIfNeeded();
 
   void Trace(blink::Visitor*) override;
@@ -62,6 +63,7 @@
 
   std::unique_ptr<WebRTCRtpReceiver> receiver_;
   Member<MediaStreamTrack> track_;
+  Member<RTCDtlsTransport> transport_;
   MediaStreamVector streams_;
 
   // The current SSRCs and CSRCs. getSynchronizationSources() returns the SSRCs
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index 9d91211..dde55c4 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_error_util.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
@@ -311,9 +312,7 @@
 }
 
 RTCDtlsTransport* RTCRtpSender::transport() {
-  if (!transceiver_)
-    return nullptr;
-  return pc_->LookupDtlsTransportByMid(transceiver_->mid());
+  return transport_;
 }
 
 RTCDtlsTransport* RTCRtpSender::rtcp_transport() {
@@ -477,6 +476,10 @@
   transceiver_ = transceiver;
 }
 
+void RTCRtpSender::set_transport(RTCDtlsTransport* transport) {
+  transport_ = transport;
+}
+
 RTCDTMFSender* RTCRtpSender::dtmf() {
   // Lazy initialization of dtmf_ to avoid overhead when not used.
   if (!dtmf_ && kind_ == "audio") {
@@ -494,6 +497,7 @@
 void RTCRtpSender::Trace(blink::Visitor* visitor) {
   visitor->Trace(pc_);
   visitor->Trace(track_);
+  visitor->Trace(transport_);
   visitor->Trace(dtmf_);
   visitor->Trace(streams_);
   visitor->Trace(last_returned_parameters_);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
index 1abbf95a..c216bec 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
@@ -64,6 +64,7 @@
   MediaStreamVector streams() const;
   void set_streams(MediaStreamVector streams);
   void set_transceiver(RTCRtpTransceiver*);
+  void set_transport(RTCDtlsTransport*);
 
   void Trace(blink::Visitor*) override;
 
@@ -74,6 +75,7 @@
   // a copy here as long as we support Plan B.
   String kind_;
   Member<MediaStreamTrack> track_;
+  Member<RTCDtlsTransport> transport_;
   Member<RTCDTMFSender> dtmf_;
   MediaStreamVector streams_;
   Member<RTCRtpSendParameters> last_returned_parameters_;
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.cc b/third_party/blink/renderer/modules/xr/xr_frame.cc
index a625f45..ae05c995 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame.cc
@@ -10,12 +10,26 @@
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/modules/xr/xr_view.h"
 #include "third_party/blink/renderer/modules/xr/xr_viewer_pose.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
 namespace blink {
 
+namespace {
+
+const char kInactiveFrame[] =
+    "XRFrame access outside the callback that produced it is invalid.";
+}
+
 XRFrame::XRFrame(XRSession* session) : session_(session) {}
 
-XRViewerPose* XRFrame::getViewerPose(XRReferenceSpace* reference_space) const {
+XRViewerPose* XRFrame::getViewerPose(XRReferenceSpace* reference_space,
+                                     ExceptionState& exception_state) const {
+  if (!active_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kInactiveFrame);
+    return nullptr;
+  }
+
   session_->LogGetPose();
 
   // Must use a reference space created from the same session.
@@ -46,7 +60,14 @@
 }
 
 XRInputPose* XRFrame::getInputPose(XRInputSource* input_source,
-                                   XRReferenceSpace* reference_space) const {
+                                   XRReferenceSpace* reference_space,
+                                   ExceptionState& exception_state) const {
+  if (!active_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      kInactiveFrame);
+    return nullptr;
+  }
+
   if (!input_source || !reference_space) {
     return nullptr;
   }
@@ -123,6 +144,10 @@
   base_pose_matrix_ = TransformationMatrix::Create(base_pose_matrix);
 }
 
+void XRFrame::Deactivate() {
+  active_ = false;
+}
+
 void XRFrame::Trace(blink::Visitor* visitor) {
   visitor->Trace(session_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.h b/third_party/blink/renderer/modules/xr/xr_frame.h
index 82adf5c..06e0a8f 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame.h
@@ -14,6 +14,7 @@
 
 namespace blink {
 
+class ExceptionState;
 class XRViewerPose;
 class XRInputPose;
 class XRInputSource;
@@ -28,16 +29,21 @@
 
   XRSession* session() const { return session_; }
 
-  XRViewerPose* getViewerPose(XRReferenceSpace*) const;
-  XRInputPose* getInputPose(XRInputSource*, XRReferenceSpace*) const;
+  XRViewerPose* getViewerPose(XRReferenceSpace*, ExceptionState&) const;
+  XRInputPose* getInputPose(XRInputSource*,
+                            XRReferenceSpace*,
+                            ExceptionState&) const;
 
   void SetBasePoseMatrix(const TransformationMatrix&);
 
   void Trace(blink::Visitor*) override;
 
+  void Deactivate();
+
  private:
   const Member<XRSession> session_;
   std::unique_ptr<TransformationMatrix> base_pose_matrix_;
+  bool active_ = true;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.idl b/third_party/blink/renderer/modules/xr/xr_frame.idl
index 2235225a..48d98f3 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.idl
+++ b/third_party/blink/renderer/modules/xr/xr_frame.idl
@@ -10,7 +10,7 @@
 ] interface XRFrame {
   readonly attribute XRSession session;
 
-  XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
-  XRInputPose? getInputPose(XRInputSource inputSource,
+  [RaisesException] XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
+  [RaisesException] XRInputPose? getInputPose(XRInputSource inputSource,
                             XRReferenceSpace referenceSpace);
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index a7f7d75..035c618 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -613,6 +613,9 @@
     // OnFrameEnd if it's still valid.
     if (!ended_)
       frame_base_layer->OnFrameEnd();
+
+    // Ensure the XRFrame cannot be used outside the callbacks.
+    presentation_frame->Deactivate();
   }
 }
 
@@ -725,6 +728,9 @@
 
   if (event->defaultPrevented())
     input_source->selection_cancelled = true;
+
+  // Ensure the frame cannot be used outside of the event handler.
+  event->frame()->Deactivate();
 }
 
 void XRSession::OnSelectEnd(XRInputSource* input_source) {
@@ -747,6 +753,9 @@
 
   if (event->defaultPrevented())
     input_source->selection_cancelled = true;
+
+  // Ensure the frame cannot be used outside of the event handler.
+  event->frame()->Deactivate();
 }
 
 void XRSession::OnSelect(XRInputSource* input_source) {
@@ -764,6 +773,9 @@
     XRInputSourceEvent* event =
         CreateInputSourceEvent(event_type_names::kSelect, input_source);
     DispatchEvent(*event);
+
+    // Ensure the frame cannot be used outside of the event handler.
+    event->frame()->Deactivate();
   }
 }
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index ce65cbaf..95c652e 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1106,6 +1106,7 @@
     "graphics/replaying_canvas.cc",
     "graphics/replaying_canvas.h",
     "graphics/scoped_interpolation_quality.h",
+    "graphics/scroll_types.h",
     "graphics/skia/image_pixel_locker.cc",
     "graphics/skia/image_pixel_locker.h",
     "graphics/skia/sk_size_hash.h",
@@ -1856,6 +1857,7 @@
     "//services/viz/public/interfaces:interfaces_blink",
     "//skia",
     "//skia:skcms",
+    "//skia:test_fonts",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party:freetype_harfbuzz",
@@ -1963,6 +1965,7 @@
 test("blink_fuzzer_unittests") {
   deps = [
     ":test_support",
+    "//skia:test_fonts",
     "//third_party/blink/public:test_support",
   ]
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
index 06930f5..0cdd260a 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
@@ -626,8 +626,11 @@
   HarfBuzzShaper shaper(string);
   scoped_refptr<ShapeResult> result = ShapeWithParameter(&shaper);
   EXPECT_EQ(length, result->NumCharacters());
-#if defined(OS_LINUX)
-  // Linux doesn't have glyphs. We can't test RunInfo without all glyphs.
+#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+  // Linux and Fuchsia use Lohit Devanagari. When using that font the shaper
+  // returns 32767 glyphs instead of 32769.
+  // TODO(crbug.com/933551): Add Noto Sans Devanagari to
+  // //third_party/test_fonts and use it here.
   if (result->NumGlyphs() != length)
     return;
 #endif
@@ -1476,8 +1479,8 @@
 }
 
 // TODO(crbug.com/870712): This test fails due to font fallback differences on
-// Android.
-#if defined(OS_ANDROID)
+// Android and Fuchsia.
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
 #define MAYBE_SafeToBreakArabicCommonLigatures \
   DISABLED_SafeToBreakArabicCommonLigatures
 #else
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher.h b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher.h
index a7db4f1..a1dd661 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher.h
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_ANIMATION_WORKLET_MUTATOR_DISPATCHER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_ANIMATION_WORKLET_MUTATOR_DISPATCHER_H_
 
+#include "base/bind.h"
 #include "third_party/blink/renderer/platform/graphics/animation_worklet_mutators_state.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
@@ -14,11 +15,20 @@
  public:
   virtual ~AnimationWorkletMutatorDispatcher() = default;
 
+  using AsyncMutationCompleteCallback = base::OnceCallback<void(MutateStatus)>;
+
   // Run the animation frame callbacks from all connected AnimationWorklets.
   virtual void MutateSynchronously(
       std::unique_ptr<AnimationWorkletDispatcherInput>) = 0;
-  virtual void MutateAsynchronously(
-      std::unique_ptr<AnimationWorkletDispatcherInput>) = 0;
+
+  // Queues the animation frame callbacks from all connected AnimationWorklets.
+  // The queuing strategy determines what action to take when busy servicing
+  // another request. The callback is triggered on completion or canceling of
+  // the mutation cycle. Returns true if mutations results are expected.
+  virtual bool MutateAsynchronously(
+      std::unique_ptr<AnimationWorkletDispatcherInput>,
+      MutateQueuingStrategy,
+      AsyncMutationCompleteCallback) = 0;
 
   // Returns true if Mutate may do something if called 'now'.
   virtual bool HasMutators() = 0;
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
index 02b1d00..2c08c50 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
@@ -122,25 +122,41 @@
       base::TimeDelta::FromMilliseconds(100), 50);
 }
 
-void AnimationWorkletMutatorDispatcherImpl::MutateAsynchronously(
-    std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input) {
-  if (mutator_map_.IsEmpty() || !mutator_input)
-    return;
+bool AnimationWorkletMutatorDispatcherImpl::MutateAsynchronously(
+    std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input,
+    MutateQueuingStrategy queuing_strategy,
+    AsyncMutationCompleteCallback done_callback) {
   DCHECK(client_);
   DCHECK(host_queue_->BelongsToCurrentThread());
+  if (mutator_map_.IsEmpty() || !mutator_input)
+    return false;
+
   if (!mutator_input_map_.IsEmpty()) {
-    // Still running mutations from a previous frame. Skip this frame to avoid
-    // lagging behind.
-    // TODO(kevers): Consider queuing pending mutation cycle. A pending tree
-    // mutation should likely be queued an active tree mutation cycle is still
-    // running.
-    return;
+    // Still running mutations from a previous frame.
+    if (queuing_strategy == MutateQueuingStrategy::kDrop) {
+      // Skip this frame to avoid lagging behind.
+      return false;
+    }
+    DCHECK(queuing_strategy == MutateQueuingStrategy::kQueueAndReplace);
+    DCHECK(!queued_mutator_input_);
+    // Preemptive queue.
+    queued_mutator_input_.reset(mutator_input.release());
+    queued_on_async_mutation_complete_ = std::move(done_callback);
+    return true;
   }
 
   mutator_input_map_ = CreateInputMap(*mutator_input);
   if (mutator_input_map_.IsEmpty())
-    return;
+    return false;
 
+  MutateAsynchronouslyInternal(std::move(done_callback));
+  return true;
+}
+
+void AnimationWorkletMutatorDispatcherImpl::MutateAsynchronouslyInternal(
+    AsyncMutationCompleteCallback done_callback) {
+  DCHECK(host_queue_->BelongsToCurrentThread());
+  on_async_mutation_complete_ = std::move(done_callback);
   int next_async_mutation_id = GetNextAsyncMutationId();
   TRACE_EVENT_ASYNC_BEGIN0("cc",
                            "AnimationWorkletMutatorDispatcherImpl::MutateAsync",
@@ -158,7 +174,6 @@
       },
       host_queue_, weak_factory_.GetWeakPtr(), next_async_mutation_id);
 
-  client_->NotifyAnimationsPending();
   RequestMutations(std::move(on_done));
 }
 
@@ -167,10 +182,17 @@
   DCHECK(client_);
   DCHECK(host_queue_->BelongsToCurrentThread());
   ApplyMutationsOnHostThread();
-  client_->NotifyAnimationsReady();
+  auto done_callback = std::move(on_async_mutation_complete_);
+  if (queued_mutator_input_.get()) {
+    mutator_input_map_ = CreateInputMap(*queued_mutator_input_);
+    queued_mutator_input_.reset();
+    // Trigger queued mutation request.
+    MutateAsynchronouslyInternal(std::move(queued_on_async_mutation_complete_));
+  }
   TRACE_EVENT_ASYNC_END0("cc",
                          "AnimationWorkletMutatorDispatcherImpl::MutateAsync",
                          async_mutation_id);
+  std::move(done_callback).Run(MutateStatus::kCompleted);
   // TODO(kevers): Add UMA metric to track the asynchronous mutate duration.
 }
 
@@ -229,6 +251,11 @@
   DCHECK(outputs_->get().IsEmpty());
 
   int num_requests = mutator_map_.size();
+  if (num_requests == 0) {
+    std::move(done_callback).Run();
+    return;
+  }
+
   int next_request_index = 0;
   outputs_->get().Grow(num_requests);
   base::RepeatingClosure on_mutator_done = base::BarrierClosure(
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h
index 1184e5b..fa90bbd 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h
@@ -51,8 +51,9 @@
   void MutateSynchronously(
       std::unique_ptr<AnimationWorkletDispatcherInput>) override;
 
-  void MutateAsynchronously(
-      std::unique_ptr<AnimationWorkletDispatcherInput>) override;
+  bool MutateAsynchronously(std::unique_ptr<AnimationWorkletDispatcherInput>,
+                            MutateQueuingStrategy,
+                            AsyncMutationCompleteCallback) override;
 
   // TODO(majidvp): Remove when timeline inputs are known.
   bool HasMutators() override;
@@ -91,11 +92,13 @@
 
   InputMap CreateInputMap(AnimationWorkletDispatcherInput& mutator_input) const;
 
-  // Dispatches mutation update requests.  The callback is triggered once
-  // all mutation updates have been computed on the animation worklet thread
-  // associated with the last mutation to complete.
+  // Dispatches mutation update requests. The callback is triggered once all
+  // mutation updates have been computed and it runs on the animation worklet
+  // thread associated with the last mutation to complete.
   void RequestMutations(WTF::CrossThreadClosure done_callback);
 
+  void MutateAsynchronouslyInternal(AsyncMutationCompleteCallback);
+
   void AsyncMutationsDone(int async_mutation_id);
 
   void ApplyMutationsOnHostThread();
@@ -131,6 +134,20 @@
   // the mutation cycle.
   scoped_refptr<OutputVectorRef> outputs_;
 
+  // Input queued for the next mutation cycle, which will automatically be
+  // triggered at the completion of the current cycle. Only one collection of
+  // inputs can be queued at any point in time. In a typical frame, we expect
+  // one request for the active tree, and one for the pending tree. The active
+  // tree mutation is best effort will be dropped when busy. A pending tree
+  // mutation is queued to ensure initial values are resolved. The pending tree
+  // activation is blocked until the previous request completes preventing the
+  // need to queue multiple requests.
+  std::unique_ptr<AnimationWorkletDispatcherInput> queued_mutator_input_;
+
+  // Active and queued callbacks for the completion of an async mutation cycle.
+  AsyncMutationCompleteCallback on_async_mutation_complete_;
+  AsyncMutationCompleteCallback queued_on_async_mutation_complete_;
+
   base::WeakPtrFactory<AnimationWorkletMutatorDispatcherImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AnimationWorkletMutatorDispatcherImpl);
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
index 7426c6d..d77bd6f 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
@@ -57,18 +57,34 @@
     return std::unique_ptr<AnimationWorkletOutput>(MutateRef(*input));
   }
 
+  // Blocks the worklet thread by posting a task that will complete only when
+  // signaled. This blocking ensures that tests of async mutations do not
+  // encounter race conditions when validating queuing strategies.
+  void BlockWorkletThread() {
+    PostCrossThreadTask(
+        *expected_runner_, FROM_HERE,
+        CrossThreadBind(
+            [](base::WaitableEvent* start_processing_event) {
+              start_processing_event->Wait();
+            },
+            WTF::CrossThreadUnretained(&start_processing_event_)));
+  }
+
+  void UnblockWorkletThread() { start_processing_event_.Signal(); }
+
   MOCK_CONST_METHOD0(GetWorkletId, int());
   MOCK_METHOD1(MutateRef,
                AnimationWorkletOutput*(const AnimationWorkletInput&));
 
   scoped_refptr<base::SingleThreadTaskRunner> expected_runner_;
+  base::WaitableEvent start_processing_event_;
 };
 
 class MockCompositorMutatorClient : public CompositorMutatorClient {
  public:
   MockCompositorMutatorClient(
       std::unique_ptr<AnimationWorkletMutatorDispatcherImpl> mutator)
-      : CompositorMutatorClient(std::move(mutator)), done_event_(nullptr) {}
+      : CompositorMutatorClient(std::move(mutator)) {}
   ~MockCompositorMutatorClient() override {}
   // gmock cannot mock methods with move-only args so we forward it to ourself.
   void SetMutationUpdate(
@@ -76,33 +92,15 @@
     SetMutationUpdateRef(output_state.get());
   }
 
-  MOCK_METHOD0(NotifyAnimationsPending, void());
-
-  void NotifyAnimationsReady() override {
-    NotifyAnimationsReadyRef();
-    if (done_event_) {
-      done_event_->Signal();
-    }
-  }
-
-  MOCK_METHOD0(NotifyAnimationsReadyRef, void());
-
-  void SignalWhenComplete(base::WaitableEvent* done_event) {
-    done_event_ = done_event;
-  }
-
   MOCK_METHOD1(SetMutationUpdateRef,
                void(cc::MutatorOutputState* output_state));
-
- private:
-  base::WaitableEvent* done_event_;  // not owned.
 };
 
 class AnimationWorkletMutatorDispatcherImplTest : public ::testing::Test {
  public:
   void SetUp() override {
-    auto mutator =
-        std::make_unique<AnimationWorkletMutatorDispatcherImpl>(false);
+    auto mutator = std::make_unique<AnimationWorkletMutatorDispatcherImpl>(
+        /*main_thread_task_runner=*/true);
     mutator_ = mutator.get();
     client_ =
         std::make_unique<::testing::StrictMock<MockCompositorMutatorClient>>(
@@ -233,9 +231,7 @@
 
   // Ensure mutator is not invoked after unregistration.
   EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0);
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
   EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
-  EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(0);
   mutator_->UnregisterAnimationWorkletMutator(first_mutator);
 
   mutator_->MutateSynchronously(CreateTestMutatorInput());
@@ -340,41 +336,49 @@
 class AnimationWorkletMutatorDispatcherImplAsyncTest
     : public AnimationWorkletMutatorDispatcherImplTest {
  public:
-  void SetUp() override {
-    if (!Thread::CompositorThread()) {
-      Thread::CreateAndSetCompositorThread();
-    }
-    AnimationWorkletMutatorDispatcherImplTest::SetUp();
+  AnimationWorkletMutatorDispatcher::AsyncMutationCompleteCallback
+  CreateIntermediateResultCallback(MutateStatus expected_result) {
+    return ConvertToBaseCallback(
+        CrossThreadBind(&AnimationWorkletMutatorDispatcherImplAsyncTest ::
+                            VerifyExpectedMutationResult,
+                        CrossThreadUnretained(this), expected_result));
   }
 
-  // Call this version of mutate and wait if expecting a mutate completion
-  // notification from the client.
-  void CallMutateAndWaitForClientCompletion(
-      MutateAsyncCallback mutate_callback) {
-    base::WaitableEvent done_event;
-    client_->SignalWhenComplete(&done_event);
-    PostCrossThreadTask(*Thread::CompositorThread()->GetTaskRunner(), FROM_HERE,
-                        std::move(mutate_callback));
-    done_event.Wait();
+  AnimationWorkletMutatorDispatcher::AsyncMutationCompleteCallback
+  CreateNotReachedCallback() {
+    return ConvertToBaseCallback(CrossThreadBind([](MutateStatus unused) {
+      NOTREACHED() << "Mutate complete callback should not have been triggered";
+    }));
   }
 
-  // Call this version of mutate and wait if there is no expectation of client
-  // notifications. There are no notificaitons if the mutate call is a no-op
-  // such as when there are no inputs.
-  void CallMutateAndWaitForCallbackCompletion(
-      MutateAsyncCallback mutate_callback) {
-    base::WaitableEvent done_event;
-    PostCrossThreadTask(*Thread::CompositorThread()->GetTaskRunner(), FROM_HERE,
-                        CrossThreadBind(
-                            [](MutateAsyncCallback mutate_callback,
-                               base::WaitableEvent* done_event) {
-                              mutate_callback.Run();
-                              done_event->Signal();
-                            },
-                            WTF::Passed(std::move(mutate_callback)),
-                            WTF::CrossThreadUnretained(&done_event)));
-    done_event.Wait();
+  AnimationWorkletMutatorDispatcher::AsyncMutationCompleteCallback
+  CreateTestCompleteCallback() {
+    return ConvertToBaseCallback(
+        CrossThreadBind(&AnimationWorkletMutatorDispatcherImplAsyncTest ::
+                            VerifyCompletedMutationResultAndFinish,
+                        CrossThreadUnretained(this)));
   }
+
+  // Executes run loop until quit closure is called.
+  void WaitForTestCompletion() { run_loop_.Run(); }
+
+  void VerifyExpectedMutationResult(MutateStatus expectation,
+                                    MutateStatus result) {
+    EXPECT_EQ(expectation, result);
+    IntermediateResultCallbackRef();
+  }
+
+  void VerifyCompletedMutationResultAndFinish(MutateStatus result) {
+    EXPECT_EQ(MutateStatus::kCompleted, result);
+    run_loop_.QuitClosure().Run();
+  }
+
+  // Verifying that intermediate result callbacks are invoked the correct number
+  // of times.
+  MOCK_METHOD0(IntermediateResultCallbackRef, void());
+
+ private:
+  base::RunLoop run_loop_;
 };
 
 TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
@@ -384,30 +388,22 @@
       MakeGarbageCollected<MockAnimationWorkletMutator>(
           first_thread->GetTaskRunner());
 
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback = CrossThreadBind(
-      [](std::unique_ptr<Thread> first_thread,
-         MockAnimationWorkletMutator* first_mutator,
-         AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            first_mutator, first_thread->GetTaskRunner());
-        async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
-      },
-      WTF::Passed(std::move(first_thread)),
-      WrapCrossThreadWeakPersistent(first_mutator),
-      WTF::CrossThreadUnretained(this));
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
 
-  Sequence s;
   EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_))
       .Times(1)
       .WillOnce(Return(new AnimationWorkletOutput()));
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
-  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1).InSequence(s);
-  EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
-  CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
+  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
+
+  EXPECT_TRUE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                             MutateQueuingStrategy::kDrop,
+                                             CreateTestCompleteCallback()));
+
+  WaitForTestCompletion();
 }
 
 TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
@@ -417,52 +413,32 @@
       MakeGarbageCollected<MockAnimationWorkletMutator>(
           first_thread->GetTaskRunner());
 
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback = CrossThreadBind(
-      [](std::unique_ptr<Thread> first_thread,
-         MockAnimationWorkletMutator* first_mutator,
-         AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            first_mutator, first_thread->GetTaskRunner());
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
 
-        AnimationWorkletInput::AddAndUpdateState state2{
-            {22, 2}, "test2", 5000, nullptr, 1};
+  AnimationWorkletInput::AddAndUpdateState state2{
+      {22, 2}, "test2", 5000, nullptr, 1};
 
-        auto input = std::make_unique<AnimationWorkletDispatcherInput>();
-        input->Add(std::move(state2));
+  auto input = std::make_unique<AnimationWorkletDispatcherInput>();
+  input->Add(std::move(state2));
 
-        async_test->mutator_->MutateAsynchronously(std::move(input));
-      },
-      WTF::Passed(std::move(first_thread)),
-      WrapCrossThreadWeakPersistent(first_mutator),
-      WTF::CrossThreadUnretained(this));
-
-  // The start of the mutation process will be synchronous. If a pending
-  // notification is not received by the time the callback returns, it will not
-  // be triggered later.
   EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
-  CallMutateAndWaitForCallbackCompletion(std::move(mutate_callback));
+
+  EXPECT_FALSE(mutator_->MutateAsynchronously(std::move(input),
+                                              MutateQueuingStrategy::kDrop,
+                                              CreateNotReachedCallback()));
 }
 
 TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
        MutationUpdateIsNotInvokedWithNoRegisteredAnimators) {
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback = CrossThreadBind(
-      [](AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        std::unique_ptr<AnimationWorkletDispatcherInput> input =
-            std::make_unique<AnimationWorkletDispatcherInput>();
-        async_test->mutator_->MutateAsynchronously(std::move(input));
-      },
-      WTF::CrossThreadUnretained(this));
-
-  // The start of the mutation process will be synchronous. If a pending
-  // notification is not received by the time the callback returns, it will not
-  // be triggered later.
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
-  CallMutateAndWaitForCallbackCompletion(std::move(mutate_callback));
+  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
+  std::unique_ptr<AnimationWorkletDispatcherInput> input =
+      std::make_unique<AnimationWorkletDispatcherInput>();
+  EXPECT_FALSE(mutator_->MutateAsynchronously(std::move(input),
+                                              MutateQueuingStrategy::kDrop,
+                                              CreateNotReachedCallback()));
 }
 
 TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
@@ -473,28 +449,20 @@
       MakeGarbageCollected<MockAnimationWorkletMutator>(
           first_thread->GetTaskRunner());
 
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback = CrossThreadBind(
-      [](std::unique_ptr<Thread> first_thread,
-         MockAnimationWorkletMutator* first_mutator,
-         AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            first_mutator, first_thread->GetTaskRunner());
-        async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
-      },
-      WTF::Passed(std::move(first_thread)),
-      WrapCrossThreadWeakPersistent(first_mutator),
-      WTF::CrossThreadUnretained(this));
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
 
-  Sequence s;
   EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_)).Times(1).WillOnce(Return(nullptr));
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
   EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
-  EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
-  CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
+
+  EXPECT_TRUE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                             MutateQueuingStrategy::kDrop,
+                                             CreateTestCompleteCallback()));
+
+  WaitForTestCompletion();
 }
 
 TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
@@ -505,50 +473,32 @@
       MakeGarbageCollected<MockAnimationWorkletMutator>(
           first_thread->GetTaskRunner());
 
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback = CrossThreadBind(
-      [](std::unique_ptr<Thread> first_thread,
-         MockAnimationWorkletMutator* first_mutator,
-         AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            first_mutator, first_thread->GetTaskRunner());
-        async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
-      },
-      WTF::Passed(std::move(first_thread)),
-      WrapCrossThreadWeakPersistent(first_mutator),
-      WTF::CrossThreadUnretained(this));
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
 
-  Sequence s;
   EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_))
       .Times(1)
       .WillOnce(Return(new AnimationWorkletOutput()));
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
-  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1).InSequence(s);
-  EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
-  CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
+  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
+
+  EXPECT_TRUE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                             MutateQueuingStrategy::kDrop,
+                                             CreateTestCompleteCallback()));
+
+  WaitForTestCompletion();
 
   // Above call blocks until complete signal is received.
   Mock::VerifyAndClearExpectations(client_.get());
 
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback2 = CrossThreadBind(
-      [](MockAnimationWorkletMutator* first_mutator,
-         AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        // Ensure mutator is not invoked after unregistration.
-        async_test->mutator_->UnregisterAnimationWorkletMutator(first_mutator);
-        async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
-      },
-      WrapCrossThreadWeakPersistent(first_mutator),
-      WTF::CrossThreadUnretained(this));
+  // Ensure mutator is not invoked after unregistration.
+  mutator_->UnregisterAnimationWorkletMutator(first_mutator);
+  EXPECT_FALSE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                              MutateQueuingStrategy::kDrop,
+                                              CreateNotReachedCallback()));
 
-  // The start of the mutation process will be synchronous. If a pending
-  // notification is not received by the time the callback returns, it will not
-  // be triggered later.
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
-  CallMutateAndWaitForCallbackCompletion(std::move(mutate_callback2));
   Mock::VerifyAndClearExpectations(client_.get());
 }
 
@@ -562,24 +512,11 @@
       MakeGarbageCollected<MockAnimationWorkletMutator>(
           first_thread->GetTaskRunner());
 
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback = CrossThreadBind(
-      [](std::unique_ptr<Thread> first_thread,
-         MockAnimationWorkletMutator* first_mutator,
-         MockAnimationWorkletMutator* second_mutator,
-         AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            first_mutator, first_thread->GetTaskRunner());
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            second_mutator, first_thread->GetTaskRunner());
-        async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
-      },
-      WTF::Passed(std::move(first_thread)),
-      WrapCrossThreadWeakPersistent(first_mutator),
-      WrapCrossThreadWeakPersistent(second_mutator),
-      WTF::CrossThreadUnretained(this));
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
+  mutator_->RegisterAnimationWorkletMutator(second_mutator,
+                                            first_thread->GetTaskRunner());
 
-  Sequence s;
   EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
@@ -592,10 +529,13 @@
   EXPECT_CALL(*second_mutator, MutateRef(_))
       .Times(1)
       .WillOnce(Return(new AnimationWorkletOutput()));
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
-  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2).InSequence(s);
-  EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
-  CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
+  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
+
+  EXPECT_TRUE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                             MutateQueuingStrategy::kDrop,
+                                             CreateTestCompleteCallback()));
+
+  WaitForTestCompletion();
 }
 
 TEST_F(
@@ -611,26 +551,11 @@
       MakeGarbageCollected<MockAnimationWorkletMutator>(
           second_thread->GetTaskRunner());
 
-  // Call MutateAsynchronously from the compositor thread.
-  MutateAsyncCallback mutate_callback = CrossThreadBind(
-      [](std::unique_ptr<Thread> first_thread,
-         std::unique_ptr<Thread> second_thread,
-         MockAnimationWorkletMutator* first_mutator,
-         MockAnimationWorkletMutator* second_mutator,
-         AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            first_mutator, first_thread->GetTaskRunner());
-        async_test->mutator_->RegisterAnimationWorkletMutator(
-            second_mutator, second_thread->GetTaskRunner());
-        async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
-      },
-      WTF::Passed(std::move(first_thread)),
-      WTF::Passed(std::move(second_thread)),
-      WrapCrossThreadWeakPersistent(first_mutator),
-      WrapCrossThreadWeakPersistent(second_mutator),
-      WTF::CrossThreadUnretained(this));
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
+  mutator_->RegisterAnimationWorkletMutator(second_mutator,
+                                            second_thread->GetTaskRunner());
 
-  Sequence s;
   EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
@@ -643,10 +568,84 @@
   EXPECT_CALL(*second_mutator, MutateRef(_))
       .Times(1)
       .WillOnce(Return(new AnimationWorkletOutput()));
-  EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
-  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2).InSequence(s);
-  EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
-  CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
+  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
+
+  EXPECT_TRUE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                             MutateQueuingStrategy::kDrop,
+                                             CreateTestCompleteCallback()));
+
+  WaitForTestCompletion();
+}
+
+TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
+       MutationUpdateDroppedWhenBusy) {
+  std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
+  MockAnimationWorkletMutator* first_mutator =
+      MakeGarbageCollected<MockAnimationWorkletMutator>(
+          first_thread->GetTaskRunner());
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
+
+  EXPECT_CALL(*first_mutator, GetWorkletId())
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(11));
+  EXPECT_CALL(*first_mutator, MutateRef(_))
+      .Times(1)
+      .WillOnce(Return(new AnimationWorkletOutput()));
+  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
+
+  // Block Responses until all requests have been queued.
+  first_mutator->BlockWorkletThread();
+  // Response for first mutator call is blocked until after the second
+  // call is sent.
+  EXPECT_TRUE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                             MutateQueuingStrategy::kDrop,
+                                             CreateTestCompleteCallback()));
+  // Second request dropped since busy processing first.
+  EXPECT_FALSE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
+                                              MutateQueuingStrategy::kDrop,
+                                              CreateNotReachedCallback()));
+  // Unblock first request.
+  first_mutator->UnblockWorkletThread();
+
+  WaitForTestCompletion();
+}
+
+TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
+       MutationUpdateQueuedWhenBusy) {
+  std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
+
+  MockAnimationWorkletMutator* first_mutator =
+      MakeGarbageCollected<MockAnimationWorkletMutator>(
+          first_thread->GetTaskRunner());
+  mutator_->RegisterAnimationWorkletMutator(first_mutator,
+                                            first_thread->GetTaskRunner());
+
+  EXPECT_CALL(*first_mutator, GetWorkletId())
+      .Times(AtLeast(2))
+      .WillRepeatedly(Return(11));
+  EXPECT_CALL(*first_mutator, MutateRef(_))
+      .Times(2)
+      .WillOnce(Return(new AnimationWorkletOutput()))
+      .WillOnce(Return(new AnimationWorkletOutput()));
+  EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
+  EXPECT_CALL(*this, IntermediateResultCallbackRef()).Times(1);
+
+  // Block Responses until all requests have been queued.
+  first_mutator->BlockWorkletThread();
+  // Response for first mutator call is blocked until after the second
+  // call is sent.
+  EXPECT_TRUE(mutator_->MutateAsynchronously(
+      CreateTestMutatorInput(), MutateQueuingStrategy::kDrop,
+      CreateIntermediateResultCallback(MutateStatus::kCompleted)));
+  // First request still processing, queue request.
+  EXPECT_TRUE(mutator_->MutateAsynchronously(
+      CreateTestMutatorInput(), MutateQueuingStrategy::kQueueAndReplace,
+      CreateTestCompleteCallback()));
+  // Unblock first request.
+  first_mutator->UnblockWorkletThread();
+
+  WaitForTestCompletion();
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutators_state.h b/third_party/blink/renderer/platform/graphics/animation_worklet_mutators_state.h
index f559e973..0cabaa5 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutators_state.h
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutators_state.h
@@ -14,6 +14,8 @@
 using AnimationWorkletDispatcherInput = cc::MutatorInputState;
 using AnimationWorkletDispatcherOutput = cc::MutatorOutputState;
 using WorkletAnimationId = cc::WorkletAnimationId;
+using MutateQueuingStrategy = cc::MutateQueuingStrategy;
+using MutateStatus = cc::MutateStatus;
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h
index f0a99e9..7be2a33 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h
@@ -8,7 +8,7 @@
 #include "base/optional.h"
 #include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h
index 049abc1c..470b482 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h
@@ -6,8 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_CLIP_CACHE_H_
 
 #include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
+#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/graphics/scroll_types.h b/third_party/blink/renderer/platform/graphics/scroll_types.h
new file mode 100644
index 0000000..1f00a31
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/scroll_types.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SCROLL_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SCROLL_TYPES_H_
+
+namespace blink {
+
+// Platform overlay scrollbars are controlled and painted by the operating
+// system (e.g., OSX and Android).  CSS overlay scrollbars are created by
+// setting overflow:overlay, and they are painted by chromium.
+enum OverlayScrollbarClipBehavior {
+  kIgnorePlatformOverlayScrollbarSize,
+  kIgnorePlatformAndCSSOverlayScrollbarSize,
+  kExcludeOverlayScrollbarSizeForHitTesting
+};
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/blink/renderer/platform/scroll/scroll_types.h b/third_party/blink/renderer/platform/scroll/scroll_types.h
index 6be6f516..da6ee9a 100644
--- a/third_party/blink/renderer/platform/scroll/scroll_types.h
+++ b/third_party/blink/renderer/platform/scroll/scroll_types.h
@@ -44,15 +44,6 @@
   return ScrollOffset(p.X(), p.Y());
 }
 
-// Platform overlay scrollbars are controlled and painted by the operating
-// system (e.g., OSX and Android).  CSS overlay scrollbars are created by
-// setting overflow:overlay, and they are painted by chromium.
-enum OverlayScrollbarClipBehavior {
-  kIgnorePlatformOverlayScrollbarSize,
-  kIgnorePlatformAndCSSOverlayScrollbarSize,
-  kExcludeOverlayScrollbarSizeForHitTesting
-};
-
 using ScrollDirection = WebScrollDirection;
 using ScrollGranularity = WebScrollGranularity;
 
@@ -272,4 +263,4 @@
 
 }  // namespace blink
 
-#endif
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_TYPES_H_
diff --git a/third_party/blink/renderer/platform/testing/run_all_tests.cc b/third_party/blink/renderer/platform/testing/run_all_tests.cc
index 8b7ea4b..88ed726 100644
--- a/third_party/blink/renderer/platform/testing/run_all_tests.cc
+++ b/third_party/blink/renderer/platform/testing/run_all_tests.cc
@@ -33,8 +33,10 @@
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_io_thread.h"
 #include "base/test/test_suite.h"
+#include "build/build_config.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
+#include "skia/ext/test_fonts.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 
@@ -51,6 +53,14 @@
 int main(int argc, char** argv) {
   blink::ScopedUnittestsEnvironmentSetup testEnvironmentSetup(argc, argv);
   int result = 0;
+
+#if defined(OS_FUCHSIA)
+  // Some unittests depend on specific fonts provided by the system (e.g. some
+  // tests load Arial). On Fuchsia the default font set contains only Roboto.
+  // Load //third_party/test_fonts to make these tests pass on Fuchsia.
+  skia::ConfigureTestFont();
+#endif
+
   {
     base::TestSuite testSuite(argc, argv);
 
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
index e2ffef20..5aebe0ba 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
@@ -56,6 +56,14 @@
     return nullptr;
   }
   uintptr_t Id() const override { return internal_->id(); }
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> DtlsTransport() override {
+    return nullptr;
+  }
+  webrtc::DtlsTransportInformation DtlsTransportInformation() override {
+    static webrtc::DtlsTransportInformation dummy(
+        webrtc::DtlsTransportState::kNew);
+    return dummy;
+  }
   WebMediaStreamTrack Track() const override { return internal_->track(); }
   WebVector<WebString> StreamIds() const override {
     return std::vector<WebString>({WebString::FromUTF8("DummyStringId")});
@@ -109,6 +117,14 @@
     return nullptr;
   }
   uintptr_t Id() const override { return id_; }
+  rtc::scoped_refptr<webrtc::DtlsTransportInterface> DtlsTransport() override {
+    return nullptr;
+  }
+  webrtc::DtlsTransportInformation DtlsTransportInformation() override {
+    static webrtc::DtlsTransportInformation dummy(
+        webrtc::DtlsTransportState::kNew);
+    return dummy;
+  }
   const WebMediaStreamTrack& Track() const override { return track_; }
   WebVector<WebString> StreamIds() const override {
     return WebVector<WebString>();
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index c28ee0c..41a925f0 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -88,14 +88,11 @@
 crbug.com/450493 http/tests/devtools/profiler/ [ Slow ]
 crbug.com/420008 http/tests/devtools/tracing/ [ Slow ]
 crbug.com/420008 virtual/threaded/http/tests/devtools/tracing/ [ Slow ]
-crbug.com/902685 http/tests/devtools/isolated-code-cache/same-origin-test.js [ Slow ]
-crbug.com/902685 http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Slow ]
+crbug.com/902685 http/tests/devtools/isolated-code-cache/ [ Slow ]
+crbug.com/902685 virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/ [ Slow ]
+crbug.com/902685 virtual/not-site-per-process/http/tests/devtools/isolated-code-cache/ [ Slow ]
 crbug.com/902685 http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
-crbug.com/902685 virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/same-origin-test.js [ Slow ]
-crbug.com/902685 virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Slow ]
 crbug.com/902685 virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
-crbug.com/902685 virtual/not-site-per-process/http/tests/devtools/isolated-code-cache/same-origin-test.js [ Slow ]
-crbug.com/902685 virtual/not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Slow ]
 
 # Misc DevTools tests that are slow
 crbug.com/246190 [ Release ] http/tests/devtools/indexeddb/ [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c87e1ad..d897682 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3025,6 +3025,11 @@
 crbug.com/918664 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-table-1a.html [ Failure Pass ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/reporting/disconnect.html [ Timeout ]
+crbug.com/626703 external/wpt/reporting/generateTestReport.html [ Timeout ]
+crbug.com/626703 external/wpt/reporting/bufferSize.html [ Timeout ]
+crbug.com/626703 external/wpt/reporting/order.html [ Timeout ]
+crbug.com/626703 external/wpt/reporting/nestedReport.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/navigation-preload/empty-preload-response-body.https.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/idlharness.https.window.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/oninstall-script-error.https.html [ Timeout ]
@@ -3109,14 +3114,6 @@
 crbug.com/626703 external/wpt/svg/interact/manual/event-attribute-001-manual.svg [ Skip ]
 crbug.com/626703 [ Retina ] virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/async_005.htm [ Timeout ]
 crbug.com/626703 external/wpt/css/css-text/tab-size/tab-size-spacing-001.html [ Failure ]
-crbug.com/626703 external/wpt/referrer-policy/generic/iframe-inheritance.html [ Timeout ]
-crbug.com/626703 virtual/outofblink-cors/external/wpt/referrer-policy/generic/iframe-inheritance.html [ Timeout ]
-crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/generic/iframe-inheritance.html [ Timeout ]
-crbug.com/626703 virtual/omt-worker-fetch/external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html [ Timeout ]
-crbug.com/626703 virtual/outofblink-cors-ns/external/wpt/referrer-policy/generic/iframe-inheritance.html [ Timeout ]
-crbug.com/626703 virtual/outofblink-cors/external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html [ Timeout ]
-crbug.com/626703 external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html [ Timeout ]
-crbug.com/626703 virtual/outofblink-cors-ns/external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-values/vh-support-atviewport.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/boundary-shaping/boundary-shaping-005.html [ Failure ]
 crbug.com/626703 external/wpt/css/CSS2/text/white-space-nowrap-attribute-001.xht [ Failure ]
diff --git a/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html b/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html
index 5a2d6b40..4d4ba8c 100644
--- a/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html
+++ b/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html
@@ -18,188 +18,203 @@
 </div>
 
 <script>
-    test(function()
+    test(() =>
     {
-        var mainAccessible = accessibilityController.accessibleElementById("main");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
 
-        assert_equals(mainAccessible.selectionStart, 0);
-        assert_equals(mainAccessible.selectionEnd, 0);
+        assert_equals(mainAccessible.selectionAnchorObject, null);
+        assert_equals(mainAccessible.selectionAnchorOffset, -1);
+        assert_equals(mainAccessible.selectionFocusObject, null);
+        assert_equals(mainAccessible.selectionFocusOffset, -1);
     }, "Initially there should be no selection under the main object.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var rootAccessible = accessibilityController.rootElement;
+        let rootAccessible = accessibilityController.rootElement;
 
         assert_equals(rootAccessible.selectionAnchorObject, null);
         assert_equals(rootAccessible.selectionAnchorOffset, -1);
         assert_equals(rootAccessible.selectionFocusObject, null);
         assert_equals(rootAccessible.selectionFocusOffset, -1);
     }, "Initially there should be no selection on the root object.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var textbox = document.getElementById("contenteditable-textbox");
+        let textbox = document.getElementById("contenteditable-textbox");
         textbox.focus();
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
 
-        assert_equals(textboxAccessible.selectionStart, 0);
-        assert_equals(textboxAccessible.selectionEnd, 0);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 0);
+        assert_equals(textboxAccessible.selectionFocusOffset, 0);
     }, "Moving the focus to an ARIA textbox should place the caret at its beginning.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var selectionRange = document.createRange();
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
-        var line1 = document.getElementById("contenteditable-line1");
-        var line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
-        var line1TextAccessible = line1Accessible.childAtIndex(0);
+        let selection = window.getSelection();
+        let selectionRange = document.createRange();
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
+        let line1 = document.getElementById("contenteditable-line1");
+        let line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
+        let line1TextAccessible = line1Accessible.childAtIndex(0);
 
         selectionRange.setStart(line1.firstChild, 1);
         selectionRange.setEnd(line1.firstChild, 1);
         selection.removeAllRanges();
         selection.addRange(selectionRange);
 
-        assert_equals(textboxAccessible.selectionStart, 1);
-        assert_equals(textboxAccessible.selectionEnd, 1);
+        assert_equals(textboxAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 1);
+        assert_equals(textboxAccessible.selectionFocusObject, line1TextAccessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, 1);
 
-        assert_equals(mainAccessible.selectionStart, 1);
-        assert_equals(mainAccessible.selectionEnd, 1);
+        assert_equals(mainAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 1);
+        assert_equals(mainAccessible.selectionFocusObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionFocusOffset, 1);
 
         assert_equals(rootAccessible.selectionAnchorObject, line1TextAccessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 1);
         assert_equals(rootAccessible.selectionFocusObject, line1TextAccessible);
         assert_equals(rootAccessible.selectionFocusOffset, 1);
     }, "Setting a new caret position in the ARIA textbox should be reflected in the accessibility APIs.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
+        let selection = window.getSelection();
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
 
         selection.removeAllRanges();
 
-        assert_equals(textboxAccessible.selectionStart, 0);
-        assert_equals(textboxAccessible.selectionEnd, 0);
+        assert_equals(textboxAccessible.selectionAnchorObject, null);
+        assert_equals(textboxAccessible.selectionAnchorOffset, -1);
+        assert_equals(textboxAccessible.selectionFocusObject, null);
+        assert_equals(textboxAccessible.selectionFocusOffset, -1);
 
-        assert_equals(mainAccessible.selectionStart, 0);
-        assert_equals(mainAccessible.selectionEnd, 0);
+        assert_equals(mainAccessible.selectionAnchorObject, null);
+        assert_equals(mainAccessible.selectionAnchorOffset, -1);
+        assert_equals(mainAccessible.selectionFocusObject, null);
+        assert_equals(mainAccessible.selectionFocusOffset, -1);
 
         assert_equals(rootAccessible.selectionAnchorObject, null);
         assert_equals(rootAccessible.selectionAnchorOffset, -1);
         assert_equals(rootAccessible.selectionFocusObject, null);
         assert_equals(rootAccessible.selectionFocusOffset, -1);
     }, "Removing the selection should remove the caret completely.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
-        var line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
-        var line1TextAccessible = line1Accessible.childAtIndex(0);
+        let selection = window.getSelection();
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
+        let line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
+        let line1TextAccessible = line1Accessible.childAtIndex(0);
 
         line1TextAccessible.setSelectedTextRange(2, 0);
 
-        assert_equals(textboxAccessible.selectionStart, 2);
-        assert_equals(textboxAccessible.selectionEnd, 2);
+        assert_equals(textboxAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 2);
+        assert_equals(textboxAccessible.selectionFocusObject, line1TextAccessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, 2);
 
-        assert_equals(mainAccessible.selectionStart, 2);
-        assert_equals(mainAccessible.selectionEnd, 2);
+        assert_equals(mainAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 2);
+        assert_equals(mainAccessible.selectionFocusObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionFocusOffset, 2);
 
         assert_equals(rootAccessible.selectionAnchorObject, line1TextAccessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 2);
         assert_equals(rootAccessible.selectionFocusObject, line1TextAccessible);
         assert_equals(rootAccessible.selectionFocusOffset, 2);
     }, "Positioning the caret using the accessibility API instead of the DOM should work.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var line2 = document.getElementById("contenteditable-line2");
+        let line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let line2 = document.getElementById("contenteditable-line2");
         line2.focus();
 
-        assert_equals(textboxAccessible.selectionStart, 0);
-        assert_equals(textboxAccessible.selectionEnd, 0);
+        assert_equals(line2Accessible.selectionAnchorObject, line2Accessible);
+        assert_equals(line2Accessible.selectionAnchorOffset, 0);
+        assert_equals(line2Accessible.selectionFocusObject, line2Accessible);
+        assert_equals(line2Accessible.selectionFocusOffset, 0);
 
-        assert_equals(mainAccessible.selectionStart, 0);
-        assert_equals(mainAccessible.selectionEnd, 0);
+        assert_equals(textboxAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 0);
+        assert_equals(textboxAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, 0);
+
+        assert_equals(mainAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 0);
+        assert_equals(mainAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(mainAccessible.selectionFocusOffset, 0);
     }, "Moving the focus into a textarea should remove the caret from the ARIA textbox.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var rootAccessible = accessibilityController.rootElement;
-        var line2Accessible = accessibilityController.focusedElement;
+        document.getElementById("contenteditable-line2").focus();
+        let rootAccessible = accessibilityController.rootElement;
+        let line2Accessible = accessibilityController.focusedElement;
 
         assert_equals(rootAccessible.selectionAnchorObject, line2Accessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 0);
         assert_equals(rootAccessible.selectionFocusObject, line2Accessible);
         assert_equals(rootAccessible.selectionFocusOffset, 0);
 
-        assert_equals(line2Accessible.selectionStart, 0);
-        assert_equals(line2Accessible.selectionEnd, 0);
+        assert_equals(line2Accessible.selectionAnchorObject, line2Accessible);
+        assert_equals(line2Accessible.selectionAnchorOffset, 0);
+        assert_equals(line2Accessible.selectionFocusObject, line2Accessible);
+        assert_equals(line2Accessible.selectionFocusOffset, 0);
     }, "Standard text fields start with the caret at the beginning of their contents.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var line2 = document.getElementById("contenteditable-line2");
-        var line2Accessible = accessibilityController.focusedElement;
+        let line2 = document.getElementById("contenteditable-line2");
+        line2.focus();
+        let line2Accessible = accessibilityController.focusedElement;
 
         line2.setSelectionRange(3, 3);
 
-        assert_equals(line2Accessible.selectionStart, 3);
-        assert_equals(line2Accessible.selectionEnd, 3);
+        assert_equals(line2Accessible.selectionAnchorObject, line2Accessible);
+        assert_equals(line2Accessible.selectionAnchorOffset, 3);
+        assert_equals(line2Accessible.selectionFocusObject, line2Accessible);
+        assert_equals(line2Accessible.selectionFocusOffset, 3);
     }, "Setting a new caret position in the textarea should be exposed in the accessibility APIs.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
-        var line2Accessible = accessibilityController.focusedElement;
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
+        document.getElementById("contenteditable-line2").focus();
+        let line2Accessible = accessibilityController.focusedElement;
 
-        assert_equals(textboxAccessible.selectionStart, 3);
-        assert_equals(textboxAccessible.selectionEnd, 3);
+        assert_equals(textboxAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 3);
+        assert_equals(textboxAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, 3);
 
-        assert_equals(mainAccessible.selectionStart, 3);
-        assert_equals(mainAccessible.selectionEnd, 3);
+        assert_equals(mainAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 3);
+        assert_equals(mainAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(mainAccessible.selectionFocusOffset, 3);
 
         assert_equals(rootAccessible.selectionAnchorObject, line2Accessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 3);
         assert_equals(rootAccessible.selectionFocusObject, line2Accessible);
         assert_equals(rootAccessible.selectionFocusOffset, 3);
     }, "Offsets in text fields should be reported from the beginning of the field and not from the top of the container.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
-        var line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
+        let line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
+        let line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
 
         assert_equals(line1Accessible.selectionAnchorObject, line2Accessible);
         assert_equals(line1Accessible.selectionAnchorOffset, 3);
@@ -210,73 +225,61 @@
         assert_equals(line2Accessible.selectionFocusObject, line2Accessible);
         assert_equals(line2Accessible.selectionFocusOffset, 3);
     }, "The caret position should be retrievable from any object.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var selectionRange = document.createRange();
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
+        let selection = window.getSelection();
+        let selectionRange = document.createRange();
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
 
-        var contenteditable = document.getElementById("contenteditable-div");
+        let contenteditable = document.getElementById("contenteditable-div");
         contenteditable.focus();
-        // The offset from the beginning of the main div to the first character of
-        // contenteditable-div.
-        var mainOffset = 9;
         // The offset from the newline character between the two lines of the
         // first paragraph to the first character of its second line.
         // (Needed for skipping wide space.)
-        var line2Offset = 13;
+        let line2Offset = 13;
 
-        var line1 = document.getElementById("paragraph1").firstChild;
-        var line2 = document.getElementById("paragraph1").lastChild;
-        var line3 = document.getElementById("paragraph2").firstChild;
-        var contenteditableLines = [ line1, line2, line3 ];
-        var contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div");
+        let line1 = document.getElementById("paragraph1").firstChild;
+        let line2 = document.getElementById("paragraph1").lastChild;
+        let line3 = document.getElementById("paragraph2").firstChild;
+        let contenteditableLines = [ line1, line2, line3 ];
+        let contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div");
 
-        for (var lineNumber = 0; lineNumber < 3; ++lineNumber) {
-            var lineOffset = lineNumber * 7;
+        for (let lineNumber = 0; lineNumber < 3; ++lineNumber) {
+            let lineOffset = lineNumber * 7;
             // Paragraphs should be separated by an empty line.
             if (lineNumber == 2)
                 ++lineOffset;
 
-            for (var characterOffset = 0; characterOffset < 7; ++characterOffset) {
+            for (let characterOffset = 0; characterOffset < 7; ++characterOffset) {
                 // Any widespace in the DOM should be stripped out.
-                var selectionOffset = characterOffset;
+                let selectionOffset = characterOffset;
                 if (lineNumber == 1)
                     selectionOffset += line2Offset;
-                var caretPosition = lineOffset + characterOffset;
 
                 selectionRange.setStart(contenteditableLines[lineNumber], selectionOffset);
                 selectionRange.setEnd(contenteditableLines[lineNumber], selectionOffset);
                 selection.removeAllRanges();
                 selection.addRange(selectionRange);
 
-                assert_equals(contenteditableAccessible.selectionStart, caretPosition);
-                assert_equals(contenteditableAccessible.selectionEnd, caretPosition);
+                assert_equals(contenteditableAccessible.selectionAnchorOffset, characterOffset);
+                assert_equals(contenteditableAccessible.selectionFocusOffset, characterOffset);
 
-                assert_equals(mainAccessible.selectionStart,
-                    mainOffset + caretPosition);
-                assert_equals(mainAccessible.selectionEnd,
-                    mainOffset + caretPosition);
+                assert_equals(mainAccessible.selectionAnchorOffset, characterOffset);
+                assert_equals(mainAccessible.selectionFocusOffset, characterOffset);
 
                 assert_equals(rootAccessible.selectionAnchorObject.name,
                     contenteditableLines[lineNumber].textContent.trim());
-                assert_equals(rootAccessible.selectionAnchorOffset,
-                    characterOffset);
+                assert_equals(rootAccessible.selectionAnchorOffset, characterOffset);
                 assert_equals(rootAccessible.selectionFocusObject.name,
                     contenteditableLines[lineNumber].textContent.trim());
-                assert_equals(rootAccessible.selectionFocusOffset,
-                    characterOffset);
+                assert_equals(rootAccessible.selectionFocusOffset, characterOffset);
             }
         }
 
     }, "Test moving the caret across two paragraphs.");
-</script>
 
-<script>
     if (window.testRunner)
         document.getElementById("main").style.display = "none";
 </script>
diff --git a/third_party/blink/web_tests/accessibility/contenteditable-selection.html b/third_party/blink/web_tests/accessibility/contenteditable-selection.html
index fd740f2..5cb78c0 100644
--- a/third_party/blink/web_tests/accessibility/contenteditable-selection.html
+++ b/third_party/blink/web_tests/accessibility/contenteditable-selection.html
@@ -17,17 +17,17 @@
 </div>
 
 <script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var selectionRange = document.createRange();
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
+        let selection = window.getSelection();
+        let selectionRange = document.createRange();
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
 
-        var textbox = document.getElementById("contenteditable-textbox");
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
-        var line1TextAccessible = line1Accessible.childAtIndex(0);
+        let textbox = document.getElementById("contenteditable-textbox");
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
+        let line1TextAccessible = line1Accessible.childAtIndex(0);
 
         // Select the entire contents of the outer ARIA textbox.
         // These include another ARIA textbox and a textarea node
@@ -37,34 +37,34 @@
         selection.removeAllRanges();
         selection.addRange(selectionRange);
 
-        assert_equals(textboxAccessible.selectionStart, 0);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 0);
         // 7 for the "Line 1" text div + 1 for the textarea node.
         // (The textarea node should be treated as a single unit.)
-        assert_equals(textboxAccessible.selectionEnd, 8);
+        assert_equals(textboxAccessible.selectionFocusOffset, 8);
 
         // Selection offsets should be the same when retrieved from the parent object.
-        assert_equals(mainAccessible.selectionStart, 0);
-        assert_equals(mainAccessible.selectionEnd, 8);
+        assert_equals(mainAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 0);
+        assert_equals(mainAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionFocusOffset, 8);
 
         assert_equals(rootAccessible.selectionAnchorObject, line1TextAccessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 0);
         assert_equals(rootAccessible.selectionFocusObject, textboxAccessible);
         assert_equals(rootAccessible.selectionFocusOffset, 8);
     }, "Test selectNodeContents on an ARIA textbox.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var selectionRange = document.createRange();
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
+        let selection = window.getSelection();
+        let selectionRange = document.createRange();
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
 
-        var contenteditable = document.getElementById("contenteditable-div");
-        var line1 = document.getElementById("paragraph1").firstChild;
-        var line3 = document.getElementById("paragraph2").firstChild;
-        var contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div");
+        let contenteditable = document.getElementById("contenteditable-div");
+        let line1 = document.getElementById("paragraph1").firstChild;
+        let line3 = document.getElementById("paragraph2").firstChild;
+        let contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div");
 
         // Select the entire contents of the second content editable.
         contenteditable.focus();
@@ -72,12 +72,11 @@
         selection.removeAllRanges();
         selection.addRange(selectionRange);
 
-        assert_equals(contenteditableAccessible.selectionStart, 0);
-        assert_equals(contenteditableAccessible.selectionEnd, 21);
+        assert_equals(contenteditableAccessible.selectionAnchorOffset, 0);
+        assert_equals(contenteditableAccessible.selectionFocusOffset, 6);
 
-        // 7 for line1 + 1 for the textarea + 1 for the blank line.
-        assert_equals(mainAccessible.selectionStart, 9);
-        assert_equals(mainAccessible.selectionEnd, 30);
+        assert_equals(mainAccessible.selectionAnchorOffset, 0);
+        assert_equals(mainAccessible.selectionFocusOffset, 6);
 
         assert_equals(rootAccessible.selectionAnchorObject.name,
             line1.textContent);
@@ -86,19 +85,17 @@
             line3.textContent);
         assert_equals(rootAccessible.selectionFocusOffset, 6);
     }, "Test selectNodeContents on a contenteditable.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var selectionRange = document.createRange();
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var line1 = document.getElementById("contenteditable-line1");
-        var line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
-        var line1TextAccessible = line1Accessible.childAtIndex(0);
+        let selection = window.getSelection();
+        let selectionRange = document.createRange();
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let line1 = document.getElementById("contenteditable-line1");
+        let line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1");
+        let line1TextAccessible = line1Accessible.childAtIndex(0);
 
         // Select only the first line of the ARIA textbox.
         selectionRange.setStart(line1.firstChild, 0);
@@ -106,135 +103,133 @@
         selection.removeAllRanges();
         selection.addRange(selectionRange);
 
-        assert_equals(textboxAccessible.selectionStart, 0);
-        assert_equals(textboxAccessible.selectionEnd, 6);
+        assert_equals(textboxAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 0);
+        assert_equals(textboxAccessible.selectionFocusObject, line1TextAccessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, 6);
 
-        assert_equals(mainAccessible.selectionStart, 0);
-        assert_equals(mainAccessible.selectionEnd, 6);
+        assert_equals(mainAccessible.selectionAnchorObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 0);
+        assert_equals(mainAccessible.selectionFocusObject, line1TextAccessible);
+        assert_equals(mainAccessible.selectionFocusOffset, 6);
 
         assert_equals(rootAccessible.selectionAnchorObject, line1TextAccessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 0);
         assert_equals(rootAccessible.selectionFocusObject, line1TextAccessible);
         assert_equals(rootAccessible.selectionFocusOffset, 6);
     }, "The effects of the setStart and setEnd methods should be reflected in the accessibility API.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var rootAccessible = accessibilityController.rootElement;
-        var line2 = document.getElementById("contenteditable-line2");
-        var line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let rootAccessible = accessibilityController.rootElement;
+        let line2 = document.getElementById("contenteditable-line2");
+        let line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
 
         line2.focus();
 
         // The selection should have been removed from the line1 div
         // due to the focus being moved.
-        assert_equals(textboxAccessible.selectionStart, 0);
-        assert_equals(textboxAccessible.selectionEnd, 0);
+        assert_equals(textboxAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 0);
+        assert_equals(textboxAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, 0);
+
         assert_equals(rootAccessible.selectionAnchorObject, line2Accessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 0);
         assert_equals(rootAccessible.selectionFocusObject, line2Accessible);
         assert_equals(rootAccessible.selectionFocusOffset, 0);
     }, "Verify that changing the focus removes the selection.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
-        var line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
-        var line2 = document.getElementById("contenteditable-line2");
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
+        let line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
+        let line2 = document.getElementById("contenteditable-line2");
 
         // Select only the second line of the ARIA textbox, that is,
         // the one found in the textarea.
         line2.setSelectionRange(0, line2.textLength);
 
-        assert_equals(line2Accessible.selectionStart, 0);
-        assert_equals(line2Accessible.selectionEnd, line2.textLength);
+        assert_equals(line2Accessible.selectionAnchorOffset, 0);
+        assert_equals(line2Accessible.selectionFocusOffset, line2.textLength);
 
-        // Unlike for other elements, offsets in text fields should be
-        // reported from the beginning of the field and not from the top of the
-        // container on which the API was called.
-        assert_equals(textboxAccessible.selectionStart, 0);
-        assert_equals(textboxAccessible.selectionEnd, line2.textLength);
+        assert_equals(textboxAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 0);
+        assert_equals(textboxAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, line2.textLength);
 
-        assert_equals(mainAccessible.selectionStart, 0);
-        assert_equals(mainAccessible.selectionEnd, line2.textLength);
+        assert_equals(mainAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 0);
+        assert_equals(mainAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(mainAccessible.selectionFocusOffset, line2.textLength);
 
         assert_equals(rootAccessible.selectionAnchorObject, line2Accessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 0);
         assert_equals(rootAccessible.selectionFocusObject, line2Accessible);
         assert_equals(rootAccessible.selectionFocusOffset, line2.textLength);
     }, "The effects of the textarea.setSelectionRange method should be reflected in the accessibility API.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
-        var line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
+        let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox");
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
+        let line2Accessible = accessibilityController.accessibleElementById("contenteditable-line2");
 
         // Selection can also be set via the accessibility API.
         line2Accessible.setSelectedTextRange(2, 3);
 
-        assert_equals(line2Accessible.selectionStart, 2);
-        assert_equals(line2Accessible.selectionEnd, 5);
+        assert_equals(line2Accessible.selectionAnchorObject, line2Accessible);
+        assert_equals(line2Accessible.selectionAnchorOffset, 2);
+        assert_equals(line2Accessible.selectionFocusObject, line2Accessible);
+        assert_equals(line2Accessible.selectionFocusOffset, 5);
 
-        assert_equals(textboxAccessible.selectionStart, 2);
-        assert_equals(textboxAccessible.selectionEnd, 5);
+        assert_equals(textboxAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionAnchorOffset, 2);
+        assert_equals(textboxAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(textboxAccessible.selectionFocusOffset, 5);
 
-        assert_equals(mainAccessible.selectionStart, 2);
-        assert_equals(mainAccessible.selectionEnd, 5);
+        assert_equals(mainAccessible.selectionAnchorObject, line2Accessible);
+        assert_equals(mainAccessible.selectionAnchorOffset, 2);
+        assert_equals(mainAccessible.selectionFocusObject, line2Accessible);
+        assert_equals(mainAccessible.selectionFocusOffset, 5);
 
         assert_equals(rootAccessible.selectionAnchorObject, line2Accessible);
         assert_equals(rootAccessible.selectionAnchorOffset, 2);
         assert_equals(rootAccessible.selectionFocusObject, line2Accessible);
         assert_equals(rootAccessible.selectionFocusOffset, 5);
     }, "Test the setSelectedTextRange accessibility API function.");
-</script>
 
-<script>
-    test(function()
+    test(() =>
     {
-        var selection = window.getSelection();
-        var selectionRange = document.createRange();
-        var mainAccessible = accessibilityController.accessibleElementById("main");
-        var rootAccessible = accessibilityController.rootElement;
+        let selection = window.getSelection();
+        let selectionRange = document.createRange();
+        let mainAccessible = accessibilityController.accessibleElementById("main");
+        let rootAccessible = accessibilityController.rootElement;
 
-        var contenteditable = document.getElementById("contenteditable-div");
-        var line1 = document.getElementById("paragraph1").firstChild;
-        var line2 = document.getElementById("paragraph1").lastChild;
-        var line3 = document.getElementById("paragraph2").firstChild;
-        var contenteditableLines = [ line1, line2, line3 ];
-        var contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div");
+        let contenteditable = document.getElementById("contenteditable-div");
+        let line1 = document.getElementById("paragraph1").firstChild;
+        let line2 = document.getElementById("paragraph1").lastChild;
+        let line3 = document.getElementById("paragraph2").firstChild;
+        let contenteditableLines = [ line1, line2, line3 ];
+        let contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div");
 
         // Select entire lines in the second content editable.
-        for (var testCase = 0; testCase < 2; ++testCase) {
+        for (let testCase = 0; testCase < 2; ++testCase) {
 
-            for (var i = 0; i < contenteditableLines.length; ++i) {
-                var start = i * 7;
-                var end = i * 7 + 6;
-                if (i == 2) {
-                    // Paragraphs have a blank line between them.
-                    ++start;
-                    ++end;
-                }
-
+            for (let i = 0; i < contenteditableLines.length; ++i) {
                 selectionRange.selectNode(contenteditableLines[i]);
                 selection.removeAllRanges();
                 selection.addRange(selectionRange);
 
-                assert_equals(contenteditableAccessible.selectionStart, start);
-                assert_equals(contenteditableAccessible.selectionEnd, end);
+                assert_equals(contenteditableAccessible.selectionAnchorOffset, 0);
+                assert_equals(contenteditableAccessible.selectionFocusOffset, 6);
 
-                assert_equals(mainAccessible.selectionStart, start + 9);
-                assert_equals(mainAccessible.selectionEnd, end + 9);
+                assert_equals(mainAccessible.selectionAnchorOffset, 0);
+                assert_equals(mainAccessible.selectionFocusOffset, 6);
 
                 assert_equals(rootAccessible.selectionAnchorObject.name,
                     contenteditableLines[i].textContent);
@@ -249,9 +244,7 @@
         }
 
     }, "The effects of the selectNode method should be reflected in the accessibility API.");
-</script>
 
-<script>
     if (window.testRunner)
         document.getElementById("main").style.display = "none";;
 </script>
diff --git a/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection-expected.txt b/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection-expected.txt
index e8c7ce1..100de3f 100644
--- a/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection-expected.txt
+++ b/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection-expected.txt
@@ -2,99 +2,99 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 0
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 0
 PASS axPassword.stringValue is "AXValue: ••••••"
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 0
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 1
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 2
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 3
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 4
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 5
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 1
-PASS axPassword.selectionEnd is 1
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 1
-PASS axPassword.selectionEnd is 2
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 1
-PASS axPassword.selectionEnd is 3
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 1
-PASS axPassword.selectionEnd is 4
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 1
-PASS axPassword.selectionEnd is 5
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 2
-PASS axPassword.selectionEnd is 2
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 2
-PASS axPassword.selectionEnd is 3
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 2
-PASS axPassword.selectionEnd is 4
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 2
-PASS axPassword.selectionEnd is 5
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 3
-PASS axPassword.selectionEnd is 3
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 3
-PASS axPassword.selectionEnd is 4
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 3
-PASS axPassword.selectionEnd is 5
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 4
-PASS axPassword.selectionEnd is 4
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 4
-PASS axPassword.selectionEnd is 5
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 5
-PASS axPassword.selectionEnd is 5
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
-PASS axPassword.selectionStart is 0
-PASS axPassword.selectionEnd is 0
-PASS axPassword.selectionStartLineNumber is 0
-PASS axPassword.selectionEndLineNumber is 0
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 0
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 1
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 2
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 3
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 4
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 5
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 1
+PASS axPassword.selectionFocusOffset is 1
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 1
+PASS axPassword.selectionFocusOffset is 2
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 1
+PASS axPassword.selectionFocusOffset is 3
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 1
+PASS axPassword.selectionFocusOffset is 4
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 1
+PASS axPassword.selectionFocusOffset is 5
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 2
+PASS axPassword.selectionFocusOffset is 2
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 2
+PASS axPassword.selectionFocusOffset is 3
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 2
+PASS axPassword.selectionFocusOffset is 4
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 2
+PASS axPassword.selectionFocusOffset is 5
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 3
+PASS axPassword.selectionFocusOffset is 3
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 3
+PASS axPassword.selectionFocusOffset is 4
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 3
+PASS axPassword.selectionFocusOffset is 5
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 4
+PASS axPassword.selectionFocusOffset is 4
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 4
+PASS axPassword.selectionFocusOffset is 5
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 5
+PASS axPassword.selectionFocusOffset is 5
+PASS axPassword is axPassword.selectionAnchorObject
+PASS axPassword is axPassword.selectionFocusObject
+PASS axPassword.selectionAnchorOffset is 0
+PASS axPassword.selectionFocusOffset is 0
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection.html b/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection.html
index 85f4f27..7756ddc 100644
--- a/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection.html
+++ b/third_party/blink/web_tests/accessibility/input-type-password-value-and-selection.html
@@ -7,47 +7,43 @@
 
     <input id="password" type="password" value="Secret">
 
-    <p id="description"></p>
-    <div id="console"></div>
-
     <script>
         description("This tests that AXValue, caret position and text selection are reported correctly for password fields.");
 
         if (window.accessibilityController) {
-            var password = document.getElementById("password");
-            var textLength = password.value.length;
+            let password = document.getElementById("password");
+            let textLength = password.value.length;
             password.focus();
-            var axPassword = accessibilityController.accessibleElementById("password");
+            window.axPassword = accessibilityController.accessibleElementById("password");
 
             // Initially the caret should be at the beginning of the field.
-            shouldBeZero("axPassword.selectionStart");
-            shouldBeZero("axPassword.selectionEnd");
-            shouldBeZero("axPassword.selectionStartLineNumber");
-            shouldBeZero("axPassword.selectionEndLineNumber");
+            shouldBe("axPassword", "axPassword.selectionAnchorObject");
+            shouldBe("axPassword", "axPassword.selectionFocusObject");
+            shouldBeZero("axPassword.selectionAnchorOffset");
+            shouldBeZero("axPassword.selectionFocusOffset");
 
             // The password field should contain 6 bullets.
             // (Bullet is the default mask character, not star.)
-            var axValue = "AXValue: " + new Array(textLength + 1).join(
+            let axValue = "AXValue: " + new Array(textLength + 1).join(
                 String.fromCharCode(8226 /* Bullet symbol. */));
             shouldBeEqualToString("axPassword.stringValue", axValue);
 
-            for (var start = 0; start < textLength; ++start) {
-                for (var end = start; end < textLength; ++end) {
+            for (let start = 0; start < textLength; ++start) {
+                for (let end = start; end < textLength; ++end) {
                     password.setSelectionRange(start, end);
-                    shouldBeEqualToNumber("axPassword.selectionStart", start);
-                    shouldBeEqualToNumber("axPassword.selectionEnd", end);
-                    shouldBeZero("axPassword.selectionStartLineNumber");
-                    shouldBeZero("axPassword.selectionEndLineNumber");
+                    shouldBe("axPassword", "axPassword.selectionAnchorObject");
+                    shouldBe("axPassword", "axPassword.selectionFocusObject");
+                    shouldBeEqualToNumber("axPassword.selectionAnchorOffset", start);
+                    shouldBeEqualToNumber("axPassword.selectionFocusOffset", end);
                 }
             }
 
             // Invalid range.
             password.setSelectionRange(7, 0);
-            shouldBeZero("axPassword.selectionStart");
-            shouldBeZero("axPassword.selectionEnd");
-            shouldBeZero("axPassword.selectionStartLineNumber");
-            shouldBeZero("axPassword.selectionEndLineNumber");
-
+            shouldBe("axPassword", "axPassword.selectionAnchorObject");
+            shouldBe("axPassword", "axPassword.selectionFocusObject");
+            shouldBeZero("axPassword.selectionAnchorOffset");
+            shouldBeZero("axPassword.selectionFocusOffset");
         }
     </script>
   </body>
diff --git a/third_party/blink/web_tests/accessibility/input-type-text-caret-position-expected.txt b/third_party/blink/web_tests/accessibility/input-type-text-caret-position-expected.txt
index bc914f2c..cd06e56 100644
--- a/third_party/blink/web_tests/accessibility/input-type-text-caret-position-expected.txt
+++ b/third_party/blink/web_tests/accessibility/input-type-text-caret-position-expected.txt
@@ -2,20 +2,34 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 0
-PASS inputAccessible.selectionStart is 1
-PASS inputAccessible.selectionEnd is 1
-PASS inputAccessible.selectionStart is 2
-PASS inputAccessible.selectionEnd is 2
-PASS inputAccessible.selectionStart is 3
-PASS inputAccessible.selectionEnd is 3
-PASS inputAccessible.selectionStart is 4
-PASS inputAccessible.selectionEnd is 4
-PASS inputAccessible.selectionStart is 5
-PASS inputAccessible.selectionEnd is 5
-PASS inputAccessible.selectionStart is 6
-PASS inputAccessible.selectionEnd is 6
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 0
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 1
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 1
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 2
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 2
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 3
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 3
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 4
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 4
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 5
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 5
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 6
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/accessibility/input-type-text-caret-position.html b/third_party/blink/web_tests/accessibility/input-type-text-caret-position.html
index b95faeb5..d2f546f 100644
--- a/third_party/blink/web_tests/accessibility/input-type-text-caret-position.html
+++ b/third_party/blink/web_tests/accessibility/input-type-text-caret-position.html
@@ -12,16 +12,18 @@
 
         if (window.accessibilityController) {
 
-            var input = document.getElementById('input');
+            let input = document.getElementById('input');
             input.focus();
-            var inputAccessible =
+            window.inputAccessible =
                 accessibilityController.accessibleElementById('input');
 
-            for (var characterIndex = 0; characterIndex < 7; ++characterIndex) {
+            for (let characterIndex = 0; characterIndex < 7; ++characterIndex) {
                 input.setSelectionRange(characterIndex, characterIndex);
-                shouldBeEqualToNumber("inputAccessible.selectionStart",
+                shouldBe("inputAccessible", "inputAccessible.selectionAnchorObject");
+                shouldBeEqualToNumber("inputAccessible.selectionAnchorOffset",
                     characterIndex);
-                shouldBeEqualToNumber("inputAccessible.selectionEnd",
+                shouldBe("inputAccessible", "inputAccessible.selectionFocusObject");
+                shouldBeEqualToNumber("inputAccessible.selectionFocusOffset",
                     characterIndex);
             }
 
diff --git a/third_party/blink/web_tests/accessibility/input-type-text-selection-expected.txt b/third_party/blink/web_tests/accessibility/input-type-text-selection-expected.txt
index eed2b83..cf6bc5f 100644
--- a/third_party/blink/web_tests/accessibility/input-type-text-selection-expected.txt
+++ b/third_party/blink/web_tests/accessibility/input-type-text-selection-expected.txt
@@ -2,94 +2,94 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 6
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 1
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 2
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 3
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 4
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 5
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 0
-PASS inputAccessible.selectionEnd is 6
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 1
-PASS inputAccessible.selectionEnd is 2
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 1
-PASS inputAccessible.selectionEnd is 3
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 1
-PASS inputAccessible.selectionEnd is 4
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 1
-PASS inputAccessible.selectionEnd is 5
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 1
-PASS inputAccessible.selectionEnd is 6
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 2
-PASS inputAccessible.selectionEnd is 3
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 2
-PASS inputAccessible.selectionEnd is 4
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 2
-PASS inputAccessible.selectionEnd is 5
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 2
-PASS inputAccessible.selectionEnd is 6
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 3
-PASS inputAccessible.selectionEnd is 4
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 3
-PASS inputAccessible.selectionEnd is 5
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 3
-PASS inputAccessible.selectionEnd is 6
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 4
-PASS inputAccessible.selectionEnd is 5
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 4
-PASS inputAccessible.selectionEnd is 6
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
-PASS inputAccessible.selectionStart is 5
-PASS inputAccessible.selectionEnd is 6
-PASS inputAccessible.selectionStartLineNumber is 0
-PASS inputAccessible.selectionEndLineNumber is 0
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 1
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 2
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 3
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 4
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 5
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 0
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 1
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 2
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 1
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 3
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 1
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 4
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 1
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 5
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 1
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 2
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 3
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 2
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 4
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 2
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 5
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 2
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 3
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 4
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 3
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 5
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 3
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 4
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 5
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 4
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
+PASS inputAccessible is inputAccessible.selectionAnchorObject
+PASS inputAccessible.selectionAnchorOffset is 5
+PASS inputAccessible is inputAccessible.selectionFocusObject
+PASS inputAccessible.selectionFocusOffset is 6
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/accessibility/input-type-text-selection.html b/third_party/blink/web_tests/accessibility/input-type-text-selection.html
index 3ac4fa6..63ac034 100644
--- a/third_party/blink/web_tests/accessibility/input-type-text-selection.html
+++ b/third_party/blink/web_tests/accessibility/input-type-text-selection.html
@@ -12,26 +12,26 @@
 
         if (window.accessibilityController) {
 
-            var input = document.getElementById('input');
+            let input = document.getElementById('input');
             input.focus();
-            var inputAccessible =
+            window.inputAccessible =
                 accessibilityController.accessibleElementById('input');
 
             // Select the entire contents.
             input.select();
-            shouldBeZero("inputAccessible.selectionStart");
-            shouldBeEqualToNumber("inputAccessible.selectionEnd", 6);
-            shouldBeEqualToNumber("inputAccessible.selectionStartLineNumber", 0);
-            shouldBeEqualToNumber("inputAccessible.selectionEndLineNumber", 0);
+            shouldBe("inputAccessible", "inputAccessible.selectionAnchorObject");
+            shouldBeZero("inputAccessible.selectionAnchorOffset");
+            shouldBe("inputAccessible", "inputAccessible.selectionFocusObject");
+            shouldBeEqualToNumber("inputAccessible.selectionFocusOffset", 6);
 
             // Select all possible text ranges.
-            for (var start = 0; start < 7; ++start) {
-                for (var end = start + 1; end < 7; ++end) {
+            for (let start = 0; start < 7; ++start) {
+                for (let end = start + 1; end < 7; ++end) {
                     input.setSelectionRange(start, end);
-                    shouldBeEqualToNumber("inputAccessible.selectionStart", start);
-                    shouldBeEqualToNumber("inputAccessible.selectionEnd", end);
-                    shouldBeZero("inputAccessible.selectionStartLineNumber");
-                    shouldBeZero("inputAccessible.selectionEndLineNumber");
+                    shouldBe("inputAccessible", "inputAccessible.selectionAnchorObject");
+                    shouldBeEqualToNumber("inputAccessible.selectionAnchorOffset", start);
+                    shouldBe("inputAccessible", "inputAccessible.selectionFocusObject");
+                    shouldBeEqualToNumber("inputAccessible.selectionFocusOffset", end);
                 }
             }
 
diff --git a/third_party/blink/web_tests/accessibility/list-with-selection.html b/third_party/blink/web_tests/accessibility/list-with-selection.html
index e9774dc..0fd688ec 100644
--- a/third_party/blink/web_tests/accessibility/list-with-selection.html
+++ b/third_party/blink/web_tests/accessibility/list-with-selection.html
@@ -34,10 +34,9 @@
 
       // Select both items in the list.
       axList.setSelectedTextRange(0, 2);
-      // |selectionStart| and |selectionEnd| are character-based.
       let selectionText = 'Item1\nItem2';
-      assert_equals(axList.selectionStart, 0, id);
-      assert_equals(axList.selectionEnd, selectionText.length, id);
+      assert_equals(axList.selectionAnchorOffset, 0, id);
+      assert_equals(axList.selectionFocusOffset, 5, id);
 
       let selection = window.getSelection();
       assert_equals(selection.toString(), selectionText, id);
diff --git a/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt b/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt
index 42d20a8..ef877e2 100644
--- a/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt
+++ b/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt
@@ -2,96 +2,100 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-PASS textareaAccessible.selectionStart is 0
-PASS textareaAccessible.selectionEnd is 0
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 0
-PASS textareaAccessible.selectionStart is 1
-PASS textareaAccessible.selectionEnd is 1
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 0
-PASS textareaAccessible.selectionStart is 2
-PASS textareaAccessible.selectionEnd is 2
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 0
-PASS textareaAccessible.selectionStart is 3
-PASS textareaAccessible.selectionEnd is 3
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 0
-PASS textareaAccessible.selectionStart is 4
-PASS textareaAccessible.selectionEnd is 4
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 0
-PASS textareaAccessible.selectionStart is 5
-PASS textareaAccessible.selectionEnd is 5
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 0
-PASS textareaAccessible.selectionStart is 6
-PASS textareaAccessible.selectionEnd is 6
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 0
-PASS textareaAccessible.selectionStart is 7
-PASS textareaAccessible.selectionEnd is 7
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 8
-PASS textareaAccessible.selectionEnd is 8
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 9
-PASS textareaAccessible.selectionEnd is 9
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 10
-PASS textareaAccessible.selectionEnd is 10
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 11
-PASS textareaAccessible.selectionEnd is 11
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 12
-PASS textareaAccessible.selectionEnd is 12
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 13
-PASS textareaAccessible.selectionEnd is 13
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 14
-PASS textareaAccessible.selectionEnd is 14
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 15
-PASS textareaAccessible.selectionEnd is 15
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 16
-PASS textareaAccessible.selectionEnd is 16
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 17
-PASS textareaAccessible.selectionEnd is 17
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 18
-PASS textareaAccessible.selectionEnd is 18
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 19
-PASS textareaAccessible.selectionEnd is 19
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 20
-PASS textareaAccessible.selectionEnd is 20
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 20
-PASS textareaAccessible.selectionEnd is 20
-PASS emptyTextareaAccessible.selectionStart is 0
-PASS emptyTextareaAccessible.selectionEnd is 0
-PASS emptyTextareaAccessible.selectionStart is 0
-PASS emptyTextareaAccessible.selectionEnd is 0
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 0
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 0
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 1
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 1
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 2
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 2
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 3
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 3
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 4
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 4
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 5
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 5
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 6
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 6
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 8
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 8
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 9
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 9
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 10
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 10
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 11
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 11
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 12
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 12
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 13
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 13
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 15
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 15
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 16
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 16
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 17
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 17
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 18
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 18
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 19
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 19
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 20
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 20
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 20
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 20
+PASS emptyTextareaAccessible is emptyTextareaAccessible.selectionAnchorObject
+PASS emptyTextareaAccessible.selectionAnchorOffset is 0
+PASS emptyTextareaAccessible is emptyTextareaAccessible.selectionFocusObject
+PASS emptyTextareaAccessible.selectionFocusOffset is 0
+PASS emptyTextareaAccessible.selectionAnchorOffset is 0
+PASS emptyTextareaAccessible.selectionFocusOffset is 0
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/accessibility/textarea-caret-position.html b/third_party/blink/web_tests/accessibility/textarea-caret-position.html
index c8cea3d2..b1f72e6 100644
--- a/third_party/blink/web_tests/accessibility/textarea-caret-position.html
+++ b/third_party/blink/web_tests/accessibility/textarea-caret-position.html
@@ -18,40 +18,44 @@
 
         if (window.accessibilityController) {
 
-            var textarea = document.getElementById('textarea');
+            let textarea = document.getElementById('textarea');
             textarea.focus();
-            var textareaAccessible =
+            window.textareaAccessible =
                 accessibilityController.accessibleElementById('textarea');
 
-            for (var i = 0; i < 3; ++i) {
-                for (var j = 0; j < 7; ++j) {
-                    var caretPosition = i * 7 + j;
+            for (let i = 0; i < 3; ++i) {
+                for (let j = 0; j < 7; ++j) {
+                    let caretPosition = i * 7 + j;
                     textarea.selectionStart = caretPosition;
                     textarea.selectionEnd = caretPosition;
-                    shouldBeEqualToNumber("textareaAccessible.selectionStart",
+                    shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject");
+                    shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset",
                         caretPosition);
-                    shouldBeEqualToNumber("textareaAccessible.selectionEnd",
+                    shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject");
+                    shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset",
                         caretPosition);
-                    shouldBeEqualToNumber("textareaAccessible.selectionStartLineNumber", i);
-                    shouldBeEqualToNumber("textareaAccessible.selectionEndLineNumber", i);
                 }
             }
 
-            var emptyTextarea = document.getElementById('textarea-empty');
+            let emptyTextarea = document.getElementById('textarea-empty');
             emptyTextarea.focus();
             // Each textarea has its own independent caret.
-            shouldBeEqualToNumber("textareaAccessible.selectionStart", 20);
-            shouldBeEqualToNumber("textareaAccessible.selectionEnd", 20);
+            shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject");
+            shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 20);
+            shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject");
+            shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 20);
 
-            var emptyTextareaAccessible =
+            window.emptyTextareaAccessible =
                 accessibilityController.accessibleElementById('textarea-empty');
-            shouldBeZero("emptyTextareaAccessible.selectionStart");
-            shouldBeZero("emptyTextareaAccessible.selectionEnd");
+            shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionAnchorObject");
+            shouldBeZero("emptyTextareaAccessible.selectionAnchorOffset");
+            shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionFocusObject");
+            shouldBeZero("emptyTextareaAccessible.selectionFocusOffset");
 
             // Setting the caret at an invalid offset should not move it.
             emptyTextarea.setSelectionRange(1, 1);
-            shouldBeZero("emptyTextareaAccessible.selectionStart");
-            shouldBeZero("emptyTextareaAccessible.selectionEnd");
+            shouldBeZero("emptyTextareaAccessible.selectionAnchorOffset");
+            shouldBeZero("emptyTextareaAccessible.selectionFocusOffset");
 
         }
     </script>
diff --git a/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt b/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt
index eda2e9ce..9c6f6ac 100644
--- a/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt
+++ b/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt
@@ -2,46 +2,44 @@
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
-PASS textareaAccessible.selectionStart is 0
-PASS textareaAccessible.selectionEnd is 25
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 3
-PASS textareaAccessible.selectionStart is 0
-PASS textareaAccessible.selectionEnd is 7
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 0
-PASS textareaAccessible.selectionEnd is 7
-PASS textareaAccessible.selectionStartLineNumber is 0
-PASS textareaAccessible.selectionEndLineNumber is 1
-PASS textareaAccessible.selectionStart is 7
-PASS textareaAccessible.selectionEnd is 14
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 7
-PASS textareaAccessible.selectionEnd is 14
-PASS textareaAccessible.selectionStartLineNumber is 1
-PASS textareaAccessible.selectionEndLineNumber is 2
-PASS textareaAccessible.selectionStart is 14
-PASS textareaAccessible.selectionEnd is 21
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 3
-PASS textareaAccessible.selectionStart is 14
-PASS textareaAccessible.selectionEnd is 21
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 3
-PASS textareaAccessible.selectionStart is 14
-PASS textareaAccessible.selectionEnd is 21
-PASS textareaAccessible.selectionStartLineNumber is 2
-PASS textareaAccessible.selectionEndLineNumber is 3
-PASS emptyTextareaAccessible.selectionStart is 0
-PASS emptyTextareaAccessible.selectionEnd is 0
-PASS emptyTextareaAccessible.selectionStartLineNumber is 0
-PASS emptyTextareaAccessible.selectionEndLineNumber is 0
-PASS emptyTextareaAccessible.selectionStart is 0
-PASS emptyTextareaAccessible.selectionEnd is 0
-PASS emptyTextareaAccessible.selectionStartLineNumber is 0
-PASS emptyTextareaAccessible.selectionEndLineNumber is 0
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 0
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 25
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 0
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 0
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 21
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 21
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 21
+PASS emptyTextareaAccessible is emptyTextareaAccessible.selectionAnchorObject
+PASS emptyTextareaAccessible.selectionAnchorOffset is 0
+PASS emptyTextareaAccessible is emptyTextareaAccessible.selectionFocusObject
+PASS emptyTextareaAccessible.selectionFocusOffset is 0
+PASS emptyTextareaAccessible.selectionAnchorOffset is 0
+PASS emptyTextareaAccessible.selectionFocusOffset is 0
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/accessibility/textarea-selection.html b/third_party/blink/web_tests/accessibility/textarea-selection.html
index 6067e3f..e90d515 100644
--- a/third_party/blink/web_tests/accessibility/textarea-selection.html
+++ b/third_party/blink/web_tests/accessibility/textarea-selection.html
@@ -18,69 +18,59 @@
 
         if (window.accessibilityController) {
 
-            var textarea = document.getElementById('textarea');
+            let textarea = document.getElementById('textarea');
             textarea.focus();
-            var textareaAccessible =
+            window.textareaAccessible =
                 accessibilityController.accessibleElementById('textarea');
 
             // Select the entire contents.
             textarea.select();
-            shouldBeZero("textareaAccessible.selectionStart");
-            shouldBeEqualToNumber("textareaAccessible.selectionEnd",
+            shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject");
+            shouldBeZero("textareaAccessible.selectionAnchorOffset");
+            shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject");
+            shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset",
                 textarea.textLength);
-            shouldBeZero("textareaAccessible.selectionStartLineNumber");
-            shouldBeEqualToNumber(
-                "textareaAccessible.selectionEndLineNumber",
-                3);
-
+            
             // Select entire lines.
-            for (var lineNumber = 0; lineNumber < 3; ++lineNumber) {
-                for (var directionIndex = 0; directionIndex < 2;
+            for (let lineNumber = 0; lineNumber < 3; ++lineNumber) {
+                for (let directionIndex = 0; directionIndex < 2;
                     ++directionIndex) {
-                    var selectionStart = lineNumber * 7;
-                    var selectionEnd = (lineNumber + 1) * 7;
-                    var direction = 'forward';
+                    let selectionAnchorOffset = lineNumber * 7;
+                    let selectionFocusOffset = (lineNumber + 1) * 7;
+                    let direction = 'forward';
                     if (directionIndex)
                         direction = 'backward';
 
-                    textarea.setSelectionRange(selectionStart, selectionEnd,
+                    textarea.setSelectionRange(selectionAnchorOffset, selectionFocusOffset,
                                                direction);
-                    shouldBeEqualToNumber("textareaAccessible.selectionStart",
-                        selectionStart);
-                    shouldBeEqualToNumber("textareaAccessible.selectionEnd",
-                        selectionEnd);
-                    shouldBeEqualToNumber(
-                        "textareaAccessible.selectionStartLineNumber",
-                        lineNumber);
-                    shouldBeEqualToNumber(
-                        "textareaAccessible.selectionEndLineNumber",
-                        lineNumber + 1);
+                    shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject");
+                    shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset",
+                        selectionAnchorOffset);
+                    shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject");
+                    shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset",
+                        selectionFocusOffset);
                 }
             }
 
-            var emptyTextarea = document.getElementById('textarea-empty');
+            let emptyTextarea = document.getElementById('textarea-empty');
             emptyTextarea.focus();
             // Each textarea has its own independent selection.
-            shouldBeEqualToNumber("textareaAccessible.selectionStart", 14);
-            shouldBeEqualToNumber("textareaAccessible.selectionEnd", 21);
-            shouldBeEqualToNumber(
-                "textareaAccessible.selectionStartLineNumber", 2);
-            shouldBeEqualToNumber(
-                "textareaAccessible.selectionEndLineNumber", 3);
-
-            var emptyTextareaAccessible =
+            shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject");
+            shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 14);
+            shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject");
+            shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 21);
+            
+            window.emptyTextareaAccessible =
                 accessibilityController.accessibleElementById('textarea-empty');
-            shouldBeZero("emptyTextareaAccessible.selectionStart");
-            shouldBeZero("emptyTextareaAccessible.selectionEnd");
-            shouldBeZero("emptyTextareaAccessible.selectionStartLineNumber");
-            shouldBeZero("emptyTextareaAccessible.selectionEndLineNumber");
-
+            shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionAnchorObject");
+            shouldBeZero("emptyTextareaAccessible.selectionAnchorOffset");
+            shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionFocusObject");
+            shouldBeZero("emptyTextareaAccessible.selectionFocusOffset");
+            
             // Setting an invalid selection should not be visible.
             emptyTextarea.setSelectionRange(1, 1);
-            shouldBeZero("emptyTextareaAccessible.selectionStart");
-            shouldBeZero("emptyTextareaAccessible.selectionEnd");
-            shouldBeZero("emptyTextareaAccessible.selectionStartLineNumber");
-            shouldBeZero("emptyTextareaAccessible.selectionEndLineNumber");
+            shouldBeZero("emptyTextareaAccessible.selectionAnchorOffset");
+            shouldBeZero("emptyTextareaAccessible.selectionFocusOffset");
 
         }
     </script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index f660c91..5190611 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -117418,11 +117418,21 @@
      {}
     ]
    ],
+   "client-hints/resources/sec-ch-ua.py": [
+    [
+     {}
+    ]
+   ],
    "client-hints/resources/square.png": [
     [
      {}
     ]
    ],
+   "client-hints/sec-ch-ua.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "clipboard-apis/META.yml": [
     [
      {}
@@ -205181,6 +205191,18 @@
      {}
     ]
    ],
+   "client-hints/sec-ch-ua.http.html": [
+    [
+     "/client-hints/sec-ch-ua.http.html",
+     {}
+    ]
+   ],
+   "client-hints/sec-ch-ua.https.html": [
+    [
+     "/client-hints/sec-ch-ua.https.html",
+     {}
+    ]
+   ],
    "clipboard-apis/async-interfaces.https.html": [
     [
      "/clipboard-apis/async-interfaces.https.html",
@@ -277053,12 +277075,52 @@
      {}
     ]
    ],
+   "reporting/bufferSize.html": [
+    [
+     "/reporting/bufferSize.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "reporting/disconnect.html": [
+    [
+     "/reporting/disconnect.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "reporting/generateTestReport.html": [
+    [
+     "/reporting/generateTestReport.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "reporting/idlharness.window.js": [
     [
      "/reporting/idlharness.window.html",
      {}
     ]
    ],
+   "reporting/nestedReport.html": [
+    [
+     "/reporting/nestedReport.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "reporting/order.html": [
+    [
+     "/reporting/order.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "requestidlecallback/basic.html": [
     [
      "/requestidlecallback/basic.html",
@@ -310346,10 +310408,26 @@
    "2dd18068427ccec7f75ecbdc7fa4e19c339f53a7",
    "support"
   ],
+  "client-hints/resources/sec-ch-ua.py": [
+   "a14a27dadf9e333784a5c636c821634b387e475f",
+   "support"
+  ],
   "client-hints/resources/square.png": [
    "01c9666a8de9d5535615aff830810e5df4b2156f",
    "support"
   ],
+  "client-hints/sec-ch-ua.http.html": [
+   "e333c605301e621133ee5e6f3d01e2525d5c058a",
+   "testharness"
+  ],
+  "client-hints/sec-ch-ua.https-expected.txt": [
+   "0bed3d3b8ad9e7882b8f420cd09684208bf31042",
+   "support"
+  ],
+  "client-hints/sec-ch-ua.https.html": [
+   "b293745365a611475697aaa7b66a8a809cad7b3c",
+   "testharness"
+  ],
   "clipboard-apis/META.yml": [
    "ecbac54806cb0d3a915c551f0a684fdc84acdd5e",
    "support"
@@ -446126,6 +446204,18 @@
    "980823f59f7e3ac4f35a31ab6df37ce16e538f54",
    "support"
   ],
+  "reporting/bufferSize.html": [
+   "b3512b3bc93b322e2015e71d70f01bc5986bc24d",
+   "testharness"
+  ],
+  "reporting/disconnect.html": [
+   "2dc5e8fd2de724e41bc0c003b4e7036d1d954371",
+   "testharness"
+  ],
+  "reporting/generateTestReport.html": [
+   "e3c2735686a05eefc9bbcfb107f76ec7275b8948",
+   "testharness"
+  ],
   "reporting/idlharness.window-expected.txt": [
    "99905f17a3c270638d312766a9cf5574dae976f5",
    "support"
@@ -446134,6 +446224,14 @@
    "17cef8183596ae1d0b307fb8ddccc7455b955966",
    "testharness"
   ],
+  "reporting/nestedReport.html": [
+   "156338ee74653bfdc843e6acceda073c02ca635b",
+   "testharness"
+  ],
+  "reporting/order.html": [
+   "c43964220c981f4b91c7c5e7eac7fc67377b367b",
+   "testharness"
+  ],
   "requestidlecallback/META.yml": [
    "9c829d3c8885bb9b594e7096a878166e07f3e278",
    "support"
@@ -446867,7 +446965,7 @@
    "support"
   ],
   "resources/testdriver.js": [
-   "e328302e4f345114654eda323dbd09a5f650d419",
+   "031be1b7e5501658d412aecc6f0cba56b3444601",
    "support"
   ],
   "resources/testdriver.js.headers": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/dynamic-available-size-iframe.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/dynamic-available-size-iframe.html
new file mode 100644
index 0000000..fc265d4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/dynamic-available-size-iframe.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<meta name="assert" content="Checks that an absolute positioned element is positioned correctly, when the available size of its parent changes due to document resize." />
+<link rel="help" href="https://crbug.com/928672">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<script src="/common/reftest-wait.js"></script>
+<p>Test passes if there is a filled green square.</p>
+<iframe id="target" height="100" width="200" src="support/dynamic-available-size-iframe.html" style="border: none;"></iframe>
+<script>
+onload = () => {
+  requestAnimationFrame(() => requestAnimationFrame(() => {
+    target.width = '100';
+    takeScreenshot();
+  }));
+};
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/support/dynamic-available-size-iframe.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/support/dynamic-available-size-iframe.html
new file mode 100644
index 0000000..8b61c87
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/support/dynamic-available-size-iframe.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<style>
+body { margin: 0; }
+.parent {
+  position: relative;
+  display: flex;
+  width: 100%;
+  height: 100px;
+  background: red;
+}
+
+.content {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  margin: auto;
+  width: 100px;
+  height: 100px;
+  background: green;
+}
+
+svg {
+  width: 50px;
+  height: 50px;
+}
+</style>
+<div class="parent">
+  <div class="content">
+    <svg xmlns="http://www.w3.org/2000/svg"></svg>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative-expected.txt
deleted file mode 100644
index 9c3a46bd..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-This is a testharness.js-based test.
-PASS delay of a new tranisition
-FAIL Positive delay of a new transition assert_equals: Initial value of delay expected 10000 but got 0
-PASS Negative delay of a new transition
-PASS endDelay of a new transition
-FAIL fill of a new transition assert_equals: Fill backwards expected "backwards" but got "none"
-PASS iterationStart of a new transition
-PASS iterations of a new transition
-PASS duration of a new transition
-PASS direction of a new transition
-PASS easing of a new transition
-PASS endTime of a new transition
-PASS activeDuration of a new transition
-PASS localTime of a new transition
-PASS localTime is always equal to currentTime
-PASS localTime reflects playbackRate immediately
-PASS progress of a new transition
-PASS progress of a new transition with positive delay in before phase
-PASS progress of a finished transition
-PASS currentIteration of a new transition
-PASS currentIteration of a new transition with positive delay in before phase
-PASS currentIteration of a finished transition
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
deleted file mode 100644
index d90daa4..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS The start time of a newly-created transition is unresolved
-PASS The start time of transitions is based on when they are generated
-PASS The start time of a transition can be set
-PASS The start time can be set to seek a transition
-FAIL Seeking a transition using start time dispatches transition events promise_test: Unhandled rejection with value: object "Error: Timed out waiting for Promise to resolve: transitionstart"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html
index 252b220..befc7a9 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/img.tentative.https.sub.html
@@ -7,16 +7,18 @@
 <script>
   // These tests reuse the `referrer-policy` infrastructure to load images that
   // encode their request headers in their pixels. Fun stuff!
-  async_test(t => {
+  promise_test(() =>
     loadImageInWindow(
       "https://{{host}}:{{ports[https][0]}}/referrer-policy/generic/subresource/image.py",
-      t.step_func_done(img => {
+      [],
+      window)
+    .then(img => {
         headers = decodeImageData(extractImageData(img)).headers;
         got = {
-        "dest": headers["sec-fetch-dest"],
-        "mode": headers["sec-fetch-mode"],
-        "site": headers["sec-fetch-site"],
-        "user": headers["sec-fetch-user"]
+          "dest": headers["sec-fetch-dest"],
+          "mode": headers["sec-fetch-mode"],
+          "site": headers["sec-fetch-site"],
+          "user": headers["sec-fetch-user"]
         };
         assert_header_equals(got, {
           "dest": "image",
@@ -25,20 +27,20 @@
           "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
-      [],
-      window);
-  }, "Same-origin image");
+    "Same-origin image");
 
-  async_test(t => {
+  promise_test(() =>
     loadImageInWindow(
       "https://{{hosts[][www]}}:{{ports[https][0]}}/referrer-policy/generic/subresource/image.py",
-      t.step_func_done(img => {
+      [],
+      window)
+    .then(img => {
         headers = decodeImageData(extractImageData(img)).headers;
         got = {
-        "dest": headers["sec-fetch-dest"],
-        "mode": headers["sec-fetch-mode"],
-        "site": headers["sec-fetch-site"],
-        "user": headers["sec-fetch-user"]
+          "dest": headers["sec-fetch-dest"],
+          "mode": headers["sec-fetch-mode"],
+          "site": headers["sec-fetch-site"],
+          "user": headers["sec-fetch-user"]
         };
         assert_header_equals(got, {
           "dest": "image",
@@ -47,20 +49,20 @@
           "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
-      [],
-      window);
-  }, "Same-site image");
+    "Same-site image");
 
-  async_test(t => {
+  promise_test(() =>
     loadImageInWindow(
       "https://{{hosts[alt][www]}}:{{ports[https][0]}}/referrer-policy/generic/subresource/image.py",
-      t.step_func_done(img => {
+      [],
+      window)
+    .then(img => {
         headers = decodeImageData(extractImageData(img)).headers;
         got = {
-        "dest": headers["sec-fetch-dest"],
-        "mode": headers["sec-fetch-mode"],
-        "site": headers["sec-fetch-site"],
-        "user": headers["sec-fetch-user"]
+          "dest": headers["sec-fetch-dest"],
+          "mode": headers["sec-fetch-mode"],
+          "site": headers["sec-fetch-site"],
+          "user": headers["sec-fetch-user"]
         };
         assert_header_equals(got, {
           "dest": "image",
@@ -69,7 +71,5 @@
           "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
-      [],
-      window);
-  }, "Cross-site image");
+    "Cross-site image");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/common.js b/third_party/blink/web_tests/external/wpt/mixed-content/generic/common.js
index 63383f8..f1574e9 100644
--- a/third_party/blink/web_tests/external/wpt/mixed-content/generic/common.js
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/generic/common.js
@@ -5,6 +5,20 @@
  *     method's JSDoc.
  */
 
+// The same content is placed as
+// - wpt/referrer-policy/generic/common.js and
+// - wpt/mixed-content/generic/common.js.
+// If you modify either one, please also update the other one.
+//
+// TODO(https://crbug.com/906850): These two files are going to be merged.
+// Currently they are duplicated only to avoid frequent mass modification
+// for each step of refactoring, as these file names are hard-coded in
+// a large number of generated test files.
+
+function timeoutPromise(t, ms) {
+  return new Promise(resolve => { t.step_timeout(resolve, ms); });
+}
+
 /**
  * Normalizes the target port for use in a URL. For default ports, this is the
  *     empty string (omitted port), otherwise it's a colon followed by the port
@@ -70,27 +84,66 @@
     el.setAttribute(attr, attrs[attr]);
 }
 
-
 /**
  * Binds to success and error events of an object wrapping them into a promise
  *     available through {@code element.eventPromise}. The success event
  *     resolves and error event rejects.
+ * This method adds event listeners, and then removes all the added listeners
+ * when one of listened event is fired.
  * @param {object} element An object supporting events on which to bind the
  *     promise.
  * @param {string} resolveEventName [="load"] The event name to bind resolve to.
  * @param {string} rejectEventName [="error"] The event name to bind reject to.
  */
 function bindEvents(element, resolveEventName, rejectEventName) {
-  element.eventPromise = new Promise(function(resolve, reject) {
-    element.addEventListener(resolveEventName  || "load", function (e) {
-      resolve(e);
-    });
-    element.addEventListener(rejectEventName || "error", function(e) {
+  element.eventPromise =
+      bindEvents2(element, resolveEventName, element, rejectEventName);
+}
+
+// Returns a promise wrapping success and error events of objects.
+// This is a variant of bindEvents that can accept separate objects for each
+// events and two events to reject, and doesn't set `eventPromise`.
+//
+// When `resolveObject`'s `resolveEventName` event (default: "load") is
+// fired, the promise is resolved with the event.
+//
+// When `rejectObject`'s `rejectEventName` event (default: "error") or
+// `rejectObject2`'s `rejectEventName2` event (default: "error") is
+// fired, the promise is rejected.
+//
+// `rejectObject2` is optional.
+function bindEvents2(resolveObject, resolveEventName, rejectObject, rejectEventName, rejectObject2, rejectEventName2) {
+  return new Promise(function(resolve, reject) {
+    const actualResolveEventName = resolveEventName || "load";
+    const actualRejectEventName = rejectEventName || "error";
+    const actualRejectEventName2 = rejectEventName2 || "error";
+
+    const resolveHandler = function(event) {
+      cleanup();
+      resolve(event);
+    };
+
+    const rejectHandler = function(event) {
       // Chromium starts propagating errors from worker.onerror to
       // window.onerror. This handles the uncaught exceptions in tests.
-      e.preventDefault();
-      reject(e);
-    });
+      event.preventDefault();
+      cleanup();
+      reject(event);
+    };
+
+    const cleanup = function() {
+      resolveObject.removeEventListener(actualResolveEventName, resolveHandler);
+      rejectObject.removeEventListener(actualRejectEventName, rejectHandler);
+      if (rejectObject2) {
+        rejectObject2.removeEventListener(actualRejectEventName2, rejectHandler);
+      }
+    };
+
+    resolveObject.addEventListener(actualResolveEventName, resolveHandler);
+    rejectObject.addEventListener(actualRejectEventName, rejectHandler);
+    if (rejectObject2) {
+      rejectObject2.addEventListener(actualRejectEventName2, rejectHandler);
+    }
   });
 }
 
@@ -150,13 +203,34 @@
 }
 
 /**
+ * requestVia*() functions return promises that are resolved on successful
+ * requests with objects of the same "type", i.e. objects that contains
+ * the same sets of keys that are fixed within one category of tests (e.g.
+ * within wpt/referrer-policy tests).
+ * wrapResult() (that should be defined outside this file) is used to convert
+ * the response bodies of subresources into the expected result objects in some
+ * cases, and in other cases the result objects are constructed more directly.
+ * TODO(https://crbug.com/906850): Clean up the semantics around this, e.g.
+ * use (or not use) wrapResult() consistently, unify the arguments, etc.
+ */
+
+/**
  * Creates a new iframe, binds load and error events, sets the src attribute and
  *     appends it to {@code document.body} .
  * @param {string} url The src for the iframe.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaIframe(url) {
-  return createRequestViaElement("iframe", {"src": url}, document.body);
+function requestViaIframe(url, additionalAttributes) {
+  const iframe = createElement(
+      "iframe",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
+  return bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow);
+          return event.data;
+        });
 }
 
 /**
@@ -169,13 +243,122 @@
   return createRequestViaElement("img", {"src": url}, document.body);
 }
 
+// Helpers for requestViaImageForReferrerPolicy().
+function loadImageInWindow(src, attributes, w) {
+  return new Promise((resolve, reject) => {
+    var image = new w.Image();
+    image.crossOrigin = "Anonymous";
+    image.onload = function() {
+      resolve(image);
+    };
+
+    // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
+    if (attributes) {
+      for (var attr in attributes) {
+        image[attr] = attributes[attr];
+      }
+    }
+
+    image.src = src;
+    w.document.body.appendChild(image)
+  });
+}
+
+function extractImageData(img) {
+    var canvas = document.createElement("canvas");
+    var context = canvas.getContext('2d');
+    context.drawImage(img, 0, 0);
+    var imgData = context.getImageData(0, 0, img.clientWidth, img.clientHeight);
+    return imgData.data;
+}
+
+function decodeImageData(rgba) {
+  var rgb = new Uint8ClampedArray(rgba.length);
+
+  // RGBA -> RGB.
+  var rgb_length = 0;
+  for (var i = 0; i < rgba.length; ++i) {
+    // Skip alpha component.
+    if (i % 4 == 3)
+      continue;
+
+    // Zero is the string terminator.
+    if (rgba[i] == 0)
+      break;
+
+    rgb[rgb_length++] = rgba[i];
+  }
+
+  // Remove trailing nulls from data.
+  rgb = rgb.subarray(0, rgb_length);
+  var string_data = (new TextDecoder("ascii")).decode(rgb);
+
+  return JSON.parse(string_data);
+}
+
+// A variant of requestViaImage for referrer policy tests.
+// This tests many patterns of <iframe>s to test referrer policy inheritance.
+// TODO(https://crbug.com/906850): Merge this into requestViaImage().
+// <iframe>-related code should be moved outside requestViaImage*().
+function requestViaImageForReferrerPolicy(url, attributes, referrerPolicy) {
+  // For images, we'll test:
+  // - images in a `srcdoc` frame to ensure that it uses the referrer
+  //   policy of its parent,
+  // - images in a top-level document,
+  // - and images in a `srcdoc` frame with its own referrer policy to
+  //   override its parent.
+
+  var iframeWithoutOwnPolicy = document.createElement('iframe');
+  var noSrcDocPolicy = new Promise((resolve, reject) => {
+        iframeWithoutOwnPolicy.srcdoc = "Hello, world.";
+        iframeWithoutOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithoutOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer2=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, attributes,
+                                 iframeWithoutOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
+
+  // Give a srcdoc iframe a referrer policy different from the top-level page's policy.
+  var iframePolicy = (referrerPolicy === "no-referrer") ? "unsafe-url" : "no-referrer";
+  var iframeWithOwnPolicy = document.createElement('iframe');
+  var srcDocPolicy = new Promise((resolve, reject) => {
+        iframeWithOwnPolicy.srcdoc = "<meta name='referrer' content='" + iframePolicy + "'>Hello world.";
+        iframeWithOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer3=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, null,
+                                 iframeWithOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
+
+  var pagePolicy = loadImageInWindow(url, attributes, window)
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
+
+  return Promise.all([noSrcDocPolicy, srcDocPolicy, pagePolicy]).then(values => {
+    assert_equals(values[0].headers.referer, values[2].headers.referer, "Referrer inside 'srcdoc' without its own policy should be the same as embedder's referrer.");
+    assert_equals((iframePolicy === "no-referrer" ? undefined : document.location.href), values[1].headers.referer, "Referrer inside 'srcdoc' should use the iframe's policy if it has one");
+    return wrapResult(values[2]);
+  });
+}
+
 /**
  * Initiates a new XHR GET request to provided URL.
  * @param {string} url The endpoint URL for the XHR.
  * @return {Promise} The promise for success/error events.
  */
 function requestViaXhr(url) {
-  return xhrRequest(url);
+  return xhrRequest(url).then(result => wrapResult(result));
 }
 
 /**
@@ -184,7 +367,9 @@
  * @return {Promise} The promise for success/error events.
  */
 function requestViaFetch(url) {
-  return fetch(url);
+  return fetch(url)
+    .then(res => res.json())
+    .then(j => wrapResult(j));
 }
 
 function dedicatedWorkerUrlThatFetches(url) {
@@ -213,10 +398,22 @@
   } catch (e) {
     return Promise.reject(e);
   }
-  bindEvents(worker, "message", "error");
   worker.postMessage('');
+  return bindEvents2(worker, "message", worker, "error")
+    .then(event => wrapResult(event.data));
+}
 
-  return worker.eventPromise;
+function requestViaSharedWorker(url) {
+  var worker;
+  try {
+    worker = new SharedWorker(url);
+  } catch(e) {
+    return Promise.reject(e);
+  }
+  const promise = bindEvents2(worker.port, "message", worker, "error")
+    .then(event => wrapResult(event.data));
+  worker.port.start();
+  return promise;
 }
 
 // Returns a reference to a worklet object corresponding to a given type.
@@ -251,13 +448,19 @@
  * @return {Promise} The promise for success/error events.
  */
 function requestViaNavigable(navigableElement, url) {
-  var iframe = createHelperIframe(guid(), true);
+  var iframe = createHelperIframe(guid(), false);
   setAttributes(navigableElement,
                 {"href": url,
                  "target": iframe.name});
-  navigableElement.click();
 
-  return iframe.eventPromise;
+  const promise =
+    bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow, "event.source");
+          return event.data;
+        });
+  navigableElement.click();
+  return promise;
 }
 
 /**
@@ -266,8 +469,11 @@
  * @param {string} url The URL to navigate to.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaAnchor(url) {
-  var a = createElement("a", {"innerHTML": "Link to resource"}, document.body);
+function requestViaAnchor(url, additionalAttributes) {
+  var a = createElement(
+      "a",
+      Object.assign({"innerHTML": "Link to resource"}, additionalAttributes),
+      document.body);
 
   return requestViaNavigable(a, url);
 }
@@ -278,9 +484,13 @@
  * @param {string} url The URL to navigate to.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaArea(url) {
-  var area = createElement("area", {}, document.body);
+function requestViaArea(url, additionalAttributes) {
+  var area = createElement(
+      "area",
+      Object.assign({}, additionalAttributes),
+      document.body);
 
+  // TODO(kristijanburnik): Append to map and add image.
   return requestViaNavigable(area, url);
 }
 
@@ -290,8 +500,15 @@
  * @param {string} url The src URL.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaScript(url) {
-  return createRequestViaElement("script", {"src": url}, document.body);
+function requestViaScript(url, additionalAttributes) {
+  const script = createElement(
+      "script",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
+
+  return bindEvents2(window, "message", script, "error", window, "error")
+    .then(event => wrapResult(event.data));
 }
 
 /**
@@ -458,7 +675,7 @@
     var websocket = new WebSocket(url);
 
     websocket.addEventListener("message", function(e) {
-      resolve(JSON.parse(e.data));
+      resolve(e.data);
     });
 
     websocket.addEventListener("open", function(e) {
@@ -468,7 +685,10 @@
     websocket.addEventListener("error", function(e) {
       reject(e)
     });
-  });
+  })
+  .then(data => {
+      return JSON.parse(data);
+    });
 }
 
 // SanityChecker does nothing in release mode. See sanity-checker.js for debug
@@ -476,3 +696,4 @@
 function SanityChecker() {}
 SanityChecker.prototype.checkScenario = function() {};
 SanityChecker.prototype.setFailTimeout = function(test, timeout) {};
+SanityChecker.prototype.checkSubresourceResult = function() {};
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/expect.py b/third_party/blink/web_tests/external/wpt/mixed-content/generic/expect.py
index 672a54c3..2c360c1d 100644
--- a/third_party/blink/web_tests/external/wpt/mixed-content/generic/expect.py
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/generic/expect.py
@@ -83,6 +83,11 @@
                                                   "mixed-content",
                                                   "generic",
                                                   "worker.js"), "rb").read()
+            elif content_type == "text/javascript":
+                response_data = open(os.path.join(request.doc_root,
+                                                  "mixed-content",
+                                                  "generic",
+                                                  "script.js"), "rb").read()
             else:
                 response_data = "/* purged */"
         elif action == "take":
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/mixed-content-test-case.js b/third_party/blink/web_tests/external/wpt/mixed-content/generic/mixed-content-test-case.js
index 8708f5a9..b2c0bcde 100644
--- a/third_party/blink/web_tests/external/wpt/mixed-content/generic/mixed-content-test-case.js
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/generic/mixed-content-test-case.js
@@ -3,6 +3,11 @@
  * @author burnik@google.com (Kristijan Burnik)
  */
 
+function wrapResult(server_data) {
+  // Currently the returned value is not used in mixed-content tests.
+  return null;
+}
+
 /**
  * MixedContentTestCase exercises all the tests for checking browser behavior
  * when resources regarded as mixed-content are requested. A single run covers
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/generic/script.js b/third_party/blink/web_tests/external/wpt/mixed-content/generic/script.js
new file mode 100644
index 0000000..2d1cdf0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/generic/script.js
@@ -0,0 +1 @@
+postMessage("", "*");
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/external-import-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/external-import-stylesheet.html
index 40f4234..6be475e 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/external-import-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/external-import-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -16,7 +17,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix +
@@ -25,19 +26,19 @@
         let check_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py" +
                         "?id=" + id + "&report-headers";
 
-        let link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(check_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_equals(message.referrer, undefined);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            let link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(check_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_equals(message.referrer, undefined);
+            });
       }, "Child css from external stylesheet.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html
index 30c5ea29..37370fdd 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
@@ -16,7 +17,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id + "&import-rule";
@@ -27,14 +28,13 @@
         style.type = 'text/css';
         style.appendChild(document.createTextNode("@import url('" + css_url + "');"));
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-          queryXhr(check_url, function(message) {
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(check_url))
+          .then(function(message) {
               assert_own_property(message, "headers");
               assert_own_property(message, "referrer");
               assert_equals(message.referrer, css_url);
-              css_test.done();
-          }, null, null, css_test);
-        }, 1000);
+            });
       }, "Child css from internal stylesheet.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html
index 52a0ded4..5362234 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -15,7 +16,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" +
                          location.port +
@@ -28,15 +29,14 @@
         let processingInstruction =
           document.createProcessingInstruction(
             "xml-stylesheet", "href=\"" +css_url + "\" type=\"text/css\"");
-        css_test.step_timeout(function() {
-            queryXhr(check_url, function(message) {
+        document.insertBefore(processingInstruction, document.firstChild);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(check_url))
+          .then(function(message) {
                 assert_own_property(message, "headers");
                 assert_own_property(message, "referrer");
                 assert_equals(message.referrer, expected);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
-        document.insertBefore(processingInstruction, document.firstChild);
+            });
       }, "Child css via a ProcessingInstruction.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/css-test-helper.js b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/css-test-helper.js
index 788df16a..0afa9e1 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/css-test-helper.js
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/css-test-helper.js
@@ -14,41 +14,34 @@
   'mask-image',
 ];
 
-// Schedules async_test's for each of the test properties
 // Parameters:
 //     testProperties: An array of test properties.
 //     testDescription: A test description
 //     testFunction: A function call which sets up the expect result and runs
 //                   the actual test
 function runSvgTests(testProperties, testDescription, testFunction) {
-  let runNextTest = function () {
-    let property = testProperties.shift();
-    if (property === undefined) {
-      return;
-    }
-
+  for (const property of testProperties) {
     let current = {
-      test: async_test(testDescription + " " + property),
       id: token(),
       property: property,
     };
 
-    current.test.step(function() { testFunction(current) });
-
-    let check_url = url_prefix + "svg.py" + "?id=" + current.id +
-                    "&report-headers";
-    current.test.step_timeout(function() {
-      queryXhr(check_url, function(message) {
-          assert_own_property(message, "headers");
-          assert_own_property(message, "referrer");
-          assert_equals(message.referrer, current.expected);
-          current.test.done();
-      }, null, null, current.test);
-    }, 800);
-  };
-
-  add_result_callback(runNextTest);
-  runNextTest();
+    promise_test(t => {
+      testFunction(current);
+      return timeoutPromise(t, 800)
+        .then(() => {
+            let check_url = url_prefix + "svg.py" + "?id=" + current.id +
+                            "&report-headers";
+            return requestViaFetch(check_url);
+          })
+        .then(message => {
+            assert_own_property(message, "headers");
+            assert_own_property(message, "referrer");
+            assert_equals(message.referrer, current.expected);
+          });
+      },
+      testDescription + " " + property);
+  }
 }
 
 function createSvg() {
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-import-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-import-stylesheet.html
index 80e3587..300960d2 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-import-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-import-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
@@ -17,7 +18,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let css_url = location.protocol + "//www1." + location.hostname + ":" +
           location.port +
@@ -29,20 +30,20 @@
         let font_url = url_prefix + "/referrer-policy/generic/subresource/font.py" +
                        "?id=" + id + "&report-headers" + "&type=font";
 
-        let link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(font_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_referrer);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            let link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_referrer);
+            });
       }, "Font from imported stylesheet (external).");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-stylesheet.html
index a91eb3f..45da018 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/external-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
@@ -16,27 +17,27 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id + "&type=font";
         let font_url = url_prefix + "/referrer-policy/generic/subresource/font.py" +
                       "?id=" + id + "&report-headers";
 
-        let link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(font_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_url);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            let link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Font from external stylesheet.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-import-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-import-stylesheet.html
index a637082..dd06b7d7 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-import-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-import-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -17,7 +18,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" +
                          location.port + "/referrer-policy/generic/subresource/";
@@ -27,14 +28,13 @@
         let style = document.createElement("style");
         style.textContent = "@import url('" + css_url + "');";
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-            queryXhr(font_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, css_url);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Font from imported stylesheet (internal).");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-stylesheet.html
index eebd864b..a0a9067 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/internal-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -16,7 +17,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let css_url = location.protocol + "//www1." + location.hostname + ":" +
                       location.port +
@@ -27,14 +28,13 @@
         let style = document.createElement("style");
         style.textContent = "@font-face { font-family: 'wpt'; font-style: normal; font-weight: normal; src: url(" + css_url + "); format('truetype'); } body { font-family: 'wpt';}";
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-            queryXhr(font_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, location.origin + "/");
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Font from internal stylesheet.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/processing-instruction.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/processing-instruction.html
index bfc42d9..a9d79a3 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/processing-instruction.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/font-face/processing-instruction.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
@@ -17,7 +18,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix +
@@ -32,15 +33,14 @@
         let processingInstruction =
           document.createProcessingInstruction(
             "xml-stylesheet", "href=\"" + css_url + "\" type=\"text/css\"");
-        css_test.step_timeout(function() {
-            queryXhr(font_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, expected);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
         document.insertBefore(processingInstruction, document.firstChild);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, expected);
+            });
       }, "Font from external stylesheet (from ProcessingInstruction).");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-import-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-import-stylesheet.html
index 80c71b0..2a7ac43 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-import-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-import-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
@@ -17,7 +18,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port +
           "/referrer-policy/generic/subresource/stylesheet.py?id=" + id +
@@ -29,20 +30,20 @@
         var img_url = url_prefix + "/referrer-policy/generic/subresource/image.py" +
                       "?id=" + id + "&report-headers";
 
-        var link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(img_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_referrer);
-                  css_test.done();
-               }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            var link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_referrer);
+            });
       }, "Image from imported stylesheet (external).");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-stylesheet.html
index ba7497b..c763ecb 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/external-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
@@ -16,27 +17,27 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         var css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id;
         var img_url = url_prefix + "/referrer-policy/generic/subresource/image.py" +
                       "?id=" + id + "&report-headers";
 
-        var link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = css_test.step_func(function() {
-          css_test.step_timeout(function() {
-              queryXhr(img_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_url);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        });
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            var link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Image from external stylesheet.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/inline-style.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/inline-style.html
index 758b6d9..da571af 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/inline-style.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/inline-style.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -16,21 +17,20 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/image.py" + "?id=" + id;
         var img_url = css_url + "&report-headers";
 
         var div = document.querySelector("div.styled");
         div.style = "content:url(" + css_url + ")";
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, location.origin + "/");
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Image from inline styles.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-import-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-import-stylesheet.html
index 24aa1858..e320e063 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-import-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-import-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -17,7 +18,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var url_prefix =  location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/";
         var css_url = url_prefix + "stylesheet.py?id=" + id;
@@ -27,14 +28,13 @@
         style.type = 'text/css';
         style.appendChild(document.createTextNode("@import url('" + css_url + "');"));
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, css_url);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Image from imported stylesheet (internal).");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-stylesheet.html
index f456788..3e2fc10 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/internal-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -16,7 +17,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/image.py" + "?id=" + id;
         var img_url = css_url + "&report-headers";
@@ -25,14 +26,13 @@
         style.type = 'text/css';
         style.appendChild(document.createTextNode("div.styled::before { content:url(" + css_url + ")}"));
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-          queryXhr(img_url, function(message) {
-            assert_own_property(message, "headers");
-            assert_own_property(message, "referrer");
-            assert_equals(message.referrer, location.origin + "/");
-            css_test.done();
-          }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Image from internal stylesheet.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/presentation-attribute.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/presentation-attribute.html
index d0a4d96f..e810df54 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/presentation-attribute.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/presentation-attribute.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
@@ -14,20 +15,19 @@
     the referrer and referrer policy from the document.</p>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/image.py" + "?id=" + id;
         var img_url = css_url + "&report-headers";
 
         document.body.background = css_url;
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, location.origin + "/");
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Image from presentation attributes.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/processing-instruction.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/processing-instruction.html
index 926147b..e5a1e20a 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/processing-instruction.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/image/processing-instruction.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
@@ -17,7 +18,7 @@
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         var css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id;
@@ -25,15 +26,14 @@
                       "?id=" + id + "&report-headers";
 
         var processingInstruction = document.createProcessingInstruction("xml-stylesheet", "href=\"" + css_url + "\" type=\"text/css\"");
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, css_url);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
         document.insertBefore(processingInstruction, document.firstChild);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Image from external stylesheet (from ProcessingInstruction).");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/external-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/external-stylesheet.html
index 148584a..dfa6fd0 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/external-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/external-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="never">
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/inline-style.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/inline-style.html
index 1f46acb..fd6e4ba 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/inline-style.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/inline-style.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html
index 08be4ef..228de6d 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/internal-stylesheet.html
@@ -7,6 +7,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html
index edeceb1..65bb642f 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/presentation-attribute.html
@@ -8,6 +8,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/processing-instruction.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/processing-instruction.html
index ba0e6673..c0e8d4b 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/processing-instruction.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/processing-instruction.html
@@ -8,6 +8,7 @@
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/common.js b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/common.js
index a16691b..f1574e9 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/common.js
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/common.js
@@ -1,54 +1,267 @@
-// NOTE: This method only strips the fragment and is not in accordance to the
-// recommended draft specification:
-// https://w3c.github.io/webappsec/specs/referrer-policy/#null
-// TODO(kristijanburnik): Implement this helper as defined by spec once added
-// scenarios for URLs containing username/password/etc.
-function stripUrlForUseAsReferrer(url) {
-  return url.replace(/#.*$/, "");
+/**
+ * @fileoverview Utilities for mixed-content in Web Platform Tests.
+ * @author burnik@google.com (Kristijan Burnik)
+ * Disclaimer: Some methods of other authors are annotated in the corresponding
+ *     method's JSDoc.
+ */
+
+// The same content is placed as
+// - wpt/referrer-policy/generic/common.js and
+// - wpt/mixed-content/generic/common.js.
+// If you modify either one, please also update the other one.
+//
+// TODO(https://crbug.com/906850): These two files are going to be merged.
+// Currently they are duplicated only to avoid frequent mass modification
+// for each step of refactoring, as these file names are hard-coded in
+// a large number of generated test files.
+
+function timeoutPromise(t, ms) {
+  return new Promise(resolve => { t.step_timeout(resolve, ms); });
 }
 
-function parseUrlQueryString(queryString) {
-  var queries = queryString.replace(/^\?/, "").split("&");
-  var params = {};
-
-  for (var i in queries) {
-    var kvp = queries[i].split("=");
-    params[kvp[0]] = kvp[1];
-  }
-
-  return params;
-};
-
-function appendIframeToBody(url, attributes) {
-  var iframe = document.createElement("iframe");
-  iframe.src = url;
-  // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
-  if (attributes) {
-    for (var attr in attributes) {
-      iframe[attr] = attributes[attr];
-    }
-  }
-  document.body.appendChild(iframe);
-
-  return iframe;
+/**
+ * Normalizes the target port for use in a URL. For default ports, this is the
+ *     empty string (omitted port), otherwise it's a colon followed by the port
+ *     number. Ports 80, 443 and an empty string are regarded as default ports.
+ * @param {number} targetPort The port to use
+ * @return {string} The port portion for using as part of a URL.
+ */
+function getNormalizedPort(targetPort) {
+  return ([80, 443, ""].indexOf(targetPort) >= 0) ? "" : ":" + targetPort;
 }
 
-function loadImageInWindow(src, callback, attributes, w) {
-  var image = new w.Image();
-  image.crossOrigin = "Anonymous";
-  image.onload = function() {
-    callback(image);
-  }
+/**
+ * Creates a GUID.
+ *     See: https://en.wikipedia.org/wiki/Globally_unique_identifier
+ *     Original author: broofa (http://www.broofa.com/)
+ *     Sourced from: http://stackoverflow.com/a/2117523/4949715
+ * @return {string} A pseudo-random GUID.
+ */
+function guid() {
+  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+    return v.toString(16);
+  });
+}
 
-  // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
-  if (attributes) {
-    for (var attr in attributes) {
-      image[attr] = attributes[attr];
+/**
+ * Initiates a new XHR via GET.
+ * @param {string} url The endpoint URL for the XHR.
+ * @param {string} responseType Optional - how should the response be parsed.
+ *     Default is "json".
+ *     See: https://xhr.spec.whatwg.org/#dom-xmlhttprequest-responsetype
+ * @return {Promise} A promise wrapping the success and error events.
+ */
+function xhrRequest(url, responseType) {
+  return new Promise(function(resolve, reject) {
+    var xhr = new XMLHttpRequest();
+    xhr.open('GET', url, true);
+    xhr.responseType = responseType || "json";
+
+    xhr.addEventListener("error", function() {
+      reject(Error("Network Error"));
+    });
+
+    xhr.addEventListener("load", function() {
+      if (xhr.status != 200)
+        reject(Error(xhr.statusText));
+      else
+        resolve(xhr.response);
+    });
+
+    xhr.send();
+  });
+}
+
+/**
+ * Sets attributes on a given DOM element.
+ * @param {DOMElement} element The element on which to set the attributes.
+ * @param {object} An object with keys (serving as attribute names) and values.
+ */
+function setAttributes(el, attrs) {
+  attrs = attrs || {}
+  for (var attr in attrs)
+    el.setAttribute(attr, attrs[attr]);
+}
+
+/**
+ * Binds to success and error events of an object wrapping them into a promise
+ *     available through {@code element.eventPromise}. The success event
+ *     resolves and error event rejects.
+ * This method adds event listeners, and then removes all the added listeners
+ * when one of listened event is fired.
+ * @param {object} element An object supporting events on which to bind the
+ *     promise.
+ * @param {string} resolveEventName [="load"] The event name to bind resolve to.
+ * @param {string} rejectEventName [="error"] The event name to bind reject to.
+ */
+function bindEvents(element, resolveEventName, rejectEventName) {
+  element.eventPromise =
+      bindEvents2(element, resolveEventName, element, rejectEventName);
+}
+
+// Returns a promise wrapping success and error events of objects.
+// This is a variant of bindEvents that can accept separate objects for each
+// events and two events to reject, and doesn't set `eventPromise`.
+//
+// When `resolveObject`'s `resolveEventName` event (default: "load") is
+// fired, the promise is resolved with the event.
+//
+// When `rejectObject`'s `rejectEventName` event (default: "error") or
+// `rejectObject2`'s `rejectEventName2` event (default: "error") is
+// fired, the promise is rejected.
+//
+// `rejectObject2` is optional.
+function bindEvents2(resolveObject, resolveEventName, rejectObject, rejectEventName, rejectObject2, rejectEventName2) {
+  return new Promise(function(resolve, reject) {
+    const actualResolveEventName = resolveEventName || "load";
+    const actualRejectEventName = rejectEventName || "error";
+    const actualRejectEventName2 = rejectEventName2 || "error";
+
+    const resolveHandler = function(event) {
+      cleanup();
+      resolve(event);
+    };
+
+    const rejectHandler = function(event) {
+      // Chromium starts propagating errors from worker.onerror to
+      // window.onerror. This handles the uncaught exceptions in tests.
+      event.preventDefault();
+      cleanup();
+      reject(event);
+    };
+
+    const cleanup = function() {
+      resolveObject.removeEventListener(actualResolveEventName, resolveHandler);
+      rejectObject.removeEventListener(actualRejectEventName, rejectHandler);
+      if (rejectObject2) {
+        rejectObject2.removeEventListener(actualRejectEventName2, rejectHandler);
+      }
+    };
+
+    resolveObject.addEventListener(actualResolveEventName, resolveHandler);
+    rejectObject.addEventListener(actualRejectEventName, rejectHandler);
+    if (rejectObject2) {
+      rejectObject2.addEventListener(actualRejectEventName2, rejectHandler);
     }
-  }
+  });
+}
 
-  image.src = src;
-  w.document.body.appendChild(image)
+/**
+ * Creates a new DOM element.
+ * @param {string} tagName The type of the DOM element.
+ * @param {object} attrs A JSON with attributes to apply to the element.
+ * @param {DOMElement} parent Optional - an existing DOM element to append to
+ *     If not provided, the returned element will remain orphaned.
+ * @param {boolean} doBindEvents Optional - Whether to bind to load and error
+ *     events and provide the promise wrapping the events via the element's
+ *     {@code eventPromise} property. Default value evaluates to false.
+ * @return {DOMElement} The newly created DOM element.
+ */
+function createElement(tagName, attrs, parentNode, doBindEvents) {
+  var element = document.createElement(tagName);
+
+  if (doBindEvents)
+    bindEvents(element);
+
+  // We set the attributes after binding to events to catch any
+  // event-triggering attribute changes. E.g. form submission.
+  //
+  // But be careful with images: unlike other elements they will start the load
+  // as soon as the attr is set, even if not in the document yet, and sometimes
+  // complete it synchronously, so the append doesn't have the effect we want.
+  // So for images, we want to set the attrs after appending, whereas for other
+  // elements we want to do it before appending.
+  var isImg = (tagName == "img");
+  if (!isImg)
+    setAttributes(element, attrs);
+
+  if (parentNode)
+    parentNode.appendChild(element);
+
+  if (isImg)
+    setAttributes(element, attrs);
+
+  return element;
+}
+
+function createRequestViaElement(tagName, attrs, parentNode) {
+  return createElement(tagName, attrs, parentNode, true).eventPromise;
+}
+
+/**
+ * Creates a new empty iframe and appends it to {@code document.body} .
+ * @param {string} name The name and ID of the new iframe.
+ * @param {boolean} doBindEvents Whether to bind load and error events.
+ * @return {DOMElement} The newly created iframe.
+ */
+function createHelperIframe(name, doBindEvents) {
+  return createElement("iframe",
+                       {"name": name, "id": name},
+                       document.body,
+                       doBindEvents);
+}
+
+/**
+ * requestVia*() functions return promises that are resolved on successful
+ * requests with objects of the same "type", i.e. objects that contains
+ * the same sets of keys that are fixed within one category of tests (e.g.
+ * within wpt/referrer-policy tests).
+ * wrapResult() (that should be defined outside this file) is used to convert
+ * the response bodies of subresources into the expected result objects in some
+ * cases, and in other cases the result objects are constructed more directly.
+ * TODO(https://crbug.com/906850): Clean up the semantics around this, e.g.
+ * use (or not use) wrapResult() consistently, unify the arguments, etc.
+ */
+
+/**
+ * Creates a new iframe, binds load and error events, sets the src attribute and
+ *     appends it to {@code document.body} .
+ * @param {string} url The src for the iframe.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaIframe(url, additionalAttributes) {
+  const iframe = createElement(
+      "iframe",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
+  return bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow);
+          return event.data;
+        });
+}
+
+/**
+ * Creates a new image, binds load and error events, sets the src attribute and
+ *     appends it to {@code document.body} .
+ * @param {string} url The src for the image.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaImage(url) {
+  return createRequestViaElement("img", {"src": url}, document.body);
+}
+
+// Helpers for requestViaImageForReferrerPolicy().
+function loadImageInWindow(src, attributes, w) {
+  return new Promise((resolve, reject) => {
+    var image = new w.Image();
+    image.crossOrigin = "Anonymous";
+    image.onload = function() {
+      resolve(image);
+    };
+
+    // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
+    if (attributes) {
+      for (var attr in attributes) {
+        image[attr] = attributes[attr];
+      }
+    }
+
+    image.src = src;
+    w.document.body.appendChild(image)
+  });
 }
 
 function extractImageData(img) {
@@ -83,35 +296,11 @@
   return JSON.parse(string_data);
 }
 
-function normalizePort(targetPort) {
-  var defaultPorts = [80, 443];
-  var isDefaultPortForProtocol = (defaultPorts.indexOf(targetPort) >= 0);
-
-  return (targetPort == "" || isDefaultPortForProtocol) ?
-          "" : ":" + targetPort;
-}
-
-function wrapResult(url, server_data) {
-  return {
-    location: url,
-    referrer: server_data.headers.referer,
-    headers: server_data.headers
-  }
-}
-
-function queryIframe(url, callback, attributes, referrer_policy, test) {
-  var iframe = appendIframeToBody(url, attributes);
-  var listener = test.step_func(function(event) {
-    if (event.source != iframe.contentWindow)
-      return;
-
-    callback(event.data, url);
-    window.removeEventListener("message", listener);
-  });
-  window.addEventListener("message", listener);
-}
-
-function queryImage(url, callback, attributes, referrerPolicy, test) {
+// A variant of requestViaImage for referrer policy tests.
+// This tests many patterns of <iframe>s to test referrer policy inheritance.
+// TODO(https://crbug.com/906850): Merge this into requestViaImage().
+// <iframe>-related code should be moved outside requestViaImage*().
+function requestViaImageForReferrerPolicy(url, attributes, referrerPolicy) {
   // For images, we'll test:
   // - images in a `srcdoc` frame to ensure that it uses the referrer
   //   policy of its parent,
@@ -119,148 +308,392 @@
   // - and images in a `srcdoc` frame with its own referrer policy to
   //   override its parent.
 
+  var iframeWithoutOwnPolicy = document.createElement('iframe');
   var noSrcDocPolicy = new Promise((resolve, reject) => {
-    var iframeWithoutOwnPolicy = document.createElement('iframe');
-    iframeWithoutOwnPolicy.srcdoc = "Hello, world.";
-    iframeWithoutOwnPolicy.onload = test.step_func(function () {
-      var nextUrl = url + "&cache_destroyer2=" + (new Date()).getTime();
-      loadImageInWindow(nextUrl, test.step_func(function (img) {
-        resolve(decodeImageData(extractImageData(img)));
-      }), attributes, iframeWithoutOwnPolicy.contentWindow);
-    });
-    document.body.appendChild(iframeWithoutOwnPolicy);
-  });
+        iframeWithoutOwnPolicy.srcdoc = "Hello, world.";
+        iframeWithoutOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithoutOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer2=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, attributes,
+                                 iframeWithoutOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
 
   // Give a srcdoc iframe a referrer policy different from the top-level page's policy.
   var iframePolicy = (referrerPolicy === "no-referrer") ? "unsafe-url" : "no-referrer";
+  var iframeWithOwnPolicy = document.createElement('iframe');
   var srcDocPolicy = new Promise((resolve, reject) => {
-    var iframeWithOwnPolicy = document.createElement('iframe');
-    iframeWithOwnPolicy.srcdoc = "<meta name='referrer' content='" + iframePolicy + "'>Hello world.";
+        iframeWithOwnPolicy.srcdoc = "<meta name='referrer' content='" + iframePolicy + "'>Hello world.";
+        iframeWithOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer3=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, null,
+                                 iframeWithOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
 
-    iframeWithOwnPolicy.onload = test.step_func(function () {
-      var nextUrl = url + "&cache_destroyer3=" + (new Date()).getTime();
-      loadImageInWindow(nextUrl, test.step_func(function (img) {
-        resolve(decodeImageData(extractImageData(img)));
-      }), null, iframeWithOwnPolicy.contentWindow);
-    });
-    document.body.appendChild(iframeWithOwnPolicy);
-  });
+  var pagePolicy = loadImageInWindow(url, attributes, window)
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
 
-  var pagePolicy = new Promise((resolve, reject) => {
-    loadImageInWindow(url, test.step_func(function (img) {
-      resolve(decodeImageData(extractImageData(img)));
-    }), attributes, window);
-  });
-
-  Promise.all([noSrcDocPolicy, srcDocPolicy, pagePolicy]).then(test.step_func(values => {
+  return Promise.all([noSrcDocPolicy, srcDocPolicy, pagePolicy]).then(values => {
     assert_equals(values[0].headers.referer, values[2].headers.referer, "Referrer inside 'srcdoc' without its own policy should be the same as embedder's referrer.");
     assert_equals((iframePolicy === "no-referrer" ? undefined : document.location.href), values[1].headers.referer, "Referrer inside 'srcdoc' should use the iframe's policy if it has one");
-    callback(wrapResult(url, values[2]), url);
-  }));
-}
-
-function queryXhr(url, callback, attributes, referrer_policy, test) {
-  var xhr = new XMLHttpRequest();
-  xhr.open('GET', url, true);
-  xhr.onreadystatechange = test.step_func(function(e) {
-    if (xhr.readyState == 4 && xhr.status == 200) {
-      var server_data = JSON.parse(xhr.responseText);
-      callback(wrapResult(url, server_data), url);
-    }
-  });
-  xhr.send();
-}
-
-function queryWorker(url, callback, attributes, referrer_policy, test) {
-  var worker = new Worker(url);
-  worker.onmessage = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
+    return wrapResult(values[2]);
   });
 }
 
-function queryModuleWorkerTopLevel(url, callback, attributes, referrer_policy, test) {
-  var worker = new Worker(url, {type: "module"});
-  worker.onmessage = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
-  });
+/**
+ * Initiates a new XHR GET request to provided URL.
+ * @param {string} url The endpoint URL for the XHR.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaXhr(url) {
+  return xhrRequest(url).then(result => wrapResult(result));
 }
 
-function querySharedWorker(url, callback, attributes, referrer_policy, test) {
-  var worker = new SharedWorker(url);
-  worker.port.onmessage = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
-  });
+/**
+ * Initiates a new GET request to provided URL via the Fetch API.
+ * @param {string} url The endpoint URL for the Fetch.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaFetch(url) {
+  return fetch(url)
+    .then(res => res.json())
+    .then(j => wrapResult(j));
 }
 
-function queryFetch(url, callback, attributes, referrer_policy, test) {
-  fetch(url).then(test.step_func(function(response) {
-      response.json().then(test.step_func(function(server_data) {
-        callback(wrapResult(url, server_data), url);
-      }));
-    })
-  );
+function dedicatedWorkerUrlThatFetches(url) {
+  return `data:text/javascript,
+    fetch('${url}')
+      .then(() => postMessage(''),
+            () => postMessage(''));`;
 }
 
-function queryNavigable(element, url, callback, attributes, test) {
-  var navigable = element
-  navigable.href = url;
-  navigable.target = "helper-iframe";
+function workerUrlThatImports(url) {
+  return `data:text/javascript,import '${url}';`;
+}
 
-  var helperIframe = document.createElement("iframe")
-  helperIframe.name = "helper-iframe"
-  document.body.appendChild(helperIframe)
-
-  // Extend element with attributes. (E.g. "referrer_policy" or "rel")
-  if (attributes) {
-    for (var attr in attributes) {
-      navigable[attr] = attributes[attr];
-    }
+/**
+ * Creates a new Worker, binds message and error events wrapping them into.
+ *     {@code worker.eventPromise} and posts an empty string message to start
+ *     the worker.
+ * @param {string} url The endpoint URL for the worker script.
+ * @param {object} options The options for Worker constructor.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaDedicatedWorker(url, options) {
+  var worker;
+  try {
+    worker = new Worker(url, options);
+  } catch (e) {
+    return Promise.reject(e);
   }
-
-  var listener = test.step_func(function(event) {
-    if (event.source != helperIframe.contentWindow)
-      return;
-    callback(event.data, url);
-    window.removeEventListener("message", listener);
-  });
-  window.addEventListener("message", listener);
-
-  navigable.click();
+  worker.postMessage('');
+  return bindEvents2(worker, "message", worker, "error")
+    .then(event => wrapResult(event.data));
 }
 
-function queryLink(url, callback, attributes, referrer_policy, test) {
-  var a = document.createElement("a");
-  a.innerHTML = "Link to subresource";
-  document.body.appendChild(a);
-  queryNavigable(a, url, callback, attributes, test)
+function requestViaSharedWorker(url) {
+  var worker;
+  try {
+    worker = new SharedWorker(url);
+  } catch(e) {
+    return Promise.reject(e);
+  }
+  const promise = bindEvents2(worker.port, "message", worker, "error")
+    .then(event => wrapResult(event.data));
+  worker.port.start();
+  return promise;
 }
 
-function queryAreaLink(url, callback, attributes, referrer_policy, test) {
-  var area = document.createElement("area");
+// Returns a reference to a worklet object corresponding to a given type.
+function get_worklet(type) {
+  if (type == 'animation')
+    return CSS.animationWorklet;
+  if (type == 'layout')
+    return CSS.layoutWorklet;
+  if (type == 'paint')
+    return CSS.paintWorklet;
+  if (type == 'audio')
+    return new OfflineAudioContext(2,44100*40,44100).audioWorklet;
+
+  assert_unreached('unknown worklet type is passed.');
+  return undefined;
+}
+
+function requestViaWorklet(type, url) {
+  try {
+    return get_worklet(type).addModule(url);
+  } catch (e) {
+    return Promise.reject(e);
+  }
+}
+
+/**
+ * Sets the href attribute on a navigable DOM element and performs a navigation
+ *     by clicking it. To avoid navigating away from the current execution
+ *     context, a target attribute is set to point to a new helper iframe.
+ * @param {DOMElement} navigableElement The navigable DOMElement
+ * @param {string} url The href for the navigable element.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaNavigable(navigableElement, url) {
+  var iframe = createHelperIframe(guid(), false);
+  setAttributes(navigableElement,
+                {"href": url,
+                 "target": iframe.name});
+
+  const promise =
+    bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow, "event.source");
+          return event.data;
+        });
+  navigableElement.click();
+  return promise;
+}
+
+/**
+ * Creates a new anchor element, appends it to {@code document.body} and
+ *     performs the navigation.
+ * @param {string} url The URL to navigate to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaAnchor(url, additionalAttributes) {
+  var a = createElement(
+      "a",
+      Object.assign({"innerHTML": "Link to resource"}, additionalAttributes),
+      document.body);
+
+  return requestViaNavigable(a, url);
+}
+
+/**
+ * Creates a new area element, appends it to {@code document.body} and performs
+ *     the navigation.
+ * @param {string} url The URL to navigate to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaArea(url, additionalAttributes) {
+  var area = createElement(
+      "area",
+      Object.assign({}, additionalAttributes),
+      document.body);
+
   // TODO(kristijanburnik): Append to map and add image.
-  document.body.appendChild(area);
-  queryNavigable(area, url, callback, attributes, test)
+  return requestViaNavigable(area, url);
 }
 
-function queryScript(url, callback, attributes, referrer_policy, test) {
-  var script = document.createElement("script");
-  script.src = url;
-  script.referrerPolicy = referrer_policy;
+/**
+ * Creates a new script element, sets the src to url, and appends it to
+ *     {@code document.body}.
+ * @param {string} url The src URL.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaScript(url, additionalAttributes) {
+  const script = createElement(
+      "script",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
 
-  var listener = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
-    window.removeEventListener("message", listener);
+  return bindEvents2(window, "message", script, "error", window, "error")
+    .then(event => wrapResult(event.data));
+}
+
+/**
+ * Creates a new form element, sets attributes, appends it to
+ *     {@code document.body} and submits the form.
+ * @param {string} url The URL to submit to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaForm(url) {
+  var iframe = createHelperIframe(guid());
+  var form = createElement("form",
+                           {"action": url,
+                            "method": "POST",
+                            "target": iframe.name},
+                           document.body);
+  bindEvents(iframe);
+  form.submit();
+
+  return iframe.eventPromise;
+}
+
+/**
+ * Creates a new link element for a stylesheet, binds load and error events,
+ *     sets the href to url and appends it to {@code document.head}.
+ * @param {string} url The URL for a stylesheet.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaLinkStylesheet(url) {
+  return createRequestViaElement("link",
+                                 {"rel": "stylesheet", "href": url},
+                                 document.head);
+}
+
+/**
+ * Creates a new link element for a prefetch, binds load and error events, sets
+ *     the href to url and appends it to {@code document.head}.
+ * @param {string} url The URL of a resource to prefetch.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaLinkPrefetch(url) {
+  var link = document.createElement('link');
+  if (link.relList && link.relList.supports && link.relList.supports("prefetch")) {
+    return createRequestViaElement("link",
+                                   {"rel": "prefetch", "href": url},
+                                   document.head);
+  } else {
+    return Promise.reject("This browser does not support 'prefetch'.");
+  }
+}
+
+/**
+ * Initiates a new beacon request.
+ * @param {string} url The URL of a resource to prefetch.
+ * @return {Promise} The promise for success/error events.
+ */
+async function requestViaSendBeacon(url) {
+  function wait(ms) {
+    return new Promise(resolve => step_timeout(resolve, ms));
+  }
+  if (!navigator.sendBeacon(url)) {
+    // If mixed-content check fails, it should return false.
+    throw new Error('sendBeacon() fails.');
+  }
+  // We don't have a means to see the result of sendBeacon() request
+  // for sure. Let's wait for a while and let the generic test function
+  // ask the server for the result.
+  await wait(500);
+  return 'allowed';
+}
+
+/**
+ * Creates a new media element with a child source element, binds loadeddata and
+ *     error events, sets attributes and appends to document.body.
+ * @param {string} type The type of the media element (audio/video/picture).
+ * @param {object} media_attrs The attributes for the media element.
+ * @param {object} source_attrs The attributes for the child source element.
+ * @return {DOMElement} The newly created media element.
+ */
+function createMediaElement(type, media_attrs, source_attrs) {
+  var mediaElement = createElement(type, {});
+
+  var sourceElement = createElement("source", {});
+
+  mediaElement.eventPromise = new Promise(function(resolve, reject) {
+    mediaElement.addEventListener("loadeddata", function (e) {
+      resolve(e);
+    });
+
+    // Safari doesn't fire an `error` event when blocking mixed content.
+    mediaElement.addEventListener("stalled", function(e) {
+      reject(e);
+    });
+
+    sourceElement.addEventListener("error", function(e) {
+      reject(e);
+    });
   });
-  window.addEventListener("message", listener);
 
-  document.body.appendChild(script);
+  setAttributes(mediaElement, media_attrs);
+  setAttributes(sourceElement, source_attrs);
+
+  mediaElement.appendChild(sourceElement);
+  document.body.appendChild(mediaElement);
+
+  return mediaElement;
 }
 
- // SanityChecker does nothing in release mode.
+/**
+ * Creates a new video element, binds loadeddata and error events, sets
+ *     attributes and source URL and appends to {@code document.body}.
+ * @param {string} url The URL of the video.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaVideo(url) {
+  return createMediaElement("video",
+                            {},
+                            {"src": url}).eventPromise;
+}
+
+/**
+ * Creates a new audio element, binds loadeddata and error events, sets
+ *     attributes and source URL and appends to {@code document.body}.
+ * @param {string} url The URL of the audio.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaAudio(url) {
+  return createMediaElement("audio",
+                            {},
+                            {"type": "audio/wav", "src": url}).eventPromise;
+}
+
+/**
+ * Creates a new picture element, binds loadeddata and error events, sets
+ *     attributes and source URL and appends to {@code document.body}. Also
+ *     creates new image element appending it to the picture
+ * @param {string} url The URL of the image for the source and image elements.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaPicture(url) {
+  var picture = createMediaElement("picture", {}, {"srcset": url,
+                                                "type": "image/png"});
+  return createRequestViaElement("img", {"src": url}, picture);
+}
+
+/**
+ * Creates a new object element, binds load and error events, sets the data to
+ *     url, and appends it to {@code document.body}.
+ * @param {string} url The data URL.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaObject(url) {
+  return createRequestViaElement("object", {"data": url, "type": "text/html"}, document.body);
+}
+
+/**
+ * Creates a new WebSocket pointing to {@code url} and sends a message string
+ * "echo". The {@code message} and {@code error} events are triggering the
+ * returned promise resolve/reject events.
+ * @param {string} url The URL for WebSocket to connect to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaWebSocket(url) {
+  return new Promise(function(resolve, reject) {
+    var websocket = new WebSocket(url);
+
+    websocket.addEventListener("message", function(e) {
+      resolve(e.data);
+    });
+
+    websocket.addEventListener("open", function(e) {
+      websocket.send("echo");
+    });
+
+    websocket.addEventListener("error", function(e) {
+      reject(e)
+    });
+  })
+  .then(data => {
+      return JSON.parse(data);
+    });
+}
+
+// SanityChecker does nothing in release mode. See sanity-checker.js for debug
+// mode.
 function SanityChecker() {}
 SanityChecker.prototype.checkScenario = function() {};
+SanityChecker.prototype.setFailTimeout = function(test, timeout) {};
 SanityChecker.prototype.checkSubresourceResult = function() {};
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/iframe-inheritance.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/iframe-inheritance.html
index 478a7ce..6c54c43 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/iframe-inheritance.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/iframe-inheritance.html
@@ -8,6 +8,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body onload="runTest()">
@@ -25,11 +26,15 @@
         document.body.appendChild(iframe);
         iframe.contentDocument.write(`
             <script src = "/referrer-policy/generic/common.js"></` + `script>
+            <script src = "/referrer-policy/generic/referrer-policy-test-case.js"></` + `script>
             <script>
               var urlPath = "/referrer-policy/generic/subresource/xhr.py";
               var url = "${location.protocol}//www1.${location.hostname}:${location.port}" + urlPath;
-              queryXhr(url, (msg) => {
-                  parent.postMessage({referrer: msg.referrer}, "*")});
+              requestViaXhr(url).then((msg) => {
+                  parent.postMessage({referrer: msg.referrer}, "*")})
+                .catch((e) => {
+                    parent.postMessage({referrer: "FAILURE"}, "*");
+                  });
             </` + "script>");
         }
       </script>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/link-rel-prefetch.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/link-rel-prefetch.html
index 5496314..16452b1d 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/link-rel-prefetch.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/link-rel-prefetch.html
@@ -14,18 +14,14 @@
     and referrer policy from the document.</p>
 
     <script>
-      var prefetch_test = async_test("Prefetched image.");
-
       var img_url = "/referrer-policy/generic/subresource/image.py";
-      prefetch_test.step_timeout(
-          function() {
-            loadImageInWindow(img_url, function (img) {
-              var message = decodeImageData(extractImageData(img));
-              prefetch_test.step(function() { assert_equals(message.headers.referer, document.location.origin + "/")});
-              prefetch_test.done();
-            }, null, window);
-          },
-          1000);
+      promise_test((t) => timeoutPromise(t, 1000)
+        .then(() => loadImageInWindow(img_url, null, window))
+        .then(function (img) {
+            var message = decodeImageData(extractImageData(img));
+            assert_equals(message.headers.referer, document.location.origin + "/");
+          }),
+        "Prefetched image.");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-and-values.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-and-values.html
index 682199b9..ccbc02be 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-and-values.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-and-values.html
@@ -6,6 +6,7 @@
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy header and header values are allowed</h1>
@@ -14,14 +15,15 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses the last recognized Referrer-Policy header value");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses the last recognized Referrer-Policy header value");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-combined.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-combined.html
index ad02ae84..7def76b9 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-combined.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-combined.html
@@ -6,6 +6,7 @@
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy header values are allowed</h1>
@@ -14,14 +15,15 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses the last recognized Referrer-Policy header value");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses the last recognized Referrer-Policy header value");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-invalid.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-invalid.html
index b1384b1..eb6c217 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-invalid.html
@@ -6,6 +6,7 @@
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy headers with one invalid</h1>
@@ -14,14 +15,15 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Referrer policy header parsing fails if one header is invalid");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.href);
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.href);
+            });
+      }, "Referrer policy header parsing fails if one header is invalid");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-unknown-token.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-unknown-token.html
index 3273c0d..91e216b2 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-unknown-token.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers-one-unknown-token.html
@@ -6,6 +6,7 @@
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy headers with one invalid</h1>
@@ -14,14 +15,15 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses last recognized referrer policy token from Referrer-Policy headers");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses last recognized referrer policy token from Referrer-Policy headers");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers.html
index 5f6a749..a74cf60 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/multiple-headers.html
@@ -6,6 +6,7 @@
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy headers are allowed</h1>
@@ -14,14 +15,15 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses the last recognized Referrer-Policy header");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses the last recognized Referrer-Policy header");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/referrer-policy-test-case.js b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/referrer-policy-test-case.js
index 2385cc2a1..2c5c8eb 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/referrer-policy-test-case.js
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/referrer-policy-test-case.js
@@ -1,3 +1,27 @@
+function wrapResult(server_data) {
+  return {
+    referrer: server_data.headers.referer,
+    headers: server_data.headers
+  }
+}
+
+// NOTE: This method only strips the fragment and is not in accordance to the
+// recommended draft specification:
+// https://w3c.github.io/webappsec/specs/referrer-policy/#null
+// TODO(kristijanburnik): Implement this helper as defined by spec once added
+// scenarios for URLs containing username/password/etc.
+function stripUrlForUseAsReferrer(url) {
+  return url.replace(/#.*$/, "");
+}
+
+function normalizePort(targetPort) {
+  var defaultPorts = [80, 443];
+  var isDefaultPortForProtocol = (defaultPorts.indexOf(targetPort) >= 0);
+
+  return (targetPort == "" || isDefaultPortForProtocol) ?
+          "" : ":" + targetPort;
+}
+
 function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
   // Pass and skip rest of the test if browser does not support fetch.
   if (scenario.subresource == "fetch-request" && !window.fetch) {
@@ -14,16 +38,16 @@
   sanityChecker.checkScenario(scenario);
 
   var subresourceInvoker = {
-    "a-tag": queryLink,
-    "area-tag": queryAreaLink,
-    "fetch-request": queryFetch,
-    "iframe-tag": queryIframe,
-    "img-tag":  queryImage,
-    "script-tag": queryScript,
-    "worker-request": queryWorker,
-    "module-worker": queryModuleWorkerTopLevel,
-    "shared-worker": querySharedWorker,
-    "xhr-request": queryXhr
+    "a-tag": requestViaAnchor,
+    "area-tag": requestViaArea,
+    "fetch-request": requestViaFetch,
+    "iframe-tag": requestViaIframe,
+    "img-tag":  requestViaImageForReferrerPolicy,
+    "script-tag": requestViaScript,
+    "worker-request": url => requestViaDedicatedWorker(url, {}),
+    "module-worker": url => requestViaDedicatedWorker(url, {type: "module"}),
+    "shared-worker": requestViaSharedWorker,
+    "xhr-request": requestViaXhr
   };
 
   var referrerUrlResolver = {
@@ -41,8 +65,6 @@
   var t = {
     _scenario: scenario,
     _testDescription: testDescription,
-    _subresourceUrl: null,
-    _expectedReferrerUrl: null,
     _constructSubresourceUrl: function() {
       // TODO(kristijanburnik): We should assert that these two domains are
       // different. E.g. If someone runs the tets over www, this would fail.
@@ -60,19 +82,20 @@
 
       var targetPort = portForProtocol[t._scenario.target_protocol];
 
-      t._subresourceUrl = t._scenario.target_protocol + "://" +
-                          domainForOrigin[t._scenario.origin] +
-                          normalizePort(targetPort) +
-                          t._scenario["subresource_path"] +
-                          "?redirection=" + t._scenario["redirection"] +
-                          "&cache_destroyer=" + (new Date()).getTime();
+      return t._scenario.target_protocol + "://" +
+             domainForOrigin[t._scenario.origin] +
+             normalizePort(targetPort) +
+             t._scenario["subresource_path"] +
+             "?redirection=" + t._scenario["redirection"] +
+             "&cache_destroyer=" + (new Date()).getTime();
     },
 
     _constructExpectedReferrerUrl: function() {
-      t._expectedReferrerUrl = referrerUrlResolver[t._scenario.referrer_url]();
+      return referrerUrlResolver[t._scenario.referrer_url]();
     },
 
-    _invokeSubresource: function(callback, test) {
+    // Returns a promise.
+    _invokeSubresource: function(resourceRequestUrl) {
       var invoker = subresourceInvoker[t._scenario.subresource];
       // Depending on the delivery method, extend the subresource element with
       // these attributes.
@@ -84,43 +107,35 @@
       var delivery_method = t._scenario.delivery_method;
 
       if (delivery_method in elementAttributesForDeliveryMethod) {
-        invoker(t._subresourceUrl,
-                callback,
-                elementAttributesForDeliveryMethod[delivery_method],
-                t._scenario.referrer_policy,
-                test);
+        return invoker(resourceRequestUrl,
+                       elementAttributesForDeliveryMethod[delivery_method],
+                       t._scenario.referrer_policy);
       } else {
-        invoker(t._subresourceUrl, callback, null, t._scenario.referrer_policy, test);
+        return invoker(resourceRequestUrl, {}, t._scenario.referrer_policy);
       }
-
     },
 
     start: function() {
-      async_test(function(test) {
+      promise_test(test => {
+          const resourceRequestUrl = t._constructSubresourceUrl();
+          const expectedReferrerUrl = t._constructExpectedReferrerUrl();
+          return t._invokeSubresource(resourceRequestUrl)
+            .then(result => {
+                // Check if the result is in valid format. NOOP in release.
+                sanityChecker.checkSubresourceResult(
+                    test, t._scenario, resourceRequestUrl, result);
 
-        t._constructSubresourceUrl();
-        t._constructExpectedReferrerUrl();
-
-        t._invokeSubresource(test.step_func(function(result) {
-          // Check if the result is in valid format. NOOP in release.
-          sanityChecker.checkSubresourceResult(
-              test, t._scenario, t._subresourceUrl, result);
-
-          // Check the reported URL.
-          test.step(function() {
-            assert_equals(result.referrer,
-                          t._expectedReferrerUrl,
-                          "Reported Referrer URL is '" +
-                          t._scenario.referrer_url + "'.");
-            assert_equals(result.headers.referer,
-                          t._expectedReferrerUrl,
-                          "Reported Referrer URL from HTTP header is '" +
-                          t._expectedReferrerUrl + "'");
-          }, "Reported Referrer URL is as expected: " + t._scenario.referrer_url);
-
-          test.done();
-        }), test);
-      }, t._testDescription);
+                // Check the reported URL.
+                assert_equals(result.referrer,
+                              expectedReferrerUrl,
+                              "Reported Referrer URL is '" +
+                              t._scenario.referrer_url + "'.");
+                assert_equals(result.headers.referer,
+                              expectedReferrerUrl,
+                              "Reported Referrer URL from HTTP header is '" +
+                              expectedReferrerUrl + "'");
+              });
+        }, t._testDescription);
     }
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html
index 1b2b12b..73b48f1 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html
@@ -8,6 +8,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: A document with an opaque origin doesn't send referrers</h1>
@@ -26,12 +27,16 @@
           iframe.srcdoc = `
               <meta name = "referrer" content = "always">
               <script src = "/referrer-policy/generic/common.js"></` + `script>
+              <script src = "/referrer-policy/generic/referrer-policy-test-case.js"></` + `script>
               <script>
                 var urlPath = "/referrer-policy/generic/subresource/xhr.py";
                 var url = "${location.protocol}//www1.${location.hostname}:${location.port}" + urlPath;
-                queryXhr(url, (msg) => {
+                requestViaXhr(url).then((msg) => {
                     parent.postMessage({referrer: msg.referrer, description: "${description}"}, "*")
-                }, null, null, test);
+                  })
+                  .catch((e) => {
+                    parent.postMessage({referrer: "FAILURE", description: "${description}"}, "*")
+                  });
               </` + "script>";
           document.body.appendChild(iframe);
         }, description);
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/area-navigate.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/area-navigate.html
index 3eb82452..c601c24 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/area-navigate.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/area-navigate.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Area Link messaging - cross-origin Area Link navigation</h1>
@@ -16,21 +17,22 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
-        var urlPath = '/referrer-policy/generic/subresource/document.py';
-        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                  urlPath;
-        queryAreaLink(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
-      }, "Area is responding with HTTP headers");
+      promise_test(function() {
+          var urlPath = '/referrer-policy/generic/subresource/document.py';
+          var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                    urlPath;
+          return requestViaArea(url)
+            .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
+        },
+        "Area is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/fetch-messaging.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/fetch-messaging.html
index edb159d..4d40245 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/fetch-messaging.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/fetch-messaging.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Fetch messaging - same-origin Fetch request</h1>
@@ -20,26 +21,21 @@
         assert_true(!!window.fetch, "Fetch is not supported by this browser.");
       }, "Fetch is supported by the browser.");
 
-      (function() {
-        if (!window.fetch)
-          return;
-
-        async_test(function(fetch_test) {
+      promise_test(function() {
           var urlPath = '/referrer-policy/generic/subresource/xhr.py';
           var url = location.protocol + "//" + location.hostname + ":" +
                     location.port + urlPath;
-          queryFetch(url, function(message) {
-            var pre = document.getElementById('received_message')
-            var headers = message.headers;
-            pre.innerHTML = "";
-            pre.innerHTML += url + ":\n\n";
-            pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n";
-            assert_own_property(headers, "host")
-            assert_own_property(headers, "connection")
-            fetch_test.done();
-          }, null, null, fetch_test);
-        }, "Fetch is responding with HTTP headers");
-      })();
+          return requestViaFetch(url)
+            .then(function(message) {
+                var pre = document.getElementById('received_message')
+                var headers = message.headers;
+                pre.innerHTML = "";
+                pre.innerHTML += url + ":\n\n";
+                pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n";
+                assert_own_property(headers, "host")
+                assert_own_property(headers, "connection")
+              });
+      }, "Fetch is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/iframe-messaging.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/iframe-messaging.html
index 606e18b..3072436b 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/iframe-messaging.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/iframe-messaging.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Iframe messaging - cross-origin iframe request</h1>
@@ -16,20 +17,20 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/document.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryIframe(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaIframe(url)
+          .then(function(message) {
+            var pre = document.getElementById('received_message')
+            var headers = message.headers;
+            pre.innerHTML = "";
+            pre.innerHTML += url + ":\n\n";
+            pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+            assert_own_property(headers, "host")
+            assert_own_property(headers, "connection")
+          });
       }, "Iframe is responding with HTTP headers");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/image-decoding.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/image-decoding.html
index 9c50ea6..b132c06 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/image-decoding.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/image-decoding.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Image decoding - cross-origin image request</h1>
@@ -16,20 +17,20 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/image.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath + "?cache_destroyer=" + (new Date()).getTime();
-        queryImage(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, "always", messaging_test);
+        return requestViaImageForReferrerPolicy(url, undefined, "always")
+          .then(function(message) {
+            var pre = document.getElementById('received_message')
+            var headers = message.headers;
+            pre.innerHTML = "";
+            pre.innerHTML += url + ":\n\n";
+            pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+            assert_own_property(headers, "host")
+            assert_own_property(headers, "connection")
+          });
       }, "Image is encoding headers as JSON.");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/link-navigate.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/link-navigate.html
index 95582f6..583458b5 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/link-navigate.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/link-navigate.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Link messaging - cross-origin Link navigation</h1>
@@ -16,20 +17,20 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/document.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryLink(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaAnchor(url)
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "Link is responding with HTTP headers");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/script-messaging.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/script-messaging.html
index f73f440..7bc36dc 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/script-messaging.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/script-messaging.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Script messaging - cross-origin Script request</h1>
@@ -16,20 +17,20 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/script.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryScript(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaScript(url)
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "Script is responding with HTTP headers");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/worker-messaging.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/worker-messaging.html
index fd75918..9daf6d5 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/worker-messaging.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/worker-messaging.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Worker messaging - same-origin Worker request</h1>
@@ -16,20 +17,20 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/worker.py';
         var url = location.protocol + "//" + location.hostname + ":" +
                   location.port + urlPath;
-        queryWorker(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaDedicatedWorker(url, {})
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "Worker is responding with HTTP headers");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/xhr-messaging.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/xhr-messaging.html
index 6ef4a9cf..a020805 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/xhr-messaging.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/subresource-test/xhr-messaging.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>XHR messaging - cross-origin XHR request</h1>
@@ -16,20 +17,20 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/xhr.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryXhr(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaXhr(url)
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "XHR is responding with HTTP headers");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/unsupported-csp-referrer-directive.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/unsupported-csp-referrer-directive.html
index 475efa5..40942a8 100644
--- a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/unsupported-csp-referrer-directive.html
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/unsupported-csp-referrer-directive.html
@@ -7,6 +7,7 @@
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: CSP 'referrer' directive should not be supported</h1>
@@ -15,14 +16,14 @@
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryImage(url, function(message) {
-          assert_equals(message.referrer, document.location.href);
-          test.done();
-        }, null, 'always', test);
+        return requestViaImageForReferrerPolicy(url, null, 'always')
+          .then(function(message) {
+            assert_equals(message.referrer, document.location.href);
+          });
       }, "Image has a referrer despite CSP 'referrer' directive");
     </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/reporting/bufferSize.html b/third_party/blink/web_tests/external/wpt/reporting/bufferSize.html
new file mode 100644
index 0000000..b3512b3b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/bufferSize.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Reporting: Buffer size</title>
+<link rel="author" title="Paul Meyer" href="paulmeyer@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+  // Test the buffer size (100) of ReportingObserver.
+  async_test(async function(test) {
+    for (i = 0; i != 110; ++i)
+      await test_driver.generate_test_report("" + i);
+
+    var observer = new ReportingObserver(function(reports) {
+      test.step(function() {
+        // Only (the most recent) 100 reports should be observed, even though
+        // 110 were buffered.
+        assert_equals(reports.length, 100);
+        for(i = 0; i != 100; ++i) {
+          assert_equals(reports[i].body.message, "" + (i + 10));
+        }
+      });
+
+      test.done();
+    }, {buffered: true});
+    observer.observe();
+  }, "Buffer size");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/reporting/disconnect.html b/third_party/blink/web_tests/external/wpt/reporting/disconnect.html
new file mode 100644
index 0000000..2dc5e8f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/disconnect.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Reporting: Disconnect</title>
+<link rel="author" title="Paul Meyer" href="paulmeyer@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+  async_test(async function(test) {
+    var observer = new ReportingObserver(function(reports, observer) {
+      test.step(function() {
+        assert_equals(reports.length, 1);
+        assert_equals(reports[0].body.message, "Test message.");
+      });
+      test.done();
+    });
+    observer.observe();
+
+    // The observer should still receive this report even though disconnect()
+    // is called immediately afterwards.
+    await test_driver.generate_test_report("Test message.");
+    observer.disconnect();
+  }, "Disconnect");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html b/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
new file mode 100644
index 0000000..e3c2735
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Reporting: Generate Test Report</title>
+<link rel="author" title="Paul Meyer" href="paulmeyer@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+  // Test that the "generate_test_report" API works.
+  async_test(function(test) {
+    var observer = new ReportingObserver(function(reports) {
+      test.step(function() {
+        assert_equals(reports.length, 1);
+        // Ensure that the contents of the report are valid.
+        assert_equals(reports[0].type, "test");
+        assert_true(reports[0].url.endsWith("reporting/generateTestReport.html"));
+        assert_equals(reports[0].body.message, "Test message.");
+      });
+      test.done();
+    });
+    observer.observe();
+
+    // This should result in a "test" type report being generated and observed.
+    test_driver.generate_test_report("Test message.");
+  }, "Generate Test Report");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/reporting/nestedReport.html b/third_party/blink/web_tests/external/wpt/reporting/nestedReport.html
new file mode 100644
index 0000000..156338e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/nestedReport.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Reporting: Nested report</title>
+<link rel="author" title="Paul Meyer" href="paulmeyer@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+  // Test that reports can be generated within a ReportingObserver
+  // callback. These reports should be received by the same observer.
+  async_test(function(test) {
+    var step = 0;
+    var observer = new ReportingObserver(async function(reports, observer) {
+      test.step(function() {
+        assert_equals(reports.length, 1);
+        assert_equals(reports[0].body.message, "" + step);
+      });
+
+      ++step;
+      if (step == 3)
+        test.done();
+
+      test_driver.generate_test_report("" + step);
+    });
+    observer.observe();
+
+    test_driver.generate_test_report("0");
+  }, "Nested report");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/reporting/order.html b/third_party/blink/web_tests/external/wpt/reporting/order.html
new file mode 100644
index 0000000..c439642
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/order.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Reporting: Order</title>
+<link rel="author" title="Paul Meyer" href="paulmeyer@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<p id="error">No error</p>
+<script>
+  var count = 0;
+  async_test(function(test) {
+    var observer = new ReportingObserver(function(reports) {
+      test.step(function() {
+        // Reports should be received in the same order that they were
+        // generated.
+        for(i in reports) {
+          assert_equals(reports[i].body.message, "" + count++);
+        }
+      });
+
+      if (count == 10)
+        test.done();
+    });
+    observer.observe();
+
+    for (i = 0; i != 10; ++i)
+      test_driver.generate_test_report("" + i);
+  }, "Order");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver.js b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
index e328302e..031be1b 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testdriver.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
@@ -194,6 +194,20 @@
          */
         action_sequence: function(actions) {
             return window.test_driver_internal.action_sequence(actions);
+        },
+
+        /**
+         * Generates a test report on the current page
+         *
+         * The generate_test_report function generates a report (to be observed
+         * by ReportingObserver) for testing purposes, as described in
+         * {@link https://w3c.github.io/reporting/#generate-test-report-command}
+         *
+         * @returns {Promise} fulfilled after the report is generated, or
+         *                    rejected if the report generation fails
+         */
+        generate_test_report: function(message) {
+            return window.test_driver_internal.generate_test_report(message);
         }
     };
 
@@ -281,6 +295,17 @@
          */
         action_sequence: function(actions) {
             return Promise.reject(new Error("unimplemented"));
+        },
+
+        /**
+         * Generates a test report on the current page
+         *
+         * @param {String} message - the message to be contained in the report
+         * @returns {Promise} fulfilled after the report is generated, or
+         *                    rejected if the report generation fails
+         */
+        generate_test_report: function(message) {
+            return Promise.reject(new Error("unimplemented"));
         }
     };
 })();
diff --git a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/support/testharness-helper.sub.js b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/support/testharness-helper.sub.js
index 8b58eb9..d7be710 100644
--- a/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/support/testharness-helper.sub.js
+++ b/third_party/blink/web_tests/external/wpt/upgrade-insecure-requests/support/testharness-helper.sub.js
@@ -1,3 +1,9 @@
+// Used by /mixed-content/generic/common.js.
+function wrapResult(server_data) {
+  // Currently the returned value is not used in mixed-content tests.
+  return null;
+}
+
 const Host = {
   SAME_ORIGIN: "same-origin",
   CROSS_ORIGIN: "cross-origin",
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any.worker-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any.worker-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any.worker-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any.worker-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any.worker-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any.worker-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any.worker-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/get-set.any.worker-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.worker-expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.worker-expected.txt
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html
index c687911..fb8aaaf 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html
@@ -69,10 +69,14 @@
       }
       await callee.setRemoteDescription(offer);
       const [callee_transceiver1, callee_transceiver2] = callee.getTransceivers();
-      assert_not_equals(callee_transceiver1.receiver.transport, null);
-      assert_not_equals(callee_transceiver2.receiver.transport, null);
+      // According to spec, setRemoteDescription only updates the transports
+      // if the remote description is an answer.
+      assert_equals(callee_transceiver1.receiver.transport, null);
+      assert_equals(callee_transceiver2.receiver.transport, null);
       const answer = await callee.createAnswer();
       await callee.setLocalDescription(answer);
+      assert_not_equals(callee_transceiver1.receiver.transport, null);
+      assert_not_equals(callee_transceiver2.receiver.transport, null);
       // At this point, bundle should have kicked in.
       assert_equals(callee_transceiver1.receiver.transport,
                     callee_transceiver2.receiver.transport);
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrFrame_lifetime.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrFrame_lifetime.https.html
new file mode 100644
index 0000000..971a6d7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrFrame_lifetime.https.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    let immersiveTestName = "XRFrame methods throw exceptions outside of the " +
+      "requestAnimationFrame callback for immersive sessions";
+    let nonImmersiveTestName = "XRFrame methods throw exceptions outside of the " +
+      "requestAnimationFrame callback for non-immersive sessions";
+
+    let fakeDeviceInitParams = { supportsImmersive:true };
+
+    let immersiveSessionOptions = { mode: 'immersive-vr' };
+    let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
+
+    let testFunction = (testSession, testController, t) => new Promise((resolve) => {
+      let staleFrame = null;
+      let currentReferenceSpace = null;
+
+      function onFrame(time, xrFrame) {
+        t.step(() => {
+          assert_true(xrFrame instanceof XRFrame);
+        });
+
+        staleFrame = xrFrame;
+        step_timeout(afterFrame, 0);
+      }
+
+      function afterFrame() {
+        t.step(() => {
+          // Attempting to call a method on the frame outside the callback that
+          // originally provided it should cause it to throw an exception.
+          assert_throws('InvalidStateError', () => staleFrame.getViewerPose(currentReferenceSpace));
+        });
+
+        // Test does not complete until the this function has executed.
+        resolve();
+      }
+
+      testSession.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((referenceSpace) => {
+        currentReferenceSpace = referenceSpace;
+        testSession.requestAnimationFrame(onFrame);
+      });
+    });
+
+    xr_session_promise_test(immersiveTestName, testFunction,
+      fakeDeviceInitParams, immersiveSessionOptions);
+    xr_session_promise_test(nonImmersiveTestName, testFunction,
+      fakeDeviceInitParams, nonImmersiveSessionOptions);
+
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/xhr-in-moduleworker-expected.txt b/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/xhr-in-moduleworker-expected.txt
deleted file mode 100644
index 93eb19c..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/xhr-in-moduleworker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Base URL in module dedicated workers: XHR assert_equals: expected "gamma\n" but got "beta\n"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt
deleted file mode 100644
index bbf490d..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL WorkerLocation with redirects: module dedicated workers assert_equals: expected "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/post-location-members.js?a" but got "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/helper-redirect.py?fail"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/fast/events/popup-blocking-timers6-expected.txt b/third_party/blink/web_tests/fast/events/popup-blocking-timers6-expected.txt
index e5a528f..a4c9a08 100644
--- a/third_party/blink/web_tests/fast/events/popup-blocking-timers6-expected.txt
+++ b/third_party/blink/web_tests/fast/events/popup-blocking-timers6-expected.txt
@@ -3,5 +3,5 @@
 
 TEST COMPLETE
 
-Test calling window.open() with a 1001 ms delay. A popup should not be allowed.
+Test calling window.open() with a 5001 ms delay. A popup should not be allowed.
 PASS newWindow is null
diff --git a/third_party/blink/web_tests/fast/events/popup-blocking-timers6.html b/third_party/blink/web_tests/fast/events/popup-blocking-timers6.html
index 06034d6..b69660e 100644
--- a/third_party/blink/web_tests/fast/events/popup-blocking-timers6.html
+++ b/third_party/blink/web_tests/fast/events/popup-blocking-timers6.html
@@ -15,14 +15,14 @@
             setTimeout(function() {
                 newWindow = window.open("about:blank");
                 self.focus();
-                debug("Test calling window.open() with a 1001 ms delay. A popup should not be allowed.")
+                debug("Test calling window.open() with a 5001 ms delay. A popup should not be allowed.")
                 shouldBeNull("newWindow");
 
                 if (window.testRunner)
                     testRunner.notifyDone();
-            }, 1001);
+            }, 5001);
             if (window.eventSender)
-                eventSender.leapForward(1001);
+                eventSender.leapForward(5001);
         }
 
         function clickButton() {
diff --git a/third_party/blink/web_tests/fast/replaced/object-set-type-with-no-data-crash.html b/third_party/blink/web_tests/fast/replaced/object-set-type-with-no-data-crash.html
new file mode 100644
index 0000000..f991c97
--- /dev/null
+++ b/third_party/blink/web_tests/fast/replaced/object-set-type-with-no-data-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<object id="target" type="text/html"></object>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+  test(()=> {
+    var target = document.getElementById("target");
+    target.type = "text/html";
+  }, "No crash or DCHECK failure");
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-cpm-loose-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-cpm-loose-expected.txt
deleted file mode 100644
index 2195487b..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-cpm-loose-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-PASS 30FB  KATAKANA MIDDLE DOT may appear at line start if zh and loose
-PASS FF1A  FULLWIDTH COLON may appear at line start if zh and loose
-PASS FF1B  FULLWIDTH SEMICOLON may appear at line start if zh and loose
-PASS FF65  HALFWIDTH KATAKANA MIDDLE DOT may appear at line start if zh and loose
-PASS 203C  DOUBLE EXCLAMATION MARK may appear at line start if zh and loose
-PASS 2047  DOUBLE QUESTION MARK may appear at line start if zh and loose
-PASS 2048  QUESTION EXCLAMATION MARK may appear at line start if zh and loose
-PASS 2049  EXCLAMATION QUESTION MARK may appear at line start if zh and loose
-PASS FF01  FULLWIDTH EXCLAMATION MARK may appear at line start if zh and loose
-PASS FF1F  FULLWIDTH QUESTION MARK may appear at line start if zh and loose
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-po-loose-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-po-loose-expected.txt
deleted file mode 100644
index c182a8d..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-po-loose-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-PASS 00B0  DEGREE SIGN may appear at line start if zh and loose
-PASS 2030  PER MILLE SIGN may appear at line start if zh and loose
-PASS 2032  PRIME may appear at line start if zh and loose
-PASS 2033  DOUBLE PRIME may appear at line start if zh and loose
-PASS 2035  REVERSED PRIME may appear at line start if zh and loose
-PASS 2103  DEGREE CELSIUS may appear at line start if zh and loose
-PASS 2109  DEGREE FAHRENHEIT may appear at line start if zh and loose
-PASS FE6A  SMALL PERCENT SIGN may appear at line start if zh and loose
-PASS FF05  FULLWIDTH PERCENT SIGN may appear at line start if zh and loose
-PASS FFE0  FULLWIDTH CENT SIGN may appear at line start if zh and loose
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/resources/cross-origin-module-script.html b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/resources/cross-origin-module-script.html
new file mode 100644
index 0000000..b51719a
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/resources/cross-origin-module-script.html
@@ -0,0 +1 @@
+<script type="module" src="http://localhost:8000/devtools/resources/v8-cache-script.cgi"></script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/resources/same-origin-module-script.html b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/resources/same-origin-module-script.html
new file mode 100644
index 0000000..42173b1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/resources/same-origin-module-script.html
@@ -0,0 +1 @@
+<script type="module" src="/devtools/resources/v8-cache-script.cgi"></script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/same-origin-module-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/same-origin-module-test-expected.txt
new file mode 100644
index 0000000..c03e4f1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/same-origin-module-test-expected.txt
@@ -0,0 +1,114 @@
+Tests V8 code cache for javascript resources
+
+--- Trace events related to code caches ------
+v8.compileModule Properties:
+{
+    data : {
+        columnNumber : 1
+        lineNumber : 1
+        notStreamedReason : "module script"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.cgi
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compileModule"
+}
+Text details for v8.compileModule: v8-cache-script.cgi
+v8.compileModule Properties:
+{
+    data : {
+        columnNumber : 1
+        lineNumber : 1
+        notStreamedReason : "module script"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.cgi
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compileModule"
+}
+Text details for v8.compileModule: v8-cache-script.cgi
+v8.compileModule Properties:
+{
+    data : {
+        cacheProduceOptions : "code"
+        columnNumber : 1
+        lineNumber : 1
+        notStreamedReason : "module script"
+        producedCacheSize : <number>
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.cgi
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compileModule"
+}
+Text details for v8.compileModule: v8-cache-script.cgi
+v8.compileModule Properties:
+{
+    data : {
+        cacheConsumeOptions : "code"
+        cacheRejected : false
+        columnNumber : 1
+        consumedCacheSize : <number>
+        lineNumber : 1
+        notStreamedReason : "module script"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.cgi
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compileModule"
+}
+Text details for v8.compileModule: v8-cache-script.cgi
+v8.compileModule Properties:
+{
+    data : {
+        columnNumber : 1
+        lineNumber : 1
+        notStreamedReason : "module script"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.cgi
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compileModule"
+}
+Text details for v8.compileModule: v8-cache-script.cgi
+v8.compileModule Properties:
+{
+    data : {
+        cacheConsumeOptions : "code"
+        cacheRejected : false
+        columnNumber : 1
+        consumedCacheSize : <number>
+        lineNumber : 1
+        notStreamedReason : "module script"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.cgi
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compileModule"
+}
+Text details for v8.compileModule: v8-cache-script.cgi
+v8.compileModule Properties:
+{
+    data : {
+        cacheConsumeOptions : "code"
+        cacheRejected : false
+        columnNumber : 1
+        consumedCacheSize : <number>
+        lineNumber : 1
+        notStreamedReason : "module script"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.cgi
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compileModule"
+}
+Text details for v8.compileModule: v8-cache-script.cgi
+-----------------------------------------------
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/same-origin-module-test.js b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/same-origin-module-test.js
new file mode 100644
index 0000000..de3e33a
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/isolated-code-cache/same-origin-module-test.js
@@ -0,0 +1,63 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Tests V8 code cache for javascript resources\n`);
+  await TestRunner.loadModule('performance_test_runner');
+  await TestRunner.showPanel('timeline');
+
+  // Clear browser cache to avoid any existing entries for the fetched
+  // scripts in the cache.
+  SDK.multitargetNetworkManager.clearBrowserCache();
+
+  // There are two scripts:
+  // [A] http://127.0.0.1:8000/devtools/resources/v8-cache-script.cgi
+  // [B] http://localhost:8000/devtools/resources/v8-cache-script.cgi
+
+  // An iframe that loads [A].
+  // The script is executed as a parser-inserted script,
+  // to keep the ScriptResource on the MemoryCache.
+  // ScriptResources for dynamically-inserted <script>s can be
+  // garbage-collected and thus removed from MemoryCache after its execution.
+  const scope = 'resources/same-origin-module-script.html';
+  // An iframe that loads [B].
+  const scopeCrossOrigin = 'resources/cross-origin-module-script.html';
+
+  TestRunner.addResult('--- Trace events related to code caches ------');
+  await PerformanceTestRunner.startTimeline();
+
+  // Load [A] thrice. With the current V8 heuristics (defined
+  // in third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc) we produce
+  // cache on second fetch and consume it in the third fetch. This tests these
+  // heuristics.
+  // Note that addIframe() waits for iframe's load event, which waits for the
+  // <script> loading.
+  await TestRunner.addIframe(scope);
+  await TestRunner.addIframe(scope);
+  await TestRunner.addIframe(scope);
+
+  // Load [B]. Should not use the cached code.
+  await TestRunner.addIframe(scopeCrossOrigin);
+
+  // Load [A] again from MemoryCache. Should use cached code.
+  await TestRunner.addIframe(scope);
+
+  // Clear [A] from MemoryCache. Blink evicts previous Resource when a
+  // new request to the same URL but with different resource type is started.
+  // We fetch() to the URL of [A], and thus evicts the previous ScriptResource
+  // of [A].
+  await TestRunner.evaluateInPageAsync(
+      `fetch('/devtools/resources/v8-cache-script.cgi')`);
+
+  // Load [A] from Disk Cache. As we cleared [A] from MemoryCache, this
+  // doesn't hit MemoryCache, but still hits Disk Cache.
+  await TestRunner.addIframe(scope);
+
+  await PerformanceTestRunner.stopTimeline();
+  PerformanceTestRunner.printTimelineRecordsWithDetails(
+      TimelineModel.TimelineModel.RecordType.CompileModule);
+
+  TestRunner.addResult('-----------------------------------------------');
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 7630189..8fa8e25 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -748,6 +748,8 @@
     static method createDataPipe
     static method createMessagePipe
     static method createSharedBuffer
+    static method getDocumentInterfaceBrokerHandle
+    static method replaceDocumentInterfaceBrokerForTesting
     attribute @@toStringTag
     attribute RESULT_ABORTED
     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/blink/web_tests/mojo/document-interface-broker-override.html b/third_party/blink/web_tests/mojo/document-interface-broker-override.html
new file mode 100644
index 0000000..8e1da98b
--- /dev/null
+++ b/third_party/blink/web_tests/mojo/document-interface-broker-override.html
@@ -0,0 +1,45 @@
+<body>
+<script src="../resources/document-interface-broker-helpers.js"></script>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings_lite.js"></script>
+<script src="file:///gen/url/mojom/url.mojom-lite.js"></script>
+<script src="file:///gen/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom-lite.js"></script>
+<script src="file:///gen/third_party/blink/public/mojom/frame/document_interface_broker.mojom-lite.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+  // Create a test implementation of FrameHostTestInterface
+  const frameHostTestImpl = new blink.mojom.FrameHostTestInterfaceCallbackRouter;
+  frameHostTestImpl.getName.addListener(() => ({ name: 'TestFrameHostTestImpl' }));
+
+  const brokerProxy = new blink.mojom.DocumentInterfaceBrokerProxy(
+      Mojo.getDocumentInterfaceBrokerHandle());
+
+  const testInterfaceProxyBeforeOverride = new blink.mojom.FrameHostTestInterfaceProxy;
+  console.log("proxy" + typeof(testInterfaceProxyBeforeOverride));
+  brokerProxy.getFrameHostTestInterface(testInterfaceProxyBeforeOverride.$.createRequest());
+
+  // Verify that RenderFrameHostImpl's implementation gets called without an override
+  testInterfaceProxyBeforeOverride.getName().then(reply => {
+    assert_equals(reply.name, 'RenderFrameHostImpl');
+  });
+
+  setDocumentInterfaceBrokerOverrides({ getFrameHostTestInterface: request => {
+    frameHostTestImpl.bindHandle(request.handle);
+  }});
+
+  const testInterfaceProxyAfterOverride = new blink.mojom.FrameHostTestInterfaceProxy;
+  brokerProxy.getFrameHostTestInterface(testInterfaceProxyAfterOverride.$.createRequest());
+
+  // Verify that the test implementation gets called after the override
+  return testInterfaceProxyAfterOverride.getName().then(reply => {
+    assert_equals(reply.name, 'TestFrameHostTestImpl');
+  });
+},
+'Appropriate DocumentInterfaceBroker implementations are called before and after overriding');
+
+</script>
+ </body>
+ </html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/resources/document-interface-broker-helpers.js b/third_party/blink/web_tests/resources/document-interface-broker-helpers.js
new file mode 100644
index 0000000..e27e910a
--- /dev/null
+++ b/third_party/blink/web_tests/resources/document-interface-broker-helpers.js
@@ -0,0 +1,29 @@
+'use strict';
+
+/**
+ * Allows to override specific interface request handlers for
+ * DocumentInterfaceBroker for testing purposes
+ * @param {Object} overrides an object where the keys are names of
+ *     DocumentInterfaceBroker's methods and the values are corresponding handler
+ *     functions taking a request parameter and binding its handle to the relevant
+ *     testing implementation.
+ * Example:
+ *   const testFooImpl = new FooInterfaceCallbackRouter;
+ *   ... override FooInterface methods ...
+ *   setDocumentInterfaceBrokerOverrides({getFooInterface: request => {
+ *     testFooImpl.bindHandle(request.handle);
+ *   }});
+ */
+function setDocumentInterfaceBrokerOverrides(overrides) {
+  const {handle0, handle1} = Mojo.createMessagePipe();
+  const realBrokerProxy = new blink.mojom.DocumentInterfaceBrokerProxy(
+      Mojo.replaceDocumentInterfaceBrokerForTesting(handle0));
+
+  for (const method of Object.keys(overrides)) {
+    realBrokerProxy[method] = overrides[method];
+  }
+
+  // Use the real broker (with overrides) as the implementation of the JS-side broker
+  const testBrokerBinding = new blink.mojom.DocumentInterfaceBroker(realBrokerProxy);
+  testBrokerBinding.bindHandle(handle1);
+}
diff --git a/third_party/blink/web_tests/resources/testdriver-vendor.js b/third_party/blink/web_tests/resources/testdriver-vendor.js
index a806a721..95b584b 100644
--- a/third_party/blink/web_tests/resources/testdriver-vendor.js
+++ b/third_party/blink/web_tests/resources/testdriver-vendor.js
@@ -84,6 +84,7 @@
     for (let i = 0; i < actions.length; i++) {
       var last_x_position = 0;
       var last_y_position = 0;
+      var first_pointer_down = false;
       for (let j = 0; j < actions[i].actions.length; j++) {
         if ('origin' in actions[i].actions[j]) {
           if (actions[i].actions[j].origin == "viewport") {
@@ -126,6 +127,16 @@
           actions[i].actions[j].x = last_x_position;
           actions[i].actions[j].y = last_y_position;
         }
+
+        if ('parameters' in actions[i] && actions[i].parameters.pointerType == "touch") {
+          if (actions[i].actions[j].type == "pointerMove" && !first_pointer_down) {
+            actions[i].actions[j].type = "pause";
+          } else if (actions[i].actions[j].type == "pointerDown") {
+            first_pointer_down = true;
+          } else if (actions[i].actions[j].type == "pointerUp") {
+            first_pointer_down = false;
+          }
+        }
       }
     }
 
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-sharedworker-expected.txt
deleted file mode 100644
index 4f65337..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-sharedworker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL importScripts assert_equals: expected "gamma/script.js" but got "beta/script.js"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-worker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-worker-expected.txt
deleted file mode 100644
index 4f65337..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL importScripts assert_equals: expected "gamma/script.js" but got "beta/script.js"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/worker-in-worker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/worker-in-worker-expected.txt
deleted file mode 100644
index a8ed8c5f..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/worker-in-worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Base URL in workers: new Worker() assert_equals: expected "gamma" but got "beta"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-sharedworker-expected.txt
deleted file mode 100644
index 4bb4dcb..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-sharedworker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL xhr-worker assert_equals: expected "gamma\n" but got "beta\n"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-worker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-worker-expected.txt
deleted file mode 100644
index 4bb4dcb..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL xhr-worker assert_equals: expected "gamma\n" but got "beta\n"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-expected.txt
deleted file mode 100644
index 0fcc95f..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL WorkerLocation with redirects: classic dedicated workers assert_equals: expected "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/post-location-members.js?a" but got "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/helper-redirect.py?fail"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-sharedworker-expected.txt
deleted file mode 100644
index 600eacd..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-sharedworker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL redirect assert_equals: expected "/workers/interfaces/WorkerGlobalScope/location/redirect.js" but got "/common/redirect.py"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-redirect-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-redirect-expected.txt
deleted file mode 100644
index d344f4d2..0000000
--- a/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-redirect-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: Unsafe attempt to load URL http://localhost:8000/workers/resources/worker-redirect-target.js from frame with URL http://127.0.0.1:8000/resources/redirect.php?url=http://localhost:8000/workers/resources/worker-redirect-target.js. Domains, protocols and ports must match.
-
-Test that loading the worker's script does not allow a cross origin redirect (bug 26146)
-
-SUCCESS: threw exception (SecurityError: Failed to construct 'Worker': Script at 'http://localhost:8000/workers/resources/worker-target.js' cannot be accessed from origin 'http://127.0.0.1:8000'.) when attempting to cross origin while loading the worker script.
-SUCCESS: threw error when attempting to redirected cross origin while loading the worker script.
-DONE
-
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index f445684..07c27e5e 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -702,6 +702,8 @@
 [Worker]     static method createDataPipe
 [Worker]     static method createMessagePipe
 [Worker]     static method createSharedBuffer
+[Worker]     static method getDocumentInterfaceBrokerHandle
+[Worker]     static method replaceDocumentInterfaceBrokerForTesting
 [Worker]     attribute @@toStringTag
 [Worker]     attribute RESULT_ABORTED
 [Worker]     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 0296a9e..4a0b0fa 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -4655,6 +4655,8 @@
     static method createDataPipe
     static method createMessagePipe
     static method createSharedBuffer
+    static method getDocumentInterfaceBrokerHandle
+    static method replaceDocumentInterfaceBrokerForTesting
     attribute @@toStringTag
     attribute RESULT_ABORTED
     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 51bdf80..570dd6f 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -687,6 +687,8 @@
 [Worker]     static method createDataPipe
 [Worker]     static method createMessagePipe
 [Worker]     static method createSharedBuffer
+[Worker]     static method getDocumentInterfaceBrokerHandle
+[Worker]     static method replaceDocumentInterfaceBrokerForTesting
 [Worker]     attribute @@toStringTag
 [Worker]     attribute RESULT_ABORTED
 [Worker]     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index 43af897..80dd6bc1 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -1287,6 +1287,18 @@
 chrome.automation.AutomationNode.prototype.lineThrough;
 
 /**
+ * The font family of this node.
+ * @type {string|undefined}
+ */
+chrome.automation.AutomationNode.prototype.fontFamily;
+
+/**
+ * The font size of this node.
+ * @type {number|undefined}
+ */
+chrome.automation.AutomationNode.prototype.fontSize;
+
+/**
  * Indicates whether this node is selected, unselected, or neither.
  * @type {(boolean|undefined)}
  * @see https://developer.chrome.com/extensions/automation#type-selected
diff --git a/third_party/grpc/BUILD.gn b/third_party/grpc/BUILD.gn
index 0ff0399..190242d 100644
--- a/third_party/grpc/BUILD.gn
+++ b/third_party/grpc/BUILD.gn
@@ -548,6 +548,10 @@
   ]
 }
 
+# A dummy group which is needed to make the fuzz targets discoverable.
+group("fuzzers") {
+}
+
 fuzzer_test("grpc_alts_credentials_fuzzer") {
   sources = [
     "src/test/core/security/alts_credentials_fuzzer.cc",
@@ -578,6 +582,7 @@
     ":grpc_internal_config",
   ]
   seed_corpus = "src/test/core/end2end/fuzzers/api_fuzzer_corpus"
+  dict = "src/test/core/end2end/fuzzers/api_fuzzer.dictionary"
 }
 
 fuzzer_test("grpc_client_fuzzer") {
@@ -594,6 +599,7 @@
     ":grpc_internal_config",
   ]
   seed_corpus = "src/test/core/end2end/fuzzers/client_fuzzer_corpus"
+  dict = "src/test/core/end2end/fuzzers/hpack.dictionary"
 }
 
 fuzzer_test("grpc_hpack_parser_fuzzer_test") {
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index 5186ba0..99d42cb 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: b9f19d327ece7e05c74898fbb6fdc551c6d3f344
+Revision: f67ec5180f476830e839226b5ca948e43070fdab
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/lib/base_string_adapter_h.template b/third_party/inspector_protocol/lib/base_string_adapter_h.template
index 2383777..b0215e0 100644
--- a/third_party/inspector_protocol/lib/base_string_adapter_h.template
+++ b/third_party/inspector_protocol/lib/base_string_adapter_h.template
@@ -64,8 +64,12 @@
   static String fromInteger(int number) { return base::NumberToString(number); }
   static String fromDouble(double number) {
     String s = base::NumberToString(number);
-    if (!s.empty() && s[0] == '.')
-      s = "0" + s;
+    if (!s.empty()) {  // .123 -> 0.123; -.123 -> -0.123 for valid JSON.
+      if (s[0] == '.')
+        s.insert(/*index=*/ 0, /*count=*/ 1, /*ch=*/ '0');
+      else if (s[0] == '-' && s.size() >= 2 && s[1] == '.')
+        s.insert(/*index=*/ 1, /*count=*/ 1, /*ch=*/ '0');
+    }
     return s;
   }
   static double toDouble(const char* s, size_t len, bool* ok) {
diff --git a/third_party/jmake/BUILD.gn b/third_party/jmake/BUILD.gn
deleted file mode 100644
index 2598dbe..0000000
--- a/third_party/jmake/BUILD.gn
+++ /dev/null
@@ -1,31 +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.
-
-import("//build/config/android/rules.gni")
-
-java_binary("jmake") {
-  java_files = [
-    "src/org/pantsbuild/jmake/CompatibilityChecker.java",
-    "src/org/pantsbuild/jmake/BinaryFileWriter.java",
-    "src/org/pantsbuild/jmake/PCDEntry.java",
-    "src/org/pantsbuild/jmake/TextProjectDatabaseWriter.java",
-    "src/org/pantsbuild/jmake/Base64.java",
-    "src/org/pantsbuild/jmake/PCDContainer.java",
-    "src/org/pantsbuild/jmake/Main.java",
-    "src/org/pantsbuild/jmake/ClassFileReader.java",
-    "src/org/pantsbuild/jmake/ClassPath.java",
-    "src/org/pantsbuild/jmake/BinaryFileReader.java",
-    "src/org/pantsbuild/jmake/PrivateException.java",
-    "src/org/pantsbuild/jmake/ClassInfo.java",
-    "src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java",
-    "src/org/pantsbuild/jmake/PCDManager.java",
-    "src/org/pantsbuild/jmake/TextProjectDatabaseReader.java",
-    "src/org/pantsbuild/jmake/RefClassFinder.java",
-    "src/org/pantsbuild/jmake/Utils.java",
-    "src/org/pantsbuild/jmake/PublicExceptions.java",
-    "src/org/pantsbuild/jmake/BinaryProjectDatabaseReader.java",
-  ]
-  main_class = "org.pantsbuild.jmake.Main"
-  enable_incremental_javac_override = false
-}
diff --git a/third_party/jmake/LICENSE b/third_party/jmake/LICENSE
deleted file mode 100644
index 2fa033e..0000000
--- a/third_party/jmake/LICENSE
+++ /dev/null
@@ -1,341 +0,0 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
-
diff --git a/third_party/jmake/OWNERS b/third_party/jmake/OWNERS
deleted file mode 100644
index b4eda11..0000000
--- a/third_party/jmake/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-agrieve@chromium.org
-yfriedman@chromium.org
-
-# COMPONENT: Build
diff --git a/third_party/jmake/README.chromium b/third_party/jmake/README.chromium
deleted file mode 100644
index 0530d0f..0000000
--- a/third_party/jmake/README.chromium
+++ /dev/null
@@ -1,17 +0,0 @@
-Name: JMake
-URL: https://github.com/pantsbuild/jmake
-Version: 0
-Revision: 7761ee3e1537ccc61820c0d30061eb09edaf1c93
-License: GPL 2.0
-License File: NOT_SHIPPED
-Security Critical: No
-License Android Compatible: No
-
-Description:
-Formerly known as Javamake, jmake is a compiler wrapper that figures out the
-minimal set of .java files that need to be rebuilt given a set of .java files
-that have changed.
-
-Local Modifications:
-* Removed unneeded files
-* Added BUILD.gn
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/Base64.java b/third_party/jmake/src/org/pantsbuild/jmake/Base64.java
deleted file mode 100644
index af02ca6..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/Base64.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright (c) 2002-2013 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.util.Arrays;
-
-
-/**
- * JMake needs to run against old versions of Java, that may not have JAXB's
- * javax.xml.bind.DatatypeConverter. And we don't want JMake to depend on third-party external libraries,
- * especially not just for this.  So we implement a lightweight Base64 converter here ourselves.
-
- * Note that sun.misc.BASE64Encoder is not official API and can go away at any time. Plus it inserts
- * line breaks into its emitted string, which is not what we want. So we can't use that either.
- */
-
-public class Base64 {
-    // The easiest way to grok this code is to think of Base64 as the following chain of
-    // conversions (ignoring padding issues):
-    // 3 bytes -> 24 bits -> 4 6-bit nibbles -> 4 indexes from 0-63 -> 4 characters.
-    private static final char[] indexToDigit =
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
-    private static final int[] digitToIndex = new int[128];
-    static {
-        assert(indexToDigit.length == 64);
-        Arrays.fill(digitToIndex, -1);
-        for (int i = 0; i < indexToDigit.length; i++) digitToIndex[(int)indexToDigit[i]] = i;
-    }
-
-    private Base64() {}
-
-    public static char[] encode(byte[] in) {
-        char[] ret = new char[(in.length + 2) / 3 * 4];
-        int p = 0;
-        int i = 0;
-        while (i < in.length) {
-            // Lowest 24 bits count.
-            int bits = (in[i++] & 0xff) << 16 | (i < in.length ? in[i++] & 0xff : 0) << 8 | (i < in.length ? in[i++] & 0xff : 0);
-            ret[p++] = indexToDigit[(bits & 0xfc0000) >> 18];
-            ret[p++] = indexToDigit[(bits & 0x3f000) >> 12];
-            ret[p++] = indexToDigit[(bits & 0xfc0) >> 6];
-            ret[p++] = indexToDigit[bits & 0x3f];
-        }
-        assert(p == ret.length);
-        int padding = (3 - in.length % 3) % 3;
-        for (int j = ret.length - padding; j < ret.length; j++) ret[j] = '=';
-        return ret;
-    }
-
-    public static byte[] decode(char[] in) {
-        if (in.length % 4 != 0) throw new IllegalArgumentException("Base64-encoded string must be of length that is a multiple of 4.");
-        int len = in.length;
-        while(len > 0 && in[len - 1] == '=') len--;
-        int padding = in.length - len;
-        byte[] ret = new byte[in.length / 4 * 3 - padding];
-        int i = 0;
-        int p = 0;
-        while (i < len) {
-            char c0 = in[i++];
-            char c1 = in[i++];
-            char c2 = i < len ? in[i++] : 'A';
-            char c3 = i < len ? in[i++] : 'A';
-            if (c0 > 127 || c1 > 127 || c2 > 127 || c3 > 127) throw new IllegalArgumentException("Invalid Base64 digit in: " + c0 + c1 + c2 + c3);
-            int n0 = digitToIndex[c0];
-            int n1 = digitToIndex[c1];
-            int n2 = digitToIndex[c2];
-            int n3 = digitToIndex[c3];
-            if (n0 < 0 || n1 < 0 || n2 < 0 || n3 < 0) throw new IllegalArgumentException("Invalid Base64 digit in: " + c0 + c1 + c2 + c3);
-            int bits = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;
-            ret[p++] = (byte)((bits & 0xff0000) >> 16);
-            if (p < ret.length) ret[p++] = (byte)((bits & 0xff00) >> 8);
-            if (p < ret.length) ret[p++] = (byte)(bits & 0xff);
-        }
-        return ret;
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/BinaryFileReader.java b/third_party/jmake/src/org/pantsbuild/jmake/BinaryFileReader.java
deleted file mode 100644
index 6e2cb4e..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/BinaryFileReader.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.DataInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-/**
- * Basic operations for reading a byte array representing a binary file.
- *
- * @author  Misha Dmitriev
- *  10 November 2001
- */
-public class BinaryFileReader {
-
-    protected byte[] buf;
-    protected int curBufPos;
-    protected String fileFullPath;  // Required only for nice error reports
-
-    protected void initBuf(byte[] buf, String fileFullPath) {
-        this.buf = buf;
-        curBufPos = 0;
-        this.fileFullPath = fileFullPath;
-    }
-
-    protected char nextChar() {
-        return (char) (((buf[curBufPos++] & 255) << 8) + (buf[curBufPos++] & 255));
-    }
-
-    protected char getChar(int bufPos) {
-        return (char) (((buf[bufPos] & 255) << 8) + (buf[bufPos+1] & 255));
-    }
-
-    protected int nextInt() {
-        return ((buf[curBufPos++] & 255) << 24) + ((buf[curBufPos++] & 255) << 16) +
-                ((buf[curBufPos++] & 255) << 8) + (buf[curBufPos++] & 255);
-    }
-
-    protected int getInt(int bufPos) {
-        return ((buf[bufPos] & 255) << 24) + ((buf[bufPos+1] & 255) << 16) +
-                ((buf[bufPos+2] & 255) << 8) + (buf[bufPos+3] & 255);
-    }
-
-    protected long nextLong() {
-        long res = getLong(curBufPos);
-        curBufPos += 8;
-        return res;
-    }
-
-    protected long getLong(int bufPos) {
-        DataInputStream bufin =
-                new DataInputStream(new ByteArrayInputStream(buf, bufPos, 8));
-        try {
-            return bufin.readLong();
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-    protected float nextFloat() {
-        float res = getFloat(curBufPos);
-        curBufPos += 4;
-        return res;
-    }
-
-    protected float getFloat(int bufPos) {
-        DataInputStream bufin =
-                new DataInputStream(new ByteArrayInputStream(buf, bufPos, 4));
-        try {
-            return bufin.readFloat();
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-    protected double nextDouble() {
-        double res = getDouble(curBufPos);
-        curBufPos += 8;
-        return res;
-    }
-
-    protected double getDouble(int bufPos) {
-        DataInputStream bufin =
-                new DataInputStream(new ByteArrayInputStream(buf, bufPos, 8));
-        try {
-            return bufin.readDouble();
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/BinaryFileWriter.java b/third_party/jmake/src/org/pantsbuild/jmake/BinaryFileWriter.java
deleted file mode 100644
index 01de415..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/BinaryFileWriter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-/**
- * Basic operations for writing to a byte array representing a binary file.
- *
- * @author  Misha Dmitriev
- *  30 January 2002
- */
-public class BinaryFileWriter {
-
-    protected byte[] buf;
-    protected int curBufSize,  bufInc,  curBufPos,  threshold;
-    private boolean bufferIncreaseAllowed = true;
-
-    protected void initBuf(int initSize) {
-        buf = new byte[initSize];
-        curBufSize = initSize;
-        bufInc = initSize / 5;
-        curBufPos = 0;
-        threshold = curBufSize - bufInc;
-    }
-
-    protected void increaseBuf() {
-        if (!bufferIncreaseAllowed) {
-            return;
-        }
-        byte newBuf[] = new byte[curBufSize + bufInc];
-        System.arraycopy(buf, 0, newBuf, 0, curBufPos);
-        buf = newBuf;
-        curBufSize = buf.length;
-        threshold = curBufSize - bufInc;
-    }
-
-    // This should be called with false only when we are sure that we set the exact size of the buffer
-    // and there is no need to increase it.
-    protected void setBufferIncreaseMode(boolean increaseMode) {
-        bufferIncreaseAllowed = increaseMode;
-    }
-
-    public byte[] getBuffer() {
-        return buf;
-    }
-
-
-    protected void writeByte(byte b) {
-        if (curBufPos > threshold) {
-            increaseBuf();
-        }
-        buf[curBufPos++] = b;
-    }
-
-    protected void writeChar(int ch) {
-        buf[curBufPos++] = (byte) ((ch >> 8) & 255);
-        buf[curBufPos++] = (byte) (ch & 255);
-        if (curBufPos > threshold) {
-            increaseBuf();
-        }
-    }
-
-    protected void writeInt(int i) {
-        buf[curBufPos++] = (byte) ((i >> 24) & 255);
-        buf[curBufPos++] = (byte) ((i >> 16) & 255);
-        buf[curBufPos++] = (byte) ((i >> 8) & 255);
-        buf[curBufPos++] = (byte) (i & 255);
-        if (curBufPos > threshold) {
-            increaseBuf();
-        }
-    }
-
-    protected void writeLong(long l) {
-        buf[curBufPos++] = (byte) ((l >> 56) & 255);
-        buf[curBufPos++] = (byte) ((l >> 48) & 255);
-        buf[curBufPos++] = (byte) ((l >> 40) & 255);
-        buf[curBufPos++] = (byte) ((l >> 32) & 255);
-        buf[curBufPos++] = (byte) ((l >> 24) & 255);
-        buf[curBufPos++] = (byte) ((l >> 16) & 255);
-        buf[curBufPos++] = (byte) ((l >> 8) & 255);
-        buf[curBufPos++] = (byte) (l & 255);
-        if (curBufPos > threshold) {
-            increaseBuf();
-        }
-    }
-
-    protected void writeFloat(float f) {
-        int i = Float.floatToIntBits(f);
-        writeInt(i);
-    }
-
-    protected void writeDouble(double d) {
-        long l = Double.doubleToLongBits(d);
-        writeLong(l);
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseReader.java b/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseReader.java
deleted file mode 100644
index e75e206..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseReader.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.File;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * This class creates the internal representation of the project database from a byte array.
- *
- * @author  Misha Dmitriev
- *  2 March 2005
- */
-public class BinaryProjectDatabaseReader extends BinaryFileReader {
-
-    private String stringTable[];
-    private Map<String,PCDEntry> pcd;
-    private int nOfEntries;
-    private int pdbFormat;  // Currently supported values: 0x01030300 (jmake 1.3.3 and newer versions); 1 (all older versions)
-    // These are defined in Utils as PDB_FORMAT_CODE_LATEST and PDB_FORMAT_CODE_OLD
-
-    public Map<String,PCDEntry> readProjectDatabaseFromFile(File infile) {
-        byte buf[] = Utils.readFileIntoBuffer(infile);
-        return readProjectDatabase(buf, infile.toString());
-    }
-
-    public Map<String,PCDEntry> readProjectDatabase(byte[] pdbFile,
-            String pdbFileFullPath) {
-        initBuf(pdbFile, pdbFileFullPath);
-
-        readPreamble();
-        readStringTable();
-        pcd = new LinkedHashMap<String,PCDEntry>(nOfEntries * 4 / 3);
-
-        for (int i = 0; i < nOfEntries; i++) {
-            PCDEntry entry = readPCDEntry();
-            pcd.put(entry.className, entry);
-        }
-
-        stringTable = null;  // Help the GC
-        return pcd;
-    }
-
-    private void readPreamble() {
-        if (buf.length < Utils.magicLength + 8) {
-            pdbCorruptedException("file too short");
-        }
-
-        for (int i = 0; i < Utils.magicLength; i++) {
-            if (buf[i] != Utils.MAGIC[i]) {
-                pdbCorruptedException("wrong project database header");
-            }
-        }
-
-        curBufPos += Utils.magicLength;
-        pdbFormat = nextInt();
-        if (pdbFormat != Utils.PDB_FORMAT_CODE_OLD && pdbFormat != Utils.PDB_FORMAT_CODE_LATEST) {
-            pdbCorruptedException("wrong version number");
-        }
-
-        int pdbSize = nextInt();
-        if (buf.length != Utils.MAGIC.length + 8 + pdbSize) {
-            pdbCorruptedException("file size does not match stored value");
-        }
-
-        nOfEntries = nextInt();
-    }
-
-    private void readStringTable() {
-        int size = nextInt();
-        stringTable = new String[size];
-        for (int i = 0; i < size; i++) {
-            stringTable[i] = nextString();
-        }
-    }
-
-    private PCDEntry readPCDEntry() {
-        String className = nextStringRef();
-        String javaFileFullPath = nextStringRef();
-        long classFileLastModified = nextLong();
-        long classFileFingerprint = nextLong();
-        ClassInfo classInfo = readClassInfo();
-
-        return new PCDEntry(className, javaFileFullPath, classFileLastModified, classFileFingerprint, classInfo);
-    }
-
-    private ClassInfo readClassInfo() {
-        int i, j, len;
-        ClassInfo res = new ClassInfo();
-
-        res.name = nextStringRef();
-        if (pdbFormat >= Utils.PDB_FORMAT_CODE_133) {
-            res.javacTargetRelease = nextInt();
-        } else {
-            res.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String cpoolRefsToClasses[] = new String[len];
-            for (i = 0; i < len; i++) {
-                cpoolRefsToClasses[i] = nextStringRef();
-            }
-            res.cpoolRefsToClasses = cpoolRefsToClasses;
-            boolean isRefClassArray[] = new boolean[len];
-            for (i = 0; i < len; i++) {
-                isRefClassArray[i] = (buf[curBufPos++] != 0);
-            }
-            res.isRefClassArray = isRefClassArray;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String cpoolRefsToFieldClasses[] = new String[len];
-            for (i = 0; i < len; i++) {
-                cpoolRefsToFieldClasses[i] = nextStringRef();
-            }
-            res.cpoolRefsToFieldClasses = cpoolRefsToFieldClasses;
-            String cpoolRefsToFieldNames[] = new String[len];
-            for (i = 0; i < len; i++) {
-                cpoolRefsToFieldNames[i] = nextStringRef();
-            }
-            res.cpoolRefsToFieldNames = cpoolRefsToFieldNames;
-            String cpoolRefsToFieldSignatures[] = new String[len];
-            for (i = 0; i < len; i++) {
-                cpoolRefsToFieldSignatures[i] = nextStringRef();
-            }
-            res.cpoolRefsToFieldSignatures = cpoolRefsToFieldSignatures;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String cpoolRefsToMethodClasses[] = new String[len];
-            for (i = 0; i < len; i++) {
-                cpoolRefsToMethodClasses[i] = nextStringRef();
-            }
-            res.cpoolRefsToMethodClasses = cpoolRefsToMethodClasses;
-            String cpoolRefsToMethodNames[] = new String[len];
-            for (i = 0; i < len; i++) {
-                cpoolRefsToMethodNames[i] = nextStringRef();
-            }
-            res.cpoolRefsToMethodNames = cpoolRefsToMethodNames;
-            String cpoolRefsToMethodSignatures[] = new String[len];
-            for (i = 0; i < len; i++) {
-                cpoolRefsToMethodSignatures[i] = nextStringRef();
-            }
-            res.cpoolRefsToMethodSignatures = cpoolRefsToMethodSignatures;
-        }
-
-        res.accessFlags = nextChar();
-        res.isNonMemberNestedClass = (buf[curBufPos++] != 0);
-        if (!"java/lang/Object".equals(res.name)) {
-            res.superName = nextStringRef();
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String interfaces[] = new String[len];
-            for (i = 0; i < len; i++) {
-                interfaces[i] = nextStringRef();
-            }
-            res.interfaces = interfaces;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String fieldNames[] = new String[len];
-            for (i = 0; i < len; i++) {
-                fieldNames[i] = nextStringRef();
-            }
-            res.fieldNames = fieldNames;
-            String fieldSignatures[] = new String[len];
-            for (i = 0; i < len; i++) {
-                fieldSignatures[i] = nextStringRef();
-            }
-            res.fieldSignatures = fieldSignatures;
-            char fieldAccessFlags[] = new char[len];
-            for (i = 0; i < len; i++) {
-                fieldAccessFlags[i] = nextChar();
-            }
-            res.fieldAccessFlags = fieldAccessFlags;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            Object primitiveConstantInitValues[] = new Object[len];
-            for (i = 0; i < len; i++) {
-                byte code = buf[curBufPos++];
-                switch (code) {
-                    case 1:
-                        primitiveConstantInitValues[i] = nextStringRef();
-                        break;
-                    case 2:
-                        primitiveConstantInitValues[i] = Integer.valueOf(nextInt());
-                        break;
-                    case 3:
-                        primitiveConstantInitValues[i] = Long.valueOf(nextLong());
-                        break;
-                    case 4:
-                        primitiveConstantInitValues[i] = Float.valueOf(nextFloat());
-                        break;
-                    case 5:
-                        primitiveConstantInitValues[i] =
-                                Double.valueOf(nextDouble());
-                        break;
-                    default:  // Nothing to do
-                }
-            }
-            res.primitiveConstantInitValues = primitiveConstantInitValues;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String methodNames[] = new String[len];
-            for (i = 0; i < len; i++) {
-                methodNames[i] = nextStringRef();
-            }
-            res.methodNames = methodNames;
-            String methodSignatures[] = new String[len];
-            for (i = 0; i < len; i++) {
-                methodSignatures[i] = nextStringRef();
-            }
-            res.methodSignatures = methodSignatures;
-            char methodAccessFlags[] = new char[len];
-            for (i = 0; i < len; i++) {
-                methodAccessFlags[i] = nextChar();
-            }
-            res.methodAccessFlags = methodAccessFlags;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String checkedExceptions[][] = new String[len][];
-            for (i = 0; i < len; i++) {
-                int len1 = nextChar();
-                if (len1 > 0) {
-                    checkedExceptions[i] = new String[len1];
-                    for (j = 0; j < len1; j++) {
-                        checkedExceptions[i][j] = nextStringRef();
-                    }
-                }
-            }
-            res.checkedExceptions = checkedExceptions;
-        }
-
-        len = nextChar();
-        if (len > 0) {
-            String nestedClasses[] = new String[len];
-            for (i = 0; i < len; i++) {
-                nestedClasses[i] = nextStringRef();
-            }
-            res.nestedClasses = nestedClasses;
-        }
-
-        res.initializeImmediateTransientFields();
-        return res;
-    }
-
-    private String nextString() {
-        int length = nextChar();
-        if (buf.length < curBufPos + length) {
-            pdbCorruptedException("data error");
-        }
-        String res = (new String(buf, curBufPos, length)).intern();
-        curBufPos += length;
-        return res;
-    }
-
-    private String nextStringRef() {
-        return stringTable[nextInt()];
-    }
-
-    private void pdbCorruptedException(String message) {
-        throw new PrivateException(new PublicExceptions.PDBCorruptedException(message));
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java b/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java
deleted file mode 100644
index fafaa39..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * This class implements writing into a byte array representing a project database
- *
- * @author  Misha Dmitriev
- *  2 March 2005
- */
-public class BinaryProjectDatabaseWriter extends BinaryFileWriter {
-
-    private Map<String, PCDEntry> pcd = null;
-    private int nOfEntries;
-    private byte[] stringBuf;
-    private int curStringBufPos,  stringBufInc,  curStringBufWatermark,  stringCount;
-    private StringHashTable stringHashTable = null;
-
-    public void writeProjectDatabaseToFile(File outfile, Map<String, PCDEntry> pcd) {
-        try {
-            byte[] buf = new BinaryProjectDatabaseWriter().writeProjectDatabase(pcd);
-            FileOutputStream out = new FileOutputStream(outfile);
-            out.write(buf);
-            out.close();
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-    public byte[] writeProjectDatabase(Map<String, PCDEntry> pcd) {
-        this.pcd = pcd;
-        nOfEntries = pcd.size();
-
-        // So far the constant here is chosen rather arbitrarily
-        initBuf(nOfEntries * 1000);
-
-        stringBuf = new byte[nOfEntries * 300];
-        stringBufInc = stringBuf.length / 5;
-        curStringBufWatermark = stringBuf.length - 20;
-        stringHashTable = new StringHashTable(stringBuf.length / 8);
-
-        for (PCDEntry entry : pcd.values()) {
-            writePCDEntry(entry);
-        }
-
-        // Now we have the string buffer and the main buffer. Write the end result
-        byte[] mainBuf = buf;
-        int mainBufSize = curBufPos;
-        int preambleSize = Utils.MAGIC.length + 8;
-        int stringBufSize = curStringBufPos;
-        int pdbSize = stringBufSize + mainBufSize + 8;  // 8 is for nOfEntries and string table size
-        initBuf(preambleSize + pdbSize);
-        setBufferIncreaseMode(false);
-
-        writePreamble(pdbSize);
-        writeStringTable(stringBufSize);
-        System.arraycopy(mainBuf, 0, buf, curBufPos, mainBufSize);
-        return buf;
-    }
-
-    private void writePreamble(int pdbSize) {
-        System.arraycopy(Utils.MAGIC, 0, buf, 0, Utils.MAGIC.length);
-        curBufPos += Utils.MAGIC.length;
-
-        writeInt(Utils.PDB_FORMAT_CODE_LATEST); // Version number
-        writeInt(pdbSize);
-        writeInt(pcd.size());
-    }
-
-    private void writeStringTable(int stringBufSize) {
-        writeInt(stringCount);
-        System.arraycopy(stringBuf, 0, buf, curBufPos, stringBufSize);
-        curBufPos += stringBufSize;
-    }
-
-    private void writePCDEntry(PCDEntry entry) {
-        writeStringRef(entry.className);
-        writeStringRef(entry.javaFileFullPath);
-        writeLong(entry.oldClassFileLastModified);
-        writeLong(entry.oldClassFileFingerprint);
-        writeClassInfo(entry.oldClassInfo);
-    }
-
-    private void writeClassInfo(ClassInfo ci) {
-        int i, j, len;
-
-        writeStringRef(ci.name);
-        writeInt(ci.javacTargetRelease);
-
-        len = ci.cpoolRefsToClasses != null ? ci.cpoolRefsToClasses.length : 0;
-        writeChar(len);
-        if (len > 0) {
-            String cpoolRefsToClasses[] = ci.cpoolRefsToClasses;
-            for (i = 0; i < len; i++) {
-                writeStringRef(cpoolRefsToClasses[i]);
-            }
-            boolean isRefClassArray[] = ci.isRefClassArray;
-            for (i = 0; i < len; i++) {
-                byte b = isRefClassArray[i] ? (byte) 1 : (byte) 0;
-                writeByte(b);
-            }
-        }
-
-        len = ci.cpoolRefsToFieldClasses != null ? ci.cpoolRefsToFieldClasses.length
-                : 0;
-        writeChar(len);
-        if (len > 0) {
-            String cpoolRefsToFieldClasses[] = ci.cpoolRefsToFieldClasses;
-            for (i = 0; i < len; i++) {
-                writeStringRef(cpoolRefsToFieldClasses[i]);
-            }
-            String cpoolRefsToFieldNames[] = ci.cpoolRefsToFieldNames;
-            for (i = 0; i < len; i++) {
-                writeStringRef(cpoolRefsToFieldNames[i]);
-            }
-            String cpoolRefsToFieldSignatures[] = ci.cpoolRefsToFieldSignatures;
-            for (i = 0; i < len; i++) {
-                writeStringRef(cpoolRefsToFieldSignatures[i]);
-            }
-        }
-
-        len = ci.cpoolRefsToMethodClasses != null ? ci.cpoolRefsToMethodClasses.length
-                : 0;
-        writeChar(len);
-        if (len > 0) {
-            String cpoolRefsToMethodClasses[] = ci.cpoolRefsToMethodClasses;
-            for (i = 0; i < len; i++) {
-                writeStringRef(cpoolRefsToMethodClasses[i]);
-            }
-            String cpoolRefsToMethodNames[] = ci.cpoolRefsToMethodNames;
-            for (i = 0; i < len; i++) {
-                writeStringRef(cpoolRefsToMethodNames[i]);
-            }
-            String cpoolRefsToMethodSignatures[] =
-                    ci.cpoolRefsToMethodSignatures;
-            for (i = 0; i < len; i++) {
-                writeStringRef(cpoolRefsToMethodSignatures[i]);
-            }
-        }
-
-        writeChar(ci.accessFlags);
-        byte b = ci.isNonMemberNestedClass ? (byte) 1 : (byte) 0;
-        writeByte(b);
-        if (!"java/lang/Object".equals(ci.name)) {
-            writeStringRef(ci.superName);
-        }
-
-        len = ci.interfaces != null ? ci.interfaces.length : 0;
-        writeChar(len);
-        if (len > 0) {
-            String interfaces[] = ci.interfaces;
-            for (i = 0; i < len; i++) {
-                writeStringRef(interfaces[i]);
-            }
-        }
-
-        len = ci.fieldNames != null ? ci.fieldNames.length : 0;
-        writeChar(len);
-        if (len > 0) {
-            String fieldNames[] = ci.fieldNames;
-            for (i = 0; i < len; i++) {
-                writeStringRef(fieldNames[i]);
-            }
-            String fieldSignatures[] = ci.fieldSignatures;
-            for (i = 0; i < len; i++) {
-                writeStringRef(fieldSignatures[i]);
-            }
-            char fieldAccessFlags[] = ci.fieldAccessFlags;
-            for (i = 0; i < len; i++) {
-                writeChar(fieldAccessFlags[i]);
-            }
-        }
-
-        len = ci.primitiveConstantInitValues != null ? ci.primitiveConstantInitValues.length
-                : 0;
-        writeChar(len);
-        if (len > 0) {
-            Object primitiveConstantInitValues[] =
-                    ci.primitiveConstantInitValues;
-            for (i = 0; i < len; i++) {
-                Object pc = primitiveConstantInitValues[i];
-                if (pc != null) {
-                    if (pc instanceof String) {
-                        writeByte((byte)1);
-                        writeStringRef((String) pc);
-                    } else if (pc instanceof Integer) {
-                        writeByte((byte)2);
-                        writeInt(((Integer) pc).intValue());
-                    } else if (pc instanceof Long) {
-                        writeByte((byte)3);
-                        writeLong(((Long) pc).longValue());
-                    } else if (pc instanceof Float) {
-                        writeByte((byte)4);
-                        writeFloat(((Float) pc).floatValue());
-                    } else if (pc instanceof Double) {
-                        writeByte((byte)5);
-                        writeDouble(((Double) pc).doubleValue());
-                    }
-                } else {
-                    writeByte((byte)0);
-                }
-            }
-        }
-
-        len = ci.methodNames != null ? ci.methodNames.length : 0;
-        writeChar(len);
-        if (len > 0) {
-            String methodNames[] = ci.methodNames;
-            for (i = 0; i < len; i++) {
-                writeStringRef(methodNames[i]);
-            }
-            String methodSignatures[] = ci.methodSignatures;
-            for (i = 0; i < len; i++) {
-                writeStringRef(methodSignatures[i]);
-            }
-            char methodAccessFlags[] = ci.methodAccessFlags;
-            for (i = 0; i < len; i++) {
-                writeChar(methodAccessFlags[i]);
-            }
-        }
-
-        len = ci.checkedExceptions != null ? ci.checkedExceptions.length : 0;
-        writeChar(len);
-        if (len > 0) {
-            String checkedExceptions[][] = ci.checkedExceptions;
-            for (i = 0; i < len; i++) {
-                int lenl = checkedExceptions[i] != null ? checkedExceptions[i].length
-                        : 0;
-                writeChar(lenl);
-                if (lenl > 0) {
-                    for (j = 0; j < lenl; j++) {
-                        writeStringRef(checkedExceptions[i][j]);
-                    }
-                }
-            }
-        }
-
-        len = ci.nestedClasses != null ? ci.nestedClasses.length : 0;
-        writeChar(len);
-        if (len > 0) {
-            String nestedClasses[] = ci.nestedClasses;
-            for (i = 0; i < len; i++) {
-                writeStringRef(nestedClasses[i]);
-            }
-        }
-    }
-
-    private void writeString(String s) {
-        byte sb[] = s.getBytes();
-        int len = sb.length;
-        if (curStringBufPos + len > curStringBufWatermark) {
-            // May need to adapt stringBufInc
-            if (len >= stringBufInc) {
-                stringBufInc = (stringBufInc + len) * 2;
-            } else {
-                stringBufInc = (stringBufInc * 5) / 4;  // Still increase a little - observations show that otherwise we usually get here 20 more times
-            }
-            byte newStringBuf[] = new byte[stringBuf.length + stringBufInc];
-            System.arraycopy(stringBuf, 0, newStringBuf, 0, curStringBufPos);
-            stringBuf = newStringBuf;
-            curStringBufWatermark = stringBuf.length - 20;
-        }
-        stringBuf[curStringBufPos++] = (byte) ((len >> 8) & 255);
-        stringBuf[curStringBufPos++] = (byte) (len & 255);
-        System.arraycopy(sb, 0, stringBuf, curStringBufPos, len);
-        curStringBufPos += len;
-    }
-
-    private void writeStringRef(String s) {
-        int stringRef = stringHashTable.get(s);
-        if (stringRef == -1) {
-            stringHashTable.add(s, stringCount);
-            stringRef = stringCount;
-            writeString(s);
-            stringCount++;
-        }
-        writeInt(stringRef);
-    }
-
-    /** Maps Strings to integer numbers (their positions in String table) */
-    static class StringHashTable {
-
-        String keys[];
-        int values[];
-        int size, nOfElements, watermark;
-
-        StringHashTable(int size) {
-            size = makeLikePrimeNumber(size);
-            this.size = size;
-            keys = new String[size];
-            values = new int[size];
-            nOfElements = 0;
-            watermark = size * 3 / 4;
-        }
-
-        final int get(String key) {
-            int pos = (key.hashCode() & 0x7FFFFFFF) % size;
-
-            while (keys[pos] != null && !keys[pos].equals(key)) {
-                pos = (pos + 3) % size; // Relies on the fact that size % 3 != 0
-            }
-            if (key.equals(keys[pos])) {
-                return values[pos];
-            } else {
-                return -1;
-            }
-        }
-
-        final void add(String key, int value) {
-            if (nOfElements > watermark) {
-                rehash();
-            }
-
-            int pos = (key.hashCode() & 0x7FFFFFFF) % size;
-            while (keys[pos] != null) {
-                pos = (pos + 3) % size;  // Relies on the fact that size % 3 != 0
-            }
-            keys[pos] = key;
-            values[pos] = value;
-            nOfElements++;
-        }
-
-        private final void rehash() {
-            String oldKeys[] = keys;
-            int oldValues[] = values;
-            int oldSize = size;
-            size = makeLikePrimeNumber(size * 3 / 2);
-            keys = new String[size];
-            values = new int[size];
-            nOfElements = 0;
-            watermark = size * 3 / 4;
-
-            for (int i = 0; i < oldSize; i++) {
-                if (oldKeys[i] != null) {
-                    add(oldKeys[i], oldValues[i]);
-                }
-            }
-        }
-
-        private final int makeLikePrimeNumber(int no) {
-            no = (no / 2) * 2 + 1;  // Make it an odd number
-            // Find the nearest "approximately prime" number
-            boolean prime = false;
-            do {
-                no += 2;
-                prime =
-                        (no % 3 != 0 && no % 5 != 0 && no % 7 != 0 && no % 11 != 0 &&
-                        no % 13 != 0 && no % 17 != 0 && no % 19 != 0 && no % 23 != 0 &&
-                        no % 29 != 0 && no % 31 != 0 && no % 37 != 0 && no % 41 != 0);
-            } while (!prime);
-            return no;
-        }
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/ClassFileReader.java b/third_party/jmake/src/org/pantsbuild/jmake/ClassFileReader.java
deleted file mode 100644
index 3ece22d9..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/ClassFileReader.java
+++ /dev/null
@@ -1,595 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.lang.reflect.Modifier;
-
-
-/**
- * This class implements reading a byte array representing a class file and converting it into ClassInfo.
- *
- * @author  Misha Dmitriev
- *  2 March 2005
- */
-public class ClassFileReader extends BinaryFileReader {
-
-    public static final int JAVA_MAGIC = -889275714;    // 0xCAFEBABE
-    public static final int JAVA_MINOR_VERSION = 0;
-    public static final int JAVA_MIN_MAJOR_VERSION = 45;
-    public static final int JAVA_MIN_MINOR_VERSION = 3;
-    public static final int DEFAULT_MAJOR_VERSION = 46;
-    public static final int DEFAULT_MINOR_VERSION = 0;
-    public static final int JDK14_MAJOR_VERSION = 48;
-    public static final int JDK15_MAJOR_VERSION = 49;
-    public static final int JDK16_MAJOR_VERSION = 50;
-    public static final int JDK17_MAJOR_VERSION = 51;
-    public static final int JDK18_MAJOR_VERSION = 52;
-    public static final int CONSTANT_Utf8 = 1;
-    public static final int CONSTANT_Unicode = 2;
-    public static final int CONSTANT_Integer = 3;
-    public static final int CONSTANT_Float = 4;
-    public static final int CONSTANT_Long = 5;
-    public static final int CONSTANT_Double = 6;
-    public static final int CONSTANT_Class = 7;
-    public static final int CONSTANT_String = 8;
-    public static final int CONSTANT_Fieldref = 9;
-    public static final int CONSTANT_Methodref = 10;
-    public static final int CONSTANT_InterfaceMethodref = 11;
-    public static final int CONSTANT_NameandType = 12;
-    public static final int CONSTANT_MethodHandle = 15;
-    public static final int CONSTANT_MethodType = 16;
-    public static final int CONSTANT_InvokeDynamic = 18;
-    private ClassInfo classInfo = null;
-    private int cpOffsets[];
-    private Object cpObjectCache[];
-    private byte cpTags[];
-
-    public void readClassFile(byte[] classFile, ClassInfo classInfo, String classFileFullPath, boolean readFullInfo) {
-        initBuf(classFile, classFileFullPath);
-        this.classInfo = classInfo;
-
-        readPreamble();
-        readConstantPool(readFullInfo);
-        readIntermediate();
-        if (readFullInfo) {
-            readFields();
-            readMethods();
-            readAttributes();
-        }
-    }
-
-    private int versionWord(int major, int minor) {
-        return major * 1000 + minor;
-    }
-
-    private void readPreamble() {
-        int magic = nextInt();
-        if (magic != JAVA_MAGIC) {
-            throw classFileParseException("Illegal start of class file");
-        }
-        int minorVersion = nextChar();
-        int majorVersion = nextChar();
-        if (majorVersion > JDK14_MAJOR_VERSION ||
-                versionWord(majorVersion, minorVersion) <
-                versionWord(JAVA_MIN_MAJOR_VERSION, JAVA_MIN_MINOR_VERSION) ) {
-            if (majorVersion == JDK18_MAJOR_VERSION) {
-                classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_18;
-            } else if (majorVersion == JDK17_MAJOR_VERSION) {
-                classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_17;
-            } else if (majorVersion == JDK16_MAJOR_VERSION) {
-                classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_16;
-            } else if (majorVersion == JDK15_MAJOR_VERSION) {
-                classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_15;
-            } else {
-                throw classFileParseException("Wrong version: " + majorVersion + "." + minorVersion);
-            }
-        } else {
-            classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST;
-        }
-    }
-
-    private void readConstantPool(boolean readFullInfo) {
-        int classRefsNo = 0;
-        int fieldRefsNo = 0;
-        int methodRefsNo = 0;
-
-        cpOffsets = new int[nextChar()];
-        cpTags = new byte[cpOffsets.length];
-        int ofs, len, classIdx, nameAndTypeIdx, nameIdx, sigIdx, utf8Idx;
-        int i = 1;
-        while (i < cpOffsets.length) {
-            byte tag = buf[curBufPos++];
-            cpOffsets[i] = curBufPos;
-            cpTags[i] = tag;
-            i++;
-            switch (tag) {
-                case CONSTANT_Utf8:
-                    len = nextChar();
-                    curBufPos += len;
-                    break;
-
-                case CONSTANT_Class:
-                    classRefsNo++;
-                    curBufPos += 2;
-                    break;
-
-                case CONSTANT_String:
-                case CONSTANT_MethodType:
-                    curBufPos += 2;
-                    break;
-
-                case CONSTANT_Fieldref:
-                    fieldRefsNo++;
-                    curBufPos += 4;
-                    break;
-
-                case CONSTANT_Methodref:
-                case CONSTANT_InterfaceMethodref:
-                    methodRefsNo++;
-                    curBufPos += 4;
-                    break;
-
-                case CONSTANT_MethodHandle:
-                    curBufPos += 3;
-                    break;
-
-                case CONSTANT_NameandType:
-                case CONSTANT_Integer:
-                case CONSTANT_Float:
-                case CONSTANT_InvokeDynamic:
-                    curBufPos += 4;
-                    break;
-
-                case CONSTANT_Long:
-                case CONSTANT_Double:
-                    curBufPos += 8;
-                    i++;
-                    break;
-
-                default:
-                    throw classFileParseException("Bad constant pool tag: " + tag + " at " + Integer.toString(curBufPos - 1));
-            }
-        }
-
-        cpObjectCache = new Object[cpOffsets.length];
-        if (!readFullInfo) {
-            return;
-        }
-
-        classInfo.cpoolRefsToClasses = new String[classRefsNo];
-        classInfo.isRefClassArray = new boolean[classRefsNo];
-        classInfo.cpoolRefsToFieldClasses = new String[fieldRefsNo];
-        classInfo.cpoolRefsToFieldNames = new String[fieldRefsNo];
-        classInfo.cpoolRefsToFieldSignatures = new String[fieldRefsNo];
-        classInfo.cpoolRefsToMethodClasses = new String[methodRefsNo];
-        classInfo.cpoolRefsToMethodNames = new String[methodRefsNo];
-        classInfo.cpoolRefsToMethodSignatures = new String[methodRefsNo];
-
-        int curClassRef = 0;
-        int curFieldRef = 0;
-        int curMethodRef = 0;
-
-        for (i = 0; i < cpOffsets.length; i++) {
-            ofs = cpOffsets[i];
-            switch (cpTags[i]) {
-                case CONSTANT_Class:
-                    utf8Idx = getChar(ofs);
-                    classInfo.cpoolRefsToClasses[curClassRef++] =
-                            classNameAtCPIndex(utf8Idx, classInfo.isRefClassArray, curClassRef - 1);
-                    //System.out.println("Read cpool ref to class: " + classInfo.cpoolRefsToClasses[curClassRef-1]);
-                    break;
-
-                case CONSTANT_Fieldref:
-                    classIdx = getChar(ofs);
-                    nameAndTypeIdx = getChar(ofs + 2);
-                    if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndTypeIdx] != CONSTANT_NameandType) {
-                        badCPReference(ofs, i);
-                    }
-                    classInfo.cpoolRefsToFieldClasses[curFieldRef] =
-                            classNameAtCPIndex(getChar(cpOffsets[classIdx]));
-
-                    ofs = cpOffsets[nameAndTypeIdx];
-                    nameIdx = getChar(ofs);
-                    sigIdx = getChar(ofs + 2);
-                    if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CONSTANT_Utf8) {
-                        badCPReference(ofs, i);
-                    }
-                    classInfo.cpoolRefsToFieldNames[curFieldRef] =
-                            utf8AtCPIndex(nameIdx);
-                    classInfo.cpoolRefsToFieldSignatures[curFieldRef] =
-                            signatureAtCPIndex(sigIdx);
-                    //System.out.println("Read cpool ref to field: " + classInfo.cpoolRefsToFieldNames[curFieldRef] + " " +
-                    //                   classInfo.cpoolRefsToFieldSignatures[curFieldRef]);
-                    curFieldRef++;
-                    break;
-
-                case CONSTANT_Methodref:
-                case CONSTANT_InterfaceMethodref:
-                    classIdx = getChar(ofs);
-                    nameAndTypeIdx = getChar(ofs + 2);
-                    if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndTypeIdx] != CONSTANT_NameandType) {
-                        badCPReference(ofs, i);
-                    }
-                    classInfo.cpoolRefsToMethodClasses[curMethodRef] =
-                            classNameAtCPIndex(getChar(cpOffsets[classIdx]));
-
-                    ofs = cpOffsets[nameAndTypeIdx];
-                    nameIdx = getChar(ofs);
-                    sigIdx = getChar(ofs + 2);
-                    if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CONSTANT_Utf8) {
-                        badCPReference(ofs, i);
-                    }
-                    classInfo.cpoolRefsToMethodNames[curMethodRef] =
-                            utf8AtCPIndex(nameIdx);
-                    classInfo.cpoolRefsToMethodSignatures[curMethodRef] =
-                            signatureAtCPIndex(sigIdx);
-                    //System.out.println("Read cpool ref to method: " + classInfo.cpoolRefsToMethodNames[curMethodRef] + " " +
-                    //                   classInfo.cpoolRefsToMethodSignatures[curMethodRef]);
-                    curMethodRef++;
-                    break;
-            }
-        }
-    }
-
-    private void readIntermediate() {
-        int i, classIdx, superClassIdx;
-
-        classInfo.accessFlags = nextChar();
-        classIdx = nextChar();
-        if (cpTags[classIdx] != CONSTANT_Class) {
-            throw classFileParseException("Bad reference to this class name");
-        }
-        classInfo.name = classNameAtCPIndex(getChar(cpOffsets[classIdx]));
-        superClassIdx = nextChar();
-        if (!"java/lang/Object".equals(classInfo.name)) {
-            if (cpTags[superClassIdx] != CONSTANT_Class) {
-                throw classFileParseException("Bad reference to super class name");
-            }
-            classInfo.superName =
-                    classNameAtCPIndex(getChar(cpOffsets[superClassIdx]));
-        }
-
-        char intfCount = nextChar();
-        if (intfCount != 0) {
-            classInfo.interfaces = new String[intfCount];
-            for (i = 0; i < intfCount; i++) {
-                classIdx = nextChar();
-                if (cpTags[classIdx] != CONSTANT_Class) {
-                    throw classFileParseException("Bad reference to an implemented interface");
-                }
-                classInfo.interfaces[i] =
-                        classNameAtCPIndex(getChar(cpOffsets[classIdx]));
-            }
-        }
-    }
-
-    private void readFields() {
-        int i, j;
-
-        char definedFieldCount = nextChar();
-        if (definedFieldCount == 0) {
-            return;
-        }
-
-        String names[] = new String[definedFieldCount];
-        String signatures[] = new String[definedFieldCount];
-        char accessFlags[] = new char[definedFieldCount];
-
-        // We are not going to record information on private fields which have either primitive or non-project-class
-        // (typically core-class) types. Such fields cannot affect anything except their own class, so we don't need them.
-        int ri = 0;
-
-        for (i = 0; i < definedFieldCount; i++) {
-            char flags = nextChar();
-            String name = utf8AtCPIndex(nextChar());
-            String sig = signatureAtCPIndex(nextChar());
-
-            boolean recordField =
-                    !(Modifier.isPrivate(flags) &&
-                    (ClassInfo.isPrimitiveFieldSig(sig) || classInfo.isNonProjectClassTypeFieldSig(sig)));
-
-            int attrCount = nextChar();
-            for (j = 0; j < attrCount; j++) {
-                int attrNameIdx = nextChar();
-                int attrLen = nextInt();
-                if (recordField && utf8AtCPIndex(attrNameIdx).equals("ConstantValue") &&
-                        Modifier.isFinal(flags)) {
-                    if (classInfo.primitiveConstantInitValues == null) {
-                        classInfo.primitiveConstantInitValues =
-                                new Object[definedFieldCount];
-                    }
-                    int constValueIdx = nextChar();
-                    switch (cpTags[constValueIdx]) {
-                        case CONSTANT_String:
-                            classInfo.primitiveConstantInitValues[ri] =
-                                    utf8AtCPIndex(getChar(cpOffsets[constValueIdx]));
-                            break;
-
-                        case CONSTANT_Integer:
-                            classInfo.primitiveConstantInitValues[ri] =
-                                    Integer.valueOf(getInt(cpOffsets[constValueIdx]));
-                            break;
-
-                        case CONSTANT_Long:
-                            classInfo.primitiveConstantInitValues[ri] =
-                                    Long.valueOf(getLong(cpOffsets[constValueIdx]));
-                            break;
-
-                        case CONSTANT_Float:
-                            classInfo.primitiveConstantInitValues[ri] =
-                                    Float.valueOf(getFloat(cpOffsets[constValueIdx]));
-                            break;
-
-                        case CONSTANT_Double:
-                            classInfo.primitiveConstantInitValues[ri] =
-                                    Double.valueOf(getDouble(cpOffsets[constValueIdx]));
-                            break;
-
-                        default:
-                            badCPEntry(constValueIdx);
-                    }
-
-                } else {
-                    curBufPos += attrLen;
-                }
-            }
-
-            if (recordField) {
-                names[ri] = name;
-                signatures[ri] = sig;
-                accessFlags[ri] = flags;
-                ri++;
-            }
-        }
-
-        if (ri == definedFieldCount) {
-            classInfo.fieldNames = names;
-            classInfo.fieldSignatures = signatures;
-            classInfo.fieldAccessFlags = accessFlags;
-        } else if (ri > 0) {
-            classInfo.fieldNames = new String[ri];
-            classInfo.fieldSignatures = new String[ri];
-            classInfo.fieldAccessFlags = new char[ri];
-            System.arraycopy(names, 0, classInfo.fieldNames, 0, ri);
-            System.arraycopy(signatures, 0, classInfo.fieldSignatures, 0, ri);
-            System.arraycopy(accessFlags, 0, classInfo.fieldAccessFlags, 0, ri);
-        }
-    }
-
-    private void readMethods() {
-        int i, j;
-
-        char methodCount = nextChar();
-        if (methodCount == 0) {
-            return;
-        }
-
-        String names[] = new String[methodCount];
-        String signatures[] = new String[methodCount];
-        char accessFlags[] = new char[methodCount];
-
-        for (i = 0; i < methodCount; i++) {
-            accessFlags[i] = nextChar();
-            names[i] = utf8AtCPIndex(nextChar());
-            signatures[i] = signatureAtCPIndex(nextChar());
-
-            int attrCount = nextChar();
-            for (j = 0; j < attrCount; j++) {
-                int attrNameIdx = nextChar();
-                int attrLen = nextInt();
-                if (utf8AtCPIndex(attrNameIdx).equals("Exceptions")) {
-                    if (classInfo.checkedExceptions == null) {
-                        classInfo.checkedExceptions = new String[methodCount][];
-                    }
-                    int nExceptions = nextChar();
-                    String exceptions[] = new String[nExceptions];
-                    for (int k = 0; k < nExceptions; k++) {
-                        int excClassIdx = nextChar();
-                        if (cpTags[excClassIdx] != CONSTANT_Class) {
-                            badCPEntry(excClassIdx);
-                        }
-                        exceptions[k] =
-                                classNameAtCPIndex(getChar(cpOffsets[excClassIdx]));
-                    }
-                    classInfo.checkedExceptions[i] = exceptions;
-                } else {
-                    curBufPos += attrLen;
-                }
-            }
-        }
-
-        classInfo.methodNames = names;
-        classInfo.methodSignatures = signatures;
-        classInfo.methodAccessFlags = accessFlags;
-    }
-
-    /**
-     * This method actually reads only the information related to the nested classes, and
-     * records only those of them which are first level nested classes of this class. The class
-     * may also reference other classes which are not package members through the same
-     * InnerClasses attribute - their names would be processed when their respective enclosing
-     * classes are read.
-     */
-    private void readAttributes() {
-        String nestedClassPrefix = classInfo.name + "$";
-
-        char attrCount = nextChar();
-
-        for (int i = 0; i < attrCount; i++) {
-            int attrNameIdx = nextChar();
-            int attrLen = nextInt();
-            if (utf8AtCPIndex(attrNameIdx).equals("InnerClasses")) {
-                int nOfClasses = nextChar();
-                String nestedClasses[] = new String[nOfClasses];
-                char nestedClassAccessFlags[] = new char[nOfClasses];
-                boolean nestedClassNonMember[] = new boolean[nOfClasses];
-                int curIdx = 0;
-                for (int j = 0; j < nOfClasses; j++) {
-                    int innerClassInfoIdx = nextChar();
-                    int outerClassInfoIdx = nextChar();
-                    int innerClassNameIdx = nextChar();
-                    char innerClassAccessFlags = nextChar();
-
-                    // Even if a class is private or non-member (innerClassAccessFlags has private bit set or
-                    // outerClassInfoIdx == 0), we still should take this class into account, since it may e.g. extend
-                    // a public class/implement a public interface, which, in turn, may be changed incompatibly.
-
-                    String nestedClassFullName = classNameAtCPIndex(getChar(cpOffsets[innerClassInfoIdx]));
-
-                    // We are only interested the nested classes whose enclosing class is this one.
-                    if (!nestedClassFullName.startsWith(nestedClassPrefix))
-                        continue;
-
-                    // We are only interested in the directly nested classes of this class.
-                    String nestedClassNameSuffix = nestedClassFullName.substring(nestedClassPrefix.length());
-
-                    if (innerClassNameIdx == 0) {
-                        // Nested class is anonymous. Suffix must be all digits.
-                        if (findFirstNonDigit(nestedClassNameSuffix) != -1)
-                            continue;
-                    } else {
-                        // Nested class is named.
-                        String nestedClassSimpleName = utf8AtCPIndex(innerClassNameIdx);
-                        // The simple case is Outer$Inner.
-                        if (!nestedClassNameSuffix.equals(nestedClassSimpleName)) {
-                            // The more complicated case is a local class. In JDK 1.5+ These are named,
-                            // e.g., Outer$1Inner. Pre-JDK 1.5 they are named e.g., Outer$1$Inner.
-                            int p = findFirstNonDigit(nestedClassNameSuffix);
-                            if (p == -1)
-                                continue;
-                            if (classInfo.javacTargetRelease == Utils.JAVAC_TARGET_RELEASE_OLDEST &&
-                                nestedClassNameSuffix.charAt(p++) != '$')
-                                continue;
-                            if (!nestedClassNameSuffix.substring(p).equals(nestedClassSimpleName))
-                                continue;
-                        }
-                    }
-
-                    // The name has passed all checks, so register it.
-
-                    nestedClasses[curIdx] = nestedClassFullName;
-                    nestedClassAccessFlags[curIdx] = innerClassAccessFlags;
-                    nestedClassNonMember[curIdx] = (outerClassInfoIdx == 0);
-                    curIdx++;
-                }
-                if (curIdx == nOfClasses) {
-                    classInfo.nestedClasses = nestedClasses;
-                    classInfo.nestedClassAccessFlags = nestedClassAccessFlags;
-                    classInfo.nestedClassNonMember = nestedClassNonMember;
-                } else if (curIdx > 0) {
-                    // We found fewer nested classes for this class than we originally expected, but still more than 0.
-                    // Create a new array to fit their number exactly.
-                    classInfo.nestedClasses = new String[curIdx];
-                    classInfo.nestedClassAccessFlags = new char[curIdx];
-                    classInfo.nestedClassNonMember = new boolean[curIdx];
-                    System.arraycopy(nestedClasses, 0, classInfo.nestedClasses, 0, curIdx);
-                    System.arraycopy(nestedClassAccessFlags, 0, classInfo.nestedClassAccessFlags, 0, curIdx);
-                    System.arraycopy(nestedClassNonMember, 0, classInfo.nestedClassNonMember, 0, curIdx);
-                }
-            } else {
-                curBufPos += attrLen;
-            }
-        }
-    }
-
-    private int findFirstNonDigit(String s) {
-        for (int i = 0; i < s.length(); i++) {
-            if (!Character.isDigit(s.charAt(i)))
-                return i;
-        }
-        return -1;
-    }
-
-    private String utf8AtCPIndex(int idx) {
-        if (cpTags[idx] != CONSTANT_Utf8) {
-            throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
-        }
-        if (cpObjectCache[idx] == null) {
-            int utf8Len = getChar(cpOffsets[idx]);
-            // String interning reduces the size of the disk database very significantly
-            // (by one-third in one observed case), and also speeds up database search.
-            cpObjectCache[idx] =
-                    (new String(buf, cpOffsets[idx] + 2, utf8Len)).intern();
-        }
-        return (String) cpObjectCache[idx];
-    }
-
-    private String classNameAtCPIndex(int idx) {
-        return classNameAtCPIndex(idx, null, 0);
-    }
-
-    /**
-     * Read class name at the given CONSTANT_Utf8 constant pool index, and return it
-     * trimmed of the possible '[' and 'L' prefixes and the ';' suffix.
-     */
-    private String classNameAtCPIndex(int idx, boolean isRefClassArray[], int isArrayIdx) {
-        if (cpTags[idx] != CONSTANT_Utf8) {
-            throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
-        }
-        boolean isArray = false;
-        if (cpObjectCache[idx] == null) {
-            int utf8Len = getChar(cpOffsets[idx]);
-            int stPos = cpOffsets[idx] + 2;
-            int initStPos = stPos;
-            while (buf[stPos] == '[') {
-                stPos++;
-            }
-            if (stPos != initStPos) {
-                isArray = true;
-                if (buf[stPos] == 'L') {
-                    stPos++;
-                    utf8Len--;  // To get rid of the terminating ';'
-                }
-            }
-            utf8Len = utf8Len - (stPos - initStPos);
-            cpObjectCache[idx] = (new String(buf, stPos, utf8Len)).intern();
-            if (isRefClassArray != null) {
-                isRefClassArray[isArrayIdx] = isArray;
-            }
-        }
-        return (String) cpObjectCache[idx];
-    }
-
-    // We replace all "Lclassname;" in signatures with "@classname#" to simplify signature parsing during reference checking
-    private String signatureAtCPIndex(int idx) {
-        if (cpTags[idx] != CONSTANT_Utf8) {
-            throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
-        }
-        if (cpObjectCache[idx] == null) {
-            int utf8Len = getChar(cpOffsets[idx]);
-            byte tmp[] = new byte[utf8Len];
-            System.arraycopy(buf, cpOffsets[idx] + 2, tmp, 0, utf8Len);
-            boolean inClassName = false;
-            for (int i = 0; i < utf8Len; i++) {
-                if (!inClassName) {
-                    if (tmp[i] == 'L') {
-                        tmp[i] = '@';
-                        inClassName = true;
-                    }
-                } else if (tmp[i] == ';') {
-                    tmp[i] = '#';
-                    inClassName = false;
-                }
-            }
-            cpObjectCache[idx] = (new String(tmp)).intern();
-        }
-        return (String) cpObjectCache[idx];
-    }
-
-    private void badCPReference(int ofs, int i) {
-        throw classFileParseException("Bad constant pool reference: " + ofs + " from entry " + i);
-    }
-
-    private void badCPEntry(int entryNo) {
-        throw classFileParseException("Constant pool entry " + entryNo + " : invalid type");
-    }
-
-    private PrivateException classFileParseException(String msg) {
-        return new PrivateException(new PublicExceptions.ClassFileParseException(
-                "Error reading class file " + fileFullPath + ":\n" + msg));
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/ClassInfo.java b/third_party/jmake/src/org/pantsbuild/jmake/ClassInfo.java
deleted file mode 100644
index 9bfcd5a..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/ClassInfo.java
+++ /dev/null
@@ -1,746 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.Serializable;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A reflection of a class, in the form that allows fast checks and information obtaining.
- *
- * @author Misha Dmitriev
- *  5 April 2004
- */
-@SuppressWarnings("serial")
-public class ClassInfo implements Serializable {
-
-    public static final int VER_OLD = 0;  // Old version
-    public static final int VER_NEW = 1;  // New version
-    public static final int NO_VERSIONS = 2;  // Non-project class, no change tracking
-    private transient PCDManager pcdm;
-    transient int verCode;                          // Version code for this ClassInfo - one of the above.
-    String name = null;
-    transient String packageName;                   // Package name; restored when database is reloaded
-    int javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST; // Can have values from Utils.JAVAC_TARGET_RELEASE_xxx
-    String cpoolRefsToClasses[];          // Directly referenced class names trimmed of array and 'L' prefixes and ';' suffixes
-    boolean isRefClassArray[];            // Indicates if a directly referenced class is actually an array class
-    // In all signatures we replace the 'L' and ';' symbols that enclose non-primitive type names with '@' and '#' respectively,
-    // so that class names inside signatures can be located fast and unambiguously.
-    String cpoolRefsToFieldClasses[];     // Defining classes of referenced fields, trimmed of enclosing 'L' and ';' symbols
-    String cpoolRefsToFieldNames[];       // Names of referenced fields
-    String cpoolRefsToFieldSignatures[];  // Signatures of referenced fields
-    String cpoolRefsToMethodClasses[];    // Defining classes of referenced methods, trimmed of enclosing 'L' and ';' symbols
-    String cpoolRefsToMethodNames[];      // Names of referenced methods
-    String cpoolRefsToMethodSignatures[]; // Signatures of referenced methods
-    char accessFlags;                     // isInterface flag included
-    boolean isNonMemberNestedClass = false; // True if this is a non-member nested class
-    String superName;
-    String interfaces[];
-    String fieldNames[];
-    String fieldSignatures[];
-    char fieldAccessFlags[];
-    Object primitiveConstantInitValues[];
-    String methodNames[];
-    String methodSignatures[];
-    char methodAccessFlags[];
-    String checkedExceptions[][];
-    transient ClassInfo directSubclasses[];       // Direct subclasses. Created lazily and not preserved on disk.
-    transient String directlyEnclosingClass;      // Directly enclosing class name; restored when database is reloaded
-    transient String topLevelEnclosingClass;      // Top-level enclosing class name; restored when database is reloaded
-    String nestedClasses[];             // Names of all nested classes. Don't make transient - it's used to check
-    // if nested classes for this class were added/deleted in new version
-    transient char nestedClassAccessFlags[];      // No need to store this information permanently
-    transient boolean nestedClassNonMember[];     // Ditto
-
-    /** Creates new ClassInfo out of a class file. The last parameter is needed only to produce sensible error reports.*/
-    public ClassInfo(byte[] classFileBytes, int verCode, PCDManager pcdm, String classFileFullPath) {
-        this.pcdm = pcdm;
-        this.verCode = verCode;
-        pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileFullPath, true);
-        packageName = Utils.getPackageName(name);
-        directlyEnclosingClass =
-                Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease);
-        topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
-    }
-
-    /**
-     * Create a "lightweight" ClassInfo, that contains just the class name, super name, interfaces, flags and verCode.
-     * Used for non-project classes, that don't change themselves, for which we are only interested in type hierarchy structure.
-     */
-    public ClassInfo(byte[] classFileBytes, PCDManager pcdm, String classFileFullPath) {
-        this.pcdm = pcdm;
-        this.verCode = NO_VERSIONS;
-        pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileFullPath, false);
-        packageName = Utils.getPackageName(name);
-        directlyEnclosingClass =
-                Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease);
-        topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
-    }
-
-    /** Even more lightweight variant - created for a deleted non-project class, to enable minimum possible checks. */
-    public ClassInfo(String name, PCDManager pcdm) {
-        this.pcdm = pcdm;
-        this.verCode = NO_VERSIONS;
-        this.name = name;
-        packageName = Utils.getPackageName(name);
-        directlyEnclosingClass = Utils.getDirectlyEnclosingClass(name, 0);
-        topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
-    }
-
-    public ClassInfo() {
-    }
-
-    /** Initialize transient data that can be initialized immediately after this ClassInfo is read from the project database */
-    public void initializeImmediateTransientFields() {
-        verCode = VER_OLD;
-
-        packageName = Utils.getPackageName(name);
-
-        directlyEnclosingClass =
-                Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease);
-        topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
-    }
-
-    /**
-     * Called to restore the pointer to the current PCDManager after this ClassInfo is brought back
-     * from the store.
-     */
-    public void restorePCDM(PCDManager pcdm) {
-        this.pcdm = pcdm;
-    }
-
-    public boolean isInterface() {
-        return Modifier.isInterface(accessFlags);
-    }
-
-    public boolean isAbstract() {
-        return Modifier.isAbstract(accessFlags);
-    }
-
-    public boolean isPublic() {
-        return Modifier.isPublic(accessFlags);
-    }
-
-    /**
-     * Returns the names of the superclasses of the given class (transitively), that belong
-     * to the same project, plus those of the superclasses that can be found on the class path
-     * supplied to jmake, and on the boot class path.
-     */
-    public List<String> getAllSuperclassNames() {
-        List<String> res = new ArrayList<String>();
-        String superName = this.superName;
-        while (superName != null && !"java/lang/Object".equals(superName)) {
-            res.add(superName);
-            ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName);
-            if (classInfo == null) { // Class not in project (or deleted?). Try to find it and further superclasses in non-project classes
-                ClassPath.getSuperclasses(superName, res, pcdm);
-                break;
-            }
-            superName = classInfo.superName;
-        }
-        return res;
-    }
-
-    /**
-     * Returns the set of names of the interfaces transitively implemented by the given
-     * class, that belong to the same project.
-     */
-    public Set<String> getAllImplementedIntfNames() {
-        Set<String> res = new LinkedHashSet<String>();
-        addImplementedInterfaceNames(false, res);
-        return res;
-    }
-
-    /** Add to the given set the names of direct/all interfaces implemented by the given class. */
-    private void addImplementedInterfaceNames(boolean directOnly,
-            Set<String> intfSet) {
-        if (interfaces != null) {
-            for (int i = 0; i < interfaces.length; i++) {
-                String superIntfName = interfaces[i];
-                intfSet.add(superIntfName);
-                if (directOnly) {
-                    continue;
-                }
-                ClassInfo superIntfInfo =
-                        pcdm.getClassInfoForName(verCode, superIntfName);
-                if (superIntfInfo == null) {  // Class not in project
-                    ClassPath.addAllImplementedInterfaceNames(superIntfName, intfSet, pcdm);
-                } else {
-                    superIntfInfo.addImplementedInterfaceNames(false, intfSet);
-                }
-            }
-        }
-
-        if (directOnly || superName == null ||
-                "java/lang/Object".equals(superName)) {
-            return;
-        }
-        ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName);
-        if (superInfo == null) {  // Class not in project
-            ClassPath.addAllImplementedInterfaceNames(superName, intfSet, pcdm);
-        } else {
-            superInfo.addImplementedInterfaceNames(false, intfSet);
-        }
-    }
-
-    /** Returns the array of all direct subclasses of this class (array of zero length if there are none). */
-    public ClassInfo[] getDirectSubclasses() {
-        if (directSubclasses != null) {
-            return directSubclasses;
-        }
-
-        List<ClassInfo> listRes = new ArrayList<ClassInfo>();
-
-        for (PCDEntry entry : pcdm.entries()) {
-            ClassInfo classInfo = pcdm.getClassInfoForPCDEntry(verCode, entry);
-            if (classInfo == null) {
-                continue;  // New or deleted class, depending on verCode
-            }
-            if (classInfo.superName.equals(name)) {
-                listRes.add(classInfo);
-            }
-        }
-
-        directSubclasses = listRes.toArray(new ClassInfo[listRes.size()]);
-        return directSubclasses;
-    }
-
-    /** Check if the initial values for the given primitive constatnts in two classes are the same. */
-    public static boolean constFieldInitValuesEqual(ClassInfo oldClassInfo, int oldFieldNo,
-            ClassInfo newClassInfo, int newFieldNo) {
-        Object oldInitValue = oldClassInfo.primitiveConstantInitValues == null ? null
-                : oldClassInfo.primitiveConstantInitValues[oldFieldNo];
-        Object newInitValue = newClassInfo.primitiveConstantInitValues == null ? null
-                : newClassInfo.primitiveConstantInitValues[newFieldNo];
-        if (oldInitValue == newInitValue) {
-            return true;
-        }
-        if (oldInitValue == null || newInitValue == null) {
-            return false;
-        }
-
-        if (oldInitValue instanceof Integer) {
-            if (((Integer) oldInitValue).intValue() == ((Integer) newInitValue).intValue()) {
-                return true;
-            } else {
-                return false;
-            }
-        } else if (oldInitValue instanceof String) {
-            if ( ((String) oldInitValue).equals((String) newInitValue) ) {
-                return true;
-            } else {
-                return false;
-            }
-        } else if (oldInitValue instanceof Long) {
-            if (((Long) oldInitValue).longValue() == ((Long) newInitValue).longValue()) {
-                return true;
-            } else {
-                return false;
-            }
-        } else if (oldInitValue instanceof Float) {
-            if (((Float) oldInitValue).floatValue() == ((Float) newInitValue).floatValue()) {
-                return true;
-            } else {
-                return false;
-            }
-        } else if (oldInitValue instanceof Double) {
-            if (((Double) oldInitValue).doubleValue() == ((Double) newInitValue).doubleValue()) {
-                return true;
-            } else {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    public boolean implementsInterfaceDirectly(String intfName) {
-        if (interfaces == null) {
-            return false;
-        }
-        for (int i = 0; i < interfaces.length; i++) {
-            if (intfName.equals(interfaces[i])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Check if this class implements interface I or any subinterface of I directly */
-    public boolean implementsIntfOrSubintfDirectly(String intfName) {
-        if (interfaces == null) {
-            return false;
-        }
-        for (int i = 0; i < interfaces.length; i++) {
-            if (intfName.equals(interfaces[i])) {
-                return true;
-            }
-            // An interface can have multiple superinterfaces, all of which are listed in its "interfaces" array
-            // (although in the .java source it "extends" them all).
-            ClassInfo superIntfInfo =
-                    pcdm.getClassInfoForName(verCode, interfaces[i]);
-            if (superIntfInfo == null) {
-                continue;  // Class not in project
-            }
-            if (superIntfInfo.implementsIntfOrSubintfDirectly(intfName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Class C implements interface I indirectly, if C or some superclass of C directly implements I
-     * or some subinterface of I.
-     */
-    public boolean implementsInterfaceDirectlyOrIndirectly(String intfName) {
-        if (interfaces == null) {
-            return false;
-        }
-
-        if (implementsIntfOrSubintfDirectly(intfName)) {
-            return true;
-        }
-
-        if (superName != null) {
-            ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName);
-            if (superInfo == null) {
-                return false;  // Class not in project
-            }
-            return superInfo.implementsInterfaceDirectlyOrIndirectly(intfName);
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns true if this class declares a field with the same name and type as
-     * the field number fieldNo in class classInfo.
-     */
-    public boolean declaresField(ClassInfo classInfo, int fieldNo) {
-        if (fieldNames == null) {
-            return false;
-        }
-        String fieldName = classInfo.fieldNames[fieldNo];
-        String fieldSignature = classInfo.fieldSignatures[fieldNo];
-
-        for (int i = 0; i < fieldNames.length; i++) {
-            if (fieldName.equals(fieldNames[i]) &&
-                    fieldSignature.equals(fieldSignatures[i])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Returns true if this class declares a field with the given name, signature and access */
-    public boolean declaresField(String name, String signature, boolean isStatic) {
-        if (fieldNames == null) {
-            return false;
-        }
-        signature = ("@" + signature + "#").intern();
-        for (int i = 0; i < fieldNames.length; i++) {
-            if (name.equals(fieldNames[i]) &&
-                    signature.equals(fieldSignatures[i]) &&
-                    Modifier.isStatic(fieldAccessFlags[i]) == isStatic) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if this class declares a method with the same name and signature as
-     * the method number methodNo in class classInfo.
-     */
-    public boolean declaresMethod(ClassInfo classInfo, int methodNo) {
-        if (methodNames == null) {
-            return false;
-        }
-        String methodName = classInfo.methodNames[methodNo];
-        String methodSignature = classInfo.methodSignatures[methodNo];
-
-        for (int i = 0; i < methodNames.length; i++) {
-            if (methodName.equals(methodNames[i]) &&
-                    methodSignature.equals(methodSignatures[i])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * If this class declares a method with the same name and signature as the given method,
-     * return its position. Otherwise, return -1.
-     */
-    public int getDeclaredMethodPos(ClassInfo classInfo, int methodNo) {
-        if (methodNames == null) {
-            return -1;
-        }
-        String methodName = classInfo.methodNames[methodNo];
-        String methodSignature = classInfo.methodSignatures[methodNo];
-
-        for (int i = 0; i < methodNames.length; i++) {
-            if (methodName.equals(methodNames[i]) &&
-                    methodSignature.equals(methodSignatures[i])) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Returns a nonnegative number (position in the method array) if this class declares a method with the
-     * name methodName, and -1 otherwise.
-     */
-    public int declaresSameNameMethod(String methodName) {
-        if (methodNames == null) {
-            return -1;
-        }
-        for (int j = 0; j < methodNames.length; j++) {
-            if (methodName.equals(methodNames[j])) {
-                return j;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Check if this class references the given class in different ways, depending on thorDegree parameter.
-     * thorDegree = 0: the given class (but not its array class) directly from the constantpool.
-     *
-     * thorDegree = 1: the given class or its array class directly from the constantpool, as a
-     * type of a data field, as a type in a method signature or a thrown exception, as a directly
-     * implemented interface or a direct superclass
-     *
-     * thorDegree = 2: the given class or its array class directly or indirectly from the
-     * constantpool, as a type of a data field, as a type in a method signature or a thrown exception,
-     * as a directly/indirectly implemented interface or a direct/indirect superclass.
-     *
-     * isRefTypeInterface indicates whether className is an interface.
-     */
-    public boolean referencesClass(String className, boolean isRefTypeInterface, int thorDegree) {
-        int i;
-
-        if (thorDegree == 0) {
-            if (cpoolRefsToClasses == null) {
-                return false;
-            }
-            for (i = 0; i < cpoolRefsToClasses.length; i++) {
-                if (!isRefClassArray[i] &&
-                        className.equals(cpoolRefsToClasses[i])) {
-                    return true;
-                }
-            }
-        } else {
-            if (isSubclassOf(className, (thorDegree == 1))) {
-                return true;
-            }
-            if (isRefTypeInterface) {
-                if (thorDegree == 1) {
-                    if (implementsInterfaceDirectly(className)) {
-                        return true;
-                    }
-                } else {
-                    // Check for indirectly implemented interfaces
-                    if (implementsInterfaceDirectlyOrIndirectly(className)) {
-                        return true;
-                    }
-                }
-            }
-
-            if (cpoolRefsToClasses != null) {
-                for (i = 0; i < cpoolRefsToClasses.length; i++) {
-                    if (className.equals(cpoolRefsToClasses[i])) {
-                        return true;
-                    }
-                }
-            }
-            if (thorDegree == 2) {
-                // Check for indirect references from the constantpool
-                if (cpoolRefsToFieldSignatures != null) {
-                    for (i = 0; i < cpoolRefsToFieldSignatures.length; i++) {
-                        if (signatureIncludesClassName(cpoolRefsToFieldSignatures[i], className)) {
-                            return true;
-                        }
-                    }
-                }
-                if (cpoolRefsToMethodNames != null) {
-                    for (i = 0; i < cpoolRefsToMethodSignatures.length; i++) {
-                        if (signatureIncludesClassName(cpoolRefsToMethodSignatures[i], className)) {
-                            return true;
-                        }
-                    }
-                }
-            }
-
-            if (fieldSignatures != null) {
-                for (i = 0; i < fieldSignatures.length; i++) {
-                    if (signatureIncludesClassName(fieldSignatures[i], className)) {
-                        return true;
-                    }
-                }
-            }
-            if (methodSignatures != null) {
-                for (i = 0; i < methodSignatures.length; i++) {
-                    if (signatureIncludesClassName(methodSignatures[i], className)) {
-                        return true;
-                    }
-                }
-            }
-            if (checkedExceptions != null) {
-                for (i = 0; i < checkedExceptions.length; i++) {
-                    if (checkedExceptions[i] != null) {
-                        String excArray[] = checkedExceptions[i];
-                        for (int j = 0; j < excArray.length; j++) {
-                            if (className.equals(excArray[j])) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        return false;
-    }
-
-    private static boolean signatureIncludesClassName(String signature, String className) {
-        int stIndex = signature.indexOf(className);
-        if (stIndex == -1) {
-            return false;
-        }
-        return ((stIndex != 0 && signature.charAt(stIndex - 1) == '@' && signature.charAt(stIndex + className.length()) == '#') ||
-                (stIndex == 0 && signature.length() == className.length()));
-    }
-
-    public boolean isSubclassOf(String className, boolean directOnly) {
-        if (className.equals(superName)) {
-            return true;
-        }
-        if (directOnly) {
-            return false;
-        }
-        String superName = this.superName;
-        while (superName != null) {
-            if (className.equals(superName)) {
-                return true;
-            }
-            ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName);
-            if (classInfo == null) {
-                break;  // Class not in project
-            }
-            superName = classInfo.superName;
-        }
-        return false;
-    }
-
-    /**
-     * Check if this class references field number fieldNo of class fieldDefClassInfo. Let us call
-     * this field C.f. Actual reference contained in the constant pool may be not to C.f itself,
-     * but to Csub.f, where Csub is some subclass of C such that neither Csub nor any other class
-     * located between C and Csub in the class hierarchy redeclares f. We look up both "real"
-     * references C.f and "fake" references such as Csub.f.
-     */
-    public boolean referencesField(ClassInfo fieldDefClassInfo, int fieldNo) {
-        if (cpoolRefsToFieldNames == null) {
-            return false;
-        }
-        String fieldDefClassName = fieldDefClassInfo.name;
-        String fieldName = fieldDefClassInfo.fieldNames[fieldNo];
-        String fieldSig = fieldDefClassInfo.fieldSignatures[fieldNo];
-        for (int i = 0; i < cpoolRefsToFieldNames.length; i++) {
-            if (fieldName.equals(cpoolRefsToFieldNames[i]) &&
-                    fieldSig.equals(cpoolRefsToFieldSignatures[i]) ) {
-                if (fieldDefClassName.equals(cpoolRefsToFieldClasses[i]) ) {
-                    return true;  // "real" reference
-                } else {  // Check if this is a "fake" reference that resolves to the above "real" reference
-                    ClassInfo classInThisCpool =
-                            pcdm.getClassInfoForName(verCode, cpoolRefsToFieldClasses[i]);
-                    if (classInThisCpool == null) {
-                        continue;  // Class not in project
-                    }
-                    if (!classInThisCpool.isSubclassOf(fieldDefClassInfo.name, false)) {
-                        continue;
-                    }
-
-                    // Ok, now check that this field is not actually redeclared in fieldDefClassInfo or
-                    // somewhere in between it and classInThisCpool
-                    boolean redeclared = false;
-                    ClassInfo curClass = classInThisCpool;
-                    do {
-                        if (curClass.declaresField(fieldDefClassInfo, fieldNo)) {
-                            redeclared = true;
-                            break;
-                        }
-                        String superName = curClass.superName;
-                        curClass = pcdm.getClassInfoForName(verCode, superName);
-                        if (curClass == null) {
-                            break;
-                        }
-                    } while (curClass != fieldDefClassInfo);
-                    if (!redeclared) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Check if this class references method number methodNo of class methodDefClassInfo. Let us
-     * call this method C.m. Actual reference contained in the constant pool may be not to C.m
-     * itself, but to Csub.m, where Csub is some subclass of C such that neither Csub nor any
-     * other class located between C and Csub in the class hierarchy redeclares m. We look up
-     * both "real" references C.m and "fake" references such as Csub.m.
-     */
-    public boolean referencesMethod(ClassInfo methodDefClassInfo, int methodNo) {
-        if (cpoolRefsToMethodNames == null) {
-            return false;
-        }
-        String methodDefClassName = methodDefClassInfo.name;
-        String methodName = methodDefClassInfo.methodNames[methodNo];
-        String methodSig = methodDefClassInfo.methodSignatures[methodNo];
-        for (int i = 0; i < cpoolRefsToMethodNames.length; i++) {
-            if (methodName.equals(cpoolRefsToMethodNames[i]) &&
-                    methodSig.equals(cpoolRefsToMethodSignatures[i])) {
-                if (methodDefClassName.equals(cpoolRefsToMethodClasses[i])) {
-                    return true;  // "real" reference
-                } else {  // Check if this is a "fake" reference that resolves to the above "real" reference
-                    // Be careful - class in the cpool may be not a project class (e.g. a core class).
-                    ClassInfo classInThisCpool =
-                            pcdm.getClassInfoForName(verCode, cpoolRefsToMethodClasses[i]);
-                    if (classInThisCpool == null) {
-                        continue;  // Class not in project
-                    }
-                    if (classInThisCpool.isSubclassOf(methodDefClassInfo.name, false)) {
-                        // Ok, now check that this method is not actually redeclared in classInThisCpool (which is
-                        // lower in the hierarchy) or somewhere in between it and classInThisCpool
-                        boolean redeclared = false;
-                        ClassInfo curClass = classInThisCpool;
-                        do {
-                            if (curClass.declaresMethod(methodDefClassInfo, methodNo)) {
-                                redeclared = true;
-                                break;
-                            }
-                            String superName = curClass.superName;
-                            curClass =
-                                    pcdm.getClassInfoForName(verCode, superName);
-                            if (curClass == null) {
-                                break;
-                            }
-                        } while (curClass != methodDefClassInfo);
-                        if (!redeclared) {
-                            return true;
-                        }
-                    } else if (methodDefClassInfo.isInterface() && classInThisCpool.implementsIntfOrSubintfDirectly(methodDefClassName)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * If this class has a method that throws the given exception, return its index. Otherwise return -1.
-     * The search starts from method with index startMethodIdx.
-     */
-    public int hasMethodThrowingException(ClassInfo excClassInfo, int startMethodIdx) {
-        if (checkedExceptions == null) {
-            return -1;
-        }
-        if (startMethodIdx >= checkedExceptions.length) {
-            return -1;
-        }
-        String excName = excClassInfo.name;
-        for (int i = startMethodIdx; i < checkedExceptions.length; i++) {
-            if (checkedExceptions[i] == null) {
-                continue;
-            }
-            String[] exc = checkedExceptions[i];
-            for (int j = 0; j < exc.length; j++) {
-                if (exc[j].equals(excName)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    public static abstract class MethodHandler {
-
-        abstract void handleMethod(ClassInfo ci, int methodIdx);
-    }
-
-    /**
-     * Check this class and all its superclasses (if includeSuperclasses == true) and superinterfaces (if includeInterfaces == true)
-     * for a method with the given name. If such a method is found, call h.handleMethod(classInfo, methodIdx).
-     */
-    public void findExistingSameNameMethods(String methodName, boolean includeSuperclasses, boolean includeInterfaces, MethodHandler h) {
-        String className = name;
-        ClassInfo classInfo;
-        while (className != null) {
-            classInfo = pcdm.getClassInfoForName(verCode, className);
-            if (classInfo == null) {
-                break;  // Class not in project
-            }
-            String mNames[] = classInfo.methodNames;
-            int mNamesLen = mNames != null ? mNames.length : 0;
-            for (int i = 0; i < mNamesLen; i++) {
-                if (methodName.equals(mNames[i])) {
-                    h.handleMethod(classInfo, i);
-                }
-            }
-            if (includeInterfaces && classInfo.interfaces != null) {
-                String intfNames[] = classInfo.interfaces;
-                for (int i = 0; i < intfNames.length; i++) {
-                    ClassInfo superIntfInfo =
-                            pcdm.getClassInfoForName(verCode, intfNames[i]);
-                    if (superIntfInfo == null) {
-                        continue;  // Class not in project
-                    }
-                    superIntfInfo.findExistingSameNameMethods(methodName, true, includeInterfaces, h);
-                }
-            }
-            if (includeSuperclasses) {
-                className = classInfo.superName;
-            } else {
-                return;
-            }
-        }
-    }
-
-    public static boolean isPrimitiveFieldSig(String fieldSig) {
-        return fieldSig.indexOf('@') == -1;
-    }
-
-    /**
-     * Check if the given signature is of a class type, and that class does not belong to the project.
-     * It used to be a check for just a core type name, but sometimes people use JDK sources as e.g. a test
-     * case - so better perform a universal (and entirely correct, unlike just a core type name) test here.
-     */
-    public boolean isNonProjectClassTypeFieldSig(String fieldSig) {
-        int stPos = fieldSig.indexOf('@');
-        if (stPos == -1) {
-            return false;
-        }
-        int endPos = fieldSig.indexOf('#');
-        String className = fieldSig.substring(stPos + 1, endPos);
-        return (!pcdm.isProjectClass(verCode, className));
-    }
-
-    /** For debugging. */
-    public String toString() {
-        return name + (verCode == VER_OLD ? " OLD" : " NEW");
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/ClassPath.java b/third_party/jmake/src/org/pantsbuild/jmake/ClassPath.java
deleted file mode 100644
index e6e1aebd..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/ClassPath.java
+++ /dev/null
@@ -1,448 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * An instance of this class represents a class path, on which binary classes can be looked up.
- * It also provides several static methods to create and utilize several specific class paths used
- * throughout jmake.
- *
- * @author Misha Dmitriev
- *  12 October 2004
- */
-public class ClassPath {
-
-    private PathEntry[] paths;
-    private static ClassPath projectClassPath;   // Class path (currently it can contain only JARs) containing sourceless project classes.
-    // See also the comment to standardClassPath.
-    private static ClassPath standardClassPath;  // Class path that the user specifies via the -classpath option. A sum of the
-    // standardClassPath, the projectClassPath, and the virtualPath is passed to the compiler. Each of these
-    // class paths are also used to look up non-project superclasses/superinterfaces of
-    // project classes.
-    private static ClassPath bootClassPath,  extClassPath; // Class paths that by default are sun.boot.class.path and all JARs on
-    // java.ext.class.path, respectively. They are used to look up non-project
-    // superclasses/superinterfaces of project classes. Their values can be changed using
-    // setBootClassPath() and setExtDirs().
-    private static ClassPath virtualPath; // Class path that the user specifies via the -vpath option.
-    private static String compilerUserClassPath; // Class path to be passed to the compiler; equals to the sum of values of parameters of
-    // setClassPath() and setProjectClassPath() methods.
-    private static String standardClassPathStr,  projectClassPathStr,  bootClassPathStr,  extDirsStr,
-            virtualPathStr;
-    private static Map<String,ClassInfo> classCache;
-
-
-    static {
-        resetOnFinish();
-    }
-
-    /**
-     * Needed since some environments, e.g. NetBeans, can keep jmake classes in memory
-     * permanently. Thus unchanged class paths from previous, possibly unrelated invocations
-     * of jmake, may interfere with the current settings.
-     */
-    public static void resetOnFinish() {
-        projectClassPath = standardClassPath = bootClassPath = extClassPath = virtualPath =
-                null;
-        compilerUserClassPath = null;
-        standardClassPathStr = projectClassPathStr = bootClassPathStr =
-                extDirsStr = virtualPathStr = null;
-        classCache = new LinkedHashMap<String,ClassInfo>();
-    }
-
-    public static void setClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
-        standardClassPathStr = value;
-        standardClassPath = new ClassPath(value, false);
-    }
-
-    public static void setProjectClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
-        projectClassPathStr = value;
-        projectClassPath = new ClassPath(value, true);
-    }
-
-    public static void setBootClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
-        bootClassPathStr = value;
-        bootClassPath = new ClassPath(value, false);
-    }
-
-    public static void setExtDirs(String value) throws PublicExceptions.InvalidCmdOptionException {
-        extDirsStr = value;
-        // Extension class path needs special handling, since it consists of directories, which contain .jars
-        // So we need to find all these .jars in all these dirs and add them to extClassPathElementList
-        List<String> extClassPathElements = new ArrayList<String>();
-        for (StringTokenizer tok =
-                new StringTokenizer(value, File.pathSeparator); tok.hasMoreTokens();) {
-            File extDir = new File(tok.nextToken());
-            String[] extJars = extDir.list(new FilenameFilter() {
-
-                public boolean accept(File dir, String name) {
-                    name = name.toLowerCase(Locale.ENGLISH);
-                    return name.endsWith(".zip") || name.endsWith(".jar");
-                }
-            });
-            if (extJars == null) {
-                continue;
-            }
-            for (int i = 0; i < extJars.length; i++) {
-                extClassPathElements.add(extDir + File.separator + extJars[i]);
-            }
-        }
-        extClassPath = new ClassPath(extClassPathElements, false);
-    }
-
-    public static void setVirtualPath(String value) throws PublicExceptions.InvalidCmdOptionException {
-        if (value == null) {
-            throw new PublicExceptions.InvalidCmdOptionException("null argument");
-        }
-        StringTokenizer st = new StringTokenizer(value, File.pathSeparator);
-        while (st.hasMoreElements()) {
-            String dir = st.nextToken();
-            if ( ! (new File(dir)).isDirectory()) {
-                throw new PublicExceptions.InvalidCmdOptionException("Virtual path must contain only directories." +
-                        " Entry " + dir + " is not a directory.");
-            }
-        }
-        virtualPathStr = value;
-        virtualPath = new ClassPath(value, false);
-    }
-
-    public static void initializeAllClassPaths() {
-        // First set the compiler class path value
-        if (standardClassPathStr == null && projectClassPathStr == null) {
-            compilerUserClassPath = ".";
-        } else if (standardClassPathStr == null) {
-            compilerUserClassPath = projectClassPathStr;
-        } else if (projectClassPathStr == null) {
-            compilerUserClassPath = standardClassPathStr;
-        } else {
-            compilerUserClassPath =
-                    standardClassPathStr + File.pathSeparator + projectClassPathStr;
-        }
-
-        if (virtualPathStr != null) {
-            compilerUserClassPath += File.pathSeparator + virtualPathStr;
-        }
-
-        if (standardClassPathStr == null) {
-            try {
-                String tmp = ".";
-                if (virtualPathStr != null) {
-                    tmp += File.pathSeparator + virtualPathStr;
-                }
-                standardClassPath = new ClassPath(tmp, false);
-            } catch (PublicExceptions.InvalidCmdOptionException ex) { /* Should not happen */ }
-        }
-        if (projectClassPathStr == null) {
-            projectClassPath = new ClassPath();
-        }
-
-        // Create the core class path as a combination of sun.boot.class.path and java.ext.dirs contents
-        if (bootClassPathStr == null) {
-            try {
-                bootClassPath =
-                        new ClassPath(System.getProperty("sun.boot.class.path"), false);
-            } catch (PublicExceptions.InvalidCmdOptionException ex) { /* Shouldn't happen */ }
-        // bootClassPathStr should remain null, so that nothing that the user didn't specify is passed to the compiler
-        }
-
-        if (extDirsStr == null) {
-            try {
-                setExtDirs(System.getProperty("java.ext.dirs"));
-            } catch (PublicExceptions.InvalidCmdOptionException ex) { /* Shouldn't happen */ }
-            // extDirsStr should remain null, so that nothing that the user didn't specify is passed to the compiler
-            extDirsStr = null;
-        }
-    }
-
-    /** Never returns null - if classpath wasn't set explicitly, returns "." */
-    public static String getCompilerUserClassPath() {
-        return compilerUserClassPath;
-    }
-
-    /** Will return null if boot class path wasn't explicitly specified */
-    public static String getCompilerBootClassPath() {
-        return bootClassPathStr;
-    }
-
-    /** Will return null if extdirs weren't explicitly specified */
-    public static String getCompilerExtDirs() {
-        return extDirsStr;
-    }
-
-    /** Will return null if virtualPath wasn't explicitly specified */
-    public static String getVirtualPath() {
-        return virtualPathStr;
-    }
-
-    /**
-     * For the given class return the list of all of its superclasses (excluding Object), that can be loaded from
-     * projectClassPath or standardClassPath, plus the first superclass that can be loaded from coreClassPath.
-     * The latter is an optimization based on the assumption that core classes never change, or rather the programmer
-     * will recompile everything when they switch to a new JDK version. The optimization prevents us from wasting time
-     * repeatedly loading the same sets of core classes.
-     */
-    public static void getSuperclasses(String className,
-            Collection<String> res, PCDManager pcdm) {
-        int iterNo = 0;
-        while (!"java/lang/Object".equals(className)) {
-            ClassInfo ci = getClassInfoForName(className, pcdm);
-            if (ci == null) {
-                return;
-            }
-            if (iterNo++ > 0) {
-                res.add(ci.name);
-            }
-            className = ci.superName;
-        }
-    }
-
-    /**
-     * Add to the given set the names of all interfaces implemented by the given class, that can be loaded from
-     * projectClassPath or standardClassPath, plus the first interface on each branch that can be loaded from
-     * coreClassPath. It's the same optimization as in getSuperclasses().
-     */
-    public static void addAllImplementedInterfaceNames(String className,
-            Set<String> intfSet, PCDManager pcdm) {
-        if ("java/lang/Object".equals(className)) {
-            return;
-        }
-        ClassInfo ci = getClassInfoForName(className, pcdm);
-        if (ci == null) {
-            return;
-        }
-        String[] interfaces = ci.interfaces;
-        if (interfaces != null) {
-            for (int i = 0; i < interfaces.length; i++) {
-                intfSet.add(interfaces[i]);
-                addAllImplementedInterfaceNames(interfaces[i], intfSet, pcdm);
-            }
-        }
-
-        String superName = ci.superName;
-        if (superName != null) {
-            addAllImplementedInterfaceNames(superName, intfSet, pcdm);
-        }
-    }
-
-    public static String[] getProjectJars() {
-        if (projectClassPath == null || projectClassPath.isEmpty()) {
-            return null;
-        }
-        PathEntry paths[] = projectClassPath.paths;
-        String[] ret = new String[paths.length];
-        for (int i = 0; i < paths.length; i++) {
-            ret[i] = paths[i].toString();
-        }
-        return ret;
-    }
-
-    public static ClassInfo getClassInfoForName(String className, PCDManager pcdm) {
-        ClassInfo info = classCache.get(className);
-        if (info != null) {
-            return info;
-        }
-
-        byte buf[] = bootClassPath.getBytesForClass(className);
-        if (buf == null) {
-            buf = extClassPath.getBytesForClass(className);
-        }
-        if (buf == null) {
-            buf = standardClassPath.getBytesForClass(className);
-        }
-        if (buf == null) {
-            buf = projectClassPath.getBytesForClass(className);
-        }
-        if (buf == null) {
-            return null;
-        }
-
-        info = new ClassInfo(buf, pcdm, className);
-        classCache.put(className, info);
-        return info;
-    }
-
-    /** Returns the class loader that would load classes from the given class path. */
-    public static ClassLoader getClassLoaderForPath(String classPath) throws Exception {
-        boolean isWindows = System.getProperty("os.name").startsWith("Win");
-        ClassPath cp = new ClassPath(classPath, false);
-        PathEntry[] paths = cp.paths;
-        URL[] urls = new URL[paths.length];
-        for (int i = 0; i < paths.length; i++) {
-            String dirOrJar = paths[i].toString();
-            if (!(dirOrJar.startsWith("file://") || dirOrJar.startsWith("http://"))) {
-                // On Windows, if I have path specified as "file://c:\...", (i.e. with the drive name) URLClassLoader works
-                // unbelievably slow. However, if an additional slash is added, like : "file:///c:\...", the speed becomes
-                // normal. To me it looks like a bug, but, anyway, I am taking measure here.
-                if (isWindows && dirOrJar.charAt(1) == ':') {
-                    dirOrJar = "/" + dirOrJar;
-                }
-                dirOrJar = new File(dirOrJar).toURI().toString();
-            }
-            if (!(dirOrJar.endsWith(".jar") || dirOrJar.endsWith(".zip") || dirOrJar.endsWith(File.separator))) {
-                dirOrJar += File.separator; // So that URLClassLoader correctly handles it as a directory
-            }
-            urls[i] = new URL(dirOrJar);
-        }
-
-        return new URLClassLoader(urls);
-    //} catch (java.net.MalformedURLException e) {
-
-    //}
-    }
-
-
-    // ------------------------------------ Private implementation --------------------------------------------
-    private ClassPath() {
-        paths = new PathEntry[0];
-    }
-
-    private ClassPath(String classPath, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
-        if (classPath == null) {
-            throw new PublicExceptions.InvalidCmdOptionException("null argument");
-        }
-        List<String> vec = new ArrayList<String>();
-
-        for (StringTokenizer tok =
-                new StringTokenizer(classPath, File.pathSeparator); tok.hasMoreTokens();) {
-            String path = tok.nextToken();
-            vec.add(path);
-        }
-        init(vec, isJarOnly);
-    }
-
-    private ClassPath(List<String> pathEntries, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
-        init(pathEntries, isJarOnly);
-    }
-
-    private void init(List<String> pathEntries, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
-        if (pathEntries == null) {
-            throw new PublicExceptions.InvalidCmdOptionException("null argument");
-        }
-        List<PathEntry> vec = new ArrayList<PathEntry>(pathEntries.size());
-        for (int i = 0; i < pathEntries.size(); i++) {
-            String path = pathEntries.get(i);
-            if (!path.equals("")) {
-                File file = new File(path);
-                try {
-                    if (file.exists() && file.canRead()) {
-                        if (file.isDirectory()) {
-                            if (isJarOnly) {
-                                throw new PublicExceptions.InvalidCmdOptionException("directories are not allowed on this class path: " + path);
-                            }
-                            vec.add(new Dir(file));
-                        } else {
-                            vec.add(new Zip(new ZipFile(file)));
-                        }
-                    } else if (isJarOnly) {
-                        throw new IOException("file does not exist");
-                    }
-                } catch (IOException e) {
-                    if (isJarOnly) {
-                        throw new PublicExceptions.InvalidCmdOptionException("error initializing class path component " + path + ": " + e.getMessage());
-                    }
-                }
-            }
-        }
-
-        paths = new PathEntry[vec.size()];
-        vec.toArray(paths);
-    }
-
-    private boolean isEmpty() {
-        return paths.length == 0;
-    }
-
-    private byte[] getBytesForClass(String className) {
-        String fileName = className + ".class";
-        for (int i = 0; i < paths.length; i++) {
-            byte buf[] = paths[i].getBytesForClassFile(fileName);
-            if (buf != null) {
-                return buf;
-            }
-        }
-        return null;
-    }
-
-    public String toString() {
-        if (paths == null) {
-            return "NULL";
-        }
-        StringBuilder res = new StringBuilder();
-        for (int i = 0; i < paths.length; i++) {
-            res.append(paths[i].toString());
-        }
-        return res.toString();
-    }
-
-
-    // ------------------------------------ Private helper classes --------------------------------------------
-    private static abstract class PathEntry {
-
-        abstract byte[] getBytesForClassFile(String fileName);
-
-        public abstract String toString();
-    }
-
-    private static class Dir extends PathEntry {
-
-        private String dir;
-
-        Dir(File f) throws IOException {
-            dir = f.getCanonicalPath();
-        }
-
-        byte[] getBytesForClassFile(String fileName) {
-            File file = new File(dir + File.separatorChar + fileName);
-            if (file.exists()) {
-                return Utils.readFileIntoBuffer(file);
-            } else {
-                return null;
-            }
-        }
-
-        public String toString() {
-            return dir;
-        }
-    }
-
-    private static class Zip extends PathEntry {
-
-        private ZipFile zip;
-
-        Zip(ZipFile z) {
-            zip = z;
-        }
-
-        byte[] getBytesForClassFile(String fileName) {
-            ZipEntry entry = zip.getEntry(fileName);
-            if (entry != null) {
-                return Utils.readZipEntryIntoBuffer(zip, entry);
-            } else {
-                return null;
-            }
-        }
-
-        public String toString() {
-            return zip.getName();
-        }
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/CompatibilityChecker.java b/third_party/jmake/src/org/pantsbuild/jmake/CompatibilityChecker.java
deleted file mode 100644
index bd74cfb3..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/CompatibilityChecker.java
+++ /dev/null
@@ -1,610 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.lang.reflect.Modifier;
-import java.util.List;
-import java.util.Set;
-
-/**
- * This class implements checking of source compatibility of classes and supporting operations
- *
- * @author Misha Dmitriev
- *  12 March 2004
- */
-public class CompatibilityChecker {
-
-    private PCDManager pcdm;
-    private RefClassFinder rf;
-    ClassInfo oldClassInfo = null;
-    ClassInfo newClassInfo = null;
-    private boolean versionsCompatible;
-    private boolean publicConstantChanged;
-
-    public CompatibilityChecker(PCDManager pcdm, boolean failOnDependentJar, boolean noWarnOnDependentJar) {
-        this.pcdm = pcdm;
-        publicConstantChanged = false;
-        rf = new RefClassFinder(pcdm, failOnDependentJar, noWarnOnDependentJar);
-    }
-
-    /**
-     * Compares the two class versions for the given PCDEntry. Returns true if all changes are source
-     * compatible, and false otherwise.
-     */
-    public boolean compareClassVersions(PCDEntry entry) {
-        // I once had the following optimization here with the comment "No sense to make any further checks if
-        // everything is recompiled anyway", but now I believe it's wrong. For each class that was found changed
-        // we need to know whether the new version is compatible with the old or not, since this may determine
-        // whether the new version of this class is promoted into the pdb or not (see PCDManager.updateClassInfoInPCD()).
-        // So, all changed classes should be checked just to correctly determine version compatibility.
-        // if (publicConstantChanged) return false;
-
-        oldClassInfo = pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
-        newClassInfo = pcdm.getClassInfoForPCDEntry(ClassInfo.VER_NEW, entry);
-
-        rf.initialize(oldClassInfo.name, entry.javaFileFullPath.endsWith(".jar"));
-        versionsCompatible = true;
-
-        checkAccessFlags();
-        checkSuperclasses();
-        checkImplementedInterfaces();
-        checkFields();
-        checkMethodsAndConstructors();
-
-        return versionsCompatible;
-    }
-
-    /** Find all dependent classes for a deleted class. */
-    public void checkDeletedClass(PCDEntry entry) {
-        oldClassInfo = entry.oldClassInfo;
-        rf.initialize(oldClassInfo.name, entry.javaFileFullPath.endsWith(".jar"));
-        rf.findReferencingClassesForDeletedClass(oldClassInfo);
-        // It may happen that the only reference to deleted class X is via "X.class" construct
-        String packageToLookIn =
-                oldClassInfo.isPublic() ? null : oldClassInfo.packageName;
-        rf.findClassesDeclaringField(("class$" + oldClassInfo.name).intern(), "java/lang/Class", true, packageToLookIn);
-        checkForFinalFields();
-    }
-
-    /** Returns the names of classes affected by source incompatible changes to the new version of the checked class. */
-    public String[] getAffectedClasses() {
-        return rf.getAffectedClassNames();
-    }
-
-    /** All of the following methods return true if no source incompatible changes found, and false otherwise */
-    private void checkAccessFlags() {
-        char oldClassFlags = oldClassInfo.accessFlags;
-        char newClassFlags = newClassInfo.accessFlags;
-        if (oldClassFlags == newClassFlags) {
-            return;
-        }
-
-        if (!Modifier.isFinal(oldClassFlags) && Modifier.isFinal(newClassFlags)) {
-            versionsCompatible = false;
-            rf.findDirectSubclasses(oldClassInfo);
-        }
-
-        if (!Modifier.isAbstract(oldClassFlags) && Modifier.isAbstract(newClassFlags)) {
-            versionsCompatible = false;
-            rf.findReferencingClasses0(oldClassInfo);
-        }
-
-        // Now to accessibility modifiers checking...
-        if (Modifier.isPublic(newClassFlags)) {
-            return;
-        }
-
-        if (Modifier.isProtected(newClassFlags)) {
-            if (Modifier.isPublic(oldClassFlags)) {
-                versionsCompatible = false;
-                rf.findDiffPackageAndNotSubReferencingClasses1(oldClassInfo);
-            }
-        } else if (Modifier.isPrivate(newClassFlags)) {
-            if (!Modifier.isPrivate(oldClassFlags)) {
-                versionsCompatible = false;
-            } else {
-                return;  // private -> private, nothing more to check
-            }
-            if (Modifier.isPublic(oldClassFlags)) {
-                rf.findReferencingClasses1(oldClassInfo);
-            } else if (Modifier.isProtected(oldClassFlags)) {
-                rf.findThisPackageOrSubReferencingClasses1(oldClassInfo);
-            } else {
-                rf.findThisPackageReferencingClasses1(oldClassInfo);
-            }
-        } else {  // newClassFlags has default access, since public has already been excluded
-            if (Modifier.isPublic(oldClassFlags)) {
-                versionsCompatible = false;
-                rf.findDiffPackageReferencingClasses1(oldClassInfo);
-            } else if (Modifier.isProtected(oldClassFlags)) {
-                versionsCompatible = false;
-                rf.findDiffPackageAndSubReferencingClasses1(oldClassInfo);
-            }
-        }
-    }
-
-    private void checkSuperclasses() {
-        List<String> oldSuperNames = oldClassInfo.getAllSuperclassNames();
-        List<String> newSuperNames = newClassInfo.getAllSuperclassNames();
-
-        int oldNamesSizeMinusOne = oldSuperNames.size() - 1;
-        for (int i = 0; i <= oldNamesSizeMinusOne; i++) {
-            String oldSuperName = oldSuperNames.get(i);
-            if (!newSuperNames.contains(oldSuperName)) {
-                versionsCompatible = false;
-                ClassInfo missingSuperClass =
-                        pcdm.getClassInfoForName(ClassInfo.VER_OLD, oldSuperName);
-                if (missingSuperClass == null) { // This class is not in project
-                    missingSuperClass =
-                            ClassPath.getClassInfoForName(oldSuperName, pcdm);
-                    if (missingSuperClass == null) {
-                        missingSuperClass = new ClassInfo(oldSuperName, pcdm);
-                    }
-                }
-                rf.findReferencingClasses2(missingSuperClass, oldClassInfo);
-            }
-        }
-
-        // Now check if the class is an exception, and its kind has changed from unchecked to checked
-        if (oldClassInfo.isInterface() || oldSuperNames.size() == 0) {
-            return;
-        }
-        if (!(oldSuperNames.contains("java/lang/RuntimeException") || oldSuperNames.contains("java/lang/Error"))) {
-            return;
-        }
-        if (!(newSuperNames.contains("java/lang/RuntimeException") || newSuperNames.contains("java/lang/Error"))) {
-            if (!newSuperNames.contains("java/lang/Throwable")) {
-                return;
-            }
-            // Ok, exception kind has changed from unchecked to checked.
-            versionsCompatible = false;
-            rf.findReferencingClasses0(oldClassInfo);
-            rf.findRefsToMethodsThrowingException(oldClassInfo);
-        }
-    }
-
-    private void checkImplementedInterfaces() {
-        Set<String> oldIntfNames = oldClassInfo.getAllImplementedIntfNames();
-        Set<String> newIntfNames = newClassInfo.getAllImplementedIntfNames();
-
-        for (String oldIntfName : oldIntfNames) {
-            if (!newIntfNames.contains(oldIntfName)) {
-                versionsCompatible = false;
-                ClassInfo missingSuperInterface =
-                        pcdm.getClassInfoForName(ClassInfo.VER_OLD, oldIntfName);
-                if (missingSuperInterface == null) { // This class is not in project
-                    missingSuperInterface =
-                            ClassPath.getClassInfoForName(oldIntfName, pcdm);
-                    if (missingSuperInterface == null) {
-                        missingSuperInterface = new ClassInfo(oldIntfName, pcdm);
-                    }
-                }
-                rf.findReferencingClasses2(missingSuperInterface, oldClassInfo);
-            }
-        }
-
-        // Check if the class is abstract, and an interface has been added to its list of implemented interfaces
-        if (newClassInfo.isAbstract()) {
-            for (String newIntfName : newIntfNames) {
-                if (!oldIntfNames.contains(newIntfName)) {
-                    versionsCompatible = false;
-                    rf.findConcreteSubclasses(oldClassInfo);
-                    break;
-                }
-            }
-        }
-    }
-
-    private void checkFields() {
-        String oFNames[] = oldClassInfo.fieldNames;
-        String oFSignatures[] = oldClassInfo.fieldSignatures;
-        char oFFlags[] = oldClassInfo.fieldAccessFlags;
-        String nFNames[] = newClassInfo.fieldNames;
-        String nFSignatures[] = newClassInfo.fieldSignatures;
-        char nFFlags[] = newClassInfo.fieldAccessFlags;
-        int oFLen = oFNames != null ? oFNames.length : 0;
-        int nFLen = nFNames != null ? nFNames.length : 0;
-
-        int oFMod, nFMod;
-        String oFName, oFSig, nFName;
-        int i, j, k, endIdx;
-        int nonMatchingNewFields = nFLen;
-
-        for (i = 0; i < oFLen; i++) {
-            oFMod = oFFlags[i];
-            if (Modifier.isPrivate(oFMod)) {
-                continue;  // Changes to private fields don't affect compatibility
-            }
-            oFName = oFNames[i];
-            oFSig = oFSignatures[i];
-            boolean found = false;
-
-            // Look for the same field in the new version considering name and type
-            endIdx = nFLen - 1;
-            k = i < nFLen ? i : endIdx;
-            for (j = 0; j < nFLen; j++) {
-                if (oFName.equals(nFNames[k]) &&
-                        oFSig.equals(nFSignatures[k])) {
-                    found = true;
-                    break;
-                }
-                if (k < endIdx) {
-                    k++;
-                } else {
-                    k = 0;
-                }
-            }
-
-            if (found) {
-                nonMatchingNewFields--;
-                nFMod = nFFlags[k];
-                checkFieldModifiers(oFMod, nFMod, i, k);
-                if (publicConstantChanged) {
-                    return;
-                }
-            } else { // Matching field not found
-                if (Modifier.isStatic(oFMod) && Modifier.isFinal(oFMod) &&
-                        oldClassInfo.primitiveConstantInitValues != null &&
-                        oldClassInfo.primitiveConstantInitValues[i] != null) {
-                    // Compile-time constant deleted
-                    versionsCompatible = false;
-                    rf.findAllProjectClasses(oldClassInfo, i);
-                    if (Modifier.isPublic(oFMod)) {
-                        publicConstantChanged = true;
-                        return;
-                    }
-                } else {
-                    versionsCompatible = false;
-                    rf.findReferencingClassesForField(oldClassInfo, i);
-                }
-            }
-        }
-
-        if (nonMatchingNewFields > 0) { // There are some fields declared in the new version which don't exist in the old one
-            // Look for fields hiding same-named fields in superclasses
-            for (i = 0; i < nFLen; i++) {
-                nFName = nFNames[i];
-
-                boolean found = false;
-                for (j = 0; j < oFLen; j++) {
-                    if (nFName.equals(oFNames[j])) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (found) {
-                    continue;  // nFName is not an added field
-                }
-                String superName = oldClassInfo.superName;
-                ClassInfo superInfo;
-                while (superName != null) {
-                    superInfo =
-                            pcdm.getClassInfoForName(ClassInfo.VER_OLD, superName);
-                    if (superInfo == null) {
-                        break;
-                    }
-                    String[] superOFNames = superInfo.fieldNames;
-                    int superOFNamesLen = superOFNames != null ? superOFNames.length
-                            : 0;
-                    for (j = 0; j < superOFNamesLen; j++) {
-                        if (nFName == superOFNames[j]) {
-                            versionsCompatible = false;
-                            rf.findReferencingClassesForField(superInfo, j);
-                        }
-                    }
-                    superName = superInfo.superName;
-                }
-            }
-        }
-    }
-
-    /** It is already known that old field is not private */
-    private void checkFieldModifiers(int oFMod, int nFMod, int oldFieldIdx, int newFieldIdx) {
-        if (oFMod == nFMod) {
-            if (Modifier.isFinal(oFMod) &&
-                    (!ClassInfo.constFieldInitValuesEqual(oldClassInfo, oldFieldIdx, newClassInfo, newFieldIdx))) {
-                versionsCompatible = false;
-                rf.findAllProjectClasses(oldClassInfo, oldFieldIdx);
-                if (Modifier.isPublic(oFMod)) {
-                    publicConstantChanged = true;  // Means we will have to recompile ALL project classes
-                }
-                return;
-            }
-        }
-
-        // These tests are ordered such that if a previous test succeeds, there is no need to do further tests, since that
-        // former test will cause more classes to be checked than any of the further tests. That is why it is possible to
-        // check properties that are in fact independent (e.g. accessibility vs. static/non-static) together. But this
-        // optimization only works since all kinds of tests result in the same kind of find..ReferencingClassesForField()
-        // outcome. For methods this is not true, and so there we have to check independent properties separately.
-        if (Modifier.isStatic(oFMod) && Modifier.isFinal(oFMod) && // oFMod is known to be non-private
-                (!Modifier.isFinal(nFMod) || !ClassInfo.constFieldInitValuesEqual(oldClassInfo, oldFieldIdx, newClassInfo, newFieldIdx))) {
-            versionsCompatible = false;
-            rf.findAllProjectClasses(oldClassInfo, oldFieldIdx);
-            if (Modifier.isPublic(oFMod)) {
-                publicConstantChanged = true;
-            }
-        } else if (Modifier.isPrivate(nFMod) || // oFMod is known to be non-private
-                (!Modifier.isFinal(oFMod) && Modifier.isFinal(nFMod)) ||
-                (Modifier.isStatic(oFMod) != Modifier.isStatic(nFMod)) ||
-                (Modifier.isVolatile(oFMod) != Modifier.isVolatile(nFMod))) {
-            versionsCompatible = false;
-            rf.findReferencingClassesForField(oldClassInfo, oldFieldIdx);
-        } else if (Modifier.isPublic(oFMod) && Modifier.isProtected(nFMod)) {
-            versionsCompatible = false;
-            rf.findDiffPackageReferencingClassesForField(oldClassInfo, oldFieldIdx);
-        } else if ((Modifier.isPublic(oFMod) || Modifier.isProtected(oFMod)) &&
-                (!(Modifier.isPublic(nFMod) || Modifier.isProtected(nFMod) || Modifier.isPrivate(nFMod)))) {
-            versionsCompatible = false;
-            if (Modifier.isPublic(oFMod)) {
-                rf.findDiffPackageReferencingClassesForField(oldClassInfo, oldFieldIdx);
-            } else {
-                rf.findDiffPackageAndSubReferencingClassesForField(oldClassInfo, oldFieldIdx);
-            }
-        }
-    }
-
-    private void checkForFinalFields() {
-        char oFFlags[] = oldClassInfo.fieldAccessFlags;
-        int oFLen = oldClassInfo.fieldNames != null ? oldClassInfo.fieldNames.length
-                : 0;
-        int oFMod;
-
-        for (int i = 0; i < oFLen; i++) {
-            oFMod = oFFlags[i];
-            if (Modifier.isPrivate(oFMod)) {
-                continue;  // Changes to private fields don't affect compatibility
-            }
-            if (Modifier.isStatic(oFMod) && Modifier.isFinal(oFMod)) {
-                rf.findAllProjectClasses(oldClassInfo, i);
-                if (Modifier.isPublic(oFMod)) {
-                    publicConstantChanged = true;
-                    return;
-                }
-            }
-        }
-    }
-
-    private void checkMethodsAndConstructors() {
-        String oMNames[] = oldClassInfo.methodNames;
-        String oMSignatures[] = oldClassInfo.methodSignatures;
-        char oMFlags[] = oldClassInfo.methodAccessFlags;
-        String nMNames[] = newClassInfo.methodNames;
-        String nMSignatures[] = newClassInfo.methodSignatures;
-        char nMFlags[] = newClassInfo.methodAccessFlags;
-        int oMLen = oMNames != null ? oMNames.length : 0;
-        int nMLen = nMNames != null ? nMNames.length : 0;
-
-        int oMMod, nMMod;
-        String oMName, oMSig, nMName, nMSig;
-        int i, j, k, endIdx;
-        int nonMatchingNewMethods = nMLen;
-
-        for (i = 0; i < oMLen; i++) {
-            oMMod = oMFlags[i];
-            if (Modifier.isPrivate(oMMod)) {
-                continue;  // Changes to private methods don't affect compatibility
-            }
-            oMName = oMNames[i];
-            oMSig = oMSignatures[i];
-            boolean found = false;
-
-            // Look for the same method in the new version considering name and signature
-            endIdx = nMLen - 1;
-            k = i < nMLen ? i : endIdx;
-            for (j = 0; j < nMLen; j++) {
-                if (oMName == nMNames[k] && oMSig == nMSignatures[k]) {
-                    found = true;
-                    break;
-                }
-                if (k < endIdx) {
-                    k++;
-                } else {
-                    k = 0;
-                }
-            }
-
-            if (found) {
-                nonMatchingNewMethods--;
-                nMMod = nMFlags[k];
-                if (oMMod != nMMod) {
-                    checkMethodModifiers(oMMod, nMMod, i);
-                }
-
-                // Check if the new method throws more exceptions than the old one
-                if (newClassInfo.checkedExceptions != null && newClassInfo.checkedExceptions[k] != null) {
-                    if (oldClassInfo.checkedExceptions == null) {
-                        versionsCompatible = false;
-                        rf.findReferencingClassesForMethod(oldClassInfo, i);
-                    } else if (oldClassInfo.checkedExceptions[i] == null) {
-                        versionsCompatible = false;
-                        rf.findReferencingClassesForMethod(oldClassInfo, i);
-                    } else {
-                        String oldExceptions[] =
-                                oldClassInfo.checkedExceptions[i];
-                        String newExceptions[] =
-                                newClassInfo.checkedExceptions[k];
-                        for (int ei = 0; ei < newExceptions.length; ei++) {
-                            String newEx = newExceptions[ei];
-                            found = false;
-                            for (int ej = 0; ej < oldExceptions.length; ej++) {
-                                if (newEx.equals(oldExceptions[ej])) {
-                                    found = true;
-                                    break;
-                                }
-                            }
-                            if (!found) {
-                                versionsCompatible = false;
-                                rf.findReferencingClassesForMethod(oldClassInfo, i);
-                                break;
-                            }
-                        }
-                    }
-                }
-            } else { // Matching method not found
-                versionsCompatible = false;
-                rf.findReferencingClassesForMethod(oldClassInfo, i);
-                // Deleting a concrete method from an abstract class is a special case
-                if (oldClassInfo.isAbstract() && !Modifier.isAbstract(oMMod)) {
-                    rf.findConcreteSubclassesNotOverridingAbstractMethod(oldClassInfo, oldClassInfo, i);
-                }
-            }
-        }
-
-        if (nonMatchingNewMethods > 0) {  // There are some methods/constructors declared in the new version which don't exist in the old one
-            if (!oldClassInfo.isInterface()) {
-                for (i = 0; i < nMLen; i++) {
-                    nMMod = nMFlags[i];
-                    if (Modifier.isPrivate(nMMod)) {
-                        continue;
-                    }
-                    String newMName = nMNames[i];
-                    final String newMSig = nMSignatures[i];
-                    final boolean isStatic = Modifier.isStatic(nMMod);
-
-                    boolean found = false;
-                    for (j = 0; j < oMLen; j++) {
-                        if (newMName.equals(oMNames[j]) &&
-                                newMSig.equals(oMSignatures[j])) {
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (found) {
-                        continue;  // nMName is not an added method
-                    }
-                    // Check if the new method is a static one that hides an inherited static method
-                    // Check if the new method overloads an existing (declared or inherited) method. Overloading test is rough -
-                    // we just check if the number of parameters is the same. Note that if a new constructor has been added, it
-                    // can be treated in the same way, except that we shouldn't look up "same name methods" for it in superclasses.
-                    oldClassInfo.findExistingSameNameMethods(newMName,
-                            !newMName.equals("<init>"), false,
-                            new ClassInfo.MethodHandler() {
-
-                        void handleMethod(ClassInfo classInfo, int methodIdx) {
-                            String otherMSig =
-                                    classInfo.methodSignatures[methodIdx];
-                            if ((newMSig.equals(otherMSig) && isStatic &&
-                                    classInfo != oldClassInfo) ||
-                                    (newMSig != otherMSig &&
-                                    Utils.sameParamNumber(newMSig, otherMSig))) {
-                                versionsCompatible = false;
-                                rf.findReferencingClassesForMethod(classInfo, methodIdx);
-                            }
-                        }
-                    });
-
-                    if (Modifier.isAbstract(nMMod)) {
-                        // An abstract method added to the class. Find any concrete subclasses that don't override
-                        // or inherit a concrete implementation of this method.
-                        versionsCompatible = false;
-                        rf.findConcreteSubclassesNotOverridingAbstractMethod(oldClassInfo, newClassInfo, i);
-                    }
-                    // Check if there is a method with the same name in some subclass, such that it now overrides
-                    // or overloads the added method.
-                    if (subclassesDeclareSameNameMethod(oldClassInfo, newMName)) {
-                        versionsCompatible = false;
-                    }
-                }
-            } else {  // We are checking an interface.
-                for (i = 0; i < nMLen; i++) {
-                    String newMName = nMNames[i];
-                    final String newMSig = nMSignatures[i];
-
-                    boolean found = false;
-                    for (j = 0; j < oMLen; j++) {
-                        if (newMName == oMNames[j] && newMSig == oMSignatures[j]) {
-                            found = true;
-                            break;
-                        }
-                    }
-
-                    if (!found) {
-                        versionsCompatible = false;
-
-                        // Check if the new method overloads an existing (declared or inherited) method. Overloading test is rough -
-                        // we just check if the number of parameters is the same.
-                        oldClassInfo.findExistingSameNameMethods(newMName, true, true, new ClassInfo.MethodHandler() {
-
-                            void handleMethod(ClassInfo classInfo, int methodIdx) {
-                                String otherMSig =
-                                        classInfo.methodSignatures[methodIdx];
-                                if (newMSig != otherMSig &&
-                                        Utils.sameParamNumber(newMSig, otherMSig)) {
-                                    rf.findReferencingClassesForMethod(classInfo, methodIdx);
-                                }
-                            }
-                        });
-
-                        rf.findDirectlyAndOtherwiseImplementingConcreteClasses(oldClassInfo);
-                        rf.findAbstractSubtypesWithSameNameMethod(oldClassInfo, newMName, newMSig);
-                        break;
-                    }
-                }
-            }
-        }
-    }
-
-    private void checkMethodModifiers(int oMMod, int nMMod, int oldMethodIdx) {
-        if (Modifier.isPrivate(nMMod)) {
-            versionsCompatible = false;
-            rf.findReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
-        } else if (Modifier.isPublic(oMMod) && Modifier.isProtected(nMMod)) {
-            versionsCompatible = false;
-            rf.findDiffPackageReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
-        } else if ((Modifier.isPublic(oMMod) || Modifier.isProtected(oMMod)) &&
-                (!(Modifier.isPublic(nMMod) || Modifier.isProtected(nMMod) || Modifier.isPrivate(nMMod)))) {
-            versionsCompatible = false;
-            if (Modifier.isPublic(oMMod)) {
-                rf.findDiffPackageReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
-            } else {
-                rf.findDiffPackageAndSubReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
-            }
-        } else if ((Modifier.isPrivate(oMMod) && !Modifier.isPrivate(nMMod)) ||
-                (Modifier.isProtected(oMMod) && Modifier.isPublic(nMMod)) ||
-                (!(Modifier.isPublic(oMMod) || Modifier.isProtected(oMMod) || Modifier.isPrivate(oMMod)) &&
-                (Modifier.isPublic(nMMod) || Modifier.isProtected(nMMod)))) {
-            versionsCompatible = false;
-            rf.findSubclassesReimplementingMethod(oldClassInfo, oldMethodIdx);
-        }
-
-        if ((!Modifier.isAbstract(oMMod) && Modifier.isAbstract(nMMod)) ||
-                (Modifier.isStatic(oMMod) != Modifier.isStatic(nMMod))) {
-            versionsCompatible = false;
-            rf.findReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
-            if (!Modifier.isAbstract(oMMod) && Modifier.isAbstract(nMMod)) {
-                rf.findConcreteSubclassesNotOverridingAbstractMethod(oldClassInfo, newClassInfo, oldMethodIdx);
-            }
-        }
-        if (!Modifier.isFinal(oMMod) && Modifier.isFinal(nMMod)) {
-            versionsCompatible = false;
-            rf.findSubclassesReimplementingMethod(oldClassInfo, oldMethodIdx);
-        }
-    }
-
-    /**
-     * Returns true if any subclass(es), direct or indirect, declare a method with name methodName.
-     * For each such occurence, referencing classes are looked up and added to the list of affected classes.
-     */
-    private boolean subclassesDeclareSameNameMethod(ClassInfo oldClassInfo, String methodName) {
-        boolean res = false;
-        ClassInfo[] directSubclasses = oldClassInfo.getDirectSubclasses();
-        for (int i = 0; i < directSubclasses.length; i++) {
-            ClassInfo subclass = directSubclasses[i];
-            int methNo = subclass.declaresSameNameMethod(methodName);
-            if (methNo >= 0) {
-                rf.addToAffectedClassNames(subclass.name);
-                rf.findReferencingClassesForMethod(subclass, methNo);
-                res = true;
-            }
-            if (subclassesDeclareSameNameMethod(subclass, methodName)) {
-                res = true;
-            }
-        }
-        return res;
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/Main.java b/third_party/jmake/src/org/pantsbuild/jmake/Main.java
deleted file mode 100644
index 0e805548..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/Main.java
+++ /dev/null
@@ -1,899 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.Reader;
-import java.io.StreamTokenizer;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The main class of the <b>jmake</b> tool.<p>
- *
- * Has several entrypoints: <code>main</code>, <code>mainExternal</code>, <code>mainProgrammatic</code>,
- * <code>mainExternalControlled</code> and <code>mainProgrammaticControlled</code>.
- * The first is not intended to be used by applications other than <b>jmake</b> itself, whereas the
- * rest can be used to call <b>jmake</b> externally with various degrees of control over its behaviour.
- * See method comments for more details.
- *
- * @author Misha Dmitriev
- *  12 October 2004
- */
-public class Main {
-
-    static final String DEFAULT_STORE_NAME = "jmake.pdb";
-    static final String VERSION = "1.3.8-11";
-    private String pdbFileName = null;
-    private List<String> allProjectJavaFileNamesList =
-            new ArrayList<String>(100);
-    private String allProjectJavaFileNames[];
-    private String addedJavaFileNames[],  removedJavaFileNames[],  updatedJavaFileNames[];
-    private String destDir = "";
-    private List<String> javacAddArgs = new ArrayList<String>();
-    private String jcPath,  jcMainClass,  jcMethod;
-    private String jcExecApp;
-    boolean controlledExecution = false;
-    Object externalApp = null;
-    Method externalCompileSourceFilesMethod = null;
-    private boolean failOnDependentJar = false,  noWarnOnDependentJar = false;
-    private boolean pdbTextFormat = false;
-    private PCDManager pcdm = null;
-    private String dependencyFile;
-    private static final String optNames[] = {"-h", "-help", "-d", "-pdb", "-C", "-jcpath", "-jcmainclass", "-jcmethod", "-jcexec", "-Xtiming", "-version",
-        "-warnlimit", "-failondependentjar", "-nowarnondependentjar", "-classpath", "-projclasspath", "-bootclasspath", "-extdirs", "-vpath", "-pdb-text-format",
-        "-depfile"};
-    private static final int optArgs[] = {0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1};
-    private static final int OPT_H = 0;
-    private static final int OPT_HELP = 1;
-    private static final int OPT_D = 2;
-    private static final int OPT_STORE = 3;
-    private static final int OPT_JAVAC_OPT = 4;
-    private static final int OPT_JCPATH = 5;
-    private static final int OPT_JCMAINCLASS = 6;
-    private static final int OPT_JCMETHOD = 7;
-    private static final int OPT_JCEXEC = 8;
-    private static final int OPT_TIMING = 9;
-    private static final int OPT_VERSION = 10;
-    private static final int OPT_WARNLIMIT = 11;
-    private static final int OPT_FAILONDEPJAR = 12;
-    private static final int OPT_NOWARNONDEPJAR = 13;
-    private static final int OPT_CLASSPATH = 14;
-    private static final int OPT_PROJECTCLASSPATH = 15;
-    private static final int OPT_BOOTCLASSPATH = 16;
-    private static final int OPT_EXTDIRS = 17;
-    private static final int OPT_VPATH = 18;
-    private static final int OPT_PDB_TEXT_FORMAT = 19;
-    private static final int OPT_DEPENDENCY_FILE = 20;
-
-    /** Construct a new instance of Main. */
-    public Main() {
-    }
-
-    /**
-     * Checks whether the argument is a legal jmake option. Returns its number or
-     * -1 if not found.
-     */
-    private static int inOptions(String option) {
-        if (option.startsWith(optNames[OPT_JAVAC_OPT])) {
-            return OPT_JAVAC_OPT;
-        }
-        for (int i = 0; i < optNames.length; i++) {
-            if (option.equals(optNames[i])) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private void processCommandLine(String args[]) {
-        if ((args.length == 0) || (args.length == 1 && args[0].equals(optNames[OPT_HELP]))) {
-            printUsage();
-            throw new PrivateException(new PublicExceptions.NoActionRequestedException());
-        }
-
-        List<String> argsV = new ArrayList<String>();
-        for (int i = 0; i < args.length; i++) {
-            argsV.add(args[i]);
-        }
-        int argsSt = 0;
-        String arg;
-
-        while (argsSt < argsV.size()) {
-            arg = argsV.get(argsSt++);
-            if (arg.charAt(0) == '-') {
-                int argN = inOptions(arg);
-                if (argN != -1) {
-                    if (argsSt + optArgs[argN] > argsV.size()) {
-                        optRequiresArg("\"" + optNames[argN] + "\"");
-                    }
-                } else {
-                    bailOut(arg + ERR_IS_INVALID_OPTION);
-                }
-
-                switch (argN) {
-                    case OPT_H:
-                    case OPT_HELP:
-                        printUsage();
-                        throw new PrivateException(new PublicExceptions.NoActionRequestedException());
-                    case OPT_D:
-                        destDir = argsV.get(argsSt);
-                        javacAddArgs.add("-d");
-                        javacAddArgs.add(argsV.get(argsSt));
-                        argsSt++;
-                        break;
-                    case OPT_CLASSPATH:
-                        try {
-                            setClassPath(argsV.get(argsSt++));
-                        } catch (PublicExceptions.InvalidCmdOptionException ex) {
-                            bailOut(ex.getMessage());
-                        }
-                        break;
-                    case OPT_PROJECTCLASSPATH:
-                        try {
-                            setProjectClassPath(argsV.get(argsSt++));
-                        } catch (PublicExceptions.InvalidCmdOptionException ex) {
-                            bailOut(ex.getMessage());
-                        }
-                        break;
-                    case OPT_STORE:
-                        pdbFileName = argsV.get(argsSt++);
-                        break;
-                    case OPT_JAVAC_OPT:
-                        String javacArg =
-                                (argsV.get(argsSt - 1)).substring(2);
-                        if (javacArg.equals("-d") ||
-                                javacArg.equals("-classpath") ||
-                                javacArg.equals("-bootclasspath") ||
-                                javacArg.equals("-extdirs")) {
-                            bailOut(javacArg + ERR_SHOULD_BE_EXPLICIT);
-                        }
-                        javacAddArgs.add(javacArg);
-                        break;
-                    case OPT_JCPATH:
-                        if (jcExecApp != null) {
-                            bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
-                        }
-                        jcPath = argsV.get(argsSt++);
-                        break;
-                    case OPT_JCMAINCLASS:
-                        if (jcExecApp != null) {
-                            bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
-                        }
-                        jcMainClass = argsV.get(argsSt++);
-                        break;
-                    case OPT_JCMETHOD:
-                        if (jcExecApp != null) {
-                            bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
-                        }
-                        jcMethod = argsV.get(argsSt++);
-                        break;
-                    case OPT_JCEXEC:
-                        if (jcPath != null || jcMainClass != null || jcMethod != null) {
-                            bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
-                        }
-                        jcExecApp = argsV.get(argsSt++);
-                        break;
-                    case OPT_TIMING:
-                        Utils.setTimingOn();
-                        break;
-                    case OPT_VERSION:
-                        // Utils.printInfoMessage("jmake version " + VERSION); // Printed anyway at present...
-                        throw new PrivateException(new PublicExceptions.NoActionRequestedException());
-                    case OPT_WARNLIMIT:
-                        try {
-                            Utils.warningLimit =
-                                    Integer.parseInt(argsV.get(argsSt++));
-                        } catch (NumberFormatException e) {
-                            Utils.ignore(e);
-                            bailOut(argsV.get(argsSt) + ERR_IS_INVALID_OPTION);
-                        }
-                        break;
-                    case OPT_FAILONDEPJAR:
-                        if (noWarnOnDependentJar) {
-                            bailOut("it is not allowed to use -nowarnondependentjar and -failondependentjar together");
-                        }
-                        failOnDependentJar = true;
-                        break;
-                    case OPT_NOWARNONDEPJAR:
-                        if (failOnDependentJar) {
-                            bailOut("it is not allowed to use -failondependentjar and -nowarnondependentjar together");
-                        }
-                        noWarnOnDependentJar = true;
-                        break;
-                    case OPT_BOOTCLASSPATH:
-                        try {
-                            setBootClassPath(argsV.get(argsSt++));
-                        } catch (PublicExceptions.InvalidCmdOptionException ex) {
-                            bailOut(ex.getMessage());
-                        }
-                        break;
-                    case OPT_EXTDIRS:
-                        try {
-                            setExtDirs(argsV.get(argsSt++));
-                        } catch (PublicExceptions.InvalidCmdOptionException ex) {
-                            bailOut(ex.getMessage());
-                        }
-                        break;
-                    case OPT_VPATH:
-                        try {
-                            setVirtualPath(argsV.get(argsSt++));
-                        } catch (PublicExceptions.InvalidCmdOptionException ex) {
-                            bailOut(ex.getMessage());
-                        }
-                        break;
-                    case OPT_PDB_TEXT_FORMAT:
-                        pdbTextFormat = true;
-                        break;
-                    case OPT_DEPENDENCY_FILE:
-                        dependencyFile = argsV.get(argsSt++);
-                        break;
-                    default:
-                        bailOut(arg + ERR_IS_INVALID_OPTION);
-                }
-            } else {    // Not an option, at least does not start with "-". Treat it as a command line file name or source name.
-                if (arg.length() > 1 && arg.charAt(0) == '@') {
-                    arg = arg.substring(1);
-                    loadCmdFile(arg, argsV);
-                } else {
-                    if (!arg.endsWith(".java")) {
-                        bailOut(arg + ERR_IS_INVALID_OPTION);
-                    }
-                    allProjectJavaFileNamesList.add(arg);
-                }
-            }
-        }
-    }
-
-    /** Load @-file that can contain anything that command line can contain. */
-    private void loadCmdFile(String name, List<String> argsV) {
-        try {
-            Reader r = new BufferedReader(new FileReader(name));
-            StreamTokenizer st = new StreamTokenizer(r);
-            st.resetSyntax();
-            st.wordChars(' ', 255);
-            st.whitespaceChars(0, ' ');
-            st.commentChar('#');
-            st.quoteChar('"');
-            st.quoteChar('\'');
-            while (st.nextToken() != StreamTokenizer.TT_EOF) {
-                argsV.add(st.sval);
-            }
-            r.close();
-        } catch (IOException e) {
-            throw new PrivateException(new PublicExceptions.CommandFileReadException(name + ":\n" + e.getMessage()));
-        }
-    }
-
-    /**
-     * Main entrypoint for applications that want to call <b>jmake</b> externally and are willing
-     * to handle exceptions that it may throw.
-     *
-     * @param  args  command line arguments passed to <b>jmake</b>.
-     *
-     * @throws PublicExceptions.NoActionRequestedException  if <b>jmake</b> was not requested to do any real work;
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid command line option was detected;
-     * @throws PublicExceptions.PDBCorruptedException       if project database is corrupted;
-     * @throws PublicExceptions.CommandFileReadException    if there was error reading a command file;
-     * @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
-     *                                                       or compilation errors were detected;
-     * @throws PublicExceptions.ClassFileParseException     if there was error parsing a class file;
-     * @throws PublicExceptions.ClassNameMismatchException  if there is a mismatch between the deduced and the actual class name;
-     * @throws PublicExceptions.InvalidSourceFileExtensionException if a supplied source file has an invalid extension (not .java);
-     * @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
-     * @throws PublicExceptions.DoubleEntryException        if more than one entry for the same class is found in the project
-     * @throws FileNotFoundException                        if a <code>.java</code> or a <code>.class</code> file was not found;
-     * @throws IOException                                  if there was an I/O problem of any kind;
-     * @throws PublicExceptions.InternalException           if an internal problem that should never happen was detected.
-     */
-    public void mainProgrammatic(String args[]) throws
-            PublicExceptions.NoActionRequestedException,
-            PublicExceptions.InvalidCmdOptionException,
-            PublicExceptions.PDBCorruptedException,
-            PublicExceptions.CommandFileReadException,
-            PublicExceptions.CompilerInteractionException,
-            PublicExceptions.ClassFileParseException,
-            PublicExceptions.ClassNameMismatchException,
-            PublicExceptions.InvalidSourceFileExtensionException,
-            PublicExceptions.JarDependsOnSourceException,
-            PublicExceptions.DoubleEntryException,
-            FileNotFoundException,
-            IOException,
-            PublicExceptions.InternalException {
-        try {
-            Utils.printInfoMessage("Jmake version " + VERSION);
-            if (!controlledExecution) {
-                processCommandLine(args);
-                String[] projectJars = ClassPath.getProjectJars();
-                if (projectJars != null) {
-                    for (int i = 0; i < projectJars.length; i++) {
-                        allProjectJavaFileNamesList.add(projectJars[i]);
-                    }
-                }
-                allProjectJavaFileNames =
-                        allProjectJavaFileNamesList.toArray(new String[allProjectJavaFileNamesList.size()]);
-            } else {
-                String[] projectJars = ClassPath.getProjectJars();
-                if (projectJars != null) {
-                    String newNames[] =
-                            new String[allProjectJavaFileNames.length + projectJars.length];
-                    System.arraycopy(allProjectJavaFileNames, 0, newNames, 0, allProjectJavaFileNames.length);
-                    System.arraycopy(projectJars, 0, newNames, allProjectJavaFileNames.length, projectJars.length);
-                    allProjectJavaFileNames = newNames;
-                }
-            }
-
-            Utils.startTiming(Utils.TIMING_PDBREAD);
-            PCDContainer pcdc;
-            pcdc = PCDContainer.load(pdbFileName, pdbTextFormat);
-            Utils.stopAndPrintTiming("DB read", Utils.TIMING_PDBREAD);
-
-            pcdm = new PCDManager(pcdc, allProjectJavaFileNames,
-                addedJavaFileNames, removedJavaFileNames, updatedJavaFileNames,
-                destDir, javacAddArgs, failOnDependentJar, noWarnOnDependentJar,
-                dependencyFile);
-
-            pcdm.initializeCompiler(jcExecApp, jcPath, jcMainClass, jcMethod, externalApp, externalCompileSourceFilesMethod);
-
-            pcdm.run();
-        } catch (PrivateException e) {
-            Throwable origException = e.getOriginalException();
-            if (origException instanceof PublicExceptions.NoActionRequestedException) {
-                throw (PublicExceptions.NoActionRequestedException) origException;
-            } else if (origException instanceof PublicExceptions.InvalidCmdOptionException) {
-                throw (PublicExceptions.InvalidCmdOptionException) origException;
-            } else if (origException instanceof PublicExceptions.CommandFileReadException) {
-                throw (PublicExceptions.CommandFileReadException) origException;
-            } else if (origException instanceof PublicExceptions.PDBCorruptedException) {
-                throw (PublicExceptions.PDBCorruptedException) origException;
-            } else if (origException instanceof PublicExceptions.CompilerInteractionException) {
-                throw (PublicExceptions.CompilerInteractionException) origException;
-            } else if (origException instanceof PublicExceptions.ClassFileParseException) {
-                throw (PublicExceptions.ClassFileParseException) origException;
-            } else if (origException instanceof PublicExceptions.ClassNameMismatchException) {
-                throw (PublicExceptions.ClassNameMismatchException) origException;
-            } else if (origException instanceof PublicExceptions.InvalidSourceFileExtensionException) {
-                throw (PublicExceptions.InvalidSourceFileExtensionException) origException;
-            } else if (origException instanceof PublicExceptions.JarDependsOnSourceException) {
-                throw (PublicExceptions.JarDependsOnSourceException) origException;
-            } else if (origException instanceof PublicExceptions.DoubleEntryException) {
-                throw (PublicExceptions.DoubleEntryException) origException;
-            } else if (origException instanceof FileNotFoundException) {
-                throw (FileNotFoundException) origException;
-            } else if (origException instanceof IOException) {
-                throw (IOException) origException;
-            } else if (origException instanceof PublicExceptions.InternalException) {
-                throw (PublicExceptions.InternalException) origException;
-            }
-        } finally {
-            ClassPath.resetOnFinish();
-        }
-    }
-
-    /**
-     * Main entrypoint for applications that want to call <b>jmake</b> externally and would prefer
-     * receiving an error code instead of an exception in case something goes wrong.<p>
-     *
-     * @param  args  command line arguments passed to <b>jmake</b>.
-     *
-     * @return <dl>
-     *  <dt><code>  0</code>  if everything was successful;
-     *  <dt><code> -1</code>  invalid command line option detected;
-     *  <dt><code> -2</code>  error reading command file;
-     *  <dt><code> -3</code>  project database corrupted;
-     *  <dt><code> -4</code>  error initializing or calling the compiler;
-     *  <dt><code> -5</code>  compilation error;
-     *  <dt><code> -6</code>  error parsing a class file;
-     *  <dt><code> -7</code>  file not found;
-     *  <dt><code> -8</code>  I/O exception;
-     *  <dt><code> -9</code>  internal jmake exception;
-     *  <dt><code>-10</code>  deduced and actual class name mismatch;
-     *  <dt><code>-11</code>  invalid source file extension;
-     *  <dt><code>-12</code>  a class in a <code>JAR</code> is found dependent on a class with the .java source;
-     *  <dt><code>-13</code>  more than one entry for the same class is found in the project
-     *  <dt><code>-20</code> internal Java error (caused by <code>java.lang.InternalError</code>);
-     *  <dt><code>-30</code> internal Java error (caused by <code>java.lang.RuntimeException</code>).
-     *  </dl>
-     */
-    public int mainExternal(String args[]) {
-        try {
-            mainProgrammatic(args);
-        } catch (PublicExceptions.NoActionRequestedException e0) {
-            // Nothing to do
-        } catch (PublicExceptions.InvalidCmdOptionException e1) {
-            Utils.printErrorMessage(e1.getMessage());
-            return -1;
-        } catch (PublicExceptions.CommandFileReadException e2) {
-            Utils.printErrorMessage("error parsing command file:");
-            Utils.printErrorMessage(e2.getMessage());
-            return -2;
-        } catch (PublicExceptions.PDBCorruptedException e3) {
-            Utils.printErrorMessage("project database corrupted: " + e3.getMessage());
-            return -3;
-        } catch (PublicExceptions.CompilerInteractionException e4) {
-            if (e4.getOriginalException() != null) {
-                Utils.printErrorMessage("error interacting with the compiler: ");
-                Utils.printErrorMessage(e4.getMessage());
-                Utils.printErrorMessage("original exception:");
-                Utils.printErrorMessage(e4.getOriginalException().getMessage());
-                return -4;
-            } else { // Otherwise there is a compilation error, and the compiler has already printed a lot...
-                return -5;
-            }
-        } catch (PublicExceptions.ClassFileParseException e6) {
-            Utils.printErrorMessage(e6.getMessage());
-            return -6;
-        } catch (FileNotFoundException e7) {
-            Utils.printErrorMessage(e7.getMessage());
-            return -7;
-        } catch (IOException e8) {
-            Utils.printErrorMessage(e8.getMessage());
-            return -8;
-        } catch (PublicExceptions.InternalException e9) {
-            Utils.printErrorMessage("internal jmake exception detected:");
-            Utils.printErrorMessage(e9.getMessage());
-            Utils.printErrorMessage(Utils.REPORT_PROBLEM);
-            Utils.printErrorMessage("the stack trace is as follows:");
-            e9.printStackTrace();
-            return -9;
-        } catch (PublicExceptions.ClassNameMismatchException e10) {
-            Utils.printErrorMessage(e10.getMessage());
-            return -10;
-        } catch (PublicExceptions.InvalidSourceFileExtensionException e11) {
-            Utils.printErrorMessage(e11.getMessage());
-            return -11;
-        } catch (PublicExceptions.JarDependsOnSourceException e12) {
-            Utils.printErrorMessage(e12.getMessage());
-            return -12;
-        } catch (PublicExceptions.DoubleEntryException e13) {
-            Utils.printErrorMessage(e13.getMessage());
-            return -13;
-        } catch (InternalError e20) {
-            Utils.printErrorMessage("internal Java error: " + e20);
-            Utils.printErrorMessage("Consult the following stack trace for more info:");
-            e20.printStackTrace();
-            return -20;
-        } catch (RuntimeException e30) {
-            Utils.printErrorMessage("internal Java exception: " + e30);
-            Utils.printErrorMessage("Consult the following stack trace for more info:");
-            e30.printStackTrace();
-            return -30;
-        }
-
-        return 0;
-    }
-
-    /**
-     * Main entrypoint for applications such as Ant, that want to have full control over
-     * compilations that <b>jmake</b> invokes, and are willing to handle exceptions
-     * that it may throw.
-     *
-     * @param javaFileNames    array of strings that specify <code>.java</code> file names.
-     * @param destDirName      name of the destination directory (<b>jmake</b> will look up binary classes
-     *                         in there, it should be the same as the one used by the Java compiler method).
-     *                         If <code>null</code> is passed, classes will be looked up in the same directories
-     *                         as their sources, in agreement with the default Java compiler behaviour.
-     * @param pdbFileName      project database file name (if <code>null</code> is passed,
-     *                         a file with the default name placed in the current directory will be used).
-     * @param externalApp      an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
-     * @param externalCompileSourceFilesMethod    a method of the form <code>int x(String[] args)</code>. It
-     *                         should return <code>0</code> if compilation is successful and any non-zero value
-     *                         otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
-     *                         recompile in the form of canonical full path file names.
-     *
-     * @throws PublicExceptions.NoActionRequestedException  if <b>jmake</b> was not requested to do any real work;
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid command line option was detected;
-     * @throws PublicExceptions.PDBCorruptedException       if project database is corrupted;
-     * @throws PublicExceptions.CommandFileReadException    if there was error reading a command file;
-     * @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
-     *                                                       or compilation errors were detected;
-     * @throws PublicExceptions.ClassFileParseException     if there was error parsing a class file;
-     * @throws PublicExceptions.ClassNameMismatchException  if there is a mismatch between the deduced and the actual class name;
-     * @throws PublicExceptions.InvalidSourceFileExtensionException if a specified source file has an invalid extension (not .java);
-     * @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
-     * @throws PublicExceptions.DoubleEntryException        if more than one entry for the same class is found in the project
-     * @throws PublicExceptions.InternalException           if an internal problem that should never happen was detected.
-     * @throws FileNotFoundException                        if a <code>.java</code> or a <code>.class</code> file was not found;
-     * @throws IOException                                  if there was an I/O problem of any kind;
-     */
-    public void mainProgrammaticControlled(String javaFileNames[], String destDirName, String pdbFileName,
-            Object externalApp, Method externalCompileSourceFilesMethod) throws
-            PublicExceptions.NoActionRequestedException,
-            PublicExceptions.InvalidCmdOptionException,
-            PublicExceptions.PDBCorruptedException,
-            PublicExceptions.CommandFileReadException,
-            PublicExceptions.CompilerInteractionException,
-            PublicExceptions.ClassFileParseException,
-            PublicExceptions.ClassNameMismatchException,
-            PublicExceptions.InvalidSourceFileExtensionException,
-            PublicExceptions.JarDependsOnSourceException,
-            PublicExceptions.DoubleEntryException,
-            PublicExceptions.InternalException,
-            FileNotFoundException,
-            IOException {
-
-        controlledExecution = true;
-        this.pdbFileName = pdbFileName;
-        this.destDir = destDirName;
-        this.allProjectJavaFileNames = javaFileNames;
-        this.externalApp = externalApp;
-        this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
-
-        mainProgrammatic(null);
-    }
-
-    /**
-     * Main entrypoint for applications such as Ant, that want to have full control over
-     * compilations that <b>jmake</b> invokes, and do not want to handle exceptions that it
-     * may throw. Error codes returned are the same as <code>mainExternal(String[])</code> returns.
-     *
-     * @param javaFileNames    array of strings that specify <code>.java</code> file names.
-     * @param destDirName      name of the destination directory (<b>jmake</b> will look up binary classes
-     *                         in there, it should be the same as the one used by the Java compiler method).
-     *                         If <code>null</code> is passed, classes will be looked up in the same directories
-     *                         as their sources, in agreement with the default Java compiler behaviour.
-     * @param pdbFileName      project database file name (if <code>null</code> is passed,
-     *                         a file with the default name placed in the current directory will be used).
-     * @param externalApp      an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
-     * @param externalCompileSourceFilesMethod    a method of the form <code>int x(String[] args)</code>. It
-     *                         should return <code>0</code> if compilation is successful and any non-zero value
-     *                         otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
-     *                         recompile in the form of canonical full path file names.
-     *
-     * @see #mainExternal(String[])
-     */
-    public int mainExternalControlled(String javaFileNames[], String destDirName, String pdbFileName,
-            Object externalApp, Method externalCompileSourceFilesMethod) {
-        controlledExecution = true;
-        this.pdbFileName = pdbFileName;
-        this.destDir = destDirName;
-        this.allProjectJavaFileNames = javaFileNames;
-        this.externalApp = externalApp;
-        this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
-
-        return mainExternal(null);
-    }
-
-    /**
-     * Main entrypoint for applications such as IDEs, that themselves keep track of updated/added/removed sources,
-     * want to have full control over compilations that <b>jmake</b> invokes, and are willing to handle exceptions
-     * that it may throw.
-     *
-     * @param addedJavaFileNames   names of <code>.java</code> files just added to the project
-     * @param removedJavaFileNames names of <code>.java</code> files just removed from the project
-     * @param updatedJavaFileNames names of updated project <code>.java</code> files
-     * @param destDirName      name of the destination directory (<b>jmake</b> will look up binary classes
-     *                         in there, it should be the same as the one used by the Java compiler method).
-     *                         If <code>null</code> is passed, classes will be looked up in the same directories
-     *                         as their sources, in agreement with the default Java compiler behaviour.
-     * @param pdbFileName      project database file name (if <code>null</code> is passed,
-     *                         a file with the default name placed in the current directory will be used).
-     * @param externalApp      an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
-     * @param externalCompileSourceFilesMethod    a method of the form <code>int x(String[] args)</code>. It
-     *                         should return <code>0</code> if compilation is successful and any non-zero value
-     *                         otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
-     *                         recompile in the form of canonical full path file names.
-     *
-     * @throws PublicExceptions.NoActionRequestedException  if <b>jmake</b> was not requested to do any real work;
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid command line option was detected;
-     * @throws PublicExceptions.PDBCorruptedException       if project database is corrupted;
-     * @throws PublicExceptions.CommandFileReadException    if there was error reading a command file;
-     * @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
-     *                                                       or compilation errors were detected;
-     * @throws PublicExceptions.ClassFileParseException     if there was error parsing a class file;
-     * @throws PublicExceptions.ClassNameMismatchException  if there is a mismatch between the deduced and the actual class name;
-     * @throws PublicExceptions.InvalidSourceFileExtensionException if a specified source file has an invalid extension (not .java);
-     * @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
-     * @throws PublicExceptions.DoubleEntryException        if more than one entry for the same class is found in the project
-     * @throws PublicExceptions.InternalException           if an internal problem that should never happen was detected.
-     * @throws FileNotFoundException                        if a <code>.java</code> or a <code>.class</code> file was not found;
-     * @throws IOException                                  if there was an I/O problem of any kind;
-     */
-    public void mainProgrammaticControlled(String addedJavaFileNames[], String removedJavaFileNames[], String updatedJavaFileNames[],
-            String destDirName, String pdbFileName,
-            Object externalApp, Method externalCompileSourceFilesMethod) throws
-            PublicExceptions.NoActionRequestedException,
-            PublicExceptions.InvalidCmdOptionException,
-            PublicExceptions.PDBCorruptedException,
-            PublicExceptions.CommandFileReadException,
-            PublicExceptions.CompilerInteractionException,
-            PublicExceptions.ClassFileParseException,
-            PublicExceptions.ClassNameMismatchException,
-            PublicExceptions.InvalidSourceFileExtensionException,
-            PublicExceptions.JarDependsOnSourceException,
-            PublicExceptions.DoubleEntryException,
-            PublicExceptions.InternalException,
-            FileNotFoundException,
-            IOException {
-
-        controlledExecution = true;
-        this.pdbFileName = pdbFileName;
-        this.destDir = destDirName;
-        this.addedJavaFileNames = addedJavaFileNames;
-        this.removedJavaFileNames = removedJavaFileNames;
-        this.updatedJavaFileNames = updatedJavaFileNames;
-        this.externalApp = externalApp;
-        this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
-
-        mainProgrammatic(null);
-    }
-
-    /**
-     * Main entrypoint for applications such as IDEs, that themselves keep track of updated/added/removed sources,
-     * want to have full control over compilations that <b>jmake</b> invokes, and do not want to handle exceptions
-     * that it may throw. Error codes returned are the same as <code>mainExternal(String[])</code> returns.
-     *
-     * @param addedJavaFileNames   names of <code>.java</code> files just added to the project
-     * @param removedJavaFileNames names of <code>.java</code> files just removed from the project
-     * @param updatedJavaFileNames names of updated project <code>.java</code> files
-     * @param destDirName      name of the destination directory (<b>jmake</b> will look up binary classes
-     *                         in there, it should be the same as the one used by the Java compiler method).
-     *                         If <code>null</code> is passed, classes will be looked up in the same directories
-     *                         as their sources, in agreement with the default Java compiler behaviour.
-     * @param pdbFileName      project database file name (if <code>null</code> is passed,
-     *                         a file with the default name placed in the current directory will be used).
-     * @param externalApp      an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
-     * @param externalCompileSourceFilesMethod    a method of the form <code>int x(String[] args)</code>. It
-     *                         should return <code>0</code> if compilation is successful and any non-zero value
-     *                         otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
-     *                         recompile in the form of canonical full path file names.
-     *
-     * @see #mainExternal(String[])
-     */
-    public int mainExternalControlled(String addedJavaFileNames[], String removedJavaFileNames[], String updatedJavaFileNames[],
-            String destDirName, String pdbFileName,
-            Object externalApp, Method externalCompileSourceFilesMethod) {
-        controlledExecution = true;
-        this.pdbFileName = pdbFileName;
-        this.destDir = destDirName;
-        this.addedJavaFileNames = addedJavaFileNames;
-        this.removedJavaFileNames = removedJavaFileNames;
-        this.updatedJavaFileNames = updatedJavaFileNames;
-        this.externalApp = externalApp;
-        this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
-
-        return mainExternal(null);
-    }
-
-    /**
-     * Main entrypoint for the standalone <b>jmake</b> application. This method calls does little but calling
-     * <code>mainExternal</code>, and its execution always completes with <code>System.exit(code)</code>,
-     * where <code>code</code> is the value returned by <code>mainExternal</code>.
-     *
-     * @see #mainExternal(String[])
-     * @see #mainProgrammatic(String[])
-     *
-     * @param  args  command line arguments passed to <b>jmake</b>
-     */
-    public static void main(String args[]) {
-        Utils.startTiming(Utils.TIMING_TOTAL);
-
-        Main m = new Main();
-        int exitCode = m.mainExternal(args);
-
-        Utils.stopAndPrintTiming("Total", Utils.TIMING_TOTAL);
-        if ( exitCode != 0 ) {
-            System.exit(exitCode);
-        }
-    }
-
-    /**
-     * Customize the output of <b>jmake</b>.
-     *
-     * @see #setOutputStreams(PrintStream, PrintStream, PrintStream)
-     *
-     * @param printInfoMessages    specify whether to print information messages
-     * @param printWarningMessages specify whether to print warning messages
-     * @param printErrorMessages   specify whether to print error messages
-     */
-    public static void customizeOutput(boolean printInfoMessages,
-            boolean printWarningMessages,
-            boolean printErrorMessages) {
-        Utils.customizeOutput(printInfoMessages, printWarningMessages, printErrorMessages);
-    }
-
-    /**
-     * Set the class path to be used by the compiler, and also by the dependency checker for the purposes of
-     * superclass/superinterface change tracking. For the compiler, this class path will be merged with the
-     * project class path (set via setProjectClassPath(String)). Other than that, its value will be used only to
-     * look up superclasses/superinterfaces of project classes. Note that non-project superclasses and
-     * superinterfaces are first looked up at the boot class path, then on the extension class path, and then
-     * on this class path.
-     *
-     * @see #setProjectClassPath(String)
-     * @see #setBootClassPath(String)
-     * @see #setExtDirs(String)
-     *
-     * @param classPath  the value of the class path, in the usual format (i.e. entries that are directories
-     *                   or JARs, separated by colon or semicolon depending on the platform).
-     *
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid class path value is specified.
-     */
-    public static void setClassPath(String classPath) throws PublicExceptions.InvalidCmdOptionException {
-        ClassPath.setClassPath(classPath);
-    }
-
-    /**
-     * Set the class path to be used by the compiler, and also by the dependency checker for the purposes of
-     * superclass/superinterface change tracking and sourceless class dependency checking. For the compiler,
-     * and also in order to look up superclasses/superinterfaces of project classes, this class path will be
-     * merged with the standard class path (set via setClassPath(String)). But in addition, all binary classes
-     * that are on this class path are stored in the project database and checked for updates every time jmake
-     * is invoked. Any changes to these classes trigger the standard dependency checking procedure. However,
-     * dependent classes are looked up only among the "normal" project classes, i.e. those that have sources.
-     * Therefore sourceless classes are assumed to always be mutually consistent.
-     *
-     * Currently only JAR files can be present on this class path.
-     *
-     * @see #setClassPath(String)
-     *
-     * @param projectClassPath  the value of the class path, in the usual format (i.e. entries that are directories
-     *                          or JARs, separated by colon or semicolon depending on the platform).
-     *
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid class path value is specified.
-     */
-    public static void setProjectClassPath(String projectClassPath) throws PublicExceptions.InvalidCmdOptionException {
-        ClassPath.setProjectClassPath(projectClassPath);
-    }
-
-    /**
-     * Set the boot class path to be used by the compiler (-bootclasspath option) and also by the dependency
-     * checker (by default, the value of "sun.boot.class.path" property is used).
-     *
-     * @see #setClassPath(String)
-     *
-     * @param classPath   the value of the boot class path, in the usual format (i.e. entries that are directories
-     *                   or JARs, separated by colon or semicolon depending on the platform).
-     *
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid class path value is specified.
-     */
-    public static void setBootClassPath(String classPath) throws PublicExceptions.InvalidCmdOptionException {
-        ClassPath.setBootClassPath(classPath);
-    }
-
-    /**
-     * Set the extensions location to be used by the compiler (-extdirs option) and also by the dependency
-     * checker (by default, the value of "java.ext.dirs" property is used).
-     *
-     * @see #setClassPath(String)
-     *
-     * @param dirs   the value of extension directories, in the usual format (one or more directory names
-     *               separated by colon or semicolon depending on the platform).
-     *
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid class path value is specified.
-     */
-    public static void setExtDirs(String dirs) throws PublicExceptions.InvalidCmdOptionException {
-        ClassPath.setExtDirs(dirs);
-    }
-
-    /**
-     * Set the virtual path used to find both source and class files that are part of the project
-     * but are not in the local directory.
-     *
-     * @see #setClassPath(String)
-     *
-     * @param dirs   the value of extension directories, in the usual format (one or more directory names
-     *               separated by colon or semicolon depending on the platform).
-     *
-     * @throws PublicExceptions.InvalidCmdOptionException   if invalid path value is specified.
-     */
-    public static void setVirtualPath(String dirs) throws PublicExceptions.InvalidCmdOptionException {
-        ClassPath.setVirtualPath(dirs);
-    }
-
-    /** Produce no warning or error message upon a dependent <code>JAR</code> detection. */
-    public static final int DEPJAR_NOWARNORERROR = 0;
-    /** Produce a warning upon a dependent <code>JAR</code> detection. */
-    public static final int DEPJAR_WARNING = 1;
-    /** Produce an error message (throw an exception) upon a dependent <code>JAR</code> detection. */
-    public static final int DEPJAR_ERROR = 2;
-
-    /**
-     * Set the response of <b>jmake</b> in case a dependence of a class located in a <code>JAR</code> file on a
-     * class with a <code>.java</code> source is detected (such dependencies are highly discouraged, since it is not
-     * possible to recompile a class in the <code>JAR</code> that has no source).
-     *
-     * @param code  response type: DEPJAR_NOWARNORERROR, DEPJAR_WARNING (default behaviour) or DEPJAR_ERROR.
-     */
-    public void setResponseOnDependentJar(int code) {
-        switch (code) {
-            case DEPJAR_NOWARNORERROR:
-                noWarnOnDependentJar = true;
-                failOnDependentJar = false;
-                break;
-            case DEPJAR_WARNING:
-                noWarnOnDependentJar = false;
-                failOnDependentJar = false;
-                break;
-            case DEPJAR_ERROR:
-                noWarnOnDependentJar = false;
-                failOnDependentJar = true;
-                break;
-        }
-    }
-
-    /**
-     * Return the names of all classes that <b>jmake</b>, on this invocation, found updated - either because
-     * <b>jmake</b> itself recompiled them or because they were updated independently (their timestamp/checksum
-     * found different from those contained in the project database).
-     */
-    public String[] getUpdatedClasses() {
-        return pcdm.getAllUpdatedClassesAsStringArray();
-    }
-
-    /**
-     * Set the output print streams to be used by <b>jmake</b>.
-     *
-     * @see #customizeOutput(boolean, boolean, boolean)
-     *
-     * @param out   print stream to be used for information ("logging") messages that <b>jmake</b> emits
-     * @param warn  print stream to be used for warning messages
-     * @param err   print stream to be used for error messages
-     */
-    public static void setOutputStreams(PrintStream out, PrintStream warn, PrintStream err) {
-        Utils.setOutputStreams(out, warn, err);
-    }
-
-    /** Get the version of this copy of <b>jmake</b> */
-    public static String getVersion() {
-        return VERSION;
-    }
-    private static final String ERR_IS_INVALID_OPTION =
-            " is an invalid option or argument.";
-    private static final String ERR_NO_TWO_COMPILER_OPTIONS =
-            "You may not specify both compiler class and compiler executable application";
-    private static final String ERR_SHOULD_BE_EXPLICIT =
-            " compiler option should be specified directly as a jmake option";
-
-    private static void bailOut(String s) {
-        throw new PrivateException(new PublicExceptions.InvalidCmdOptionException("jmake: " + s + "\nRun \"jmake -h\" for help."));
-    }
-
-    private static void optRequiresArg(String s) {
-        bailOut("the " + s + " option requires an argument.");
-    }
-
-    private static void printUsage() {
-        Utils.printInfoMessage("Usage: jmake <options> <.java files> <@files>");
-        Utils.printInfoMessage("where possible options include:");
-        Utils.printInfoMessage("  -h, -help             print this help message");
-        Utils.printInfoMessage("  -version              print the product version number");
-        Utils.printInfoMessage("  -pdb <file name>      specify non-default project database file");
-        Utils.printInfoMessage("  -pdb-text-format      if specified, pdb file is stored in text format");
-        Utils.printInfoMessage("  -d <directory>        specify where to place generated class files");
-        Utils.printInfoMessage("  -classpath <path>     specify where to find user class files");
-        Utils.printInfoMessage("  -projclasspath <path> specify where to find sourceless project classes");
-        Utils.printInfoMessage("                        (currently only JARs are allowed on this path)");
-        Utils.printInfoMessage("  -C<option>            specify an option to be passed to the Java compiler");
-        Utils.printInfoMessage("                        (this option's arguments should also be preceded by -C)");
-        Utils.printInfoMessage("  -jcpath <path>        specify the class path for a non-default Java compiler");
-        Utils.printInfoMessage("                        (default is <JAVAHOME>/lib/tools.jar)");
-        Utils.printInfoMessage("  -jcmainclass <class>  specify the main class for a non-default Java compiler");
-        Utils.printInfoMessage("                        (default is com.sun.tools.javac.Main)");
-        Utils.printInfoMessage("  -jcmethod <method>    specify the method to call in the Java compiler class");
-        Utils.printInfoMessage("                        (default is \"compile(String args[])\")");
-        Utils.printInfoMessage("  -jcexec <file name>   specify a binary non-default Java compiler application");
-        Utils.printInfoMessage("  -failondependentjar   fail if a class on projectclasspath depends on a class");
-        Utils.printInfoMessage("                        with .java source (by default, a warning is issued)");
-        Utils.printInfoMessage("  -nowarnondependentjar no warning or error if a class on projectclasspath");
-        Utils.printInfoMessage("                        depends on a class with a .java source (use with care)");
-        Utils.printInfoMessage("  -warnlimit <number>   specify the maximum number of warnings (20 by default)");
-        Utils.printInfoMessage("  -bootclasspath <path> override location of bootstrap class files");
-        Utils.printInfoMessage("  -extdirs <dirs>       override location of installed extensions");
-        Utils.printInfoMessage("  -vpath <dirs>         a list of directories to search for Java and class files similar to GNUMake's VPATH");
-        Utils.printInfoMessage("  -depfile <path>       a file generated by the compiler containing additional java->class mappings");
-        Utils.printInfoMessage("");
-        Utils.printInfoMessage("Examples:");
-        Utils.printInfoMessage("  jmake -d classes -classpath .;mylib.jar X.java Y.java Z.java");
-        Utils.printInfoMessage("  jmake -pdb myproject.pdb -jcexec c:\\java\\jikes\\jikes.exe @myproject.src");
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/PCDContainer.java b/third_party/jmake/src/org/pantsbuild/jmake/PCDContainer.java
deleted file mode 100644
index b02cfa4..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/PCDContainer.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.File;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * This class is a persistent container for the Project Class Directory, that can
- * read and write itself from/to disk.
- *
- * @author Misha Dmitriev
- *  12 November 2001
- */
-public class PCDContainer {
-
-    /** The data structure (currently {@link LinkedHashMap}) for PCD, that maps class name to
-    record containing information about the class */
-    Map<String,PCDEntry> pcd;
-    String storeName;
-    boolean textFormat;
-
-    private PCDContainer(Map<String,PCDEntry> pcd, String storeName, boolean textFormat) {
-        this.storeName = storeName;
-        this.pcd = pcd;
-        this.textFormat = textFormat;
-    }
-
-    public static PCDContainer load(String storeName, boolean textFormat) {
-        if (storeName == null) {
-            storeName = Main.DEFAULT_STORE_NAME;
-        }
-        File storeFile = Utils.checkFileForName(storeName);
-        if (storeFile != null) {
-            Utils.printInfoMessageNoEOL("Opening project database...  ");
-            Map<String,PCDEntry> pcd;
-            if (textFormat) {
-                pcd = new TextProjectDatabaseReader().readProjectDatabaseFromFile(storeFile);
-            } else {
-                pcd = new BinaryProjectDatabaseReader().readProjectDatabaseFromFile(storeFile);
-            }
-            PCDContainer pcdc = new PCDContainer(pcd, storeName, textFormat);
-            Utils.printInfoMessage("Done.");
-            return pcdc;
-        }
-        return new PCDContainer(null, storeName, textFormat);
-    }
-
-    public void save() {
-        Utils.printInfoMessageNoEOL("Writing project database...  ");
-        File outfile = new File(storeName);
-        if (textFormat) {
-            new TextProjectDatabaseWriter().writeProjectDatabaseToFile(outfile, pcd);
-        } else {
-            new BinaryProjectDatabaseWriter().writeProjectDatabaseToFile(outfile, pcd);
-        }
-        Utils.printInfoMessage("Done.");
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/PCDEntry.java b/third_party/jmake/src/org/pantsbuild/jmake/PCDEntry.java
deleted file mode 100644
index 72fb3a4..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/PCDEntry.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.File;
-
-/**
- * An instance of this class represents an entry in the Project Class Directory.
- *
- * @author Misha Dmitriev
- *  29 March 2002
- */
-public class PCDEntry {
-    // Class versions compare results
-
-    static final int CV_UNCHECKED = 0;
-    static final int CV_COMPATIBLE = 1;
-    static final int CV_INCOMPATIBLE = 2;
-    static final int CV_DELETED = 3;
-    static final int CV_NEW = 4;
-    static final int CV_NEWER_FOUND_NEARER = 5;
-    String className;           // Dots are replaced with slashes for convenience
-    transient String classFileFullPath;
-    String javaFileFullPath;
-    long oldClassFileLastModified;
-    transient long newClassFileLastModified;
-    long oldClassFileFingerprint;
-    transient long newClassFileFingerprint;
-    ClassInfo oldClassInfo;
-    transient ClassInfo newClassInfo;
-    transient int checkResult;         // Reflects the result of class version comparison
-    transient boolean checked;             // Mark entries for classes that have been checked and found existing.
-    // It helps to detect double entries for the same class in the project file list,
-    // and also not to confuse them with the case when a .java source for a class is moved.
-
-    /** This constructor is called to initialize a record for a class that has just been added to the project. */
-    public PCDEntry(String className,
-            String javaFileFullPath,
-            String classFileFullPath,
-            long classFileLastModified,
-            long classFileFingerprint,
-            ClassInfo classInfo) {
-        this.className = className;
-        this.classFileFullPath = classFileFullPath;
-        this.javaFileFullPath = javaFileFullPath;
-        this.oldClassFileLastModified = this.newClassFileLastModified =
-                classFileLastModified;
-        this.oldClassFileFingerprint = this.newClassFileFingerprint =
-                classFileFingerprint;
-        this.newClassInfo = classInfo;
-        checked = true;
-    }
-
-    /**
-     * This constructor is called to initialize a record for a class that
-     * exists at least in the previous version of the project.
-     */
-    public PCDEntry(String className,
-            String javaFileFullPath,
-            long classFileLastModified,
-            long classFileFingerprint,
-            ClassInfo classInfo) {
-        this.className = className;
-        this.javaFileFullPath = javaFileFullPath;
-        this.oldClassFileLastModified = classFileLastModified;
-        this.oldClassFileFingerprint = classFileFingerprint;
-        this.oldClassInfo = classInfo;
-    }
-
-    // Debugging
-    public String toString() {
-        return "className = " + className +
-                "; classFileFullPath = " + classFileFullPath +
-                "; javaFileFullPath = " + javaFileFullPath;
-    }
-
-    /**
-     * Returns the name of the class that corresponds to the file name, i.e. the public class
-     */
-    private String getExpectedClassName() {
-        File path = new File(javaFileFullPath);
-        int index = -1;
-        do {
-            index = className.indexOf('/', index + 1);
-            path = path.getParentFile();
-        } while (index != -1);
-        String pathString = path.toString();
-        if (!pathString.endsWith("/"))
-            pathString += "/";
-        // It is assumed that the javaFileFillPath ends with .java
-        int javaPathWithoutSuffix = javaFileFullPath.length() - 5;
-        return javaFileFullPath.substring(pathString.length(),
-                                          javaPathWithoutSuffix);
-    }
-
-    /**
-     * A class that neither has the same name as the java file, nor an inner class, is
-     * package-private.
-     */
-    public boolean isPackagePrivateClass() {
-        String expectedClassName = getExpectedClassName();
-
-        return !(className.equals(expectedClassName)
-                 || (className.startsWith(expectedClassName)
-                     && className.charAt(expectedClassName.length()) == '$'));
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/PCDManager.java b/third_party/jmake/src/org/pantsbuild/jmake/PCDManager.java
deleted file mode 100644
index 5ff3cd1..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/PCDManager.java
+++ /dev/null
@@ -1,1603 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.zip.Adler32;
-
-/**
- * This class implements management of the Project Class Directory, automatic tracking
- * of changes and recompilation of .java sources for a project.
- *
- * @author Misha Dmitriev
- *  23 January 2003
- */
-public class PCDManager {
-
-    private PCDContainer pcdc;
-    private Map<String,PCDEntry> pcd;   // Maps project class names to PCDEntries
-    private String projectJavaAndJarFilesArray[];
-    private String addedJavaAndJarFilesArray[],  removedJavaAndJarFilesArray[],  updatedJavaAndJarFilesArray[];
-    private List<String> newJavaFiles;
-    private Set<String> updatedJavaFiles;
-    private Set<String> recompiledJavaFiles;
-    private Set<String> updatedClasses;       // This set is emptied on every new internal jmake iteration...
-    private Set<String> allUpdatedClasses;    // whereas in this one the names of all updated classes found during this jmake invocation are stored.
-    private Set<String> updatedAndCheckedClasses;
-    private Set<String> deletedClasses;
-    private Set<String> updatedJarFiles;
-    private Set<String> stableJarFiles;
-    private Set<String> newJarFiles;
-    private Set<String> deletedJarFiles;
-    /* Dependencies from the dependencyFile, if any */
-    private Map<String, List<String>> extraDependencies;
-
-    private String destDir;
-    private boolean destDirSpecified;
-    private List<String> javacAddArgs;
-    private Class<?> compilerClass;
-    private Method compileMethod;
-    private String jcExecApp;
-    private Object externalApp;
-    private Method externalCompileSourceFilesMethod;
-    private Adler32 checkSum;
-    private CompatibilityChecker cv;
-    private ClassFileReader cfr;
-    private boolean newProject = false;
-    private String dependencyFile = null;
-    private static boolean backSlashFileSeparator = File.separatorChar != '/';
-
-    /**** Interface to the class ****/
-    /**
-     * Either projectJavaAndJarFilesArray != null and added.. == removed.. == updatedJavaAndJarFilesArray == null,
-     * or projectJavaAndJarFilesArray == null and one or more of others != null.
-     * When PCDManager is called from Main, this is guaranteed, since separate entrypoint functions initialize
-     * either one or another of the above argument groups, but never both.
-     */
-    public PCDManager(PCDContainer pcdc,
-                      String projectJavaAndJarFilesArray[],
-                      String addedJavaAndJarFilesArray[],
-                      String removedJavaAndJarFilesArray[],
-                      String updatedJavaAndJarFilesArray[],
-                      String in_destDir,
-                      List<String> javacAddArgs,
-                      boolean failOnDependentJar,
-                      boolean noWarnOnDependentJar,
-                      String dependencyFile) {
-        this.pcdc = pcdc;
-        if (pcdc.pcd == null) {
-            pcd = new LinkedHashMap<String,PCDEntry>();
-            pcdc.pcd = pcd;
-            newProject = true;
-        } else {
-            pcd = pcdc.pcd;
-        }
-
-        this.projectJavaAndJarFilesArray = projectJavaAndJarFilesArray;
-        this.addedJavaAndJarFilesArray = addedJavaAndJarFilesArray;
-        this.removedJavaAndJarFilesArray = removedJavaAndJarFilesArray;
-        this.updatedJavaAndJarFilesArray = updatedJavaAndJarFilesArray;
-        this.dependencyFile = dependencyFile;
-        newJavaFiles = new ArrayList<String>();
-        updatedJavaFiles = new LinkedHashSet<String>();
-        recompiledJavaFiles = new LinkedHashSet<String>();
-        updatedAndCheckedClasses = new LinkedHashSet<String>();
-        deletedClasses = new LinkedHashSet<String>();
-        allUpdatedClasses = new LinkedHashSet<String>();
-
-        updatedJarFiles = new LinkedHashSet<String>();
-        stableJarFiles = new LinkedHashSet<String>();
-        newJarFiles = new LinkedHashSet<String>();
-        deletedJarFiles = new LinkedHashSet<String>();
-
-        initializeDestDir(in_destDir);
-        this.javacAddArgs = javacAddArgs;
-
-        checkSum = new Adler32();
-
-        cv = new CompatibilityChecker(this, failOnDependentJar, noWarnOnDependentJar);
-        cfr = new ClassFileReader();
-    }
-
-    public Collection<PCDEntry> entries() {
-        return pcd.values();
-    }
-
-    public ClassFileReader getClassFileReader() {
-        return cfr;
-    }
-
-    public ClassInfo getClassInfoForName(int verCode, String className) {
-        PCDEntry pcde = pcd.get(className);
-        if (pcde != null) {
-            return getClassInfoForPCDEntry(verCode, pcde);
-        } else {
-            return null;
-        }
-    }
-
-    public boolean isProjectClass(int verCode, String className) {
-        if (verCode == ClassInfo.VER_OLD) {
-            return pcd.containsKey(className);
-        } else {
-            PCDEntry pcde = pcd.get(className);
-            return (pcde != null && pcde.checkResult != PCDEntry.CV_DELETED);
-        }
-    }
-
-    /**
-     * Get an instance of ClassInfo (load a class file if necessary) for the given version (old or new) of
-     * the class determined by pcde. For an old class version, always returns a non-null result; but for a new
-     * version, null is returned if class file is not found. In most of the current uses of this method null result
-     * is not checked, because it's either called for an old version or it is already known that the .class file
-     * should be present; nevertheless, beware!
-     */
-    public ClassInfo getClassInfoForPCDEntry(int verCode, PCDEntry pcde) {
-        if (verCode == ClassInfo.VER_OLD) {
-            return pcde.oldClassInfo;
-        }
-
-        ClassInfo res = pcde.newClassInfo;
-        if (res == null) {
-            byte classFileBytes[];
-            String classFileFullPath = null;
-            if (pcde.javaFileFullPath.endsWith(".java")) {
-                File classFile = Utils.checkFileForName(pcde.classFileFullPath);
-                if (classFile == null) {
-                    return null;  // Class file not found.
-                }
-                classFileBytes = Utils.readFileIntoBuffer(classFile);
-                classFileFullPath = pcde.classFileFullPath;
-            } else {
-                try {
-                    JarFile jarFile = new JarFile(pcde.javaFileFullPath);
-                    JarEntry jarEntry =
-                            jarFile.getJarEntry(pcde.className + ".class");
-                    if (jarEntry == null) {
-                        return null;
-                    }
-                    classFileBytes =
-                            Utils.readZipEntryIntoBuffer(jarFile, jarEntry);
-                } catch (IOException ex) {
-                    throw new PrivateException(ex);
-                }
-            }
-            res =
-                    new ClassInfo(classFileBytes, verCode, this, classFileFullPath);
-            pcde.newClassInfo = res;
-        }
-        return res;
-    }
-
-    /**
-     * Returns null if class is compileable (has a .java source) and not recompiled yet, "" if
-     * class has already been recompiled or has been deleted from project, and the class's .jar
-     * name if class comes from a jar, hence is uncompileable.
-     */
-    public String classAlreadyRecompiledOrUncompileable(String className) {
-        PCDEntry pcde = pcd.get(className);
-        if (pcde == null) {
-            //!!!
-            for (String keyName : pcd.keySet()) {
-                PCDEntry entry = pcd.get(keyName);
-                if (entry.className.equals(className)) {
-                    System.out.println("ERROR: inconsistent entry: key = " +
-                            keyName + ", name in entry = " + entry.className);
-                }
-            }
-            //!!!
-            throw internalException(className + " not in project when it should be");
-        }
-        if (pcde.checkResult == PCDEntry.CV_DELETED) {
-            return "";
-        }
-        if (pcde.javaFileFullPath.endsWith(".jar")) {
-            return pcde.javaFileFullPath;
-        } else {
-            return (recompiledJavaFiles.contains(pcde.javaFileFullPath) ? "" : null);
-        }
-    }
-
-    /**
-     * Compiler initialization depends on compiler type specified.
-     * If jcExecApp != null, i.e. an external executable compiler application is used, and nothing has to be done.
-     * If externalApp != null, that is, jmake is called by an external application such as Ant, which
-     * manages compilation in its own way, and also nothing has to be done.
-     * Otherwise, load the compiler class and method (either specified through jcPath, jcMainClass and jcMethod,
-     * or the default one.
-     */
-    public void initializeCompiler(String jcExecApp,
-            String jcPath, String jcMainClass, String jcMethod,
-            Object externalApp, Method externalCompileSourceFilesMethod) {
-        ClassPath.initializeAllClassPaths();
-
-        if (externalApp != null) {
-            this.externalApp = externalApp;
-            this.externalCompileSourceFilesMethod =
-                    externalCompileSourceFilesMethod;
-            return;
-        }
-        if (jcExecApp != null) {
-            this.jcExecApp = jcExecApp;
-            return;
-        }
-
-        if (jcPath == null) {
-            String javaHome = System.getProperty("java.home");
-            // In my tests it ends with '/jre'. Or it could be ending with '/bin' as well? Let's assume it can be both and delete
-            // this latter directory.
-            if (javaHome.endsWith(File.separator + "jre") || javaHome.endsWith(File.separator + "bin")) {
-                javaHome = javaHome.substring(0, javaHome.length() - 4);
-            }
-            jcPath = javaHome + "/lib/tools.jar";
-        }
-        ClassLoader compilerLoader;
-        try {
-            compilerLoader = ClassPath.getClassLoaderForPath(jcPath);
-        } catch (Exception ex) {
-            throw compilerInteractionException("error opening compiler path", ex, 0);
-        }
-
-        if (jcMainClass == null) {
-            jcMainClass = "com.sun.tools.javac.Main";
-        }
-        if (jcMethod == null) {
-            jcMethod = "compile";
-        }
-
-        try {
-            compilerClass = compilerLoader.loadClass(jcMainClass);
-        } catch (ClassNotFoundException e) {
-            throw compilerInteractionException("error loading compiler main class " + jcMainClass, e, 0);
-        }
-
-        Class<?>[] args = new Class<?>[]{String[].class};
-        try {
-            compileMethod = compilerClass.getMethod(jcMethod, args);
-        } catch (Exception e) {
-            throw compilerInteractionException("error getting method com.sun.tools.javac.Main.compile(String args[])", e, 0);
-        }
-    }
-
-    /** Main entrypoint for this class */
-    public void run() {
-        Utils.startTiming(Utils.TIMING_SYNCHRO);
-        synchronizeProjectFilesAndPCD();
-        Utils.stopAndPrintTiming("Synchro", Utils.TIMING_SYNCHRO);
-        Utils.printTiming("of which synchro check file", Utils.TIMING_SYNCHRO_CHECK_JAVA_FILES);
-
-        Utils.startTiming(Utils.TIMING_FIND_UPDATED_JAVA_FILES);
-        findUpdatedJavaAndJarFiles();
-        Utils.stopAndPrintTiming("findUpdatedJavaAndJarFiles", Utils.TIMING_FIND_UPDATED_JAVA_FILES);
-        Utils.printTiming("of which classFileObsoleteOrDeleted", Utils.TIMING_CLASS_FILE_OBSOLETE_OR_DELETED);
-
-        // Let's free some memory
-        projectJavaAndJarFilesArray = null;
-
-        updatedClasses = new LinkedHashSet<String>();
-        dealWithClassesInUpdatedJarFiles();
-
-        int iterNo = 0;
-        int res = 0;
-        while (iterNo == 0 || updatedJavaFiles.size() != 0 || newJavaFiles.size() != 0) {
-            // It may happen that we didn't find any updated or new .java files. However, we still need to enter
-            // this loop because there may be some class files that need compatibility checking. This can happen
-            // either if somebody had recompiled their sources bypassing jmake, or if their checking during the
-            // previous invocation of jmake failed, because their dependent code recompilation failed.
-            if (updatedJavaFiles.size() > 0 || newJavaFiles.size() > 0) {
-                Utils.startTiming(Utils.TIMING_COMPILE);
-                int intermediateRes = recompileUpdatedJavaFiles();
-                Utils.stopAndPrintTiming("Compile", Utils.TIMING_COMPILE);
-                if (intermediateRes != 0) {
-                    res = intermediateRes;
-                }
-            }
-
-            Utils.startTiming(Utils.TIMING_PDBUPDATE);
-            // New classes can be added to pdb only if compilation was successful, i.e. the new project version is consistent.
-            if (iterNo++ == 0 && res == 0) {
-                findClassFilesForNewJavaAndJarFiles();
-                findClassFilesForUpdatedJavaFiles();
-                dealWithNestedClassesForUpdatedJavaFiles();
-            }
-            Utils.stopAndPrintTiming("Entering new classes in PDB", Utils.TIMING_PDBUPDATE);
-
-            updatedJavaFiles.clear();
-            newJavaFiles.clear();
-
-            Utils.startTiming(Utils.TIMING_FIND_UPDATED_CLASSES);
-            findUpdatedClasses();
-            Utils.stopAndPrintTiming("Find updated classes", Utils.TIMING_FIND_UPDATED_CLASSES);
-
-            Utils.startTiming(Utils.TIMING_CHECK_UPDATED_CLASSES);
-            checkDeletedClasses();
-            checkUpdatedClasses();
-            Utils.stopAndPrintTiming("Check updated classes", Utils.TIMING_CHECK_UPDATED_CLASSES);
-
-            updatedClasses = new LinkedHashSet<String>();
-            if (ClassPath.getVirtualPath() != null) {
-                if (res != 0)
-                    break;
-            }
-        }
-
-        Utils.startTiming(Utils.TIMING_PDBWRITE);
-        updateClassFilesInfoInPCD(res);
-        pcdc.save();
-        Utils.stopAndPrintTiming("PDB write", Utils.TIMING_PDBWRITE);
-
-        if (res != 0) {
-            throw compilerInteractionException("compilation error(s)", null, res);
-        }
-    }
-
-    /**
-     * Find the newly-created class files for existing java files.
-     */
-    private void findClassFilesForUpdatedJavaFiles() {
-        if (dependencyFile == null)
-            return;
-
-        Set<String> allClasses = new HashSet<String>();
-
-        Map<String, List<String>> dependencies = parseDependencyFile();
-        for (String file : updatedJavaFiles) {
-            List<String> myDeps = dependencies.get(file);
-            if (myDeps != null) {
-                PCDEntry parent = getNamedPCDE(file, dependencies);
-                for (String dependency : myDeps) {
-                    allClasses.add(dependency);
-                    if (pcd.containsKey(dependency))
-                        continue;
-                    findClassFileOnFilesystem(file, parent, dependency, false);
-                }
-            }
-        }
-        for (Map.Entry<String, PCDEntry> entry : pcd.entrySet()) {
-            String cls = entry.getKey();
-            if (!allClasses.contains(cls)) {
-                PCDEntry pcde = entry.getValue();
-                if (updatedJavaFiles.contains(pcde.javaFileFullPath)) {
-                    deletedClasses.add(cls);
-                }
-            }
-        }
-    }
-
-    public String[] getAllUpdatedClassesAsStringArray() {
-        String[] res = new String[allUpdatedClasses.size()];
-        int i = 0;
-        for (String updatedClass : allUpdatedClasses) {
-            res[i++] = updatedClass.replace('/', '.');
-        }
-        return res;
-    }
-
-    /**
-     * Synchronize projectJavaAndJarFilesArray and PCD, i.e. leave only those entries in the PCD which have their
-     * .java (.jar) files in projectJavaAndJarFilesArray. New .java files in projectJavaAndJarFilesArray (i.e. those
-     * for which there are no entries in the PCD yet) are added to newJavaFiles; new .jar files are added to newJarFiles.
-     * Alternatively, just use the supplied arrays of added and deleted .java and .jar files.
-     *
-     * For entries whose .java files are not in the PCD anymore, try to delete .class files. We need to do that before
-     * compilation to avoid the situation when a .java file is removed but compilation succeeds because the .class file
-     * is still there.
-     *
-     * Unfortunately, we also need to delete all class files for non-nested classes whose names differ from their .java
-     * file name, because we can't tell when they've been removed from their .java files -- but it's only safe to do this
-     * for files that originate from java files that we're compiling this round.
-     *
-     * Upon return from this method, all of the .java and .jar files in the PCD are known to exist.
-     */
-    private void synchronizeProjectFilesAndPCD() {
-        if (projectJavaAndJarFilesArray != null) {
-            Set<String> pcdJavaFilesSet = new LinkedHashSet<String>(pcd.size() * 3 / 2);
-            for(PCDEntry entry : entries()) {
-                pcdJavaFilesSet.add(entry.javaFileFullPath);
-            }
-
-            Set<String> canonicalPJF =
-                    new LinkedHashSet<String>(projectJavaAndJarFilesArray.length * 3 / 2);
-
-            // Add .java files that are not in PCD to newJavaFiles; add .jar files that are not in PCD to newJarFiles.
-            for (int i = 0; i < projectJavaAndJarFilesArray.length; i++) {
-                String projFileName = projectJavaAndJarFilesArray[i];
-                Utils.startTiming(Utils.TIMING_SYNCHRO_CHECK_TMP);
-                File projFile = Utils.checkFileForName(projFileName);
-                Utils.stopAndAddTiming(Utils.TIMING_SYNCHRO_CHECK_TMP, Utils.TIMING_SYNCHRO_CHECK_JAVA_FILES);
-                if (projFile == null) {
-                    throw new PrivateException(new FileNotFoundException("specified source file " + projFileName + " not found."));
-                }
-                // The main reason for using getAbsolutePath() instead of more reliable getCanonicalPath() is the fact that
-                // sometimes users may name the actual files containing Java code in some custom way, and give javac/jmake
-                // symbolic links to these files (that have correct .java names) instead. getCanonicalPath(), however, returns the
-                // real (i.e. user custom) file name, which will confuse our test below and then javac.
-                String absoluteProjFileName = projFile.getAbsolutePath();
-                // On Windows, make sure the drive letter is always in lower case
-                if (backSlashFileSeparator) {
-                    absoluteProjFileName =
-                            Utils.convertDriveLetterToLowerCase(absoluteProjFileName);
-                }
-                canonicalPJF.add(absoluteProjFileName);
-                if (!pcdJavaFilesSet.contains(absoluteProjFileName)) {
-                    if (absoluteProjFileName.endsWith(".java")) {
-                        newJavaFiles.add(absoluteProjFileName);
-                    } else if (absoluteProjFileName.endsWith(".jar")) {
-                        newJarFiles.add(absoluteProjFileName);
-                    } else {
-                        throw new PrivateException(new PublicExceptions.InvalidSourceFileExtensionException("specified source file " + projFileName + " has an invalid extension (not .java or .jar)."));
-                    }
-                }
-            }
-
-            // Find the entries containing .java or .jar files that are not in project anymore
-            for (Entry<String, PCDEntry> entry : pcd.entrySet()) {
-                String key = entry.getKey();
-                PCDEntry e = entry.getValue();
-                e.oldClassInfo.restorePCDM(this);
-                if (canonicalPJF.contains(e.javaFileFullPath)) {
-                    if (e.isPackagePrivateClass()) {
-                        initializeClassFileFullPath(e);
-                        new File(e.classFileFullPath).delete();
-                    }
-                } else {
-                    if (ClassPath.getVirtualPath() == null) {
-                        deletedClasses.add(key);
-                    } else {
-                        // Okay, not found locally, but virtual path was defined, so try it now....
-                        if ( (e.oldClassFileFingerprint == projectJavaAndJarFilesArray.length &&
-                                newJavaFiles.size() == 0) ||
-                                Utils.checkFileForName(e.javaFileFullPath) != null)
-                        {
-                            e.checkResult = PCDEntry.CV_NEWER_FOUND_NEARER;
-                            e.oldClassFileFingerprint = projectJavaAndJarFilesArray.length;
-                        }
-                        else
-                        {
-                            String classFound = null;
-                            String sourceFound = null;
-                            // Find source and class file via virtual path
-                            String path = ClassPath.getVirtualPath();
-                            // TODO(Eric Ayers): IntelliJ static analysis shows several useless
-                            // expressions that make this loop a no-op.
-                            for (StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
-                                !(classFound != null && sourceFound != null) && st.hasMoreTokens();)
-                            {
-                                String fullPath = st.nextToken()+File.separator+e.className;
-                                if (sourceFound != null && new File(fullPath+".java").exists())
-                                {
-                                    sourceFound = fullPath + ".java";
-                                }
-                                if (classFound != null && new File(fullPath+".class").exists())
-                                {
-                                    classFound = fullPath + ".class";
-                                }
-                            }
-                            // TODO(Eric Ayers): IntelliJ static analysis shows that this expression
-                            // is always true.
-                            if (classFound == null)
-                            {
-                                deletedClasses.add(key);
-                                if (e.javaFileFullPath.endsWith(".jar"))
-                                {
-                                    deletedJarFiles.add(e.javaFileFullPath);
-                                }
-                                else
-                                {
-                                    initializeClassFileFullPath(e);
-                                    (new File(e.classFileFullPath)).delete();
-                                }
-                            }
-                            else if (sourceFound != null)
-                            {
-                                newJavaFiles.add(sourceFound);
-                                e.checkResult = PCDEntry.CV_NEWER_FOUND_NEARER;
-                                e.oldClassFileFingerprint = projectJavaAndJarFilesArray.length;
-                            }
-                            else
-                            {
-                                classFound = classFound.replace('/', File.separatorChar);
-                                throw new PrivateException(new FileNotFoundException("deleted class " + classFound + " still exists."));
-                            }
-                        }
-                    }
-                    if (e.javaFileFullPath.endsWith(".jar")) {
-                        deletedJarFiles.add(e.javaFileFullPath);
-                    } else {  // Try to delete a class file for the removed project class.
-                        initializeClassFileFullPath(e);
-                        (new File(e.classFileFullPath)).delete();
-                    }
-                }
-            }
-        } else { // projectJavaAndJarFilesArray == null - use supplied arrays of added and removed .java and .jar files
-            if (addedJavaAndJarFilesArray != null) {
-                for (String fileName : addedJavaAndJarFilesArray) {
-                    fileName = fileName.intern();
-                    if (fileName.endsWith(".java")) {
-                        newJavaFiles.add(fileName);
-                    } else if (fileName.endsWith(".jar")) {
-                        newJarFiles.add(fileName);
-                    } else {
-                        throw new PrivateException(new PublicExceptions.InvalidSourceFileExtensionException(
-                            "specified source file " + fileName + " has an invalid extension (not .java or .jar)."));
-                    }
-                }
-            }
-
-            Set<String> removedJavaAndJarFilesSet = null;
-            if (removedJavaAndJarFilesArray != null) {
-                removedJavaAndJarFilesSet = new LinkedHashSet<String>();
-                for (String fileName : removedJavaAndJarFilesArray) {
-                    fileName = fileName.intern();
-                    removedJavaAndJarFilesSet.add(fileName);
-                    if (fileName.endsWith(".jar")) {
-                        deletedJarFiles.add(fileName);
-                    }
-                }
-            }
-
-            for (Entry<String, PCDEntry> entry : pcd.entrySet()) {
-                String key = entry.getKey();
-                PCDEntry e = entry.getValue();
-                e.oldClassInfo.restorePCDM(this);
-                if (removedJavaAndJarFilesSet != null &&
-                        removedJavaAndJarFilesSet.contains(e.javaFileFullPath)) {
-                    deletedClasses.add(key);
-                    if (!e.javaFileFullPath.endsWith(".jar")) {  // Try to delete a class file for the removed project class.
-                        initializeClassFileFullPath(e);
-                        (new File(e.classFileFullPath)).delete();
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * In the end of run, update the information in the project database for the class files which have
-     * been updated and checked, or deleted. If compilationResult == 0, i.e. all recompilations were
-     * successful, information for new versions of all of the classes is made permanent, and entries
-     * for deleted classes are removed permanently. Otherwise, information is updated only for those
-     * classes whose old and new versions were found source compatible.
-     */
-    private void updateClassFilesInfoInPCD(int compilationResult) {
-        // If the project appears to be inconsistent after changes, make a preliminary pass that will deal with enclosing
-        // classes for deleted nested classes. The problem with them can be as follows: we delete a nested class C$X,
-        // which is still referenced from somewhere. However, C has not changed at all or at least incompatibly, and
-        // thus we update its PCDEntry, which now does not reference C$X. Other parts of jmake require that a nested
-        // class is always referenced from its directly enclusing class, thus to keep the PCD consistent we have to remove
-        // C$X from the PCD. On the next invocation of jmake, C$X is not in the PDB at all, and thus any classes that
-        // may still reference it and have not been updated are not checked => project becomes inconsistent. We could do
-        // better by immediately marking enclosing classes incompatible once we detect that a deleted nested class is
-        // really referenced from somewhere, but the solution below seems to be more robust.
-        if (compilationResult != 0) {
-            for (String className : updatedAndCheckedClasses) {
-                PCDEntry entry = pcd.get(className);
-                if (entry.checkResult == PCDEntry.CV_DELETED &&
-                        !"".equals(entry.oldClassInfo.directlyEnclosingClass)) {
-                    PCDEntry enclEntry =
-                            pcd.get(entry.oldClassInfo.directlyEnclosingClass);
-                    enclEntry.checkResult = PCDEntry.CV_INCOMPATIBLE;
-                }
-            }
-        }
-
-        for (String className : updatedAndCheckedClasses) {
-            PCDEntry entry = pcd.get(className);
-            if (entry.checkResult == PCDEntry.CV_UNCHECKED) {
-                continue;
-            }
-            if (ClassPath.getVirtualPath() != null) {
-                if (entry.checkResult == PCDEntry.CV_NEWER_FOUND_NEARER) {
-                    continue;
-                }
-            }
-            if (entry.checkResult == PCDEntry.CV_DELETED) {
-                if (compilationResult == 0) {
-                    pcd.remove(className);  // Only if consistency checking is ok, a deleted class can be safely removed from the PCD
-                }
-            } else if (entry.checkResult == PCDEntry.CV_COMPATIBLE ||
-                    entry.checkResult == PCDEntry.CV_NEW ||
-                    (entry.checkResult == PCDEntry.CV_INCOMPATIBLE && compilationResult == 0)) {
-                if (entry.newClassInfo == null) {  // "Safety net" for the (hopefully unlikely) case we overlooked something before...
-                    Utils.printWarningMessage("Warning: internal information inconsistency detected during pdb updating");
-                    Utils.printWarningMessage(Utils.REPORT_PROBLEM);
-                    Utils.printWarningMessage("Class name: " + className);
-                    if (entry.checkResult == PCDEntry.CV_NEW) {
-                        pcd.remove(className);
-                    } else {
-                        continue;
-                    }
-                }
-                entry.oldClassFileLastModified = entry.newClassFileLastModified;
-                entry.oldClassFileFingerprint = entry.newClassFileFingerprint;
-                entry.oldClassInfo = entry.newClassInfo;
-            }
-        }
-    }
-
-    /**
-     * Find all .java files on the filesystem,  for which the .class file does not exist
-     * or is newer than the .java file. Also find all .jar files for which the timestamp
-     * has changed. Alternatively, just use the supplied array of updated .java/.jar files.
-     */
-    private void findUpdatedJavaAndJarFiles() {
-        boolean projectSpecifiedAsAllSources =
-                projectJavaAndJarFilesArray != null;
-        for (PCDEntry entry : entries()) {
-            if (deletedClasses.contains(entry.className)) {
-                continue;
-            }
-            if (entry.javaFileFullPath.endsWith(".java")) {
-                initializeClassFileFullPath(entry);
-                if (projectSpecifiedAsAllSources) {
-                    if (ClassPath.getVirtualPath() != null) {
-                        String paths[] = ClassPath.getVirtualPath().split(File.pathSeparator);
-                        String tmpClassName = entry.className;
-                        tmpClassName = tmpClassName.replaceAll("\\Q$\\E.*$", "");
-                        for (int i=0; i<paths.length; i++) {
-                            String tmpFilename = paths[i] + File.separator + tmpClassName + ".java";
-                            File tmpFile = new File(tmpFilename);
-                            if (tmpFile.exists()) {
-                                entry.javaFileFullPath = tmpFile.getAbsolutePath();
-                                break;
-                            }
-                        }
-                    }
-                    Utils.startTiming(Utils.TIMING_CLASS_FILE_OBSOLETE_TMP);
-                    if (classFileObsoleteOrDeleted(entry)) {
-                        updatedJavaFiles.add(entry.javaFileFullPath);
-                    }
-                    Utils.stopAndAddTiming(Utils.TIMING_CLASS_FILE_OBSOLETE_TMP, Utils.TIMING_CLASS_FILE_OBSOLETE_OR_DELETED);
-                }
-                entry.checked = true;
-            } else {  // Class coming from a .jar file. Mark this entry as checked only if its JAR hasn't changed
-                if (projectJavaAndJarFilesArray != null) {
-                    entry.checked = !checkJarFileForUpdate(entry);
-                }
-            }
-        }
-
-        // Lists of updated/added/deleted source files specified instead of a full list of project sources
-        if (!projectSpecifiedAsAllSources && updatedJavaAndJarFilesArray != null) {
-            for (int i = 0; i < updatedJavaAndJarFilesArray.length; i++) {
-                if (updatedJavaAndJarFilesArray[i].endsWith(".java")) {
-                    updatedJavaFiles.add(updatedJavaAndJarFilesArray[i]);
-                } else {
-                    updatedJarFiles.add(updatedJavaAndJarFilesArray[i]);
-                }
-            }
-        }
-    }
-
-    private boolean classFileObsoleteOrDeleted(PCDEntry entry) {
-        if (ClassPath.getVirtualPath() != null) {
-            File file1 = new File(entry.javaFileFullPath);
-            if (!file1.exists())
-                throw new PrivateException(new FileNotFoundException("specified source file " +
-                        entry.javaFileFullPath + " not found."));
-            if (file1.lastModified() < entry.oldClassFileLastModified)
-            {
-                return false;
-            }
-        }
-        File classFile = Utils.checkFileForName(entry.classFileFullPath);
-        if (classFile == null || !classFile.exists()) {
-            return true; // Class file has been deleted
-        }
-        File javaFile = new File(entry.javaFileFullPath); // Guaranteed to exist at this point
-        if (classFile.lastModified() <= javaFile.lastModified()) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean checkJarFileForUpdate(PCDEntry entry) {
-        String jarFileName = entry.javaFileFullPath;
-        if (stableJarFiles.contains(jarFileName)) {
-            return false;
-        } else if (updatedJarFiles.contains(jarFileName) ||
-                newJarFiles.contains(jarFileName) ||
-                deletedJarFiles.contains(jarFileName)) {
-            return true;
-        } else {
-            File jarFile = new File(jarFileName); // Guaranteed to exist at this point.
-            if (entry.oldClassFileLastModified != jarFile.lastModified()) {
-                updatedJarFiles.add(jarFileName);
-                return true;
-            } else {
-                stableJarFiles.add(jarFileName);
-                return false;
-            }
-        }
-    }
-
-    public int recompileUpdatedJavaFiles() {
-        if (externalApp != null) {
-            return recompileUpdatedJavaFilesUsingExternalMethod();
-        } else {
-            return recompileUpdatedJavaFilesOurselves();
-        }
-    }
-
-    private int recompileUpdatedJavaFilesOurselves() {
-        int filesNo = updatedJavaFiles.size() + newJavaFiles.size();
-        int addArgsNo = javacAddArgs.size();
-        int argsNo = addArgsNo + filesNo + 2;
-        String compilerBootClassPath, compilerExtDirs;
-        if ((compilerBootClassPath = ClassPath.getCompilerBootClassPath()) != null) {
-            argsNo += 2;
-        }
-        if ((compilerExtDirs = ClassPath.getCompilerExtDirs()) != null) {
-            argsNo += 2;
-        }
-        if (jcExecApp != null) {
-            argsNo++;
-        }
-        String args[] = new String[argsNo];
-        int pos = 0;
-        if (jcExecApp != null) {
-            args[pos++] = jcExecApp;
-        }
-        for (int i = 0; i < addArgsNo; i++) {
-            args[pos++] = javacAddArgs.get(i);
-        }
-        args[pos++] = "-classpath";
-        args[pos++] = ClassPath.getCompilerUserClassPath();
-        if (compilerBootClassPath != null) {
-            args[pos++] = "-bootclasspath";
-            args[pos++] = compilerBootClassPath;
-        }
-        if (compilerExtDirs != null) {
-            args[pos++] = "-extdirs";
-            args[pos++] = compilerExtDirs;
-        }
-        if (!newProject) {
-            Utils.printInfoMessage("Recompiling source files:");
-        }
-        for (String javaFileFullPath : updatedJavaFiles) {
-            if (!newProject) {
-                Utils.printInfoMessage(javaFileFullPath);
-            }
-            recompiledJavaFiles.add(args[pos++] = javaFileFullPath);
-        }
-        for (int j = 0; j < newJavaFiles.size(); j++) {
-            String javaFileFullPath = newJavaFiles.get(j);
-            if (!newProject) {
-                Utils.printInfoMessage(javaFileFullPath);
-            }
-            recompiledJavaFiles.add(args[pos++] = javaFileFullPath);
-        }
-
-        if (jcExecApp == null) {  // Executing javac or some other compiler within the same JVM
-            Object reflectArgs[] = new Object[1];
-            reflectArgs[0] = args;
-            try {
-                Object dummy = compilerClass.newInstance();
-                Integer res = (Integer) compileMethod.invoke(dummy, reflectArgs);
-                return res.intValue();
-            } catch (Exception e) {
-                throw compilerInteractionException("exception thrown when trying to invoke the compiler method", e, 0);
-            }
-        } else {  // Executing an external Java compiler, such as jikes
-            int exitCode = 0;
-            try {
-                Process p = Runtime.getRuntime().exec(args);
-                InputStream pErr = p.getErrorStream();
-                InputStream pOut = p.getInputStream();
-                boolean terminated = false;
-
-                while (!terminated) {
-                    try {
-                        exitCode = p.exitValue();
-                        terminated = true;
-                    } catch (IllegalThreadStateException itse) { // Process not yet terminated, wait for some time
-                        Utils.ignore(itse);
-                        Utils.delay(100);
-                    }
-                    try {
-                        Utils.readAndPrintBytesFromStream(pErr, System.err);
-                        Utils.readAndPrintBytesFromStream(pOut, System.out);
-                    } catch (IOException ioe1) {
-                        throw compilerInteractionException("I/O error when reading the compiler application output", ioe1, exitCode);
-                    }
-                }
-                return exitCode;
-            } catch (IOException ioe2) {
-                throw compilerInteractionException("I/O error when trying to invoke the compiler application", ioe2, exitCode);
-            }
-        }
-    }
-
-    /** Execution under complete control of external app - use externally supplied method to recompile classes */
-    private int recompileUpdatedJavaFilesUsingExternalMethod() {
-        int filesNo = updatedJavaFiles.size() + newJavaFiles.size();
-        String[] fileNames = new String[filesNo];
-        int i = 0;
-        for (String updatedFile : updatedJavaFiles) {
-            recompiledJavaFiles.add(fileNames[i] = updatedFile);
-        }
-        for (int j = 0; j < newJavaFiles.size(); j++) {
-            recompiledJavaFiles.add(fileNames[i++] = newJavaFiles.get(j));
-        }
-
-        try {
-            Integer res =
-                    (Integer) externalCompileSourceFilesMethod.invoke(externalApp, new Object[]{fileNames});
-            return res.intValue();
-        } catch (IllegalAccessException e1) {
-            throw compilerInteractionException("compiler method is not accessible", e1, 0);
-        } catch (IllegalArgumentException e2) {
-            throw compilerInteractionException("illegal arguments passed to compiler method", e2, 0);
-        } catch (InvocationTargetException e3) {
-            throw compilerInteractionException("exception when executing the compiler method", e3, 0);
-        }
-    }
-
-    /**
-     * For each .java file from newJavaFiles, find all of the .class files, the names of which we can
-     * logically deduce (a top-level class with the same name, and all of the nested classes),
-     * and put the info on them into the PCD. Also include any class files from the dependencyFile,
-     * if any. For each .jar file from newJarFiles, find all of the .class files in that archive and
-     * put info on them into the PCD.
-     */
-    private void findClassFilesForNewJavaAndJarFiles() {
-        for (String javaFileFullPath : newJavaFiles) {
-            PCDEntry pcde =
-                    findClassFileOnFilesystem(javaFileFullPath, null, null, false);
-
-            if (pcde == null) {
-                // .class file not found - possible compilation error
-                if (missingClassIsOk(javaFileFullPath)) {
-                    continue;
-                } else {
-                    throw new PrivateException(new PublicExceptions.ClassNameMismatchException(
-                            "Could not find class file for " + javaFileFullPath));
-                }
-            }
-            Set<String> entries = new HashSet<String>();
-            if (pcde.checkResult == PCDEntry.CV_NEW) {  // It's really a new .java file, not a moved one
-                entries.addAll(findAndUpdateAllNestedClassesForClass(pcde, false));
-            } else {
-                entries.addAll(findAndUpdateAllNestedClassesForClass(pcde, true));
-            }
-            entries.add(pcde.className);
-            if (dependencyFile != null) {
-                Map<String, List<String>> dependencies = parseDependencyFile();
-                List<String> myDeps = dependencies.get(javaFileFullPath);
-                if (myDeps != null) {
-                    for (String dependency : myDeps) {
-                        if (entries.contains(dependency))
-                            continue;
-                        findClassFileOnFilesystem(javaFileFullPath, pcde,
-                                dependency, false);
-                    }
-                }
-            }
-        }
-
-        for (String newJarFile : newJarFiles) {
-            processAllClassesFromJarFile(newJarFile);
-        }
-    }
-
-    /**
-     * Parse an extra dependency file.  The format of the file is a series of lines,
-     * each consisting of:
-     * SourceFileName.java -> ClassName
-     * (these file names are relative to destDir)
-     */
-    private Map<String, List<String>> parseDependencyFile() {
-        if (!destDirSpecified)
-            throw new RuntimeException("Dependency files require destDir");
-        if (extraDependencies != null)
-            return extraDependencies;
-        BufferedReader in = null;
-        try {
-            extraDependencies = new HashMap<String, List<String>>();
-            in = new BufferedReader(new FileReader(dependencyFile));
-            int lineNumber = 0;
-            while (true) {
-                lineNumber ++;
-                String line = in.readLine();
-                if (line == null)
-                    break;
-                String[] parts = line.split("->");
-                if (parts.length != 2) {
-                    throw new RuntimeException("Failed to parse line " + lineNumber + " of " + dependencyFile
-                                               + ". Expected {foo.java} -> {classname}.");
-                }
-                String src = parts[0].trim();
-                src = new File(destDir, src).getCanonicalPath();
-                String cls = parts[1].trim();
-                List<String> classes = extraDependencies.get(src);
-                if (classes == null) {
-                    classes = new ArrayList<String>();
-                    extraDependencies.put(src, classes);
-                }
-                cls = cls.substring(0, cls.length() - 6); // strip trailing ".class"
-                classes.add(cls);
-            }
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        } finally {
-            if (in != null)
-                try {
-                    in.close();
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-        }
-        return extraDependencies;
-    }
-
-    /**
-     * In most cases we want to fail the build if a class cannot be found.
-     *
-     * However there is one common valid case where a .java file might not contain
-     * a class: package-info.java files.
-     *
-     * See this doc for more info: http://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html
-     */
-    private boolean missingClassIsOk(String javaFileFullPath) {
-        return javaFileFullPath != null && "package-info.java".equals(new File(javaFileFullPath).getName());
-    }
-
-    /**
-     * Find the .class file for the given javaFileFullPath and create a new PCDEntry for it.
-     * If enclosingClassPCDE is null, the named top-level class for the given .java file is looked up.
-     * Otherwise, the specified class specified by nestedClassFullName is looked up.
-     */
-    private PCDEntry findClassFileOnFilesystem(String javaFileFullPath, PCDEntry enclosingClassPCDE, String nestedClassFullName, boolean isNested) {
-        String classFileFullPath = null;
-        String fullClassName;
-        File classFile = null;
-
-        if (enclosingClassPCDE == null) { // Looking for a top-level class. May need to locate an appropriate directory.
-            // Remove the ".java" suffix. A Windows disk-name prefix, such as 'c:', will be cut off later automatically
-            fullClassName =
-                    javaFileFullPath.substring(0, javaFileFullPath.length() - 5);
-            if (destDirSpecified) {
-                // Search for the .class file. We first assume the longest possible name. In case of failure,
-                // we cut the assumed top-most package from it and repeat the search.
-                while (classFile == null) {
-                    classFileFullPath = destDir + fullClassName + ".class";
-                    classFile = Utils.checkFileForName(classFileFullPath);
-                    if (classFile == null) {
-                        int cutIndex = fullClassName.indexOf(File.separatorChar);
-                        if (cutIndex == -1) {
-                            // Most probably, there was an error during compilation of this file.
-                            // This does not prevent us from continuing.
-                            Utils.printWarningMessage("Warning: unable to find .class file corresponding to source " + javaFileFullPath + ": expected " + classFileFullPath);
-
-                            return null;
-                        }
-                        fullClassName = fullClassName.substring(cutIndex + 1);
-                    }
-                }
-            } else {
-                classFileFullPath = fullClassName + ".class";
-                classFile = Utils.checkFileForName(classFileFullPath);
-                if (classFile == null) {
-                    Utils.printWarningMessage("Warning: unable to find .class file corresponding to source " + javaFileFullPath);
-                    return null;
-                }
-            }
-        } else {  // Looking for a nested class, which always sits in the same directory as its enclosing class
-            classFileFullPath =
-                    Utils.getClassFileFullPathForNestedClass(enclosingClassPCDE.classFileFullPath, nestedClassFullName);
-            classFile = Utils.checkFileForName(classFileFullPath);
-            if (classFile == null) {
-                Utils.printWarningMessage("Warning: unable to find .class file corresponding to nested class " + nestedClassFullName);
-                return null;
-            }
-            fullClassName = nestedClassFullName;
-        }
-
-        if (backSlashFileSeparator) {
-            fullClassName = fullClassName.replace(File.separatorChar, '/');
-        }
-
-        byte classFileBytes[] = Utils.readFileIntoBuffer(classFile);
-        ClassInfo classInfo =
-                new ClassInfo(classFileBytes, ClassInfo.VER_NEW, this, classFileFullPath);
-        if (isNested) {
-            if (!classInfo.directlyEnclosingClass.equals(enclosingClassPCDE.newClassInfo.name)) {
-                // Check if the above strings are like A and A$1. If so, there is actually no problem - the correct
-                // answer is A$1. The reason why just A was determined as a directly enclosing class when parsing
-                // class classInfo is due to the ambiguous interpretation of names like A$1$B. Such a name may mean
-                // (1) a non-member local nested class B of A, or (2) a member class B of an anonymous nested class A$1.
-                // When parsing any non-toplevel class, the first interpretation is always used.
-                // NOTE FOR JDK 1.5 - starting from this version, there is no ambiguity anymore.
-                // (1) will be called A$1B, and (2) will still be A$1$B
-                String a = classInfo.directlyEnclosingClass;
-                String ad1 = enclosingClassPCDE.newClassInfo.name;
-                if (!((classInfo.javacTargetRelease == Utils.JAVAC_TARGET_RELEASE_OLDEST) &&
-                        (ad1.startsWith(a + "$") && Character.isDigit(ad1.charAt(a.length() + 1))))) {
-                    throw new PrivateException(new PublicExceptions.ClassFileParseException(
-                            "Enclosing class names for class " + classInfo.name + " don't match:\n" +
-                            classInfo.directlyEnclosingClass + " and " + enclosingClassPCDE.newClassInfo.name));
-                }
-            }
-        }
-
-        // If dest dir was specified, check if the deduced name is equal to the one in this class (in this case
-        // they should necessarily match). Otherwise, without parsing the .java file, we can't reliably say what the
-        // full class name (actually, its package part) should be - so we just note the name.
-        if (destDirSpecified) {
-            if (!fullClassName.equals(classInfo.name)) {
-                throw new PrivateException(new PublicExceptions.ClassNameMismatchException(
-                        "Error: deduced class name is different from the real one for source " +
-                        javaFileFullPath + "\n" + fullClassName + " and " + classInfo.name));
-            }
-        } else {
-            fullClassName = classInfo.name;
-        }
-
-        if (enclosingClassPCDE != null) {
-            javaFileFullPath = enclosingClassPCDE.javaFileFullPath;
-        }
-        long classFileLastMod = classFile.lastModified();
-        long classFileFP = computeFP(classFileBytes);
-
-        if (pcd.containsKey(fullClassName)) {
-            PCDEntry pcde = pcd.get(fullClassName);
-            // If this entry has already been checked, it's a second entry for the same class, which is illegal.
-            if (pcde.checkResult == PCDEntry.CV_NEWER_FOUND_NEARER) {
-                // Newer copy of same file found in closer layer
-                // Reset to CV_UNCHECKED and skip redundnacy check
-                // as we know this would be redundant
-                pcde.checkResult = PCDEntry.CV_UNCHECKED;
-            } else {
-                if (pcde.checked) {
-                    throw new PrivateException(new PublicExceptions.DoubleEntryException(
-                            "Two entries for class " + classInfo.name + " detected: " + pcde.javaFileFullPath + " and " + javaFileFullPath));
-                }
-            }
-            // Otherwise, it means that the .java file for this class has been moved. jmake initially interprets
-            // a new source file name as a new class, and it's only at this point that we can actually see that it was
-            // only a move. We update javaFileFullPath for nested classes after we return from here.
-            pcde.javaFileFullPath = javaFileFullPath;
-            pcde.classFileFullPath = classFileFullPath;
-            pcde.newClassInfo = classInfo;
-            if (deletedClasses.contains(fullClassName)) {
-                deletedClasses.remove(fullClassName);
-            }
-            return pcde;
-        }
-
-        PCDEntry pcde = new PCDEntry(fullClassName,
-                javaFileFullPath,
-                classFileFullPath, classFileLastMod, classFileFP,
-                classInfo);
-        pcde.checkResult = PCDEntry.CV_NEW;          // So that later it's promoted into oldClassInfo correctly
-        updatedAndCheckedClasses.add(fullClassName); // So that the above happens
-        pcd.put(fullClassName, pcde);
-        return pcde;
-    }
-
-    /**
-     * For the given class, find all direct nested classes (which may include reading their .class files from the
-     * class path) and set their access flags (contained in this, enclosing class, object) appropriately. If
-     * this class is a one coming from a .java source, repeat the procedure for each nested class in turn.
-     * Otherwise, i.e. if a class comes from a .jar, don't bother, since we will come across each of these
-     * classes anyway - when scanning their .jar. If 'move' parameter is true, it means that this method is called for
-     * a class that is not new, but has been moved (and possibly updated).
-     */
-    private Set<String> findAndUpdateAllNestedClassesForClass(PCDEntry pcde, boolean move) {
-        ClassInfo classInfo = pcde.newClassInfo;
-        if (classInfo.nestedClasses == null) {
-            return Collections.emptySet();
-        }
-        Set<String> entries = new LinkedHashSet<String>();
-        String nestedClasses[] = classInfo.nestedClasses;
-        String javaFileFullPath = pcde.javaFileFullPath;
-        String enclosingClassFileFullPath = pcde.classFileFullPath;
-        boolean isJavaSourceFile = javaFileFullPath.endsWith(".java");
-
-        for (int i = 0; i < nestedClasses.length; i++) {
-            PCDEntry nestedPCDE = pcd.get(nestedClasses[i]);
-            if (nestedPCDE == null) {
-                if (isJavaSourceFile) {
-                    nestedPCDE =
-                            findClassFileOnFilesystem(null, pcde, nestedClasses[i], true);
-                }
-                // For classes that come from a .jar, pcde should already be there. Otherwise this class just doesn't exist.
-                if (nestedPCDE == null) {
-                    // Probably a compilation error, such that enclosing class is compiled but nested is not.
-                    throw new PrivateException(new PublicExceptions.ClassNameMismatchException(
-                            "Could not find class file for " + pcde.toString()));
-                }
-            }
-            if (move) {
-                if (deletedClasses.contains(nestedClasses[i])) {
-                    deletedClasses.remove(nestedClasses[i]);
-                }
-                nestedPCDE.javaFileFullPath = javaFileFullPath;
-                if (javaFileFullPath.endsWith(".java")) {
-                    nestedPCDE.classFileFullPath =
-                            Utils.getClassFileFullPathForNestedClass(enclosingClassFileFullPath, nestedClasses[i]);
-                } else {
-                    nestedPCDE.classFileFullPath = javaFileFullPath;
-                }
-            }
-            if (nestedPCDE.newClassInfo == null) {
-                getClassInfoForPCDEntry(ClassInfo.VER_NEW, nestedPCDE);
-            }
-            nestedPCDE.newClassInfo.accessFlags =
-                    pcde.newClassInfo.nestedClassAccessFlags[i];
-            nestedPCDE.newClassInfo.isNonMemberNestedClass =
-                    pcde.newClassInfo.nestedClassNonMember[i];
-
-            entries.add(nestedPCDE.className);
-            entries.addAll(findAndUpdateAllNestedClassesForClass(nestedPCDE, move));
-        }
-        return entries;
-    }
-
-    /**
-     * Take care of new nested classes that could have been generated from already existing .java sources,
-     * and of nested classes that do not exist anymore because they were deleted from these sources.
-     */
-    private void dealWithNestedClassesForUpdatedJavaFiles() {
-        if (updatedJavaFiles.size() == 0) {
-            return;
-        }
-
-        // First put PCDEntries for all updated classes that have nested classes into a temporary list.
-        // That's because we can then find new nested classes, which we will need to add to the PCD, which
-        // may probably conflict with us still iterating over it.
-        List<PCDEntry> updatedEntries = new ArrayList<PCDEntry>();
-        for (PCDEntry pcde : entries()) {
-            if (pcde.checkResult == PCDEntry.CV_NEW) {
-                continue;  // This class has just been added to the PCD
-            }
-            if (updatedJavaFiles.contains(pcde.javaFileFullPath)) {
-                ClassInfo oldClassInfo = pcde.oldClassInfo;
-                ClassInfo newClassInfo =
-                        getClassInfoForPCDEntry(ClassInfo.VER_NEW, pcde);
-                if (newClassInfo == null) {
-                    deletedClasses.add(pcde.className);
-                    continue; // Class file deleted then not re-created due to a compilation error somewhere.
-                }
-                if (oldClassInfo.nestedClasses != null || newClassInfo.nestedClasses != null) {
-                    updatedEntries.add(pcde);
-                }
-            }
-        }
-
-        if (dependencyFile != null) {
-            Map<String, List<String>> dependencies = parseDependencyFile();
-            for (String file : updatedJavaFiles) {
-                List<String> myDeps = dependencies.get(file);
-                if (myDeps == null)
-                    continue;
-                PCDEntry pcde = getNamedPCDE(file, dependencies);
-                for (String dependency : myDeps) {
-                    PCDEntry dep = pcd.get(dependency);
-                    if (dep != null)
-                        // This is an existing dep.
-                        continue;
-                    dep = findClassFileOnFilesystem(file, pcde, dependency,  false);
-                    getClassInfoForPCDEntry(ClassInfo.VER_NEW, dep);
-                    if (dep.newClassInfo.nestedClasses != null)
-                        updatedEntries.add(dep);
-                }
-            }
-        }
-        dealWithNestedClassesForUpdatedPCDEntries(updatedEntries, false);
-    }
-
-    private PCDEntry getNamedPCDE(String file, Map<String, List<String>> dependencies) {
-        List<String> depsForFile = dependencies.get(file);
-        PCDEntry pcde = null;
-        // Find a non-nested class for this java file for which we already have
-        // a pcde
-        for (String dependency : depsForFile) {
-            if (dependency.indexOf('$') != -1)
-                continue;
-            pcde = pcd.get(dependency);
-            if (pcde != null)
-                break;
-        }
-        if (pcde == null) {
-            throw new PrivateException(new PublicExceptions.InternalException(file
-                    + " was supposed to be an updated file, but there are no PCDEntries for any of its deps"));
-        }
-        return pcde;
-    }
-
-    private void dealWithNestedClassesForUpdatedPCDEntries(List<PCDEntry> entries, boolean move) {
-        for (int i = 0; i < entries.size(); i++) {
-            PCDEntry pcde = entries.get(i);
-            ClassInfo oldClassInfo = pcde.oldClassInfo;
-            ClassInfo newClassInfo = pcde.newClassInfo;
-            if (newClassInfo.nestedClasses != null) {
-                Set<String> nested = findAndUpdateAllNestedClassesForClass(pcde, move);
-                if (oldClassInfo.nestedClasses != null) {  // Check if any old nested classes don't exist anymore
-                    for (int j = 0; j < oldClassInfo.nestedClasses.length; j++) {
-                        boolean found = false;
-                        String oldNestedClass = oldClassInfo.nestedClasses[j];
-                        for (int k = 0; k < newClassInfo.nestedClasses.length; k++) {
-                            if (oldNestedClass.equals(newClassInfo.nestedClasses[k])) {
-                                found = true;
-                                break;
-                            }
-                        }
-                        if (!found) {
-                            deletedClasses.add(oldNestedClass);
-                        }
-                    }
-                }
-            } else {  // newNestedClasses == null and oldNestedClasses != null, so all nested classes have been removed in the new version
-                for (int j = 0; j < oldClassInfo.nestedClasses.length; j++) {
-                    deletedClasses.add(oldClassInfo.nestedClasses[j]);
-                }
-            }
-        }
-    }
-
-    private void findUpdatedClasses() {
-        // This (iterating over all of the classes once again after performing that in classFileObsoleteOrDeleted()) may
-        // seem time-consuming, but in reality it isn't, since the most time-consuming operation of obtaining internal
-        // file handles for class files has already been performed in classFileObsoleteOrDeleted(). Once we have done that,
-        // this re-iteration takes very small amount of time. However, if we switch from "class file older than .java
-        // file" to ".java file timestamp changed" condition for recompilation, this will have to be changed as well.
-         for (PCDEntry entry : entries()) {
-            String className = entry.className;
-            if (updatedAndCheckedClasses.contains(className) ||
-                    deletedClasses.contains(className)) {
-                continue;
-            }
-            if (!entry.javaFileFullPath.endsWith(".java")) {
-                continue; // classes from (updated) .jars have been dealt with separately
-            }
-            //DAB TODO understand this bit better.  It is needed to support -vpath, I'm just not sure why....
-            if (entry.checkResult != PCDEntry.CV_NEWER_FOUND_NEARER &&
-                    !updatedAndCheckedClasses.contains(className) &&
-                    !deletedClasses.contains(className) &&
-                    entry.javaFileFullPath.endsWith(".java") &&
-                    classFileUpdated(entry))
-            {
-            //DAB TODO this is the old way....
-            //DAB    if (classFileUpdated(entry)) {
-                updatedClasses.add(className);
-                allUpdatedClasses.add(className);
-            }
-        }
-    }
-
-    private boolean classFileUpdated(PCDEntry entry) {
-        File classFile = Utils.checkFileForName(entry.classFileFullPath);
-        if (classFile == null) {
-            return false;
-        }
-        // The only case when the above can happen is if class file was first deleted, and then there
-        // was an error recompiling its source
-
-        long classFileLastMod = classFile.lastModified();
-
-        if (classFileLastMod > entry.oldClassFileLastModified) {
-            entry.newClassFileLastModified = classFileLastMod;
-            // Check if the class was actually modified, to avoid the costly procedure of detailed version compare
-            long classFileFP = computeFP(classFile);
-            if (classFileFP != entry.oldClassFileFingerprint) {
-                entry.newClassFileFingerprint = classFileFP;
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Compare old (preserved in pdb) and new (file system) versions of updated classes, and find all
-     * potentially affected dependent classes.
-     */
-    private void checkUpdatedClasses() {
-        for (String className : updatedClasses) {
-            PCDEntry pcde = pcd.get(className);
-            getClassInfoForPCDEntry(ClassInfo.VER_NEW, pcde);
-            if (!"".equals(pcde.oldClassInfo.directlyEnclosingClass)) {
-                // The following problem can occur with nested classes. A C.java source has been changed, so that C.class is
-                // not changed or changed in a compatible way, whereas the access modifiers of C$X.class are changed in an
-                // incompatible way, so that something is broken in the project. When jmake is called for the first time,
-                // it reports the problem, then saves the info on the new version of C in the pdb. Of course, the record for
-                // C$X in the pdb is not updated, since the change to it is incompatible and recompilation of dependent sources
-                // has failed. Suppose we don't change anything and invoke jmake again. C$X is found different from its old
-                // version and is checked here again. The outcome should be the same. But since C has not changed, C.class is
-                // not read from disk and the access flags of C$X, which are stored in C.class, are not set appropriately. So
-                // in such circumstances we have wrong access flags for C$X here. To fix the problem we need to load C explicitly.
-                ClassInfo enclosingClassInfo =
-                        getClassInfoForName(ClassInfo.VER_NEW, pcde.oldClassInfo.directlyEnclosingClass);
-                //if (enclosingClassInfo == null || enclosingClassInfo.nestedClasses == null) {
-                //  System.out.println("!!! Suspicious updated class name = " + className);
-                //  System.out.println("!!! enclosingClassInfo for it = " + enclosingClassInfo);
-                //  if (enclosingClassInfo != null) {
-                //    System.out.println("!!! enclosingClassInfo.name = " + enclosingClassInfo.name);
-                //    if (enclosingClassInfo.nestedClasses == null) System.out.println("!!! enclosingClassInfo.nestedClasses = null");
-                //  }
-                //}
-                if (enclosingClassInfo.nestedClasses != null) {  // Can be that this nested class was the only one for enclosing class, and it's deleted now
-                    for (int i = 0; i < enclosingClassInfo.nestedClasses.length; i++) {
-                        if (className.equals(enclosingClassInfo.nestedClasses[i])) {
-                            pcde.newClassInfo.accessFlags =
-                                    enclosingClassInfo.nestedClassAccessFlags[i];
-                            pcde.newClassInfo.isNonMemberNestedClass =
-                                    enclosingClassInfo.nestedClassNonMember[i];
-                            break;
-                        }
-                    }
-                }
-            }
-            if (!(pcde.oldClassInfo.isNonMemberNestedClass && pcde.newClassInfo.isNonMemberNestedClass)) {
-                Utils.printInfoMessage("Checking " + pcde.className);
-                pcde.checkResult = cv.compareClassVersions(pcde) ? PCDEntry.CV_COMPATIBLE
-                        : PCDEntry.CV_INCOMPATIBLE;
-                String affectedClasses[] = cv.getAffectedClasses();
-                if (affectedClasses != null) {
-                    for (int i = 0; i < affectedClasses.length; i++) {
-                        PCDEntry affEntry = pcd.get(affectedClasses[i]);
-                        updatedJavaFiles.add(affEntry.javaFileFullPath);
-                    }
-                }
-            } else {
-                // A non-member nested class can not be referenced by the source code of any class defined outside the
-                // immediately enclosing source code block for this class. Therefore, any incompatibility in the new
-                // version of this class can affect only classes that are defined in the same source file - and they
-                // are necessarily recompiled together with this class. So there is no point in initiating version
-                // compare for this class. However, the new class version should always tembe promoted into the store, since
-                // this class itself may depend on other changing classes.
-                pcde.checkResult = PCDEntry.CV_COMPATIBLE;
-            }
-
-            updatedAndCheckedClasses.add(className);
-        }
-    }
-
-    /** Find all dependent classes for deleted classes. */
-    private void checkDeletedClasses() {
-        for (String className : deletedClasses) {
-            PCDEntry pcde = pcd.get(className);
-
-            if (pcde == null) {  // "Safety net" for the (hopefully unlikely) case. I observed it just once and couldn't identify the reason
-                Utils.printWarningMessage("Warning: internal information inconsistency when checking deleted classes");
-                Utils.printWarningMessage(Utils.REPORT_PROBLEM);
-                Utils.printWarningMessage("Class name: " + className);
-                continue;
-            }
-
-            ClassInfo oldCI = pcde.oldClassInfo;
-            if (!oldCI.isNonMemberNestedClass) { // See the comment above
-                Utils.printInfoMessage("Checking deleted class " + oldCI.name);
-                cv.checkDeletedClass(pcde);
-                String[] affectedClasses = cv.getAffectedClasses();
-                if (affectedClasses != null) {
-                    for (int i = 0; i < affectedClasses.length; i++) {
-                        PCDEntry affEntry = pcd.get(affectedClasses[i]);
-                        if (deletedClasses.contains(affEntry.className)) {
-                            continue;
-                        }
-                        updatedJavaFiles.add(affEntry.javaFileFullPath);
-                    }
-                }
-            }
-            pcde.checkResult = PCDEntry.CV_DELETED;
-            updatedAndCheckedClasses.add(className);
-        }
-        deletedClasses.clear();
-    }
-
-    /**
-     * Determine what classes in the given .jar (which may be an existing updated one, or a new one) are new,
-     * updated, or moved, and treat them accordingly.
-     */
-    private void processAllClassesFromJarFile(String jarFileName) {
-        JarFile jarFile;
-        long jarFileLastMod = 0;
-        try {
-            File file = new File(jarFileName);
-            jarFileLastMod = file.lastModified();
-            jarFile = new JarFile(jarFileName);
-        } catch (IOException ex) {
-            throw new PrivateException(ex);
-        }
-
-        List<PCDEntry> newEntries = new ArrayList<PCDEntry>();
-        List<PCDEntry> updatedEntries = new ArrayList<PCDEntry>();
-        List<PCDEntry> movedEntries = new ArrayList<PCDEntry>();
-
-        for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
-            JarEntry jarEntry = entries.nextElement();
-            String fullClassName = jarEntry.getName();
-            if (!fullClassName.endsWith(".class")) {
-                continue;
-            }
-            fullClassName =
-                    fullClassName.substring(0, fullClassName.length() - 6).intern();
-            byte classFileBytes[];
-            classFileBytes = Utils.readZipEntryIntoBuffer(jarFile, jarEntry);
-            long classFileFP = computeFP(classFileBytes);
-
-            PCDEntry pcde = pcd.get(fullClassName);
-            if (pcde != null) {
-                if (pcde.checked) {
-                    throw new PrivateException(new PublicExceptions.DoubleEntryException(
-                            "Two entries for class " + fullClassName + " detected: " + pcde.javaFileFullPath + " and " + jarFileName));
-                }
-                pcde.checked = true;
-                pcde.newClassFileLastModified = jarFileLastMod;
-                // If we are scanning an existing updated .jar file, and there is no change to the class itself,
-                // and it previously was located in the same .jar, do nothing.
-                if (pcde.oldClassFileFingerprint == classFileFP &&
-                        pcde.javaFileFullPath.equals(jarFileName)) {
-                    pcde.oldClassFileLastModified = jarFileLastMod;   // So that next time jmake is inoked, checking
-                    continue;                                         // of this.jar is not triggered.
-                }
-                if (pcde.oldClassFileFingerprint != classFileFP) {  // This class has been updated
-                    updatedClasses.add(fullClassName);
-                    allUpdatedClasses.add(fullClassName);
-                    pcde.newClassFileLastModified = jarFileLastMod;
-                    pcde.newClassFileFingerprint = classFileFP;
-                    pcde.newClassInfo =
-                            new ClassInfo(classFileBytes, ClassInfo.VER_NEW, this, fullClassName);
-                    if (pcde.oldClassInfo.nestedClasses != null || pcde.newClassInfo.nestedClasses != null) {
-                        updatedEntries.add(pcde);
-                    }
-                } else {
-                    pcde.oldClassFileLastModified = jarFileLastMod;
-                }
-                if (!pcde.javaFileFullPath.equals(jarFileName)) {
-                    // Found an existing class in a different .jar file.
-                    // May happen if the class file has been moved from one .jar to another (or into a .jar, losing its
-                    // .java source). It's only at this point that we can actually see that it was really a move.
-                    if (deletedClasses.contains(fullClassName)) {
-                        deletedClasses.remove(fullClassName);
-                    }
-                    if (pcde.oldClassInfo.nestedClasses != null) {
-                        movedEntries.add(pcde);
-                        pcde.newClassInfo =
-                                new ClassInfo(classFileBytes, ClassInfo.VER_NEW, this, fullClassName);
-                    }
-                }
-                pcde.javaFileFullPath = jarFileName;
-            } else {  // New class file
-                ClassInfo classInfo =
-                        new ClassInfo(classFileBytes, ClassInfo.VER_NEW, this, fullClassName);
-                pcde = new PCDEntry(fullClassName,
-                        jarFileName,
-                        jarFileName, jarFileLastMod, classFileFP,
-                        classInfo);
-                pcde.checkResult = PCDEntry.CV_NEW;          // So that later it's promoted into oldClassInfo correctly
-                updatedAndCheckedClasses.add(fullClassName); // So that the above happens
-                pcd.put(fullClassName, pcde);
-                if (pcde.newClassInfo.nestedClasses != null) {
-                    newEntries.add(pcde);
-                }
-            }
-        }
-
-        dealWithNestedClassesForUpdatedPCDEntries(updatedEntries, false);
-        dealWithNestedClassesForUpdatedPCDEntries(movedEntries, true);
-        for (int i = 0; i < newEntries.size(); i++) {
-            findAndUpdateAllNestedClassesForClass(newEntries.get(i), false);
-        }
-    }
-
-    /** Determine new, deleted and updated classes coming from updated .jar files. */
-    private void dealWithClassesInUpdatedJarFiles() {
-        if (updatedJarFiles.size() == 0) {
-            return;
-        }
-
-        for (String updatedJarFile : updatedJarFiles) {
-            processAllClassesFromJarFile(updatedJarFile);
-        }
-
-        // Now scan the PCD to check which classes that come from updated .jar files have not been marked as checked
-        for (PCDEntry pcde : entries()) {
-            if (updatedJarFiles.contains(pcde.javaFileFullPath)) {
-                if (!pcde.checked) {
-                    deletedClasses.add(pcde.className);
-                }
-            }
-        }
-    }
-
-    /** Check if the destination directory exists, and get the canonical path for it. */
-    private void initializeDestDir(String inDestDir) {
-        if (!(inDestDir == null || inDestDir.equals(""))) {
-            File dir = Utils.checkOrCreateDirForName(inDestDir);
-            if (dir == null) {
-                throw new PrivateException(new IOException("specified directory " + inDestDir + " cannot be created."));
-            }
-            inDestDir = getCanonicalPath(dir);
-            if (!inDestDir.endsWith(File.separator)) {
-                inDestDir += File.separatorChar;
-            }
-            destDir = inDestDir;
-            destDirSpecified = true;
-        } else {
-            destDirSpecified = false;
-        }
-    }
-
-    /**
-     * For the given PCDEntry, set the entry.classFileFullPath according to the value of the .java file full
-     * path and the value of the "-d" option at this particular jmake invocation
-     */
-    private void initializeClassFileFullPath(PCDEntry entry) {
-        String classFileFullPath;
-        if (destDirSpecified) {
-            classFileFullPath = destDir + entry.className + ".class";
-        } else {
-            String javaFileDir = entry.javaFileFullPath;
-            int cutIndex = javaFileDir.lastIndexOf(File.separatorChar);
-            if (cutIndex != -1) {
-                javaFileDir = javaFileDir.substring(0, cutIndex + 1);
-            }
-            String classFileName = entry.className;
-            cutIndex = classFileName.lastIndexOf('/');
-            if (cutIndex != -1) {
-                classFileName = classFileName.substring(cutIndex + 1);
-            }
-            classFileFullPath = javaFileDir + classFileName + ".class";
-        }
-        if (backSlashFileSeparator) {
-            classFileFullPath =
-                    classFileFullPath.replace('/', File.separatorChar);
-        }
-        entry.classFileFullPath = classFileFullPath;
-    }
-
-    private static String getCanonicalPath(File file) {
-        try {
-            return file.getCanonicalPath().intern();
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-    private long computeFP(File file) {
-        byte buf[] = Utils.readFileIntoBuffer(file);
-        return computeFP(buf);
-    }
-
-    private long computeFP(byte[] buf) {
-        checkSum.reset();
-        checkSum.update(buf);
-        return checkSum.getValue();
-    }
-
-    private PrivateException compilerInteractionException(String message, Exception origException, int errCode) {
-        return new PrivateException(new PublicExceptions.CompilerInteractionException(message, origException, errCode));
-    }
-
-    private PrivateException internalException(String message) {
-        return new PrivateException(new PublicExceptions.InternalException(message));
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/PrivateException.java b/third_party/jmake/src/org/pantsbuild/jmake/PrivateException.java
deleted file mode 100644
index b8b0c789..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/PrivateException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-/**
- * This class is used as a wrapper for a number of exceptions that are thrown by jmake. Its
- * only purpose is to help avoid using endless "throws" clauses in the code.
- *
- * @author  Misha Dmitriev
- *  12 November 2001
- */
-public class PrivateException extends RuntimeException {
-
-    private static final long serialVersionUID = 1L;
-    private Throwable originalException;
-
-    public PrivateException(Throwable e) {
-        originalException = e;
-    }
-
-    public Throwable getOriginalException() {
-        return originalException;
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/PublicExceptions.java b/third_party/jmake/src/org/pantsbuild/jmake/PublicExceptions.java
deleted file mode 100644
index 4009745..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/PublicExceptions.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-/**
- * This class is a wrapper for a number of nested classes. They define exceptions that may be thrown
- * by the <b>jmake</b> code. These exceptions are caught in the <code>Main.mainExternal</code> method,
- * or they may be caught and handled by an application invoking <b>jmake</b> programmatically
- * through <code>Main.mainProgrammatic</code> method.
- *
- * @see Main#mainExternal(String[])
- * @see Main#mainProgrammatic(String[])
- *
- * @author  Misha Dmitriev
- *  17 January 2003
- */
-public class PublicExceptions {
-
-    /**
-     * This exception is thrown whenever there is any problem initializing or calling the compiler, or
-     * when the compiler itself reports compilation errors. Depending on the nature of the problem, an
-     * instance of this class is initialized with, and then returns, an original exception signalling
-     * the problem, or the compiler application exit code.
-     */
-    public static class CompilerInteractionException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-        private Exception originalException;
-        private int compilerExitCode;
-
-        /**
-         * Initialize an instance of this exception with an error message and either an original
-         * exception or a compiler exit code.
-         */
-        CompilerInteractionException(String msg, Exception originalException, int compilerExitCode) {
-            super(msg);
-            this.originalException = originalException;
-            this.compilerExitCode = compilerExitCode;
-        }
-
-        /**
-         * Get the original exception that caused this exception. <code>null</code> is returned if there
-         * were no original exception. In that case, there was a compilation error and the compiler has
-         * returned with the exit code that can be obtained using <code>getCompilerExitCode</code> method.
-         *
-         * @see #getCompilerExitCode()
-         */
-        public Exception getOriginalException() {
-            return originalException;
-        }
-
-        /**
-         * Get the compiler exit code. If <code>0</code> is returned, this indicates that an exception was
-         * thrown during compiler initialization, or inside the compiler itself. Use <code>getOriginalExcepion</code>
-         * method to obtain that exception.
-         *
-         * @see #getOriginalException
-         */
-        public int getCompilerExitCode() {
-            return compilerExitCode;
-        }
-    }
-
-    /** Exception signalling a problem with reading project database file. */
-    public static class PDBCorruptedException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        PDBCorruptedException(String msg) {
-            super(msg);
-        }
-    }
-
-    /** Exception signalling a problem when parsing a class file */
-    public static class ClassFileParseException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        ClassFileParseException(String msg) {
-            super(msg);
-        }
-    }
-
-    /** Exception signalling that <b>jmake</b> was not requested to do any real work. */
-    public static class NoActionRequestedException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-    }
-
-    /** Exception signalling that an invalid command line option has been passed to jmake. */
-    public static class InvalidCmdOptionException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        InvalidCmdOptionException(String msg) {
-            super(msg);
-        }
-    }
-
-    /**
-     * Exception signalling a problem (typically an I/O exception) when parsing a command line file
-     * passed to <b>jmake</b>.
-     */
-    public static class CommandFileReadException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        CommandFileReadException(String msg) {
-            super(msg);
-        }
-    }
-
-    /**
-     * Exception signalling that for some class the deduced name (that is, name based on the source file name and
-     * directory) and the real name (the one read from the class file) are different.
-     */
-    public static class ClassNameMismatchException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        ClassNameMismatchException(String msg) {
-            super(msg);
-        }
-    }
-
-    /** Exception thrown if any specified source file has an invalid extension (not <code>.java</code> or <code>.jar</code>). */
-    public static class InvalidSourceFileExtensionException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        InvalidSourceFileExtensionException(String msg) {
-            super(msg);
-        }
-    }
-
-    /** Exception thrown if a dependence of a class in a <code>JAR</code> file on a class with a <code>.java</code> source is detected. */
-    public static class JarDependsOnSourceException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        JarDependsOnSourceException(String msg) {
-            super(msg);
-        }
-    }
-
-    /** Exception thrown if more than one entry for the same class is detected in the project */
-    public static class DoubleEntryException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        DoubleEntryException(String msg) {
-            super(msg);
-        }
-    }
-
-    /** Exception thrown if an internal problem that should never happen is detected. */
-    public static class InternalException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        InternalException(String msg) {
-            super(msg);
-        }
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/RefClassFinder.java b/third_party/jmake/src/org/pantsbuild/jmake/RefClassFinder.java
deleted file mode 100644
index 58c8884..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/RefClassFinder.java
+++ /dev/null
@@ -1,697 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.lang.reflect.Modifier;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * This class implements finding classes referencing other classes and members in various ways.
- *
- * @author Misha Dmitriev
- * 12 March 2004
- */
-public class RefClassFinder {
-
-    private boolean failOnDependentJar;   // If true, will fail if a dependency of a sourceless class
-    // (coming from a .jar) on a "normal" class is detected
-    private boolean noWarnOnDependentJar; // If true, not even a warning will be issued in the above case.
-    private String checkedClassName;
-    private PCDManager pcdm;
-    private Set<String> affectedClassNames;
-    private boolean checkedClassIsFromJar;
-
-    /** An instance of RefClassFinder is created once per session, passing it the global options that do not change */
-    public RefClassFinder(PCDManager pcdm, boolean failOnDependentJar, boolean noWarnOnDependentJar) {
-        this.pcdm = pcdm;
-        this.failOnDependentJar = failOnDependentJar;
-        this.noWarnOnDependentJar = noWarnOnDependentJar;
-    }
-
-    /** This method is called every time we are going to check a new class */
-    public void initialize(String checkedClassName, boolean checkedClassIsFromJar) {
-        this.checkedClassName = checkedClassName;
-        this.checkedClassIsFromJar = checkedClassIsFromJar;
-        affectedClassNames = new LinkedHashSet<String>();
-    }
-
-    /**
-     * Returns the names of project classes that were found potentially affec
-     * by the changes to the checked class.
-     */
-    public String[] getAffectedClassNames() {
-        int size = affectedClassNames.size();
-        if (size == 0) {
-            return null;
-        } else {
-            String[] ret = new String[size];
-            int i = 0;
-            for (String className : affectedClassNames) {
-                ret[i++] = className;
-            }
-            return ret;
-        }
-    }
-
-    /**
-     * Find all project classes that can access field fieldNo of class fieldClassInfo.
-     * Used if a compile-time constant is changed.
-     */
-    public void findAllProjectClasses(ClassInfo fieldClassInfo, int fieldNo) {
-      for (PCDEntry pcde : pcdm.entries()) {
-            if (pcde.checkResult == PCDEntry.CV_DELETED) {
-                continue;
-            }
-            if (pcde.javaFileFullPath.endsWith(".jar")) {
-                continue;
-            }
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (memberAccessibleFrom(fieldClassInfo, fieldNo, clientInfo, true)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-    }
-
-    /**
-     * Find all project classes that reference class with the given name
-     * (but not its array class) directly from the constantpool.
-     */
-    public void findReferencingClasses0(ClassInfo classInfo) {
-        findReferencingClasses(classInfo, 0, false, null);
-    }
-
-
-    /* In the following "find...ReferencingClasses1" methods, "referencing C" means
-     * "referencing C or its array class directly from the constant pool, as a type of a data
-     * field, as a type in a method signature or a thrown exception, as a directly implemented
-     * interface or a direct superclass".
-     */
-    /** Used for deleted classes. */
-    public void findReferencingClassesForDeletedClass(ClassInfo classInfo) {
-        String packageName = classInfo.packageName;
-        boolean isPublic = classInfo.isPublic();
-        boolean isInterface = classInfo.isInterface();
-        for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (!isPublic && packageName.equals(clientInfo.packageName)) {
-                continue;
-            }
-            if (clientInfo.referencesClass(classInfo.name, isInterface, 1)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-
-    }
-
-    /**
-     * For the given class p.C, find each project class X referencing C, that is not a member of
-     * package p and is not a direct or indirect subclass of C's directly enclosing class.
-     * (public -&gt; protected transformation)
-     */
-    public void findDiffPackageAndNotSubReferencingClasses1(ClassInfo classInfo) {
-        String packageName = classInfo.packageName;
-        String directlyEnclosingClass = classInfo.directlyEnclosingClass;
-        for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (packageName.equals(clientInfo.packageName) ||
-                    clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
-                continue;
-            }
-            if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-    }
-
-    /**
-     * For class p.C, find each project class X referencing C, whose top level enclosing
-     * class is different from that of C.
-     * (public -&gt; private transformation)
-     */
-    public void findReferencingClasses1(ClassInfo classInfo) {
-        String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
-        for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (topLevelEnclosingClass.equals(clientInfo.topLevelEnclosingClass)) {
-                continue;
-            }
-            if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-    }
-
-    /**
-     * For class p.C, find each project class X referencing C, whose direct or indirect superclass
-     * is C's directly enclosing class, or which is a member of package p, whose top level enclosing
-     * class is different from that of C.
-     * (protected -&gt; private transformation)
-     */
-    public void findThisPackageOrSubReferencingClasses1(ClassInfo classInfo) {
-        String directlyEnclosingClass = classInfo.directlyEnclosingClass;
-        String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
-        String packageName = classInfo.packageName;
-      for (PCDEntry entry : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if ((!clientInfo.packageName.equals(packageName)) &&
-                    !clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
-                continue;
-            }
-            if (clientInfo.topLevelEnclosingClass.equals(topLevelEnclosingClass)) {
-                continue;
-            }
-            if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-    }
-
-    /**
-     * For class p.C, find each project class X referencing C, which is a member of package p and whose
-     * top level enclosing class is different from that of C.
-     * (default -&gt; private transformation)
-     */
-    public void findThisPackageReferencingClasses1(ClassInfo classInfo) {
-        String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
-        String packageName = classInfo.packageName;
-        for (PCDEntry entry : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD,
-                        entry);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (!clientInfo.packageName.equals(packageName)) {
-                continue;
-            }
-            if (topLevelEnclosingClass.equals(clientInfo.topLevelEnclosingClass)) {
-                continue;
-            }
-            if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-    }
-
-    /**
-     * For class p.C, find each project class X referencing C, which is not a member of package p.
-     * (public -&gt; default transformation)
-     */
-    public void findDiffPackageReferencingClasses1(ClassInfo classInfo) {
-        String packageName = classInfo.packageName;
-        for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (clientInfo.packageName.equals(packageName)) {
-                continue;
-            }
-            if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-    }
-
-    /**
-     * For class p.C, find each project class X referencing C, which is not a member of package p and
-     * whose direct or indirect superclass is C's directly enclosing class.
-     * (protected -&gt; default transformation)
-     */
-    public void findDiffPackageAndSubReferencingClasses1(ClassInfo classInfo) {
-        String packageName = classInfo.packageName;
-        String directlyEnclosingClass = classInfo.directlyEnclosingClass;
-      for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (clientInfo.packageName.equals(packageName)) {
-                continue;
-            }
-            if (!clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
-                continue;
-            }
-            if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
-                addToAffectedClassNames(clientInfo.name);
-            }
-        }
-    }
-
-    /**
-     * Find all project classes that reference both of the classes with the
-     * given names (or array classes of one or both) directly or indirectly from the
-     * constantpool, as a type of a data field, as a type in a method signature or a
-     * thrown exception, as a directly/indirectly implemented interface or a
-     * direct/indirect superclass.
-     */
-    public void findReferencingClasses2(ClassInfo classInfo1, ClassInfo classInfo2) {
-        Set<String> refClazz1 = new LinkedHashSet<String>();
-        findReferencingClasses(classInfo1, 2, false, refClazz1);
-        Set<String> refClazz2 = new LinkedHashSet<String>();
-        findReferencingClasses(classInfo2, 2, false, refClazz2);
-
-        for (String className1 : refClazz1) {
-            if (refClazz2.contains(className1)) {
-                addToAffectedClassNames(className1);
-            }
-        }
-    }
-
-    /** Find all project classes which are direct subclasses of the given class */
-    public void findDirectSubclasses(ClassInfo classInfo) {
-        for (ClassInfo subclassInfo : classInfo.getDirectSubclasses()) {
-            addToAffectedClassNames(subclassInfo.name);
-        }
-    }
-
-    /**
-     * Find all non-abstract project classes that implement the given interface or any of its
-     * subclasses directly, and all non-abstract classes that are direct descendants of abstract
-     * classes that implement the given interface directly or indirectly. Class C implements
-     * interface I indirectly, if C or some superclass of C directly implements I or some sublcass of I.
-     */
-    public void findDirectlyAndOtherwiseImplementingConcreteClasses(ClassInfo intfInfo) {
-        for (PCDEntry entry : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (clientInfo.isInterface()) {
-                continue;
-            }
-            if (clientInfo.isAbstract()) {
-                if (clientInfo.implementsInterfaceDirectlyOrIndirectly(intfInfo.name)) {
-                    findAllNearestConcreteSubclasses(clientInfo);
-                }
-            } else {
-                if (clientInfo.implementsIntfOrSubintfDirectly(intfInfo.name)) {
-                    addToAffectedClassNames(clientInfo.name);
-                }
-            }
-        }
-    }
-
-    private void findAllNearestConcreteSubclasses(ClassInfo classInfo) {
-        for (ClassInfo subclassInfo : classInfo.getDirectSubclasses()) {
-            if (subclassInfo.isAbstract()) {
-                findAllNearestConcreteSubclasses(subclassInfo);
-            } else {
-                addToAffectedClassNames(subclassInfo.name);
-            }
-        }
-    }
-
-    /**
-     * Find all interfaces and abstract classes that implement the given interface and declare or inherit
-     * a method with the given name. For those that overload this method, find referencing classes.
-     */
-    public void findAbstractSubtypesWithSameNameMethod(ClassInfo intfInfo, String mName, final String mSig) {
-      for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo ci =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (ci == null) {
-                continue;  // New class or not in project
-            }
-            if (!(ci.isInterface() || ci.isAbstract())) {
-                continue;
-            }
-            if (ci.implementsInterfaceDirectlyOrIndirectly(intfInfo.name)) {
-                addToAffectedClassNames(ci.name);
-                // Check if the new method overloads an existing (declared or inherited) method. Overloading test is rough -
-                // we just check if the number of parameters is the same.
-                ci.findExistingSameNameMethods(mName, true, true, new ClassInfo.MethodHandler() {
-
-                    void handleMethod(ClassInfo classInfo, int otherMethodIdx) {
-                        String otherMSig =
-                                classInfo.methodSignatures[otherMethodIdx];
-                        if ( (!mSig.equals(otherMSig)) &&
-                                Utils.sameParamNumber(mSig, otherMSig)) {
-                            findReferencingClassesForMethod(classInfo, otherMethodIdx);
-                        }
-                    }
-                });
-            }
-        }
-    }
-
-    /** Find all project classes that reference the given field. */
-    public void findReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
-        findReferencingClassesForMember(classInfo, fieldNo, true, false, false);
-    }
-
-    /**
-     * Find all project classes that reference the given field and which are in
-     * different packages.
-     */
-    public void findDiffPackageReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
-        findReferencingClassesForMember(classInfo, fieldNo, true, true, false);
-    }
-
-    /**
-     * Find all project classes that reference the given field, which are in different
-     * packages and are direct or indirect subclasses of the member's declaring class
-     * (protected -&gt; default transformation).
-     */
-    public void findDiffPackageAndSubReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
-        findReferencingClassesForMember(classInfo, fieldNo, true, true, true);
-    }
-
-    /** Find all project classes that reference the given method. */
-    public void findReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
-        findReferencingClassesForMember(classInfo, methodNo, false, false, false);
-    }
-
-    /**
-     * Find all project classes that reference the given method and which are in
-     * different packages.
-     */
-    public void findDiffPackageReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
-        findReferencingClassesForMember(classInfo, methodNo, false, true, false);
-    }
-
-    /**
-     * Find all project classes that reference the given method, which are in different
-     * packages and are direct or indirect subclasses of the member's declaring class
-     * (protected -&gt; default transformation)
-     */
-    public void findDiffPackageAndSubReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
-        findReferencingClassesForMember(classInfo, methodNo, false, true, true);
-    }
-
-    /**
-     * Find all project classes that re-implement the given method and that are
-     * direct/indirect subclasses of this method's declaring class. If some subclass C
-     * re-implements the given method, we don't have to search C's subclasses further.
-     */
-    public void findSubclassesReimplementingMethod(ClassInfo classInfo, int methodNo) {
-        findSubclassesReimplementingMethod(classInfo, classInfo, methodNo);
-    }
-
-    private void findSubclassesReimplementingMethod(ClassInfo targetClass, ClassInfo methodDeclaringClass, int methodNo) {
-        for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
-            if (subclass.declaresMethod(methodDeclaringClass, methodNo)) {
-                addToAffectedClassNames(subclass.name);
-            } else {
-                findSubclassesReimplementingMethod(subclass, methodDeclaringClass, methodNo);
-            }
-        }
-    }
-
-    /**
-     * For a given class C, find all concrete direct subclasses, and all direct concrente subclasses of C's direct
-     * or indirect abstract subclasses.
-     */
-    public void findConcreteSubclasses(ClassInfo targetClass) {
-        for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
-            if (subclass.isAbstract()) {
-                findConcreteSubclasses(subclass);
-            } else {
-                addToAffectedClassNames(subclass.name);
-            }
-        }
-    }
-
-    /**
-     * Find any concrete subclasses of targetClass that don't override or inherit a concrete implementation
-     * of the given method.
-     */
-    public void findConcreteSubclassesNotOverridingAbstractMethod(ClassInfo targetClass, ClassInfo methodDeclaringClass, int methodNo) {
-        for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
-            int pos =
-                    subclass.getDeclaredMethodPos(methodDeclaringClass, methodNo);
-            if (pos == -1) { // This method is not overridden in this class
-                if (!subclass.isAbstract()) {
-                    addToAffectedClassNames(subclass.name);
-                } else {
-                    findConcreteSubclassesNotOverridingAbstractMethod(subclass, methodDeclaringClass, methodNo);
-                }
-            } else { // A chance that this method is declared abstract once again...
-                if (Modifier.isAbstract(subclass.methodAccessFlags[pos])) {
-                    findConcreteSubclassesNotOverridingAbstractMethod(subclass, methodDeclaringClass, methodNo);
-                }
-            }
-        }
-    }
-
-    /** Find all project classes that reference any method that throws the given exception. */
-    public void findRefsToMethodsThrowingException(ClassInfo excClassInfo) {
-            for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo classInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (classInfo == null) {
-                continue;  // New class
-            }
-            int methodIdx = -1;
-            do {
-                methodIdx =
-                        classInfo.hasMethodThrowingException(excClassInfo, methodIdx + 1);
-                if (methodIdx != -1) {
-                    findReferencingClassesForMethod(classInfo, methodIdx);
-                }
-            } while (methodIdx != -1);
-        }
-    }
-
-    /**
-     * Find all project classes declaring a static field with the given name. Currently used only to look up
-     * classes referencing given class X via the "X.class" construct.
-     */
-    public void findClassesDeclaringField(String name, String signature, boolean isStatic, String packageToLookIn) {
-      for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo classInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (classInfo == null) {
-                continue;  // New class
-            }
-            if (packageToLookIn != null &&
-                    !classInfo.packageName.equals(packageToLookIn)) {
-                continue;
-            }
-            if (classInfo.declaresField(name, signature, isStatic)) {
-                addToAffectedClassNames(classInfo.name);
-            }
-        }
-    }
-
-    public void addToAffectedClassNames(String className) {
-        String res = pcdm.classAlreadyRecompiledOrUncompileable(className);
-        if (res == null) {
-            affectedClassNames.add(className);
-        } else if (!"".equals(res)) {  // The dependent class comes from a .jar.
-            if (checkedClassIsFromJar || noWarnOnDependentJar) {
-                return;
-            }
-            String message = "Class " + className + " is affected by a change to " + checkedClassName + ", but can't be recompiled, " +
-                    "since it is located in archive " + res;
-            if (failOnDependentJar) {
-                throw new PrivateException(new PublicExceptions.JarDependsOnSourceException(message));
-            } else {
-                Utils.printWarningMessage("Warning: " + message);
-            }
-        }
-    }
-
-    /**
-     * Find all project classes that reference the class with the given name.
-     * The second parameter controls the "thoroughness degree", and its value is passed to ClassInfo.referencesClass()
-     * method (see the comment to it). The fromDiffPackages parameter defines whether all such classes
-     * or only classes from different packages are required.
-     */
-    private void findReferencingClasses(ClassInfo classInfo,
-            int thorDegree, boolean fromDiffPackages,
-            Set<String> ret) {
-        String packageName = classInfo.packageName;
-        for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            if (fromDiffPackages && packageName.equals(clientInfo.packageName)) {
-                continue;
-            }
-            // If thorDegree == 2, i.e. indirect references from the constantpool (e.g. a reference to a method which
-            // has classInfo as one of its formal parameter types) are taken into account, then we should check all of
-            // the classes, whether classInfo is directly accessible from them or not.
-            if (thorDegree != 2 && (!classAccessibleFrom(classInfo, clientInfo))) {
-                continue;
-            }
-
-            if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), thorDegree)) {
-                if (ret == null) {
-                    addToAffectedClassNames(clientInfo.name);
-                } else {
-                    ret.add(clientInfo.name);
-                }
-            }
-        }
-    }
-
-    /**
-     * Find all project classes that reference the given member. If fromDiffPackages
-     * is true, then only classes that do not belong to the package of the member's
-     * declaring class should be returned. If onlySubclasses is true, then only
-     * classes that are subclasses of member's declaring class should be returned.
-     */
-    private void findReferencingClassesForMember(ClassInfo declaringClassInfo, int memberNo,
-            boolean isField,
-            boolean fromDiffPackages, boolean onlySubclasses) {
-        String declaringClassName = declaringClassInfo.name;
-        String declaringClassPackage = declaringClassInfo.packageName;
-        for (PCDEntry pcde : pcdm.entries()) {
-            ClassInfo clientInfo =
-                    pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
-            if (clientInfo == null) {
-                continue;  // New class
-            }
-            String className = clientInfo.name;
-            if (className.equals(declaringClassName)) {
-                continue;
-            }
-            if (!memberAccessibleFrom(declaringClassInfo, memberNo, clientInfo, isField)) {
-                continue;
-            }
-            if (fromDiffPackages &&
-                    declaringClassPackage.equals(clientInfo.packageName)) {
-                continue;
-            }
-            if (onlySubclasses && !clientInfo.isSubclassOf(declaringClassName, false)) {
-                continue;
-            }
-
-            if (isField) {
-                if (clientInfo.referencesField(declaringClassInfo, memberNo)) {
-                    addToAffectedClassNames(clientInfo.name);
-                }
-            } else {
-                if (clientInfo.referencesMethod(declaringClassInfo, memberNo)) {
-                    addToAffectedClassNames(clientInfo.name);
-                }
-            }
-        }
-    }
-
-    /** Checks if class classInfo is accessible from class clientClassInfo. */
-    private boolean classAccessibleFrom(ClassInfo classInfo, ClassInfo clientClassInfo) {
-        char classFlags = classInfo.accessFlags;
-        String classPackage = classInfo.packageName;
-        String clientClassPackage = clientClassInfo.packageName;
-
-        if (Modifier.isPublic(classFlags)) {
-            return true;
-        } else if (Modifier.isProtected(classFlags)) {
-            if (classPackage.equals(clientClassPackage) ||
-                    clientClassInfo.isSubclassOf(classInfo.directlyEnclosingClass, false)) {
-                return true;
-            }
-        } else if (Modifier.isPrivate(classFlags)) {
-            if (classInfo.topLevelEnclosingClass.equals(clientClassInfo.topLevelEnclosingClass)) {
-                return true;
-            }
-        } else {
-            if (classPackage.equals(clientClassPackage)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Checks if member memberNo (which is a field if isField == true, and method otherwise) of class memberClassInfo is
-     * accessible from class clientClassInfo.
-     */
-    private boolean memberAccessibleFrom(ClassInfo memberClassInfo,
-            int memberNo, ClassInfo clientClassInfo, boolean isField) {
-        char memberClassFlags = memberClassInfo.accessFlags;
-        char memberFlags = isField ? memberClassInfo.fieldAccessFlags[memberNo]
-                : memberClassInfo.methodAccessFlags[memberNo];
-        String memberClassPackage = memberClassInfo.packageName;
-        String clientClassPackage = clientClassInfo.packageName;
-
-        if (Modifier.isPublic(memberClassFlags)) {
-            if (Modifier.isPublic(memberFlags)) {
-                return true;
-            } else if (Modifier.isProtected(memberFlags) &&
-                    (memberClassPackage.equals(clientClassPackage) ||
-                    clientClassInfo.isSubclassOf(memberClassInfo.name, false))) {
-                return true;
-            } else if (Modifier.isPrivate(memberFlags)) {
-                if (memberClassInfo.topLevelEnclosingClass.equals(
-                        clientClassInfo.topLevelEnclosingClass)) {
-                    return true;
-                }
-            } else if (memberClassPackage.equals(clientClassPackage)) {
-                return true;
-            }
-        } else if (Modifier.isProtected(memberClassFlags)) {
-            if (!(memberClassPackage.equals(clientClassPackage) ||
-                    clientClassInfo.isSubclassOf(memberClassInfo.directlyEnclosingClass, false))) {
-                return true;
-            }
-            if (Modifier.isPublic(memberFlags) ||
-                    Modifier.isProtected(memberFlags)) {
-                return true;
-            } else if (Modifier.isPrivate(memberFlags)) {
-                if (memberClassInfo.topLevelEnclosingClass.equals(
-                        clientClassInfo.topLevelEnclosingClass)) {
-                    return true;
-                }
-            } else {
-                if (memberClassPackage.equals(clientClassPackage)) {
-                    return true;
-                }
-            }
-        } else if (Modifier.isPrivate(memberClassFlags)) {
-            if (memberClassInfo.topLevelEnclosingClass.equals(
-                    clientClassInfo.topLevelEnclosingClass)) {
-                return true;
-            }
-        } else {  // memberClassInfo is package-private
-            if (!memberClassPackage.equals(clientClassPackage)) {
-                return false;
-            }
-            if (Modifier.isPublic(memberFlags) || Modifier.isProtected(memberFlags)) {
-                return true;
-            } else if (Modifier.isPrivate(memberFlags)) {
-                if (memberClassInfo.topLevelEnclosingClass.equals(
-                        clientClassInfo.topLevelEnclosingClass)) {
-                    return true;
-                }
-            } else {
-                return true;
-            }
-        }
-
-        return false;
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/TextProjectDatabaseReader.java b/third_party/jmake/src/org/pantsbuild/jmake/TextProjectDatabaseReader.java
deleted file mode 100644
index d227a8e..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/TextProjectDatabaseReader.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/* Copyright (c) 2002-2013 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.ObjectInputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-
-/**
- * This class creates the internal representation of the project database from a text buffer.
- *
- * The Pants build tool manipulates this data in various ways, and it's easiest for it
- * to do so by parsing text files directly.  This brings JMake into line with Zinc (the
- * Scala incremental compiler) and allows Pants to handle both uniformly.
- *
- * @author  Benjy Weinberger
- * 13 January 2013
- */
-public class TextProjectDatabaseReader {
-    public Map<String,PCDEntry> readProjectDatabaseFromFile(File infile) {
-        try {
-            BufferedReader in =
-                new BufferedReader(new InputStreamReader(new FileInputStream(infile), "UTF-8"));
-            try {
-                return readProjectDatabase(in);
-            } finally {
-                in.close();
-            }
-        } catch (FileNotFoundException e) {
-            throw new PrivateException(e);
-        } catch (UnsupportedEncodingException e) {
-            throw new PrivateException(e);
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-    public Map<String,PCDEntry> readProjectDatabase(BufferedReader in) {
-        Map<String,PCDEntry> pcd;
-        try {
-            String line = in.readLine();
-            if (!"pcd entries:".equals(line))
-                throw error("Expected: 'pcd entries:', got: " + line);
-            line = in.readLine();
-            Matcher m = Pattern.compile("^(\\d+) items$").matcher(line);
-            if (!m.matches())
-                throw error("Expected: '<n> items', got: " + line);
-            int numEntries = Integer.parseInt(m.group(1));
-            pcd = new LinkedHashMap<String, PCDEntry>(numEntries);
-            for (int i = 0; i < numEntries; i++) {
-                line = in.readLine();
-                if (line == null)
-                    throw error("Unexpected EOF");
-                String[] parts = line.split("\t");
-                if (parts.length != 5) {
-                    throw error("Invalid line: " + line);
-                }
-                String className = parts[0];
-                String javaFullFilePath = parts[1];
-                long oldClassFileLastModified = Long.parseLong(parts[2]);
-                long oldClassFileFingerprint = Long.parseLong(parts[3]);
-                ClassInfo ci = classInfoFromBase64(parts[4]);
-                PCDEntry entry = new PCDEntry(className, javaFullFilePath, oldClassFileLastModified,
-                    oldClassFileFingerprint, ci);
-                pcd.put(entry.className, entry);
-            }
-            // We're done: We have detailed dep information in the PCD entries, so we don't
-            // need to read the dep information lines from the file.
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-        return pcd;
-    }
-
-    private PrivateException error(String msg) {
-        return new PrivateException(new IllegalArgumentException(msg));
-    }
-
-    private ClassInfo classInfoFromBase64(String s) {
-        try {
-            byte[] bytes = Base64.decode(s.toCharArray());
-            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
-            ClassInfo ret = (ClassInfo)ois.readObject();
-            ret.initializeImmediateTransientFields();
-            return ret;
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        } catch (ClassNotFoundException e) {
-            throw new PrivateException(e);
-        }
-    }
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/TextProjectDatabaseWriter.java b/third_party/jmake/src/org/pantsbuild/jmake/TextProjectDatabaseWriter.java
deleted file mode 100644
index ee5c08f5..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/TextProjectDatabaseWriter.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/* Copyright (c) 2002-2013 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
-
-/**
- * This class implements writing a text stream representing a project database.
- *
- * @see TextProjectDatabaseReader for details.
- *
- * @author  Benjy Weinberger
- * 13 January 2013
- */
-public class TextProjectDatabaseWriter {
-    private static Set<String> primitives = new LinkedHashSet<String>(
-        Arrays.asList("boolean", "byte", "char", "double", "float", "int", "long", "short",
-                      "Z", "B", "C", "D", "F", "I", "J", "S"));
-
-    private ByteArrayOutputStream baos = new ByteArrayOutputStream();  // Reusable temp buffer.
-
-    public void writeProjectDatabaseToFile(File outfile, Map<String, PCDEntry> pcd) {
-        try {
-            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outfile), "UTF-8"));
-            try {
-                writeProjectDatabase(out, pcd);
-            } finally {
-                out.close();
-            }
-        } catch (FileNotFoundException e) {
-            throw new PrivateException(e);
-        } catch (UnsupportedEncodingException e) {
-            throw new PrivateException(e);
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-	public void writeProjectDatabase(Writer out, Map<String,PCDEntry> pcd) {
-        try {
-            out.write("pcd entries:\n");
-            out.write(Integer.toString(pcd.size()));
-            out.write(" items\n");
-            Map<String, Set<String>> depsBySource = new LinkedHashMap<String, Set<String>>();
-            for (PCDEntry entry : pcd.values()) {
-                writePCDEntry(out, entry);
-                Set<String> deps = depsBySource.get(entry.javaFileFullPath);
-                if (deps == null) {
-                    deps = new LinkedHashSet<String>();
-                    depsBySource.put(entry.javaFileFullPath, deps);
-                }
-                addDepsFromClassInfo(deps, entry.oldClassInfo);
-            }
-            // Write out dependency information. Note that we don't need to read this back to recreate
-            // the PCD. We write it out here just as a convenience, so that external readers of the PDB
-            // file don't have to grok our internal ClassInfo structures.
-            out.write("dependencies:\n");
-            out.write(Integer.toString(depsBySource.size()));
-            out.write(" items\n");
-            for (Map.Entry<String, Set<String>> item : depsBySource.entrySet()) {
-                out.write(item.getKey());
-                for (String s : item.getValue()) {
-                    out.write('\t');
-                    out.write(s);
-                }
-                out.write('\n');
-            }
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-	}
-
-    private void addDepsFromClassInfo(Set<String> deps, ClassInfo ci) {
-        for (String s : ci.cpoolRefsToClasses) {
-            int i = 0;
-            int j = s.length();
-
-            // Fix some inconsistencies in how we represent types internally:
-            // Despite the comment on ci.cpoolRefsToClasses, class names may be
-            // representing in it with '['s and with '@', '#' instead of 'L', ';'.
-            while (s.charAt(i) == '[') i++;
-            if (s.charAt(i) == '@') i++;
-            if (s.endsWith("#")) j--;
-            int k = s.indexOf('$');
-
-            // Take the outer class, on references to nested classes.
-            if (k != -1) j = k;
-            if (i > 0 || j < s.length())
-                s = s.substring(i, j);
-
-            // We don't need to record deps on primitive types, or arrays of them.
-            if (!primitives.contains(s))
-                deps.add(s);
-        }
-    }
-
-	private void writePCDEntry(Writer out, PCDEntry entry) {
-        try {
-            out.write(entry.className);
-            out.write('\t');
-            out.write(entry.javaFileFullPath);
-            out.write('\t');
-            out.write(Long.toString(entry.oldClassFileLastModified));
-            out.write('\t');
-            out.write(Long.toString(entry.oldClassFileFingerprint));
-            out.write('\t');
-            out.write(classInfoToBase64(entry.oldClassInfo));
-            out.write('\n');
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-	}
-
-	private char[] classInfoToBase64(ClassInfo ci) {
-        baos.reset();
-        try {
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(ci);
-            oos.close();
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-        return Base64.encode(baos.toByteArray());
-	}
-}
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/Utils.java b/third_party/jmake/src/org/pantsbuild/jmake/Utils.java
deleted file mode 100644
index 38566a9..0000000
--- a/third_party/jmake/src/org/pantsbuild/jmake/Utils.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
- *
- * This program is distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-package org.pantsbuild.jmake;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * Utility functions used by other classes from this package.
- *
- * @author Misha Dmitriev
- * 23 January 2003
- */
-public class Utils {
-
-    static final String REPORT_PROBLEM =
-            "Please report this problem to Mikhail.Dmitriev@sun.com";
-    static final byte[] MAGIC = {'J', 'a', 'v', 'a', 'm', 'a', 'k', 'e', ' ', 'P', 'r', 'o', 'j', 'e', 'c', 't', ' ', 'D', 'a', 't', 'a', 'b', 'a', 's', 'e', ' ', 'F', 'i', 'l', 'e'};
-    static final int magicLength = MAGIC.length;
-    static final int PDB_FORMAT_CODE_OLD = 1;
-    static final int PDB_FORMAT_CODE_133 = 0x01030300;
-    static final int PDB_FORMAT_CODE_LATEST = PDB_FORMAT_CODE_133;
-    static final int JAVAC_TARGET_RELEASE_OLDEST = 0x01040000;  // 1.4 and previous versions
-    static final int JAVAC_TARGET_RELEASE_15 = 0x01050000;  // if class is compiled with -target 1.5
-    static final int JAVAC_TARGET_RELEASE_16 = 0x01060000;  // if class is compiled with -target 1.6
-    static final int JAVAC_TARGET_RELEASE_17 = 0x01070000;  // if class is compiled with -target 1.7
-    static final int JAVAC_TARGET_RELEASE_18 = 0x01080000;  // if class is compiled with -target 1.8
-    static int warningLimit = 20;  // Maximum number of warnings to print
-    static final int TIMING_TOTAL = 0;
-    static final int TIMING_PDBREAD = 1;
-    static final int TIMING_SYNCHRO = 2;
-    static final int TIMING_SYNCHRO_CHECK_JAVA_FILES = 3;
-    static final int TIMING_FIND_UPDATED_JAVA_FILES = 4;
-    static final int TIMING_CLASS_FILE_OBSOLETE_OR_DELETED = 5;
-    static final int TIMING_COMPILE = 6;
-    static final int TIMING_FIND_UPDATED_CLASSES = 7;
-    static final int TIMING_CHECK_UPDATED_CLASSES = 8;
-    static final int TIMING_PDBWRITE = 9;
-    static final int TIMING_SYNCHRO_CHECK_TMP = 10;
-    static final int TIMING_CLASS_FILE_OBSOLETE_TMP = 11;
-    static final int TIMING_PDBUPDATE = 12;
-    static final int TIMING_ARRAY_LENGTH = 13;
-    private static long timings[] = new long[TIMING_ARRAY_LENGTH];
-    private static boolean timingOn = false;
-
-
-    // -------------------------------------------------------------------------------
-    // Name manipulation stuff
-    // -------------------------------------------------------------------------------
-    /**
-     * Returns package name for the given class. In case of no package, returns an
-     * empty, but non-null string. Returned string is interned.
-     */
-    public static String getPackageName(String clazzName) {
-        int ldi = clazzName.lastIndexOf('/');  // For convenience, we use system-internal slashes, not dots
-        if (ldi == -1) {
-            return "";
-        } else {
-            return clazzName.substring(0, ldi).intern();
-        }
-    }
-
-    /**
-     * Returns directly enclosing class name for the given class. If the given class is not a
-     * nested class, returns empty, but non-null string. Returned string is interned.
-     * NOTE FOR JDK 1.5: this function has to work with both old (1.4 and before) and new (1.5) ways
-     * of naming non-member classes. javacTargetRelease determines the javac version for this class;
-     * however on rare occasions (when checking a deleted non-project class) it may be 0, denoting
-     * that javac version is not known.
-     * In that case, we use the old algorithm, which is error-prone due to a bug in nested class
-     * naming that existed prior to JDK 1.5, where both a non-member local nested class B of A, and a
-     * member nested class B of anonymous class A$1, are named A$1$B.
-     */
-    public static String getDirectlyEnclosingClass(String clazzName, int javacTargetRelease) {
-        int ldi = clazzName.lastIndexOf('$');
-        if (ldi == -1) {
-            return "";
-        }
-
-        if (javacTargetRelease >= JAVAC_TARGET_RELEASE_15) {
-            return clazzName.substring(0, ldi).intern();
-        } else {   // JAVAC_TARGET_RELEASE_OLDEST or unknown
-            // Take into account local classes which are named like "EncClass$1$LocalClass", where EncClass
-            // is directly enclosing class.
-            int lldi = clazzName.lastIndexOf('$', ldi - 1);
-            if (lldi == -1 || !Character.isDigit(clazzName.charAt(lldi + 1))) {
-                return clazzName.substring(0, ldi).intern();
-            } else {
-                return clazzName.substring(0, lldi).intern();
-            }
-        }
-    }
-
-    /**
-     * Returns top-level enclosing class name for the given class. If the given class is not a
-     * nested class, returns empty, but non-null string. Returned string is interned.
-     */
-    public static String getTopLevelEnclosingClass(String clazzName) {
-        int fdi = clazzName.indexOf('$');
-        if (fdi == -1) {
-            return "";
-        }
-
-        return clazzName.substring(0, fdi).intern();
-    }
-
-    /**
-     * Given the full path for the enclosing class file and the full name for the nested class, return the supposed
-     * full path for the nested class.
-     */
-    public static String getClassFileFullPathForNestedClass(String enclosingClassFileFullPath, String nestedClassFullName) {
-        String enclosingClassDir = enclosingClassFileFullPath;
-        int cutIndex = enclosingClassDir.lastIndexOf(File.separatorChar);
-        enclosingClassDir = enclosingClassDir.substring(0, cutIndex + 1); // If slash is present, it's included, otherwise we get ""
-        cutIndex = nestedClassFullName.lastIndexOf('/');
-        String nestedClassLocalName;
-        if (cutIndex < 0) {
-            nestedClassLocalName = nestedClassFullName;
-        } else {
-            nestedClassLocalName = nestedClassFullName.substring(cutIndex + 1);
-        }
-        return enclosingClassDir + nestedClassLocalName + ".class";
-    }
-
-    /**
-     * For two strings representing signatures, check if the number of parameters in
-     * both is the same.
-     */
-    public static boolean sameParamNumber(String sig1, String sig2) {
-        return getParamNumber(sig1) == getParamNumber(sig2);
-    }
-
-    private static int getParamNumber(String sig) {
-        char ch;
-        int parNo = 0, pos = 0;
-        do {
-            ch = sig.charAt(++pos);
-            if (ch == ')') {
-                break;
-            }
-            while (ch == '[') {
-                ch = sig.charAt(++pos);
-            }
-            parNo++;
-            if (ch == '@') {
-                // We replaced all "Lclassname;" in signatures with "@classname#"
-                while (ch != '#') {
-                    ch = sig.charAt(++pos);
-                }
-            }
-        } while (ch != ')');
-        return parNo;
-    }
-
-
-    // -------------------------------------------------------------------------------
-    // File related stuff
-    // -------------------------------------------------------------------------------
-    public static File checkFileForName(String name) {
-        // For each .java file, a File object is created two times when jmake executes: first when we synchronise the PCD
-        // and the supplied .java file list (we make sure that the .java file exists), and second time when we check if a class
-        // file was updated (we compare time stamps of the .java and the .class file). I tried to call this routine for a .java
-        // class both times, and cached File objects, but it looks as if this does not bring any real speed-up (and in fact may
-        // even slow down the application). Most of the time seems to go to the underlying code creating internal File
-        // representation; once it is created, it takes little time to execute another "new File()" for it. Also, all operations
-        // on files like getCanonicalPath() or lastModified() seem to be quite expensive, so their unnecessary repetition should
-        // be avoided as much as possible.
-        if (name == null) {
-            return null;
-        }
-        File file = new File(name);
-        if (file.exists()) {
-            return file;
-        }
-        return null;
-    }
-
-    public static File checkOrCreateDirForName(String name) {
-        File file = new File(name);
-        if (!file.exists()) {
-            file.mkdirs();
-        }
-        if (file.exists()) {
-            if (!file.isDirectory()) {
-                throw new PrivateException(new PublicExceptions.InternalException(file + " is not a directory."));
-            }
-            return file;
-        }
-        return null;
-    }
-
-    public static byte[] readFileIntoBuffer(File file) {
-        try {
-            InputStream in = new FileInputStream(file);
-            int len = (int) file.length();
-            return readInputStreamIntoBuffer(in, len);
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-    public static byte[] readZipEntryIntoBuffer(ZipFile file, ZipEntry entry) {
-        try {
-            InputStream in = file.getInputStream(entry);
-            int len = (int) entry.getSize();
-            return Utils.readInputStreamIntoBuffer(in, len);
-        } catch (IOException e) {
-            throw new PrivateException(e);
-        }
-    }
-
-    public static byte[] readInputStreamIntoBuffer(InputStream in, int len) throws IOException {
-        byte buf[] = new byte[len];
-        int readBytes, ofs = 0, remBytes = len;
-        do {
-            readBytes = in.read(buf, ofs, remBytes);
-            ofs += readBytes;
-            remBytes -= readBytes;
-        } while (ofs < len);
-        in.close();
-        return buf;
-    }
-
-    public static void readAndPrintBytesFromStream(InputStream in, OutputStream out) throws IOException {
-        int avail = in.available();
-        if (avail > 0) {
-            byte outbytes[] = new byte[avail];
-            int realOutBytes = in.read(outbytes);
-            out.write(outbytes, 0, realOutBytes);
-        }
-    }
-
-    /** For a Windows path, convert the drive letter to the lower case */
-    public static String convertDriveLetterToLowerCase(String path) {
-        if (path.charAt(1) != ':') {
-            return path;
-        }
-        char drive = path.charAt(0);
-        if (Character.isUpperCase(drive)) {
-            drive = Character.toLowerCase(drive);
-            char[] chars = path.toCharArray();
-            chars[0] = drive;
-            path = new String(chars);
-        }
-        return path;
-    }
-
-    public static void ignore(Exception e) {
-        // Ignore this exception
-    }
-
-    /** Used when invoking a third-party executable compiler app */
-    public static void delay(int ms) {
-        Object o = new Object();
-        synchronized (o) {
-            try {
-                o.wait(ms);
-            } catch (InterruptedException e) {
-            }
-        }
-    }
-    // -------------------------------------------------------------------------------
-    // Custom printing stuff
-    // -------------------------------------------------------------------------------
-    private static PrintStream out = System.out;
-    private static PrintStream warn = System.out;
-    private static PrintStream err = System.err;
-    private static boolean printInfoMessages = true;
-    private static boolean printWarningMessages = true;
-    private static boolean printErrorMessages = true;
-    private static int warningNo;
-
-    public static void setOutputStreams(PrintStream out, PrintStream warn, PrintStream err) {
-        Utils.out = out;
-        Utils.warn = warn;
-        Utils.err = err;
-    }
-
-    public static void customizeOutput(boolean printInfoMessages, boolean printWarningMessages, boolean printErrorMessages) {
-        Utils.printInfoMessages = printInfoMessages;
-        Utils.printWarningMessages = printWarningMessages;
-        Utils.printErrorMessages = printErrorMessages;
-    }
-
-    public static void printInfoMessage(String message) {
-        if (printInfoMessages) {
-            out.println(message);
-        }
-    }
-
-    public static void printInfoMessageNoEOL(String message) {
-        if (printInfoMessages) {
-            out.print(message);
-        }
-    }
-
-    public static void printWarningMessage(String message) {
-        if (!printWarningMessages) {
-            return;
-        }
-        if (warningNo < warningLimit) {
-            warn.println(message);
-        } else if (warningNo == warningLimit) {
-            warn.println("jmake: more than " + warningLimit + " warnings.");
-        }
-        warningNo++;
-    }
-
-    public static void printErrorMessage(String message) {
-        if (printErrorMessages) {
-            err.println("jmake: " + message);
-        }
-    }
-
-    // -------------------------------------------------------------------------------
-    // Measuring stuff
-    // -------------------------------------------------------------------------------
-    public static void setTimingOn() {
-        timingOn = true;
-    }
-
-    public static void startTiming(int slot) {
-        timings[slot] = System.currentTimeMillis();
-    }
-
-    public static void stopAndPrintTiming(String message, int slot) {
-        if (timingOn) {
-            long time = System.currentTimeMillis() - timings[slot];
-            printInfoMessage("========== " + message + " time = " + time);
-        }
-    }
-
-    public static void printTiming(String message, int slot) {
-        if (timingOn) {
-            printInfoMessage("========== " + message + " time = " + timings[slot]);
-        }
-    }
-
-    public static void stopAndAddTiming(int slot1, int slot2) {
-        if (timingOn) {
-            long time = System.currentTimeMillis() - timings[slot1];
-            timings[slot2] += time;
-        }
-    }
-}
diff --git a/third_party/robolectric/BUILD.gn b/third_party/robolectric/BUILD.gn
index 3b90a712..3bc3d7e 100644
--- a/third_party/robolectric/BUILD.gn
+++ b/third_party/robolectric/BUILD.gn
@@ -466,9 +466,6 @@
   # here which requires_android.
   bypass_platform_checks = true
 
-  # TODO(mikecase): Remove this once crbug.com/638875 is fixed.
-  enable_incremental_javac_override = false
-
   testonly = true
   processor_args_javac =
       [ "org.robolectric.annotation.processing.shadowPackage=org.robolectric" ]
diff --git a/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index 41892a1..05c0faa 100644
--- a/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/tools/android/roll/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -170,6 +170,9 @@
                 // Target .aar file contains .so libraries that need to be extracted,
                 // and android_aar_prebuilt template will fail if it's not set explictly.
                 sb.append('  extract_native_libraries = true\n')
+                // InstallActivity class is downloaded as a part of DFM & we need to inject
+                // a call to SplitCompat.install() into it.
+                sb.append('  split_compat_class_names = [ "com/google/ar/core/InstallActivity" ]\n')
                 break
         }
     }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5673c42..29de6638 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2049,6 +2049,7 @@
   <int value="6" label="kSetup"/>
   <int value="7" label="kStylus"/>
   <int value="8" label="kLauncherSearchResult"/>
+  <int value="9" label="kLauncherSearchBoxMic"/>
 </enum>
 
 <enum name="AssistantExitPoint">
@@ -7697,7 +7698,7 @@
   <int value="-143" label="SPDY_SESSION_ALREADY_EXISTS"/>
   <int value="-142" label="MSG_TOO_BIG"/>
   <int value="-141" label="SSL_CLIENT_AUTH_SIGNATURE_FAILED"/>
-  <int value="-140" label="HTTPS_PROXY_TUNNEL_RESPONSE"/>
+  <int value="-140" label="HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT"/>
   <int value="-139" label="TEMPORARILY_THROTTLED"/>
   <int value="-138" label="NETWORK_ACCESS_DENIED"/>
   <int value="-137" label="NAME_RESOLUTION_FAILED"/>
@@ -31227,8 +31228,10 @@
   <int value="-1349826793" label="ArcInputMethod:disabled"/>
   <int value="-1349532167" label="enable-wifi-credential-sync"/>
   <int value="-1346722635" label="gesture-selection"/>
+  <int value="-1346288427" label="AutofillShowFullDisclosureLabel:disabled"/>
   <int value="-1344375439" label="ServiceWorkerPaymentApps:disabled"/>
   <int value="-1343259222" label="RegionalLocalesAsDisplayUI:disabled"/>
+  <int value="-1342961844" label="InlineUpdateFlow:disabled"/>
   <int value="-1342039126" label="AshNewSystemMenu:enabled"/>
   <int value="-1341092934" label="enable-accelerated-overflow-scroll"/>
   <int value="-1340055960" label="enable-streamlined-hosted-apps"/>
@@ -31946,6 +31949,7 @@
   <int value="-102227288" label="PasswordExport:disabled"/>
   <int value="-99781021" label="disable-roboto-font-ui"/>
   <int value="-97145978" label="DesktopPWAsStayInWindow:enabled"/>
+  <int value="-92116820" label="InlineUpdateFlow:enabled"/>
   <int value="-89690053" label="MaterialDesignUserManager:enabled"/>
   <int value="-88822940" label="ssl-version-min"/>
   <int value="-88273414" label="ContentSuggestionsShowSummary:enabled"/>
@@ -32245,6 +32249,7 @@
   <int value="417709910"
       label="AutofillSendExperimentIdsInPaymentsRPCs:disabled"/>
   <int value="422307097" label="PhysicalWeb:disabled"/>
+  <int value="422426657" label="AutofillShowFullDisclosureLabel:enabled"/>
   <int value="423615350" label="enable-tab-audio-muting"/>
   <int value="423855924" label="enable-tab-switcher-theme-colors"/>
   <int value="430959979" label="SyncStandaloneTransport:disabled"/>
@@ -36618,7 +36623,7 @@
   <int value="137" label="NAME_RESOLUTION_FAILED"/>
   <int value="138" label="NETWORK_ACCESS_DENIED"/>
   <int value="139" label="TEMPORARILY_THROTTLED"/>
-  <int value="140" label="HTTPS_PROXY_TUNNEL_RESPONSE"/>
+  <int value="140" label="HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT"/>
   <int value="141" label="SSL_CLIENT_AUTH_SIGNATURE_FAILED"/>
   <int value="142" label="MSG_TOO_BIG"/>
   <int value="143" label="SPDY_SESSION_ALREADY_EXISTS"/>
@@ -53126,6 +53131,13 @@
   <int value="3" label="Fallback for Status Bar and content"/>
 </enum>
 
+<enum name="TunnelRedirectHistogramValue">
+  <int value="0" label="Subresource fetched using an explicit proxy"/>
+  <int value="1" label="Mainframe fetched using an explicit proxy"/>
+  <int value="2" label="Subresource fetched using an auto-detected proxy"/>
+  <int value="3" label="Mainframe fetched using an auto-detected proxy"/>
+</enum>
+
 <enum name="UIEventType">
   <int value="0" label="Unknown"/>
   <int value="1" label="Touch released"/>
diff --git a/tools/metrics/histograms/generate_expired_histograms_array.py b/tools/metrics/histograms/generate_expired_histograms_array.py
index cf10df7..be99d8a 100755
--- a/tools/metrics/histograms/generate_expired_histograms_array.py
+++ b/tools/metrics/histograms/generate_expired_histograms_array.py
@@ -46,9 +46,10 @@
 # Some extra "grace" time is given to expired histograms during which they
 # will contintue to be collected and reported.  The dashboard should ignore
 # data from this period making the expiry noticeable and giving time for
-# owners to re-enable them without any discontinuity of data.
+# owners to re-enable them without any discontinuity of data. Releases are
+# geneally 6 weeks apart but sometimes 7 so +2 to be safe.
 _EXPIRE_GRACE_MSTONES = 2
-_EXPIRE_GRACE_WEEKS = _EXPIRE_GRACE_MSTONES * 6
+_EXPIRE_GRACE_WEEKS = _EXPIRE_GRACE_MSTONES * 6 + 2
 
 
 class Error(Exception):
diff --git a/tools/metrics/histograms/generate_expired_histograms_array_unittest.py b/tools/metrics/histograms/generate_expired_histograms_array_unittest.py
index a4f2d721..d93eb6a 100755
--- a/tools/metrics/histograms/generate_expired_histograms_array_unittest.py
+++ b/tools/metrics/histograms/generate_expired_histograms_array_unittest.py
@@ -155,7 +155,7 @@
 <histograms><!-- Must be alphabetical. -->
   <histogram name="FirstHistogram" expires_after="2010-11-01"/>
   <histogram name="FourthHistogram" expires_after="M61"/>
-  <histogram name="SecondHistogram" expires_after="2010-10-01"/>
+  <histogram name="SecondHistogram" expires_after="2010-09-01"/>
   <histogram name="ThirdHistogram" expires_after="M60"/>
 </histograms>
 </histogram-configuration>
@@ -166,7 +166,7 @@
 
     content = generate_expired_histograms_array._GenerateFileContent(
         histograms, branch_data, mstone_data, "header.h", "uma")
-    # These have expired but are within the 12-week/2-milestone grace period.
+    # These have expired but are within the 14-week/2-milestone grace period.
     self.assertNotIn("FirstHistogram", content);
     self.assertNotIn("FourthHistogram", content);
     # These have expired and are outside of the grace period.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b23fe2c..d6a449d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -26439,7 +26439,7 @@
   <summary>Status of drive cache metadata database open.</summary>
 </histogram>
 
-<histogram name="Drive.DeltaFeedLoadTime" units="ms" expires_after="M74">
+<histogram name="Drive.DeltaFeedLoadTime" units="ms" expires_after="M75">
   <owner>slangley@chromium.org</owner>
   <owner>weifangsun@chromium.org</owner>
   <summary>
@@ -26447,7 +26447,7 @@
   </summary>
 </histogram>
 
-<histogram name="Drive.DirectoryFeedLoadTime" units="ms" expires_after="M74">
+<histogram name="Drive.DirectoryFeedLoadTime" units="ms" expires_after="M75">
   <owner>slangley@chromium.org</owner>
   <owner>weifangsun@chromium.org</owner>
   <summary>
@@ -26523,7 +26523,7 @@
   </summary>
 </histogram>
 
-<histogram name="Drive.FullFeedLoadTime" units="ms" expires_after="M74">
+<histogram name="Drive.FullFeedLoadTime" units="ms" expires_after="M75">
   <owner>slangley@chromium.org</owner>
   <owner>weifangsun@chromium.org</owner>
   <summary>
@@ -56926,8 +56926,7 @@
   <summary>
     Tracks events when the currently navigated domain name is a lookalike to one
     of the top 10K domains or a domain that the user interacted with, resulting
-    in a navigation suggestion in the form of &quot;Did you mean to go to
-    ...&quot;.
+    in a navigation suggestion interstitial.
   </summary>
 </histogram>
 
@@ -62249,6 +62248,22 @@
   </summary>
 </histogram>
 
+<histogram name="Net.Proxy.RedirectDuringConnect"
+    enum="TunnelRedirectHistogramValue" expires_after="2019-05-19">
+  <owner>eroman@chromium.org</owner>
+  <owner>mmenke@chromium.org</owner>
+  <summary>
+    Counts how often a 302 (redirect) response is observed during CONNECT (proxy
+    tunnel establishment). The counts are bucketed by proxy settings source
+    (auto-detect vs explicit) and load type (subresource vs main frame).
+
+    To interpret this histogram, the most interesting property will be the
+    number of users in each of the buckets, which can be used to approximate
+    what fraction of users would be affected by a policy change that blocked
+    redirects on subresources/non-autodetected proxies.
+  </summary>
+</histogram>
+
 <histogram name="Net.ProxyAuthRequested.HasConnection">
   <owner>rch@chromium.org</owner>
   <summary>
@@ -125680,6 +125695,9 @@
 </histogram>
 
 <histogram name="VRAutopresentedWebVR" enum="Boolean">
+  <obsolete>
+    Deprecated as of 02/2019, VR DLAs are no longer supported.
+  </obsolete>
   <owner>ymalik@chromium.org</owner>
   <summary>
     Whether we're auto-presenting the first time we enter WebVR mode.
@@ -125725,6 +125743,9 @@
 </histogram>
 
 <histogram name="VRSessionTimeFromDLA" units="ms">
+  <obsolete>
+    Deprecated as of 02/2019, VR DLAs are no longer supported.
+  </obsolete>
   <owner>ymalik@chromium.org</owner>
   <summary>
     The duration of a single VR session initiated via a deep-linked WebVR app.
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 7f9a33d..8348620 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -931,7 +931,7 @@
 void AXTree::PopulateOrderedSetItems(const AXNode* ordered_set,
                                      const AXNode* local_parent,
                                      std::vector<const AXNode*>& items,
-                                     bool node_is_radio_button) const {
+                                     const AXNode& original_node) const {
   // Stop searching current path if roles of local_parent and ordered set match.
   // Don't compare the container to itself.
   if (!(ordered_set == local_parent)) {
@@ -939,6 +939,24 @@
       return;
   }
 
+  // Initialize necessary variables.
+  // Default hierarchical_level is 0, which represents that no hierarchical
+  // level was detected on original_node.
+  int original_level = original_node.GetIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel);
+  // If original node is ordered set, then set its hierarchical level equal to
+  // its first child to ensure the items vector gets populated.
+  // This is due to ordered sets having a hierarchical level of 0, while their
+  // nodes have non-zero hierarchical values.
+  if ((ordered_set == &original_node) &&
+      ordered_set->GetUnignoredChildAtIndex(0)) {
+    original_level = ordered_set->GetUnignoredChildAtIndex(0)->GetIntAttribute(
+        ax::mojom::IntAttribute::kHierarchicalLevel);
+  }
+  int original_node_index = original_node.GetUnignoredIndexInParent();
+  bool node_is_radio_button =
+      (original_node.data().role == ax::mojom::Role::kRadioButton);
+
   for (int i = 0; i < local_parent->child_count(); ++i) {
     const AXNode* child = local_parent->GetUnignoredChildAtIndex(i);
 
@@ -953,6 +971,23 @@
       continue;
     }
 
+    int child_level =
+        child->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
+
+    if (child_level < original_level) {
+      // If a decrease in level occurs after the original node has been
+      // examined, stop adding to this set.
+      if (original_node_index < i)
+        break;
+      // If a decrease in level has been detected before the original node
+      // has been examined, then everything previously added to items actually
+      // belongs to a different set. Clear items vector.
+      items.clear();
+      continue;
+    } else if (child_level > original_level) {
+      continue;
+    }
+
     // If role of node is kRadioButton, only add other kRadioButtons.
     if (node_is_radio_button &&
         child->data().role == ax::mojom::Role::kRadioButton)
@@ -967,7 +1002,7 @@
     // Recurse if there is a generic container or is ignored.
     if (child->data().role == ax::mojom::Role::kGenericContainer ||
         child->data().role == ax::mojom::Role::kIgnored) {
-      PopulateOrderedSetItems(ordered_set, child, items, node_is_radio_button);
+      PopulateOrderedSetItems(ordered_set, child, items, original_node);
     }
   }
 }
@@ -979,20 +1014,15 @@
                                             const AXNode* ordered_set) {
   DCHECK(ordered_set);
   std::vector<const AXNode*> items;
-
-  // True if the role of AXNode GetPosInSet() was called on is a kRadioButton.
-  bool node_is_radio_button =
-      (node.data().role == ax::mojom::Role::kRadioButton);
-
   // Find all items within ordered_set and add to vector.
-  PopulateOrderedSetItems(ordered_set, ordered_set, items,
-                          node_is_radio_button);
+  PopulateOrderedSetItems(ordered_set, ordered_set, items, node);
 
   // Keep track of the number of elements ordered_set has.
   int32_t num_elements = 0;
-
   // Necessary for calculating set_size.
   int32_t largest_assigned_set_size = 0;
+  int hierarchical_level =
+      node.GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
 
   // Compute pos_in_set_values.
   for (size_t i = 0; i < items.size(); ++i) {
@@ -1009,6 +1039,13 @@
         std::max(pos_in_set_value,
                  item->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));
 
+    // If level is specified, use author-provided value, if present.
+    if (hierarchical_level != 0 &&
+        item->HasIntAttribute(ax::mojom::IntAttribute::kPosInSet)) {
+      pos_in_set_value =
+          item->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet);
+    }
+
     // Assign pos_in_set and update role counts.
     ordered_set_info_map_[item->id()].pos_in_set = pos_in_set_value;
     num_elements = pos_in_set_value;
@@ -1035,22 +1072,29 @@
   int32_t set_size_value = std::max(
       std::max(num_elements, largest_assigned_set_size), ordered_set_candidate);
 
-  // If ordered_set is not in the cache, assign it a new set_size.
-  if (ordered_set_info_map_.find(ordered_set->id()) ==
-      ordered_set_info_map_.end())
-    ordered_set_info_map_[ordered_set->id()] = OrderedSetInfo();
-
   // Assign set_size to ordered_set.
   // Must meet one of two conditions:
   // 1. Node role matches ordered set role.
   // 2. The node that calculations were called on is the ordered_set.
-  if (node.SetRoleMatchesItemRole(ordered_set) || ordered_set == &node)
-    ordered_set_info_map_[ordered_set->id()].set_size = set_size_value;
+  if (node.SetRoleMatchesItemRole(ordered_set) || ordered_set == &node) {
+    // If ordered_set is not in the cache, assign it a new set_size.
+    if (ordered_set_info_map_.find(ordered_set->id()) ==
+        ordered_set_info_map_.end()) {
+      ordered_set_info_map_[ordered_set->id()] = OrderedSetInfo();
+      ordered_set_info_map_[ordered_set->id()].set_size = set_size_value;
+    }
+  }
 
   // Assign set_size to items.
   for (size_t j = 0; j < items.size(); ++j) {
     const AXNode* item = items[j];
-    ordered_set_info_map_[item->id()].set_size = set_size_value;
+    // If level is specified, use author-provided value, if present.
+    if (hierarchical_level != 0 &&
+        item->HasIntAttribute(ax::mojom::IntAttribute::kSetSize))
+      ordered_set_info_map_[item->id()].set_size =
+          item->GetIntAttribute(ax::mojom::IntAttribute::kSetSize);
+    else
+      ordered_set_info_map_[item->id()].set_size = set_size_value;
   }
 }
 
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index 66be06f..9dff309 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -245,7 +245,7 @@
   void PopulateOrderedSetItems(const AXNode* ordered_set,
                                const AXNode* local_parent,
                                std::vector<const AXNode*>& items,
-                               bool node_is_radio_button) const;
+                               const AXNode& original_node) const;
 
   // Helper for GetPosInSet and GetSetSize. Computes the pos_in_set and set_size
   // values of all items in ordered_set and caches those values.
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index d92c2a6ac..2b470c8 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -2192,4 +2192,112 @@
   EXPECT_EQ(list->GetSetSize(), 2);
 }
 
+// Tests GetPosInSet and GetSetSize on a flat tree representation. According
+// to the tree representation, the three elements are siblings. However,
+// due to the presence of the kHierarchicalLevel attribute, they all belong
+// to different sets.
+TEST(AXTreeTest, TestSetSizePosInSetFlatTree) {
+  AXTreeUpdate tree_update;
+  tree_update.root_id = 1;
+  tree_update.nodes.resize(4);
+  tree_update.nodes[0].id = 1;
+  tree_update.nodes[0].role = ax::mojom::Role::kTree;
+  tree_update.nodes[0].child_ids = {2, 3, 4};
+  tree_update.nodes[1].id = 2;
+  tree_update.nodes[1].role = ax::mojom::Role::kTreeItem;  // 1 of 1
+  tree_update.nodes[1].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 1);
+  tree_update.nodes[2].id = 3;
+  tree_update.nodes[2].role = ax::mojom::Role::kTreeItem;  // 1 of 1
+  tree_update.nodes[2].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 2);
+  tree_update.nodes[3].id = 4;
+  tree_update.nodes[3].role = ax::mojom::Role::kTreeItem;  // 1 of 1
+  tree_update.nodes[3].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 3);
+  AXTree tree(tree_update);
+
+  AXNode* item1_level1 = tree.GetFromId(2);
+  EXPECT_EQ(item1_level1->GetPosInSet(), 1);
+  EXPECT_EQ(item1_level1->GetSetSize(), 1);
+  AXNode* item1_level2 = tree.GetFromId(3);
+  EXPECT_EQ(item1_level2->GetPosInSet(), 1);
+  EXPECT_EQ(item1_level2->GetSetSize(), 1);
+  AXNode* item1_level3 = tree.GetFromId(4);
+  EXPECT_EQ(item1_level3->GetPosInSet(), 1);
+  EXPECT_EQ(item1_level3->GetSetSize(), 1);
+}
+
+// Tests GetPosInSet and GetSetSize on a flat tree representation, where only
+// the level is specified.
+TEST(AXTreeTest, TestSetSizePosInSetFlatTreeLevelsOnly) {
+  AXTreeUpdate tree_update;
+  tree_update.root_id = 1;
+  tree_update.nodes.resize(9);
+  tree_update.nodes[0].id = 1;
+  tree_update.nodes[0].role = ax::mojom::Role::kTree;
+  tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6, 7, 8, 9};
+  tree_update.nodes[1].id = 2;
+  tree_update.nodes[1].role = ax::mojom::Role::kTreeItem;  // 1 of 3
+  tree_update.nodes[1].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 1);
+  tree_update.nodes[2].id = 3;
+  tree_update.nodes[2].role = ax::mojom::Role::kTreeItem;  // 1 of 2
+  tree_update.nodes[2].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 2);
+  tree_update.nodes[3].id = 4;
+  tree_update.nodes[3].role = ax::mojom::Role::kTreeItem;  // 2 of 2
+  tree_update.nodes[3].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 2);
+  tree_update.nodes[4].id = 5;
+  tree_update.nodes[4].role = ax::mojom::Role::kTreeItem;  // 2 of 3
+  tree_update.nodes[4].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 1);
+  tree_update.nodes[5].id = 6;
+  tree_update.nodes[5].role = ax::mojom::Role::kTreeItem;  // 1 of 3
+  tree_update.nodes[5].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 2);
+  tree_update.nodes[6].id = 7;
+  tree_update.nodes[6].role = ax::mojom::Role::kTreeItem;  // 2 of 3
+  tree_update.nodes[6].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 2);
+  tree_update.nodes[7].id = 8;
+  tree_update.nodes[7].role = ax::mojom::Role::kTreeItem;  // 3 of 3
+  tree_update.nodes[7].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 2);
+  tree_update.nodes[8].id = 9;
+  tree_update.nodes[8].role = ax::mojom::Role::kTreeItem;  // 3 of 3
+  tree_update.nodes[8].AddIntAttribute(
+      ax::mojom::IntAttribute::kHierarchicalLevel, 1);
+  AXTree tree(tree_update);
+
+  // The order in which we query the nodes should not matter.
+  AXNode* item3_level1 = tree.GetFromId(9);
+  EXPECT_EQ(item3_level1->GetPosInSet(), 3);
+  EXPECT_EQ(item3_level1->GetSetSize(), 3);
+  AXNode* item3_level2a = tree.GetFromId(8);
+  EXPECT_EQ(item3_level2a->GetPosInSet(), 3);
+  EXPECT_EQ(item3_level2a->GetSetSize(), 3);
+  AXNode* item2_level2a = tree.GetFromId(7);
+  EXPECT_EQ(item2_level2a->GetPosInSet(), 2);
+  EXPECT_EQ(item2_level2a->GetSetSize(), 3);
+  AXNode* item1_level2a = tree.GetFromId(6);
+  EXPECT_EQ(item1_level2a->GetPosInSet(), 1);
+  EXPECT_EQ(item1_level2a->GetSetSize(), 3);
+  AXNode* item2_level1 = tree.GetFromId(5);
+  EXPECT_EQ(item2_level1->GetPosInSet(), 2);
+  EXPECT_EQ(item2_level1->GetSetSize(), 3);
+  AXNode* item2_level2 = tree.GetFromId(4);
+  EXPECT_EQ(item2_level2->GetPosInSet(), 2);
+  EXPECT_EQ(item2_level2->GetSetSize(), 2);
+  AXNode* item1_level2 = tree.GetFromId(3);
+  EXPECT_EQ(item1_level2->GetPosInSet(), 1);
+  EXPECT_EQ(item1_level2->GetSetSize(), 2);
+  AXNode* item1_level1 = tree.GetFromId(2);
+  EXPECT_EQ(item1_level1->GetPosInSet(), 1);
+  EXPECT_EQ(item1_level1->GetSetSize(), 3);
+  AXNode* ordered_set = tree.GetFromId(1);
+  EXPECT_EQ(ordered_set->GetSetSize(), 3);
+}
+
 }  // namespace ui
diff --git a/ui/aura/mus/input_method_mus.cc b/ui/aura/mus/input_method_mus.cc
index a523876..ac30d5a 100644
--- a/ui/aura/mus/input_method_mus.cc
+++ b/ui/aura/mus/input_method_mus.cc
@@ -37,6 +37,12 @@
       .Run(handled ? EventResult::HANDLED : EventResult::UNHANDLED);
 }
 
+void OnDispatchKeyEventPostIME(InputMethodMus::EventResultCallback callback,
+                               bool handled,
+                               bool stopped_propagation) {
+  CallEventResultCallback(std::move(callback), handled);
+}
+
 ws::mojom::TextInputClientDataPtr GetTextInputClientData(
     const ui::TextInputClient* client) {
   auto data = ws::mojom::TextInputClientData::New();
@@ -112,7 +118,7 @@
   if (!GetTextInputClient() || (event->flags() & ui::EF_IS_SYNTHESIZED)) {
     return DispatchKeyEventPostIME(
         event,
-        base::BindOnce(&CallEventResultCallback, std::move(ack_callback)));
+        base::BindOnce(&OnDispatchKeyEventPostIME, std::move(ack_callback)));
   }
 
   return SendKeyEventToInputMethod(*event, std::move(ack_callback));
@@ -162,11 +168,14 @@
   if (!IsTextInputClientFocused(client))
     return;
 
+  // Sends text input client data (if changed) before caret change because
+  // InputMethodChromeOS accesses the data in its OnCaretBoundsChanged.
+  OnTextInputClientDataChanged(client);
+
   if (input_method_)
     input_method_->OnCaretBoundsChanged(client->GetCaretBounds());
 
   NotifyTextInputCaretBoundsChanged(client);
-  OnTextInputClientDataChanged(client);
 }
 
 void InputMethodMus::CancelComposition(const ui::TextInputClient* client) {
diff --git a/ui/aura/mus/input_method_mus_unittest.cc b/ui/aura/mus/input_method_mus_unittest.cc
index 0dd1b83..4c60a4f 100644
--- a/ui/aura/mus/input_method_mus_unittest.cc
+++ b/ui/aura/mus/input_method_mus_unittest.cc
@@ -8,6 +8,8 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/callback.h"
+#include "base/test/bind_test_util.h"
 #include "services/ws/public/mojom/ime/ime.mojom.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/test/mus/input_method_mus_test_api.h"
@@ -21,8 +23,8 @@
 // Empty implementation of InputMethodDelegate.
 class TestInputMethodDelegate : public ui::internal::InputMethodDelegate {
  public:
-  TestInputMethodDelegate() {}
-  ~TestInputMethodDelegate() override {}
+  TestInputMethodDelegate() = default;
+  ~TestInputMethodDelegate() override = default;
 
   bool was_dispatch_key_event_post_ime_called() const {
     return was_dispatch_key_event_post_ime_called_;
@@ -31,9 +33,9 @@
   // ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* key,
-      base::OnceCallback<void(bool)> ack_callback) override {
+      DispatchKeyEventPostIMECallback callback) override {
     was_dispatch_key_event_post_ime_called_ = true;
-    CallDispatchKeyEventPostIMEAck(key, std::move(ack_callback));
+    RunDispatchKeyEventPostIMECallback(key, std::move(callback));
     return ui::EventDispatchDetails();
   }
 
@@ -51,13 +53,17 @@
 // ProcessKeyEvent().
 class TestInputMethod : public ws::mojom::InputMethod {
  public:
-  TestInputMethod() {}
-  ~TestInputMethod() override {}
+  TestInputMethod() = default;
+  ~TestInputMethod() override = default;
 
   ProcessKeyEventCallbacks* process_key_event_callbacks() {
     return &process_key_event_callbacks_;
   }
 
+  void set_on_carent_bounds_changed_closure(base::OnceClosure closure) {
+    on_caret_bounds_changed_closure_ = std::move(closure);
+  }
+
   // ui::ime::InputMethod:
   void OnTextInputStateChanged(
       ws::mojom::TextInputStatePtr text_input_state) override {
@@ -65,6 +71,8 @@
   }
   void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override {
     was_on_caret_bounds_changed_called_ = true;
+    if (on_caret_bounds_changed_closure_)
+      std::move(on_caret_bounds_changed_closure_).Run();
   }
   void OnTextInputClientDataChanged(
       ws::mojom::TextInputClientDataPtr data) override {
@@ -119,8 +127,8 @@
   bool was_cancel_composition_called_ = false;
   bool was_show_virtual_keyboard_if_enabled_called_ = false;
   ProcessKeyEventCallbacks process_key_event_callbacks_;
-
   ws::mojom::TextInputClientDataPtr text_input_client_data_;
+  base::OnceClosure on_caret_bounds_changed_closure_;
 
   DISALLOW_COPY_AND_ASSIGN(TestInputMethod);
 };
@@ -279,10 +287,10 @@
   // ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* key,
-      base::OnceCallback<void(bool)> ack_callback) override {
+      DispatchKeyEventPostIMECallback callback) override {
     was_dispatch_key_event_post_ime_called_ = true;
     input_method_mus_->SetFocusedTextInputClient(text_input_client_);
-    CallDispatchKeyEventPostIMEAck(key, std::move(ack_callback));
+    RunDispatchKeyEventPostIMECallback(key, std::move(callback));
     return ui::EventDispatchDetails();
   }
 
@@ -491,4 +499,26 @@
   }
 }
 
+// Tests that text input client data is sent over before caret change.
+TEST_F(InputMethodMusTest, TextInputClientDataAvailableBeforeCaretChange) {
+  TestTextInputClient focused_input_client;
+  TestInputMethodDelegate input_method_delegate;
+  InputMethodMus input_method_mus(&input_method_delegate, nullptr);
+  input_method_mus.SetFocusedTextInputClient(&focused_input_client);
+
+  TestInputMethod test_input_method;
+  InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method);
+
+  EXPECT_FALSE(
+      test_input_method.was_on_text_input_client_data_changed_called());
+
+  test_input_method.set_on_carent_bounds_changed_closure(
+      base::BindLambdaForTesting([&] {
+        EXPECT_TRUE(
+            test_input_method.was_on_text_input_client_data_changed_called());
+      }));
+  InputMethodMusTestApi::CallOnCaretBoundsChanged(&input_method_mus,
+                                                  &focused_input_client);
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/text_input_client_impl.cc b/ui/aura/mus/text_input_client_impl.cc
index 82640cd..22621d6 100644
--- a/ui/aura/mus/text_input_client_impl.cc
+++ b/ui/aura/mus/text_input_client_impl.cc
@@ -20,9 +20,10 @@
 // event was handled.
 void OnKeyEventProcessed(
     ws::mojom::TextInputClient::DispatchKeyEventPostIMECallback callback,
-    bool handled) {
+    bool handled,
+    bool stopped_propagation) {
   if (callback)
-    std::move(callback).Run(handled);
+    std::move(callback).Run(handled, stopped_propagation);
 }
 
 }  // namespace
@@ -82,8 +83,10 @@
     std::unique_ptr<ui::Event> event,
     DispatchKeyEventPostIMECallback callback) {
   if (!delegate_) {
-    if (callback)
-      std::move(callback).Run(false);
+    if (callback) {
+      std::move(callback).Run(/* handled */ false,
+                              /* stopped_propagation */ false);
+    }
     return;
   }
   ui::KeyEvent* key_event = event->AsKeyEvent();
@@ -100,8 +103,8 @@
     return;  // Event is being processed async.
 
   // The delegate finished processing the event. Run the ack now.
-  const bool handled = key_event->handled();
-  key_event->WillHandleAsync().Run(handled);
+  key_event->WillHandleAsync().Run(key_event->handled(),
+                                   key_event->stopped_propagation());
 }
 
 void TextInputClientImpl::EnsureCaretNotInRect(const gfx::Rect& rect) {
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 346f1c8c..8950782 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -640,10 +640,17 @@
   // Server should always supply a LocalSurfaceIdAllocation for roots.
   DCHECK(local_surface_id_allocation);
 
+  WindowTreeHostMus* window_tree_host = GetWindowTreeHostMus(window);
+  // This function is always called with the most recent LocalSurfaceId from the
+  // server. As such, the pending LocalSurfaceId is no longer applicabable and
+  // should be discarded. If we didn't reset it here, it's entirely possible a
+  // future change could attempt to incorrectly apply an old LocalSurfaceId.
+  if (window_tree_host->has_pending_local_surface_id_from_server())
+    window_tree_host->TakePendingLocalSurfaceIdFromServer();
   window->UpdateLocalSurfaceIdFromParent(*local_surface_id_allocation);
 
-  GetWindowTreeHostMus(window)->SetBoundsFromServer(
-      revert_bounds, window->GetLocalSurfaceIdAllocation());
+  window_tree_host->SetBoundsFromServer(revert_bounds,
+                                        window->GetLocalSurfaceIdAllocation());
 
   window->DidSetWindowTreeHostBoundsFromServer();
 }
@@ -663,9 +670,12 @@
         window->GetWindow()->GetLocalSurfaceIdAllocation();
     window_tree_host->SetBoundsFromServer(window_tree_host->bounds_in_dip(),
                                           lsia);
-    // This does *not* use SetWindowBoundsFromServer() as it leads to race
-    // conditions. In particular, it might incorrectly lead to the client
-    // changing the bounds when the server is also trying to change the bounds.
+    // Send the newly generated id to the server. This does *not* use
+    // WindowTreeHost:SetBounds() (which notifies the server of a bounds and id)
+    // as WindowTreeHost::SetBounds() leads to race conditions. In particular,
+    // it might incorrectly lead to the client changing the bounds when the
+    // server is also trying to change the bounds. The important thing here is
+    // to update the server of the id, not the bounds.
     tree_->UpdateLocalSurfaceIdFromChild(window->server_id(), lsia);
   } else {
     window_tree_host->SetBoundsFromServer(
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 6109d54..b614be2 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -2886,6 +2886,10 @@
   // No new bounds changes should be generated.
   EXPECT_EQ(0u,
             window_tree()->GetChangeCountForType(WindowTreeChangeType::BOUNDS));
+
+  // As the server supplied a new LocalSurfaceId the pending LocalSurfaceId
+  // should be cleared.
+  EXPECT_FALSE(top_level->host->has_pending_local_surface_id_from_server());
 }
 
 TEST_F(WindowTreeClientTest, OnEmbedGetsLocalSurfaceId) {
diff --git a/ui/aura/screen_ozone.cc b/ui/aura/screen_ozone.cc
index 28493ac..f99141b 100644
--- a/ui/aura/screen_ozone.cc
+++ b/ui/aura/screen_ozone.cc
@@ -4,6 +4,7 @@
 
 #include "ui/aura/screen_ozone.h"
 
+#include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/display/display.h"
@@ -30,7 +31,18 @@
 
   aura::WindowTreeHost* host =
       aura::WindowTreeHost::GetForAcceleratedWidget(widget);
-  return host ? host->window() : nullptr;
+  if (!host)
+    return nullptr;
+
+  gfx::NativeWindow window = host->window();
+  gfx::Point local_point = point;
+
+  aura::client::ScreenPositionClient* position_client =
+      aura::client::GetScreenPositionClient(window);
+  if (position_client)
+    position_client->ConvertPointFromScreen(window, &local_point);
+
+  return window->GetEventHandlerForPoint(local_point);
 }
 
 int ScreenOzone::GetNumDisplays() const {
@@ -89,4 +101,4 @@
   return host->GetAcceleratedWidget();
 }
 
-}  // namespace aura
\ No newline at end of file
+}  // namespace aura
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index b3db18d..7e3e64b 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -259,7 +259,7 @@
 
 ui::EventDispatchDetails WindowTreeHost::DispatchKeyEventPostIME(
     ui::KeyEvent* event,
-    base::OnceCallback<void(bool)> ack_callback) {
+    DispatchKeyEventPostIMECallback callback) {
   // If dispatch to IME is already disabled we shouldn't reach here.
   DCHECK(!dispatcher_->should_skip_ime());
   dispatcher_->set_skip_ime(true);
@@ -268,7 +268,7 @@
       event_sink()->OnEventFromSource(event);
   if (!dispatch_details.dispatcher_destroyed)
     dispatcher_->set_skip_ime(false);
-  CallDispatchKeyEventPostIMEAck(event, std::move(ack_callback));
+  RunDispatchKeyEventPostIMECallback(event, std::move(callback));
   return dispatch_details;
 }
 
diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h
index 5a33164..e4a397b 100644
--- a/ui/aura/window_tree_host.h
+++ b/ui/aura/window_tree_host.h
@@ -178,7 +178,7 @@
   // Overridden from ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* event,
-      base::OnceCallback<void(bool)> ack_callback) final;
+      DispatchKeyEventPostIMECallback callback) final;
 
   // Overridden from ui::EventSource:
   ui::EventSink* GetEventSink() override;
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index b1d52ade..6bc20ff9 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -317,6 +317,13 @@
       "dragdrop/file_info.cc",
       "dragdrop/file_info.h",
       "emoji/emoji_panel_helper.h",
+      "idle/idle.cc",
+      "idle/idle.h",
+      "idle/idle_android.cc",
+      "idle/idle_chromeos.cc",
+      "idle/idle_linux.cc",
+      "idle/idle_mac.mm",
+      "idle/idle_win.cc",
       "pointer/pointer_device.h",
       "pointer/pointer_device_util.cc",
       "pointer/touch_editing_controller.cc",
@@ -365,6 +372,7 @@
 
   if (is_fuchsia) {
     sources += [
+      "idle/idle_fuchsia.cc",
       "l10n/l10n_util_posix.cc",
       "resource/resource_bundle_fuchsia.cc",
     ]
@@ -425,6 +433,17 @@
   if (use_x11) {
     public_deps += [ "//ui/base/x" ]
     configs += [ "//build/config/linux:x11" ]
+
+    if (!is_chromeos) {
+      sources += [
+        "idle/idle_query_x11.cc",
+        "idle/idle_query_x11.h",
+        "idle/screensaver_window_finder_x11.cc",
+        "idle/screensaver_window_finder_x11.h",
+      ]
+      configs += [ "//build/config/linux:xscrnsaver" ]
+      deps += [ "//ui/gfx/x" ]
+    }
   }
 
   if (use_x11 && use_aura) {
@@ -475,6 +494,14 @@
     ]
   }
 
+  if (is_chromeos) {
+    deps += [
+      "//chromeos",
+      "//chromeos/dbus",
+    ]
+    sources -= [ "idle/idle_linux.cc" ]
+  }
+
   if (is_chromeos || (use_aura && is_linux && !use_x11)) {
     sources += [
       "dragdrop/os_exchange_data_provider_aura.cc",
@@ -572,6 +599,7 @@
     sources -= [
       "cursor/cursor_android.cc",
       "default_theme_provider.cc",
+      "idle/idle.cc",
       "l10n/l10n_font_util.cc",
       "models/button_menu_item_model.cc",
       "pointer/touch_editing_controller.cc",
diff --git a/ui/base/idle/BUILD.gn b/ui/base/idle/BUILD.gn
deleted file mode 100644
index 30cafe4b..0000000
--- a/ui/base/idle/BUILD.gn
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/jumbo.gni")
-import("//build/config/ui.gni")
-import("//testing/test.gni")
-if (is_android) {
-  import("//build/config/android/rules.gni")
-} else if (is_mac) {
-  import("//build/config/mac/rules.gni")
-}
-jumbo_component("idle") {
-  output_name = "ui_base_idle"
-  defines = [ "IS_UI_BASE_IDLE_IMPL" ]
-  sources = [
-    "idle.cc",
-    "idle.h",
-    "idle_android.cc",
-    "idle_chromeos.cc",
-    "idle_linux.cc",
-    "idle_mac.mm",
-    "idle_win.cc",
-  ]
-
-  deps = [
-    "//ui/base",
-  ]
-  public_deps = []
-  libs = []
-
-  if (is_fuchsia) {
-    sources += [ "idle_fuchsia.cc" ]
-  }
-
-  if (use_x11) {
-    public_deps += [ "//ui/base/x" ]
-    deps += [ "//ui/gfx/x" ]
-    configs += [
-      "//build/config/linux:x11",
-      "//build/config/linux:xscrnsaver",
-    ]
-    sources += [
-      "idle_query_x11.cc",
-      "idle_query_x11.h",
-      "screensaver_window_finder_x11.cc",
-      "screensaver_window_finder_x11.h",
-    ]
-  }
-
-  if (is_chromeos) {
-    deps += [
-      "//chromeos",
-      "//chromeos/dbus",
-    ]
-    sources -= [ "idle_linux.cc" ]
-  }
-
-  if (is_android) {
-    sources -= [ "idle.cc" ]
-    deps += [ "//ui/base:ui_base_jni_headers" ]
-  }
-
-  if (is_mac) {
-    libs += [
-      "Carbon.framework",
-      "Foundation.framework",
-    ]
-  }
-}
diff --git a/ui/base/idle/idle.h b/ui/base/idle/idle.h
index 83420c7..269ca44 100644
--- a/ui/base/idle/idle.h
+++ b/ui/base/idle/idle.h
@@ -5,8 +5,8 @@
 #ifndef UI_BASE_IDLE_IDLE_H_
 #define UI_BASE_IDLE_IDLE_H_
 
-#include "base/component_export.h"
 #include "build/build_config.h"
+#include "ui/base/ui_base_export.h"
 
 namespace ui {
 
@@ -20,18 +20,18 @@
 
 // For MacOSX, InitIdleMonitor needs to be called first to setup the monitor.
 #if defined(OS_MACOSX)
-COMPONENT_EXPORT(UI_BASE_IDLE) void InitIdleMonitor();
+UI_BASE_EXPORT void InitIdleMonitor();
 #endif
 
 // Calculate the Idle state. |idle_threshold| is the amount of time (in seconds)
 // before the user is considered idle.
-COMPONENT_EXPORT(UI_BASE_IDLE) IdleState CalculateIdleState(int idle_threshold);
+UI_BASE_EXPORT IdleState CalculateIdleState(int idle_threshold);
 
 // Calculate Idle time in seconds.
-COMPONENT_EXPORT(UI_BASE_IDLE) int CalculateIdleTime();
+UI_BASE_EXPORT int CalculateIdleTime();
 
 // Checks synchronously if Idle state is IDLE_STATE_LOCKED.
-COMPONENT_EXPORT(UI_BASE_IDLE) bool CheckIdleStateIsLocked();
+UI_BASE_EXPORT bool CheckIdleStateIsLocked();
 
 }  // namespace ui
 
diff --git a/ui/base/ime/input_method_auralinux_unittest.cc b/ui/base/ime/input_method_auralinux_unittest.cc
index 589c998..594e063 100644
--- a/ui/base/ime/input_method_auralinux_unittest.cc
+++ b/ui/base/ime/input_method_auralinux_unittest.cc
@@ -172,7 +172,7 @@
 
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* key_event,
-      base::OnceCallback<void(bool)> ack_callback) override {
+      DispatchKeyEventPostIMECallback callback) override {
     std::string action;
     switch (key_event->type()) {
       case ET_KEY_PRESSED:
@@ -188,7 +188,7 @@
     ss << key_event->key_code();
     action += std::string(ss.str());
     TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(action));
-    CallDispatchKeyEventPostIMEAck(key_event, std::move(ack_callback));
+    RunDispatchKeyEventPostIMECallback(key_event, std::move(callback));
     return ui::EventDispatchDetails();
   }
 
diff --git a/ui/base/ime/input_method_base.cc b/ui/base/ime/input_method_base.cc
index c57c8b57..13e0005 100644
--- a/ui/base/ime/input_method_base.cc
+++ b/ui/base/ime/input_method_base.cc
@@ -163,12 +163,14 @@
 
 ui::EventDispatchDetails InputMethodBase::DispatchKeyEventPostIME(
     ui::KeyEvent* event,
-    base::OnceCallback<void(bool)> ack_callback) const {
-  if (delegate_)
-    return delegate_->DispatchKeyEventPostIME(event, std::move(ack_callback));
+    ResultCallback result_callback) const {
+  if (delegate_) {
+    return delegate_->DispatchKeyEventPostIME(event,
+                                              std::move(result_callback));
+  }
 
-  if (ack_callback)
-    std::move(ack_callback).Run(false);
+  if (result_callback)
+    std::move(result_callback).Run(false, false);
   return EventDispatchDetails();
 }
 
diff --git a/ui/base/ime/input_method_base.h b/ui/base/ime/input_method_base.h
index 10a89ff..bd5b8cfc 100644
--- a/ui/base/ime/input_method_base.h
+++ b/ui/base/ime/input_method_base.h
@@ -71,6 +71,9 @@
   InputMethodKeyboardController* GetInputMethodKeyboardController() override;
 
  protected:
+  // See InputMethodDelegate for details on this.
+  using ResultCallback = base::OnceCallback<void(bool, bool)>;
+
   explicit InputMethodBase(internal::InputMethodDelegate* delegate = nullptr);
   InputMethodBase(internal::InputMethodDelegate* delegate,
                   std::unique_ptr<InputMethodKeyboardController> controller);
@@ -106,9 +109,11 @@
   // input type is not TEXT_INPUT_TYPE_NONE.
   void OnInputMethodChanged() const;
 
+  // See InputMethodDelegate::DispatchKeyEventPostIME(() for details on
+  // callback.
   virtual ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* event,
-      base::OnceCallback<void(bool)> ack_callback) const WARN_UNUSED_RESULT;
+      ResultCallback result_callback) const WARN_UNUSED_RESULT;
 
   // Convenience method to notify all observers of TextInputClient changes.
   void NotifyTextInputStateChanged(const TextInputClient* client);
diff --git a/ui/base/ime/input_method_chromeos.cc b/ui/base/ime/input_method_chromeos.cc
index cb583c9..61dc589 100644
--- a/ui/base/ime/input_method_chromeos.cc
+++ b/ui/base/ime/input_method_chromeos.cc
@@ -30,6 +30,32 @@
 #include "ui/gfx/geometry/rect.h"
 
 namespace ui {
+namespace {
+
+void RunAckCallbackFromPostImeCallback(
+    InputMethodChromeOS::AckCallback callback,
+    bool handled,
+    bool stopped_propagation) {
+  if (callback)
+    std::move(callback).Run(handled);
+}
+
+void RunImeCallbackIfValid(
+    internal::InputMethodDelegate::DispatchKeyEventPostIMECallback callback,
+    bool handled,
+    bool stopped_propagation) {
+  if (!callback)
+    return;
+  std::move(callback).Run(handled, stopped_propagation);
+}
+
+internal::InputMethodDelegate::DispatchKeyEventPostIMECallback
+CreateResultCallbackFromAckCallback(InputMethodChromeOS::AckCallback callback) {
+  return base::BindOnce(&RunAckCallbackFromPostImeCallback,
+                        std::move(callback));
+}
+
+}  // namespace
 
 // InputMethodChromeOS implementation -----------------------------------------
 InputMethodChromeOS::InputMethodChromeOS(
@@ -58,6 +84,9 @@
 ui::EventDispatchDetails InputMethodChromeOS::DispatchKeyEvent(
     ui::KeyEvent* event,
     AckCallback ack_callback) {
+  ResultCallback result_callback =
+      CreateResultCallbackFromAckCallback(std::move(ack_callback));
+
   DCHECK(event->IsKeyEvent());
   DCHECK(!(event->flags() & ui::EF_IS_SYNTHESIZED));
 
@@ -106,7 +135,7 @@
         // TODO(shuchen): Eventually, the language input keys should be handed
         // over to the IME extension to process. And IMF can handle if the IME
         // extension didn't handle.
-        return DispatchKeyEventPostIME(event, std::move(ack_callback));
+        return DispatchKeyEventPostIME(event, std::move(result_callback));
       }
     }
   }
@@ -121,12 +150,13 @@
       if (ExecuteCharacterComposer(*event)) {
         // Treating as PostIME event if character composer handles key event and
         // generates some IME event,
-        return ProcessKeyEventPostIME(event, std::move(ack_callback), false,
-                                      true);
+        return ProcessKeyEventPostIME(event, std::move(result_callback), false,
+                                      /* handled */ true,
+                                      /* stopped_propagation */ true);
       }
-      return ProcessUnfilteredKeyPressEvent(event, std::move(ack_callback));
+      return ProcessUnfilteredKeyPressEvent(event, std::move(result_callback));
     }
-    return DispatchKeyEventPostIME(event, std::move(ack_callback));
+    return DispatchKeyEventPostIME(event, std::move(result_callback));
   }
 
   handling_key_event_ = true;
@@ -136,23 +166,23 @@
                        weak_ptr_factory_.GetWeakPtr(),
                        // Pass the ownership of the new copied event.
                        base::Owned(new ui::KeyEvent(*event)),
-                       std::move(ack_callback));
+                       std::move(result_callback));
     GetEngine()->ProcessKeyEvent(*event, std::move(callback));
     return ui::EventDispatchDetails();
   }
-  return ProcessKeyEventDone(event, std::move(ack_callback), false);
+  return ProcessKeyEventDone(event, std::move(result_callback), false);
 }
 
 void InputMethodChromeOS::KeyEventDoneCallback(ui::KeyEvent* event,
-                                               AckCallback ack_callback,
+                                               ResultCallback result_callback,
                                                bool is_handled) {
   ignore_result(
-      ProcessKeyEventDone(event, std::move(ack_callback), is_handled));
+      ProcessKeyEventDone(event, std::move(result_callback), is_handled));
 }
 
 ui::EventDispatchDetails InputMethodChromeOS::ProcessKeyEventDone(
     ui::KeyEvent* event,
-    AckCallback ack_callback,
+    ResultCallback result_callback,
     bool is_handled) {
   DCHECK(event);
   if (event->type() == ET_KEY_PRESSED) {
@@ -168,8 +198,9 @@
   }
   ui::EventDispatchDetails details;
   if (event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED) {
-    details = ProcessKeyEventPostIME(event, std::move(ack_callback), false,
-                                     is_handled);
+    details =
+        ProcessKeyEventPostIME(event, std::move(result_callback), false,
+                               is_handled, /* stopped_propagation */ false);
   }
   handling_key_event_ = false;
   return details;
@@ -367,25 +398,26 @@
 
 ui::EventDispatchDetails InputMethodChromeOS::ProcessKeyEventPostIME(
     ui::KeyEvent* event,
-    AckCallback ack_callback,
+    ResultCallback result_callback,
     bool skip_process_filtered,
-    bool handled) {
+    bool handled,
+    bool stopped_propagation) {
   TextInputClient* client = GetTextInputClient();
   if (!client) {
     // As ibus works asynchronously, there is a chance that the focused client
     // loses focus before this method gets called.
-    return DispatchKeyEventPostIME(event, std::move(ack_callback));
+    return DispatchKeyEventPostIME(event, std::move(result_callback));
   }
 
   ui::EventDispatchDetails dispatch_details;
   if (event->type() == ET_KEY_PRESSED && handled && !skip_process_filtered)
-    return ProcessFilteredKeyPressEvent(event, std::move(ack_callback));
+    return ProcessFilteredKeyPressEvent(event, std::move(result_callback));
 
   // In case the focus was changed by the key event. The |context_| should have
   // been reset when the focused window changed.
   if (client != GetTextInputClient()) {
-    if (ack_callback)
-      std::move(ack_callback).Run(false);
+    RunImeCallbackIfValid(std::move(result_callback), /* handled */ false,
+                          /* stopped_propagation*/ false);
     return dispatch_details;
   }
   if (HasInputMethodResult())
@@ -394,32 +426,32 @@
   // In case the focus was changed when sending input method results to the
   // focused window.
   if (client != GetTextInputClient()) {
-    if (ack_callback)
-      std::move(ack_callback).Run(false);
+    RunImeCallbackIfValid(std::move(result_callback), /* handled */ false,
+                          /* stopped_propagation */ false);
     return dispatch_details;
   }
 
   if (handled) {
-    if (ack_callback)
-      std::move(ack_callback).Run(true);
+    RunImeCallbackIfValid(std::move(result_callback),
+                          /* handled */ true, stopped_propagation);
     return dispatch_details;  // IME handled the key event. do not forward.
   }
 
   if (event->type() == ET_KEY_PRESSED)
-    return ProcessUnfilteredKeyPressEvent(event, std::move(ack_callback));
+    return ProcessUnfilteredKeyPressEvent(event, std::move(result_callback));
 
   if (event->type() == ET_KEY_RELEASED)
-    return DispatchKeyEventPostIME(event, std::move(ack_callback));
+    return DispatchKeyEventPostIME(event, std::move(result_callback));
   return dispatch_details;
 }
 
 ui::EventDispatchDetails InputMethodChromeOS::ProcessFilteredKeyPressEvent(
     ui::KeyEvent* event,
-    AckCallback ack_callback) {
+    ResultCallback result_callback) {
   auto callback = base::Bind(
       &InputMethodChromeOS::PostProcessFilteredKeyPressEvent,
       weak_ptr_factory_.GetWeakPtr(), base::Owned(new ui::KeyEvent(*event)),
-      GetTextInputClient(), Passed(&ack_callback));
+      GetTextInputClient(), Passed(&result_callback));
 
   if (NeedInsertChar())
     return DispatchKeyEventPostIME(event, std::move(callback));
@@ -440,7 +472,8 @@
 void InputMethodChromeOS::PostProcessFilteredKeyPressEvent(
     ui::KeyEvent* event,
     TextInputClient* prev_client,
-    AckCallback ack_callback,
+    ResultCallback result_callback,
+    bool handled,
     bool stopped_propagation) {
   // In case the focus was changed by the key event.
   if (GetTextInputClient() != prev_client)
@@ -448,34 +481,35 @@
 
   if (stopped_propagation) {
     ResetContext();
-    if (ack_callback)
-      std::move(ack_callback).Run(true);
+    RunImeCallbackIfValid(std::move(result_callback), handled,
+                          /* stopped_propagation */ true);
     return;
   }
-  ignore_result(
-      ProcessKeyEventPostIME(event, std::move(ack_callback), true, true));
+  ignore_result(ProcessKeyEventPostIME(event, std::move(result_callback), true,
+                                       true, false));
 }
 
 ui::EventDispatchDetails InputMethodChromeOS::ProcessUnfilteredKeyPressEvent(
     ui::KeyEvent* event,
-    AckCallback ack_callback) {
+    ResultCallback result_callback) {
   return DispatchKeyEventPostIME(
       event,
       base::BindOnce(&InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent,
                      weak_ptr_factory_.GetWeakPtr(),
                      base::Owned(new ui::KeyEvent(*event)),
-                     GetTextInputClient(), std::move(ack_callback)));
+                     GetTextInputClient(), std::move(result_callback)));
 }
 
 void InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent(
     ui::KeyEvent* event,
     TextInputClient* prev_client,
-    AckCallback ack_callback,
+    ResultCallback result_callback,
+    bool handled,
     bool stopped_propagation) {
   if (stopped_propagation) {
     ResetContext();
-    if (ack_callback)
-      std::move(ack_callback).Run(true);  // true matches |stopped_propagation|.
+    RunImeCallbackIfValid(std::move(result_callback), handled,
+                          /* stopped_propagation */ true);
     return;
   }
 
@@ -488,8 +522,8 @@
   // We should return here not to send the Tab key event to RWHV.
   TextInputClient* client = GetTextInputClient();
   if (!client || client != prev_client) {
-    if (ack_callback)
-      std::move(ack_callback).Run(false);
+    RunImeCallbackIfValid(std::move(result_callback), /* handled */ false,
+                          /* stopped_propagation */ false);
     return;
   }
 
@@ -500,8 +534,8 @@
   if (ch)
     client->InsertChar(*event);
 
-  if (ack_callback)
-    std::move(ack_callback).Run(false);
+  RunImeCallbackIfValid(std::move(result_callback), handled,
+                        stopped_propagation);
 }
 
 void InputMethodChromeOS::ProcessInputMethodResult(ui::KeyEvent* event,
diff --git a/ui/base/ime/input_method_chromeos.h b/ui/base/ime/input_method_chromeos.h
index 7787491..300dd23 100644
--- a/ui/base/ime/input_method_chromeos.h
+++ b/ui/base/ime/input_method_chromeos.h
@@ -56,9 +56,10 @@
   // Process a key returned from the input method.
   virtual ui::EventDispatchDetails ProcessKeyEventPostIME(
       ui::KeyEvent* event,
-      AckCallback ack_callback,
+      ResultCallback result_callback,
       bool skip_process_filtered,
-      bool handled) WARN_UNUSED_RESULT;
+      bool handled,
+      bool stopped_propagation) WARN_UNUSED_RESULT;
 
   // Resets context and abandon all pending results and key events.
   void ResetContext();
@@ -79,23 +80,25 @@
   // when dispatching post IME.
   ui::EventDispatchDetails ProcessFilteredKeyPressEvent(
       ui::KeyEvent* event,
-      AckCallback ack_callback) WARN_UNUSED_RESULT;
+      ResultCallback result_callback) WARN_UNUSED_RESULT;
 
   // Post processes a key event that was already filtered by the input method.
   void PostProcessFilteredKeyPressEvent(ui::KeyEvent* event,
                                         TextInputClient* prev_client,
-                                        AckCallback ack_callback,
+                                        ResultCallback result_callback,
+                                        bool handled,
                                         bool stopped_propagation);
 
   // Processes a key event that was not filtered by the input method.
   ui::EventDispatchDetails ProcessUnfilteredKeyPressEvent(
       ui::KeyEvent* event,
-      AckCallback ack_callback) WARN_UNUSED_RESULT;
+      ResultCallback result_callback) WARN_UNUSED_RESULT;
 
   // Post processes a key event that was unfiltered by the input method.
   void PostProcessUnfilteredKeyPressEvent(ui::KeyEvent* event,
                                           TextInputClient* prev_client,
-                                          AckCallback ack_callback,
+                                          ResultCallback result_callback,
+                                          bool handled,
                                           bool stopped_propagation);
 
   // Sends input method result caused by the given key event to the focused text
@@ -125,10 +128,10 @@
 
   // Callback function for IMEEngineHandlerInterface::ProcessKeyEvent.
   void KeyEventDoneCallback(ui::KeyEvent* event,
-                            AckCallback ack_callback,
+                            ResultCallback result_callback,
                             bool is_handled);
   ui::EventDispatchDetails ProcessKeyEventDone(ui::KeyEvent* event,
-                                               AckCallback ack_callback,
+                                               ResultCallback result_callback,
                                                bool is_handled)
       WARN_UNUSED_RESULT;
 
diff --git a/ui/base/ime/input_method_chromeos_unittest.cc b/ui/base/ime/input_method_chromeos_unittest.cc
index 559f34d..fd1101d1 100644
--- a/ui/base/ime/input_method_chromeos_unittest.cc
+++ b/ui/base/ime/input_method_chromeos_unittest.cc
@@ -8,11 +8,13 @@
 #include <stdint.h>
 
 #include <memory>
-#include <cstring>
+#include <queue>
+#include <string>
 
 #include "base/i18n/char_iterator.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
@@ -69,13 +71,16 @@
   };
 
   // Overridden from InputMethodChromeOS:
-  ui::EventDispatchDetails ProcessKeyEventPostIME(ui::KeyEvent* key_event,
-                                                  AckCallback ack_callback,
-                                                  bool skip_process_filtered,
-                                                  bool handled) override {
+  ui::EventDispatchDetails ProcessKeyEventPostIME(
+      ui::KeyEvent* key_event,
+      ResultCallback result_callback,
+      bool skip_process_filtered,
+      bool handled,
+      bool stopped_propagation) override {
     ui::EventDispatchDetails details =
         InputMethodChromeOS::ProcessKeyEventPostIME(
-            key_event, std::move(ack_callback), skip_process_filtered, handled);
+            key_event, std::move(result_callback), skip_process_filtered,
+            handled, stopped_propagation);
     if (!skip_process_filtered) {
       process_key_event_post_ime_args_.event = *key_event;
       process_key_event_post_ime_args_.handled = handled;
@@ -196,6 +201,29 @@
                void(const std::string&, uint32_t, uint32_t, uint32_t));
 };
 
+class CachingInputMethodDelegate : public internal::InputMethodDelegate {
+ public:
+  CachingInputMethodDelegate() = default;
+  ~CachingInputMethodDelegate() override = default;
+
+  std::queue<DispatchKeyEventPostIMECallback>& callbacks() {
+    return callbacks_;
+  }
+
+  // internal::InputMethodDelegate:
+  EventDispatchDetails DispatchKeyEventPostIME(
+      KeyEvent* key_event,
+      DispatchKeyEventPostIMECallback callback) override {
+    callbacks_.emplace(std::move(callback));
+    return EventDispatchDetails();
+  }
+
+ private:
+  std::queue<DispatchKeyEventPostIMECallback> callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(CachingInputMethodDelegate);
+};
+
 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
                                 public testing::Test,
                                 public DummyTextInputClient {
@@ -221,7 +249,13 @@
     IMEBridge::Get()->SetCandidateWindowHandler(
         mock_ime_candidate_window_handler_.get());
 
-    ime_.reset(new TestableInputMethodChromeOS(this));
+    internal::InputMethodDelegate* ime_delegate = this;
+    if (ShouldCreateCachingInputMethodDelegate()) {
+      caching_input_method_delegate_ =
+          std::make_unique<CachingInputMethodDelegate>();
+      ime_delegate = caching_input_method_delegate_.get();
+    }
+    ime_ = std::make_unique<TestableInputMethodChromeOS>(ime_delegate);
     ime_->SetFocusedTextInputClient(this);
 
     // InputMethodManager owns and delete it in InputMethodManager::Shutdown().
@@ -247,11 +281,11 @@
   // Overridden from ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* event,
-      base::OnceCallback<void(bool)> ack_callback) override {
+      DispatchKeyEventPostIMECallback callback) override {
     dispatched_key_event_ = *event;
     if (stop_propagation_post_ime_)
       event->StopPropagation();
-    CallDispatchKeyEventPostIMEAck(event, std::move(ack_callback));
+    RunDispatchKeyEventPostIMECallback(event, std::move(callback));
     return ui::EventDispatchDetails();
   }
 
@@ -318,8 +352,13 @@
     caret_bounds_ = gfx::Rect();
   }
 
+ protected:
+  virtual bool ShouldCreateCachingInputMethodDelegate() { return false; }
+
   std::unique_ptr<TestableInputMethodChromeOS> ime_;
 
+  std::unique_ptr<CachingInputMethodDelegate> caching_input_method_delegate_;
+
   // Copy of the dispatched key event.
   ui::KeyEvent dispatched_key_event_;
 
@@ -1019,8 +1058,9 @@
                       0,
                       DomKey::DeadKeyFromCombiningCharacter('^'),
                       EventTimeForNow());
-  ime_->ProcessKeyEventPostIME(&eventA, InputMethodChromeOS::AckCallback(),
-                               false, true);
+  ime_->ProcessKeyEventPostIME(
+      &eventA, InputMethodDelegate::DispatchKeyEventPostIMECallback(), false,
+      true, true);
 
   const ui::KeyEvent& key_event = dispatched_key_event_;
 
@@ -1054,4 +1094,98 @@
   EXPECT_FALSE(input_method_manager_->state()->is_jp_ime());
 }
 
+class InputMethodChromeOSAsyncTest : public InputMethodChromeOSTest {
+ public:
+  InputMethodChromeOSAsyncTest() = default;
+  ~InputMethodChromeOSAsyncTest() override = default;
+
+ protected:
+  // InputMethodChromeOSTest:
+  bool ShouldCreateCachingInputMethodDelegate() override { return true; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSAsyncTest);
+};
+
+TEST_F(InputMethodChromeOSAsyncTest, StopPropagation) {
+  KeyEvent event(ET_KEY_PRESSED, VKEY_A, EF_NONE);
+
+  // As CachingInputMethodDelegate doesn't immediately dispatch, the callback
+  // should not be run immediately.
+  bool async_callback_run = false;
+  bool async_callback_handled_result = false;
+  ime_->DispatchKeyEvent(&event, base::BindLambdaForTesting([&](bool handled) {
+    async_callback_handled_result = handled;
+    async_callback_run = true;
+  }));
+  EXPECT_FALSE(async_callback_run);
+  ASSERT_EQ(1u, caching_input_method_delegate_->callbacks().size());
+
+  // Run the queued callback in the delegate, which should then notify the
+  // callback supplied to DispatchKeyEvent().
+  auto callback =
+      std::move(caching_input_method_delegate_->callbacks().front());
+  caching_input_method_delegate_->callbacks().pop();
+  std::move(callback).Run(/* handled */ true, /* stopped_propagation */ true);
+  EXPECT_TRUE(async_callback_run);
+  EXPECT_TRUE(async_callback_handled_result);
+
+  // Because |stopped_propagation| was false, no character should be inserted.
+  EXPECT_FALSE(inserted_char_);
+}
+
+TEST_F(InputMethodChromeOSAsyncTest, DidNotStopPropagation) {
+  KeyEvent event(ET_KEY_PRESSED, VKEY_A, EF_NONE);
+
+  // As CachingInputMethodDelegate doesn't immediately dispatch, the callback
+  // should not be run immediately.
+  bool async_callback_run = false;
+  bool async_callback_handled_result = false;
+  ime_->DispatchKeyEvent(&event, base::BindLambdaForTesting([&](bool handled) {
+    async_callback_handled_result = handled;
+    async_callback_run = true;
+  }));
+  EXPECT_FALSE(async_callback_run);
+  ASSERT_EQ(1u, caching_input_method_delegate_->callbacks().size());
+
+  // Run the queued callback in the delegate, which should then notify the
+  // callback supplied to DispatchKeyEvent().
+  auto callback =
+      std::move(caching_input_method_delegate_->callbacks().front());
+  caching_input_method_delegate_->callbacks().pop();
+  std::move(callback).Run(/* handled */ true, /* stopped_propagation */ false);
+  EXPECT_TRUE(async_callback_run);
+  EXPECT_TRUE(async_callback_handled_result);
+
+  // Because |stopped_propagation| was true, a character should be inserted.
+  EXPECT_TRUE(inserted_char_);
+}
+
+TEST_F(InputMethodChromeOSAsyncTest, UnhandledAndDidNotStopPropatation) {
+  KeyEvent event(ET_KEY_PRESSED, VKEY_A, EF_NONE);
+
+  // As CachingInputMethodDelegate doesn't immediately dispatch, the callback
+  // should not be run immediately.
+  bool async_callback_run = false;
+  bool async_callback_handled_result = false;
+  ime_->DispatchKeyEvent(&event, base::BindLambdaForTesting([&](bool handled) {
+    async_callback_handled_result = handled;
+    async_callback_run = true;
+  }));
+  EXPECT_FALSE(async_callback_run);
+  ASSERT_EQ(1u, caching_input_method_delegate_->callbacks().size());
+
+  // Run the queued callback in the delegate, which should then notify the
+  // callback supplied to DispatchKeyEvent().
+  auto callback =
+      std::move(caching_input_method_delegate_->callbacks().front());
+  caching_input_method_delegate_->callbacks().pop();
+  std::move(callback).Run(/* handled */ false, /* stopped_propagation */ false);
+  EXPECT_TRUE(async_callback_run);
+  EXPECT_FALSE(async_callback_handled_result);
+
+  // Because |stopped_propagation| was true, a character should be inserted.
+  EXPECT_TRUE(inserted_char_);
+}
+
 }  // namespace ui
diff --git a/ui/base/ime/input_method_delegate.cc b/ui/base/ime/input_method_delegate.cc
index f2578a514..dc950847 100644
--- a/ui/base/ime/input_method_delegate.cc
+++ b/ui/base/ime/input_method_delegate.cc
@@ -11,11 +11,13 @@
 namespace internal {
 
 // static
-void InputMethodDelegate::CallDispatchKeyEventPostIMEAck(
+void InputMethodDelegate::RunDispatchKeyEventPostIMECallback(
     KeyEvent* key_event,
-    base::OnceCallback<void(bool)> ack_callback) {
-  if (ack_callback)
-    std::move(ack_callback).Run(key_event->stopped_propagation());
+    DispatchKeyEventPostIMECallback callback) {
+  if (!callback)
+    return;
+  std::move(callback).Run(key_event->handled(),
+                          key_event->stopped_propagation());
 }
 
 }  // namespace internal
diff --git a/ui/base/ime/input_method_delegate.h b/ui/base/ime/input_method_delegate.h
index 4bfae03..70a1460 100644
--- a/ui/base/ime/input_method_delegate.h
+++ b/ui/base/ime/input_method_delegate.h
@@ -22,19 +22,22 @@
  public:
   virtual ~InputMethodDelegate() {}
 
+  using DispatchKeyEventPostIMECallback = base::OnceCallback<void(bool, bool)>;
   // Dispatch a key event already processed by the input method. Returns the
-  // status of processing, as well as running the callback |ack_callback| with
-  // the result of processing. |ack_callback| may be run asynchronously (if the
-  // delegate does processing async). |ack_callback| may not be null.
-  // Subclasses can use CallDispatchKeyEventPostIMEAck() to run the callback.
+  // status of processing, as well as running the callback |callback| with the
+  // result of processing. |callback| may be run asynchronously (if the
+  // delegate does processing async). Subclasses can use
+  // RunDispatchKeyEventPostIMECallback() to run the callback. |callback| is
+  // supplied two booleans that correspond to event->handled() and
+  // event->stopped_propagation().
   virtual EventDispatchDetails DispatchKeyEventPostIME(
       KeyEvent* key_event,
-      base::OnceCallback<void(bool)> ack_callback) = 0;
+      DispatchKeyEventPostIMECallback callback) = 0;
 
  protected:
-  static void CallDispatchKeyEventPostIMEAck(
+  static void RunDispatchKeyEventPostIMECallback(
       KeyEvent* key_event,
-      base::OnceCallback<void(bool)> ack_callback);
+      DispatchKeyEventPostIMECallback callback);
 };
 
 }  // namespace internal
diff --git a/ui/base/ime/input_method_minimal_unittest.cc b/ui/base/ime/input_method_minimal_unittest.cc
index 9eadb6a..146b02a 100644
--- a/ui/base/ime/input_method_minimal_unittest.cc
+++ b/ui/base/ime/input_method_minimal_unittest.cc
@@ -22,10 +22,10 @@
 
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* key_event,
-      base::OnceCallback<void(bool)> ack_callback) override {
+      DispatchKeyEventPostIMECallback callback) override {
     if (!propagation_post_ime_)
       key_event->StopPropagation();
-    CallDispatchKeyEventPostIMEAck(key_event, std::move(ack_callback));
+    RunDispatchKeyEventPostIMECallback(key_event, std::move(callback));
     return ui::EventDispatchDetails();
   }
 
diff --git a/ui/events/blink/scroll_predictor.cc b/ui/events/blink/scroll_predictor.cc
index aa0580f1..ab396ffd 100644
--- a/ui/events/blink/scroll_predictor.cc
+++ b/ui/events/blink/scroll_predictor.cc
@@ -63,8 +63,6 @@
     return;
 
   if (event->GetType() == WebInputEvent::kGestureScrollUpdate) {
-    TRACE_EVENT_BEGIN0("input", "ScrollPredictor::ResampleScrollEvents");
-
     // TODO(eirage): When scroll events are coalesced with pinch, we can have
     // empty original event list. In that case, we can't use the original events
     // to update the prediction. We don't want to use the aggregated event to
@@ -72,6 +70,8 @@
     if (original_events.empty())
       return;
 
+    TRACE_EVENT_BEGIN0("input", "ScrollPredictor::ResampleScrollEvents");
+
     temporary_accumulated_delta_ = current_accumulated_delta_;
     for (auto& coalesced_event : original_events)
       ComputeAccuracy(coalesced_event.event_);
diff --git a/ui/events/event.cc b/ui/events/event.cc
index 2f70fa5..5191a68 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -1149,7 +1149,7 @@
     set_flags(flags() & ~mask);
 }
 
-base::OnceCallback<void(bool)> KeyEvent::WillHandleAsync() {
+base::OnceCallback<void(bool, bool)> KeyEvent::WillHandleAsync() {
   if (cancelable())
     SetHandled();
   return std::move(async_callback_);
diff --git a/ui/events/event.h b/ui/events/event.h
index df7d1a5..c0dcecb 100644
--- a/ui/events/event.h
+++ b/ui/events/event.h
@@ -834,7 +834,7 @@
    public:
     explicit KeyDispatcherApi(KeyEvent* event) : event_(event) {}
 
-    void set_async_callback(base::OnceCallback<void(bool)> callback) {
+    void set_async_callback(base::OnceCallback<void(bool, bool)> callback) {
       event_->async_callback_ = std::move(callback);
     }
 
@@ -955,7 +955,7 @@
   // Called if the event is handled asynchronously. If the returned callback is
   // non-null, it *must* be run once async handling is complete. The argument
   // to the callback indicates if the event was handled or not.
-  base::OnceCallback<void(bool)> WillHandleAsync();
+  base::OnceCallback<void(bool, bool)> WillHandleAsync();
   bool HasAsyncCallback() const { return !async_callback_.is_null(); }
 
  protected:
@@ -996,7 +996,7 @@
   // it may be set only if and when GetCharacter() or GetDomKey() is called.
   mutable DomKey key_ = DomKey::NONE;
 
-  base::OnceCallback<void(bool)> async_callback_;
+  base::OnceCallback<void(bool, bool)> async_callback_;
 
   static KeyEvent* last_key_event_;
 #if defined(USE_X11)
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index 183e75d..4547508 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -1223,15 +1223,16 @@
   }
   // If a new file backed provided volume is mounted,
   // then redirect to it in the focused window.
-  // If crostini is mounted, redirect even if window is not focused.
   // Note, that this is a temporary solution for https://crbug.com/427776.
+  // If crostini is mounted, redirect if it is the currently selected dir.
   if (event.added.length !== 1) {
     return;
   }
   if ((window.isFocused() &&
        event.added[0].volumeType === VolumeManagerCommon.VolumeType.PROVIDED &&
        event.added[0].source === VolumeManagerCommon.Source.FILE) ||
-      event.added[0].volumeType === VolumeManagerCommon.VolumeType.CROSTINI) {
+      (event.added[0].volumeType === VolumeManagerCommon.VolumeType.CROSTINI &&
+       this.getCurrentRootType() === VolumeManagerCommon.RootType.CROSTINI)) {
     event.added[0].resolveDisplayRoot().then((displayRoot) => {
       // Resolving a display root on FSP volumes is instant, despite the
       // asynchronous call.
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.h b/ui/views/cocoa/bridged_native_widget_host_impl.h
index 2f82d8e9..86b1588 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.h
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.h
@@ -353,7 +353,7 @@
   // ui::internal::InputMethodDelegate:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* key,
-      base::OnceCallback<void(bool)> ack_callback) override;
+      DispatchKeyEventPostIMECallback callback) override;
 
   // ui::AccessibilityFocusOverrider::Client:
   id GetAccessibilityFocusedUIElement() override;
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.mm b/ui/views/cocoa/bridged_native_widget_host_impl.mm
index a156f05..6f40be4 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.mm
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.mm
@@ -1318,13 +1318,13 @@
 
 ui::EventDispatchDetails BridgedNativeWidgetHostImpl::DispatchKeyEventPostIME(
     ui::KeyEvent* key,
-    base::OnceCallback<void(bool)> ack_callback) {
+    DispatchKeyEventPostIMECallback callback) {
   DCHECK(focus_manager_);
   if (!focus_manager_->OnKeyEvent(*key))
     key->StopPropagation();
   else
     native_widget_mac_->GetWidget()->OnKeyEvent(key);
-  CallDispatchKeyEventPostIMEAck(key, std::move(ack_callback));
+  RunDispatchKeyEventPostIMECallback(key, std::move(callback));
   return ui::EventDispatchDetails();
 }
 
diff --git a/ui/views/cocoa/drag_drop_client_mac.h b/ui/views/cocoa/drag_drop_client_mac.h
index 065e730..e976b2e 100644
--- a/ui/views/cocoa/drag_drop_client_mac.h
+++ b/ui/views/cocoa/drag_drop_client_mac.h
@@ -65,7 +65,7 @@
   DropHelper drop_helper_;
 
   // The drag and drop operation.
-  int operation_;
+  int operation_ = 0;
 
   // The bridge between the content view and the drag drop client.
   BridgedNativeWidgetImpl* bridge_;  // Weak. Owns |this|.
@@ -74,7 +74,7 @@
   base::Closure quit_closure_;
 
   // Whether |this| is the source of current dragging session.
-  bool is_drag_source_;
+  bool is_drag_source_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(DragDropClientMac);
 };
diff --git a/ui/views/cocoa/drag_drop_client_mac.mm b/ui/views/cocoa/drag_drop_client_mac.mm
index 60475b3..c8ffe5d 100644
--- a/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/ui/views/cocoa/drag_drop_client_mac.mm
@@ -18,11 +18,7 @@
 
 DragDropClientMac::DragDropClientMac(BridgedNativeWidgetImpl* bridge,
                                      View* root_view)
-    : drop_helper_(root_view),
-      operation_(0),
-      bridge_(bridge),
-      quit_closure_(base::Closure()),
-      is_drag_source_(false) {
+    : drop_helper_(root_view), bridge_(bridge) {
   DCHECK(bridge);
 }
 
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index 5353a55..a25c050 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -176,12 +176,12 @@
   void OnDragEnteredScrollButton(SubmenuView* source, bool is_up);
   void OnDragExitedScrollButton(SubmenuView* source);
 
-  // Called by the Widget when a drag is about to start on a child view. This
-  // could be initiated by one of our MenuItemViews, or could be through another
-  // child View.
+  // Called by the MenuHost when a drag is about to start on a child view.
+  // This could be initiated by one of our MenuItemViews, or could be through
+  // another child View.
   void OnDragWillStart();
 
-  // Called by the Widget when the drag has completed. |should_close|
+  // Called by the MenuHost when the drag has completed. |should_close|
   // corresponds to whether or not the menu should close.
   void OnDragComplete(bool should_close);
 
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index a746afb..ab55ce0 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
@@ -2309,5 +2310,28 @@
   EXPECT_EQ(5, data.GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
 }
 
+// Tests that a menu opened asynchronously, will notify its
+// MenuControllerDelegate when accessibility performs a do default action.
+TEST_F(MenuControllerTest, AccessibilityDoDefaultCallsAccept) {
+  views::test::DisableMenuClosureAnimations();
+
+  MenuController* controller = menu_controller();
+  controller->Run(owner(), nullptr, menu_item(), gfx::Rect(),
+                  MENU_ANCHOR_TOPLEFT, false, false);
+  TestMenuControllerDelegate* delegate = menu_controller_delegate();
+  EXPECT_EQ(0, delegate->on_menu_closed_called());
+
+  MenuItemView* accepted = menu_item()->GetSubmenu()->GetMenuItemAt(0);
+  ui::AXActionData data;
+  data.action = ax::mojom::Action::kDoDefault;
+  accepted->HandleAccessibleAction(data);
+  views::test::WaitForMenuClosureAnimation();
+
+  EXPECT_EQ(1, delegate->on_menu_closed_called());
+  EXPECT_EQ(accepted, delegate->on_menu_closed_menu());
+  EXPECT_EQ(internal::MenuControllerDelegate::NOTIFY_DELEGATE,
+            delegate->on_menu_closed_notify_type());
+}
+
 }  // namespace test
 }  // namespace views
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 9e1b05f..d762be8 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -10,6 +10,7 @@
 #include "base/i18n/case_conversion.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/menu_model.h"
@@ -212,6 +213,13 @@
   }
 }
 
+bool MenuItemView::HandleAccessibleAction(const ui::AXActionData& action_data) {
+  if (action_data.action != ax::mojom::Action::kDoDefault)
+    return View::HandleAccessibleAction(action_data);
+  GetMenuController()->Accept(this, ui::EF_NONE);
+  return true;
+}
+
 // static
 bool MenuItemView::IsBubble(MenuAnchorPosition anchor) {
   return anchor == MENU_ANCHOR_BUBBLE_LEFT ||
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index 1821609..09b9b5b 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -134,6 +134,7 @@
   bool GetTooltipText(const gfx::Point& p,
                       base::string16* tooltip) const override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  bool HandleAccessibleAction(const ui::AXActionData& action_data) override;
 
   // Returns the preferred height of menu items. This is only valid when the
   // menu is about to be shown.
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc
index dcf1cd2..b5a32f36 100644
--- a/ui/views/controls/webview/web_dialog_view.cc
+++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -268,10 +268,12 @@
 }
 
 bool WebDialogView::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   if (delegate_)
-    return delegate_->HandleContextMenu(params);
-  return WebDialogWebContentsDelegate::HandleContextMenu(params);
+    return delegate_->HandleContextMenu(render_frame_host, params);
+  return WebDialogWebContentsDelegate::HandleContextMenu(render_frame_host,
+                                                         params);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/controls/webview/web_dialog_view.h b/ui/views/controls/webview/web_dialog_view.h
index 27e9742e..af89035 100644
--- a/ui/views/controls/webview/web_dialog_view.h
+++ b/ui/views/controls/webview/web_dialog_view.h
@@ -93,7 +93,8 @@
   void OnCloseContents(content::WebContents* source,
                        bool* out_close_dialog) override;
   bool ShouldShowDialogTitle() const override;
-  bool HandleContextMenu(const content::ContextMenuParams& params) override;
+  bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                         const content::ContextMenuParams& params) override;
 
   // Overridden from content::WebContentsDelegate:
   void SetContentsBounds(content::WebContents* source,
diff --git a/ui/web_dialogs/web_dialog_delegate.cc b/ui/web_dialogs/web_dialog_delegate.cc
index b2ecbf6..1ec8b495 100644
--- a/ui/web_dialogs/web_dialog_delegate.cc
+++ b/ui/web_dialogs/web_dialog_delegate.cc
@@ -34,6 +34,7 @@
 }
 
 bool WebDialogDelegate::HandleContextMenu(
+    content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
   return false;
 }
diff --git a/ui/web_dialogs/web_dialog_delegate.h b/ui/web_dialogs/web_dialog_delegate.h
index ee639fd..436b17b9 100644
--- a/ui/web_dialogs/web_dialog_delegate.h
+++ b/ui/web_dialogs/web_dialog_delegate.h
@@ -17,6 +17,7 @@
 class GURL;
 
 namespace content {
+class RenderFrameHost;
 class RenderViewHost;
 class WebContents;
 class WebUI;
@@ -116,7 +117,8 @@
   // customized menu.
   // Returns true iff you do NOT want the standard context menu to be
   // shown (because you want to handle it yourself).
-  virtual bool HandleContextMenu(const content::ContextMenuParams& params);
+  virtual bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
+                                 const content::ContextMenuParams& params);
 
   // A callback to allow the delegate to open a new URL inside |source|.
   // On return |out_new_contents| should contain the WebContents the URL