diff --git a/DEPS b/DEPS
index 21553af..f888a5f 100644
--- a/DEPS
+++ b/DEPS
@@ -142,7 +142,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'bffe9927669b41ad07e1a14bba243c4d4599e9c2',
+  'v8_revision': '785b71d8f6c054852897836640a71cb329725dba',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -150,11 +150,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'd2683459da1d6e5907fc54dc1f98398397b59841',
+  'angle_revision': 'de15ed370d5368bc313eee282eb743b07a588c8b',
   # 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': '48c8a180b599c61cf09cb242cff3479b63dbe8c4',
+  'swiftshader_revision': '032c7152644dcddbd17b77437f361bf9fd5825c8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -185,7 +185,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '4744a43b6fc4aaa5230541efad113b3cd86ea0f8',
+  'nacl_revision': '0ddc033406886a709b901e0c312872529f9705e8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -201,7 +201,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '63ab0c82ecf580fbff05dd7550bc1a9ffeb2d01a',
+  'catapult_revision': '441284b3fb3e77aaad067d117cbf3b30c60f1e01',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -273,7 +273,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': 'eea2091068037c41e52164f100802302768b674d',
+  'dawn_revision': 'c391fb7c69fd50c55e566f9612d92b2f5d251524',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -832,7 +832,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd390b317dcf7d6dda1968f6f9edccaa91804c950',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '54434e7e1dabdc79232c3eeca06c44e5c0aabab9',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -901,7 +901,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '27ec9195850108c8495dbc1ee2a10d09e8ea1921',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '66e46037d74f4323e6e31d5a1f1de4ad20808885',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1350,7 +1350,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'c1c0d6d8ad7471043631f6b4b006eba9801d9241',
+    Var('webrtc_git') + '/src.git' + '@' + 'ce723234bacfd0eac80a31d2a7540e9e33371175',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1391,7 +1391,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@93490699ae99a59888192c4117aad7fa39376cb4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@21aa9632410d795689858d67b1bce3ca0ba64c72',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_browser_terminator.cc b/android_webview/browser/aw_browser_terminator.cc
index 161d23e..7ffdcdf 100644
--- a/android_webview/browser/aw_browser_terminator.cc
+++ b/android_webview/browser/aw_browser_terminator.cc
@@ -9,11 +9,14 @@
 
 #include "android_webview/browser/aw_render_process_gone_delegate.h"
 #include "android_webview/common/aw_descriptors.h"
+#include "base/android/scoped_java_ref.h"
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
 #include "components/crash/content/app/crashpad.h"
 #include "components/crash/content/browser/crash_metrics_reporter_android.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/child_process_launcher_utils.h"
@@ -26,15 +29,16 @@
 #include "content/public/browser/web_contents.h"
 #include "jni/AwBrowserProcess_jni.h"
 
+using base::android::ScopedJavaGlobalRef;
 using content::BrowserThread;
 
 namespace android_webview {
 
 namespace {
 
-void GetAwRenderProcessGoneDelegatesForRenderProcess(
+void GetJavaWebContentsForRenderProcess(
     content::RenderProcessHost* rph,
-    std::vector<AwRenderProcessGoneDelegate*>* delegates) {
+    std::vector<ScopedJavaGlobalRef<jobject>>* java_web_contents) {
   std::unique_ptr<content::RenderWidgetHostIterator> widgets(
       content::RenderWidgetHost::GetRenderWidgetHosts());
   while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
@@ -42,39 +46,56 @@
     if (view && rph == view->GetProcess()) {
       content::WebContents* wc = content::WebContents::FromRenderViewHost(view);
       if (wc) {
-        AwRenderProcessGoneDelegate* delegate =
-            AwRenderProcessGoneDelegate::FromWebContents(wc);
-        if (delegate)
-          delegates->push_back(delegate);
+        java_web_contents->push_back(static_cast<ScopedJavaGlobalRef<jobject>>(
+            wc->GetJavaWebContents()));
       }
     }
   }
 }
 
-void OnRenderProcessGone(content::RenderProcessHost* host,
-                         base::ProcessId child_process_pid,
-                         bool crashed) {
+void OnRenderProcessGone(
+    const std::vector<ScopedJavaGlobalRef<jobject>>& java_web_contents,
+    base::ProcessId child_process_pid,
+    bool crashed) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  std::vector<AwRenderProcessGoneDelegate*> delegates;
-  GetAwRenderProcessGoneDelegatesForRenderProcess(host, &delegates);
-  for (auto* delegate : delegates) {
-    if (!delegate->OnRenderProcessGone(child_process_pid, crashed)) {
-      if (crashed) {
-        // Keeps this log unchanged, CTS test uses it to detect crash.
-        std::string message = base::StringPrintf(
-            "Render process (%d)'s crash wasn't handled by all associated  "
-            "webviews, triggering application crash.",
-            child_process_pid);
-        crash_reporter::CrashWithoutDumping(message);
-      } else {
-        // The render process was most likely killed for OOM or switching
-        // WebView provider, to make WebView backward compatible, kills the
-        // browser process instead of triggering crash.
-        LOG(ERROR) << "Render process (" << child_process_pid << ") kill (OOM"
-                   << " or update) wasn't handed by all associated webviews,"
-                   << " killing application.";
-        kill(getpid(), SIGKILL);
-      }
+
+  for (auto& java_wc : java_web_contents) {
+    content::WebContents* wc =
+        content::WebContents::FromJavaWebContents(java_wc);
+    if (!wc)
+      continue;
+
+    AwRenderProcessGoneDelegate* delegate =
+        AwRenderProcessGoneDelegate::FromWebContents(wc);
+    if (!delegate)
+      continue;
+
+    switch (delegate->OnRenderProcessGone(child_process_pid, crashed)) {
+      case AwRenderProcessGoneDelegate::RenderProcessGoneResult::kException:
+        // Let the exception propagate back to the message loop.
+        base::MessageLoopCurrentForUI::Get()->Abort();
+        return;
+      case AwRenderProcessGoneDelegate::RenderProcessGoneResult::kUnhandled:
+        if (crashed) {
+          // Keeps this log unchanged, CTS test uses it to detect crash.
+          std::string message = base::StringPrintf(
+              "Render process (%d)'s crash wasn't handled by all associated  "
+              "webviews, triggering application crash.",
+              child_process_pid);
+          crash_reporter::CrashWithoutDumping(message);
+        } else {
+          // The render process was most likely killed for OOM or switching
+          // WebView provider, to make WebView backward compatible, kills the
+          // browser process instead of triggering crash.
+          LOG(ERROR) << "Render process (" << child_process_pid << ") kill (OOM"
+                     << " or update) wasn't handed by all associated webviews,"
+                     << " killing application.";
+          kill(getpid(), SIGKILL);
+        }
+        NOTREACHED();
+        break;
+      case AwRenderProcessGoneDelegate::RenderProcessGoneResult::kHandled:
+        break;
     }
   }
 
@@ -104,7 +125,13 @@
   LOG(ERROR) << "Renderer process (" << info.pid << ") crash detected (code "
              << info.crash_signo << ").";
 
-  OnRenderProcessGone(rph, info.pid, info.is_crashed());
+  std::vector<ScopedJavaGlobalRef<jobject>> java_web_contents;
+  GetJavaWebContentsForRenderProcess(rph, &java_web_contents);
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {content::BrowserThread::UI, base::TaskPriority::HIGHEST},
+      base::BindOnce(OnRenderProcessGone, java_web_contents, info.pid,
+                     info.is_crashed()));
 }
 
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 06e9bda..98a3795 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -1493,15 +1493,23 @@
                                        aw_render_process->GetJavaObject());
 }
 
-bool AwContents::OnRenderProcessGone(int child_process_id, bool crashed) {
+AwContents::RenderProcessGoneResult AwContents::OnRenderProcessGone(
+    int child_process_id,
+    bool crashed) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
   if (obj.is_null())
-    return false;
+    return RenderProcessGoneResult::kHandled;
 
-  return Java_AwContents_onRenderProcessGone(env, obj, child_process_id,
-                                             crashed);
+  bool result =
+      Java_AwContents_onRenderProcessGone(env, obj, child_process_id, crashed);
+
+  if (HasException(env))
+    return RenderProcessGoneResult::kException;
+
+  return result ? RenderProcessGoneResult::kHandled
+                : RenderProcessGoneResult::kUnhandled;
 }
 
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index 77d5396..320ae37 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -365,7 +365,8 @@
       const base::android::JavaParamRef<jobject>& callback);
 
   // AwRenderProcessGoneDelegate overrides
-  bool OnRenderProcessGone(int child_process_id, bool crashed) override;
+  RenderProcessGoneResult OnRenderProcessGone(int child_process_id,
+                                              bool crashed) override;
 
  private:
   void InitAutofillIfNecessary(bool autocomplete_enabled);
diff --git a/android_webview/browser/aw_render_process_gone_delegate.h b/android_webview/browser/aw_render_process_gone_delegate.h
index e71629a..3e8f0ef 100644
--- a/android_webview/browser/aw_render_process_gone_delegate.h
+++ b/android_webview/browser/aw_render_process_gone_delegate.h
@@ -14,13 +14,15 @@
 // Delegate interface to handle the events that render process was gone.
 class AwRenderProcessGoneDelegate {
  public:
+  enum class RenderProcessGoneResult { kHandled, kUnhandled, kException };
   // Returns the AwRenderProcessGoneDelegate instance associated with
   // the given |web_contents|.
   static AwRenderProcessGoneDelegate* FromWebContents(
       content::WebContents* web_contents);
 
   // Notify if render process crashed or was killed.
-  virtual bool OnRenderProcessGone(int child_process_id, bool crashed) = 0;
+  virtual RenderProcessGoneResult OnRenderProcessGone(int child_process_id,
+                                                      bool crashed) = 0;
 
  protected:
   AwRenderProcessGoneDelegate() {}
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index ee15998..299347b 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -61,6 +61,7 @@
 import org.chromium.base.TraceEvent;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.CalledByNativeUnchecked;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
@@ -1371,7 +1372,7 @@
     }
 
     @VisibleForTesting
-    @CalledByNative
+    @CalledByNativeUnchecked
     protected boolean onRenderProcessGone(int childProcessID, boolean crashed) {
         if (isDestroyed(NO_WARN)) return true;
         return mContentsClient.onRenderProcessGone(new AwRenderProcessGoneDetail(
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f9f25e5..c10e464 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1408,6 +1408,7 @@
     "//ui/native_theme",
     "//ui/ozone",
     "//ui/platform_window",
+    "//ui/platform_window/mojo",
     "//ui/platform_window/stub",
     "//ui/snapshot",
     "//ui/views/window/vector_icons",
diff --git a/ash/frame/non_client_frame_view_ash.h b/ash/frame/non_client_frame_view_ash.h
index 77b5a40..71e78fb48 100644
--- a/ash/frame/non_client_frame_view_ash.h
+++ b/ash/frame/non_client_frame_view_ash.h
@@ -118,6 +118,12 @@
 
   views::Widget* frame() { return frame_; }
 
+  // Methods for testing.
+  static void set_use_empty_minimum_size_for_test(
+      bool use_empty_minimum_size_for_test) {
+    use_empty_minimum_size_for_test_ = use_empty_minimum_size_for_test;
+  }
+
  protected:
   // Called when overview mode or split view state changed. If overview mode
   // and split view mode are both active at the same time, the header of the
diff --git a/ash/host/ash_window_tree_host_platform.cc b/ash/host/ash_window_tree_host_platform.cc
index ef143bf..7c917a3 100644
--- a/ash/host/ash_window_tree_host_platform.cc
+++ b/ash/host/ash_window_tree_host_platform.cc
@@ -24,6 +24,7 @@
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/transform.h"
+#include "ui/platform_window/mojo/ime_type_converters.h"
 #include "ui/platform_window/platform_ime_controller.h"
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_init_properties.h"
diff --git a/ash/public/cpp/frame_header.cc b/ash/public/cpp/frame_header.cc
index 0c45170e..6ef1f3c 100644
--- a/ash/public/cpp/frame_header.cc
+++ b/ash/public/cpp/frame_header.cc
@@ -238,6 +238,9 @@
       views::CAPTION_BUTTON_ICON_LEFT_SNAPPED, kWindowControlLeftSnappedIcon);
   caption_button_container_->SetButtonImage(
       views::CAPTION_BUTTON_ICON_RIGHT_SNAPPED, kWindowControlRightSnappedIcon);
+
+  // Perform layout to ensure the container height is correct.
+  LayoutHeaderInternal();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 45b1b54..ed36400 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -18,6 +18,7 @@
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/drag_drop/drag_drop_controller.h"
+#include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/fps_counter.h"
@@ -143,6 +144,8 @@
   // AshTestBase:
   void SetUp() override {
     AshTestBase::SetUp();
+    NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(true);
+
     aura::Env::GetInstance()->set_throttle_input_on_resize_for_testing(false);
     shelf_view_test_api_ = std::make_unique<ShelfViewTestAPI>(
         GetPrimaryShelf()->GetShelfViewForTesting());
@@ -158,6 +161,7 @@
         false);
     FpsCounter::SetForceReportZeroAnimationForTest(false);
     trace_names_.clear();
+    NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(false);
     AshTestBase::TearDown();
   }
 
diff --git a/ash/wm/overview/scoped_overview_transform_window_unittest.cc b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
index b084578..963dc3e 100644
--- a/ash/wm/overview/scoped_overview_transform_window_unittest.cc
+++ b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/overview/scoped_overview_transform_window.h"
 
+#include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/test/ash_test_base.h"
@@ -211,6 +212,8 @@
 
 // Tests the cases when very wide or tall windows enter overview mode.
 TEST_F(ScopedOverviewTransformWindowTest, ExtremeWindowBounds) {
+  NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(true);
+
   // Add three windows which in overview mode will be considered wide, tall and
   // normal. Window |wide|, with size (400, 160) will be resized to (200, 160)
   // when the 400x200 is rotated to 200x400, and should be considered a normal
@@ -248,6 +251,8 @@
   EXPECT_EQ(GridWindowFillMode::kNormal, scoped_wide.type());
   EXPECT_EQ(GridWindowFillMode::kPillarBoxed, scoped_tall.type());
   EXPECT_EQ(GridWindowFillMode::kNormal, scoped_normal.type());
+
+  NonClientFrameViewAsh::set_use_empty_minimum_size_for_test(false);
 }
 
 // Tests that transients which should be invisible in overview do not have their
diff --git a/base/i18n/icu_util.cc b/base/i18n/icu_util.cc
index fcd6255f..f494287 100644
--- a/base/i18n/icu_util.cc
+++ b/base/i18n/icu_util.cc
@@ -44,13 +44,6 @@
 namespace base {
 namespace i18n {
 
-#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
-#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
-#if defined(OS_WIN)
-#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
-#endif
-#endif
-
 namespace {
 #if !defined(OS_NACL)
 #if DCHECK_IS_ON()
@@ -260,30 +253,7 @@
 #endif
 
   bool result;
-#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED)
-  FilePath data_path;
-  PathService::Get(DIR_ASSETS, &data_path);
-  data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME);
-
-  HMODULE module = LoadLibrary(data_path.value().c_str());
-  if (!module) {
-    LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME;
-    return false;
-  }
-
-  FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL);
-  if (!addr) {
-    LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in "
-               << ICU_UTIL_DATA_SHARED_MODULE_NAME;
-    return false;
-  }
-
-  UErrorCode err = U_ZERO_ERROR;
-  udata_setCommonData(reinterpret_cast<void*>(addr), &err);
-  // Never try to load ICU data from files.
-  udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
-  result = (err == U_ZERO_ERROR);
-#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
+#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
   // The ICU data is statically linked.
   result = true;
 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
diff --git a/base/i18n/icu_util.h b/base/i18n/icu_util.h
index 5f9948f..a861ce6 100644
--- a/base/i18n/icu_util.h
+++ b/base/i18n/icu_util.h
@@ -12,8 +12,7 @@
 #include "build/build_config.h"
 
 #define ICU_UTIL_DATA_FILE   0
-#define ICU_UTIL_DATA_SHARED 1
-#define ICU_UTIL_DATA_STATIC 2
+#define ICU_UTIL_DATA_STATIC 1
 
 namespace base {
 namespace i18n {
diff --git a/base/memory/ref_counted.cc b/base/memory/ref_counted.cc
index f3d35cf57..c65115ef 100644
--- a/base/memory/ref_counted.cc
+++ b/base/memory/ref_counted.cc
@@ -4,6 +4,9 @@
 
 #include "base/memory/ref_counted.h"
 
+#include <limits>
+#include <type_traits>
+
 #include "base/threading/thread_collision_warner.h"
 
 namespace base {
@@ -32,15 +35,35 @@
 }
 #endif
 
-// This is a security check. In 32-bit-archs, an attacker would run out of
-// address space after allocating at most 2^32 scoped_refptrs. This replicates
-// that boundary for 64-bit-archs.
+// For security and correctness, we check the arithmetic on ref counts.
+//
+// In an attempt to avoid binary bloat (from inlining the `CHECK`), we define
+// these functions out-of-line. However, compilers are wily. Further testing may
+// show that `NOINLINE` helps or hurts.
+//
 #if defined(ARCH_CPU_64_BITS)
 void RefCountedBase::AddRefImpl() const {
-  // Check if |ref_count_| overflow only on 64 bit archs since the number of
-  // objects may exceed 2^32.
-  // To avoid the binary size bloat, use non-inline function here.
-  CHECK(++ref_count_ > 0);
+  // An attacker could induce use-after-free bugs, and potentially exploit them,
+  // by creating so many references to a ref-counted object that the reference
+  // count overflows. On 32-bit architectures, there is not enough address space
+  // to succeed. But on 64-bit architectures, it might indeed be possible.
+  // Therefore, we can elide the check for arithmetic overflow on 32-bit, but we
+  // must check on 64-bit.
+  //
+  // Make sure the addition didn't wrap back around to 0. This form of check
+  // works because we assert that `ref_count_` is an unsigned integer type.
+  CHECK(++ref_count_ != 0);
+}
+
+void RefCountedBase::ReleaseImpl() const {
+  // Make sure the subtraction didn't wrap back around from 0 to the max value.
+  // That could cause memory leaks, and may induce application-semantic
+  // correctness or safety bugs. (E.g. what if we really needed that object to
+  // be destroyed at the right time?)
+  //
+  // Note that unlike with overflow, underflow could also happen on 32-bit
+  // architectures. Arguably, we should do this check on32-bit machines too.
+  CHECK(--ref_count_ != std::numeric_limits<decltype(ref_count_)>::max());
 }
 #endif
 
diff --git a/base/memory/ref_counted.h b/base/memory/ref_counted.h
index afef04ae..ac7183a 100644
--- a/base/memory/ref_counted.h
+++ b/base/memory/ref_counted.h
@@ -69,7 +69,7 @@
 
   // Returns true if the object should self-delete.
   bool Release() const {
-    --ref_count_;
+    ReleaseImpl();
 
     // TODO(maruel): Add back once it doesn't assert 500 times/sec.
     // Current thread books the critical section "AddRelease"
@@ -126,8 +126,10 @@
 
 #if defined(ARCH_CPU_64_BITS)
   void AddRefImpl() const;
+  void ReleaseImpl() const;
 #else
   void AddRefImpl() const { ++ref_count_; }
+  void ReleaseImpl() const { --ref_count_; }
 #endif
 
 #if DCHECK_IS_ON()
@@ -135,6 +137,8 @@
 #endif
 
   mutable uint32_t ref_count_ = 0;
+  static_assert(std::is_unsigned<decltype(ref_count_)>::value,
+                "ref_count_ must be an unsigned type.");
 
 #if DCHECK_IS_ON()
   mutable bool needs_adopt_ref_ = false;
diff --git a/base/memory/writable_shared_memory_region.h b/base/memory/writable_shared_memory_region.h
index 804dc00..e81e1f8 100644
--- a/base/memory/writable_shared_memory_region.h
+++ b/base/memory/writable_shared_memory_region.h
@@ -10,6 +10,7 @@
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/shared_memory_mapping.h"
 #include "base/memory/unsafe_shared_memory_region.h"
+#include "build/build_config.h"
 
 namespace base {
 
@@ -106,6 +107,15 @@
     return handle_.GetGUID();
   }
 
+#if defined(OS_WIN)
+  // On Windows it is necessary in rare cases to take a writable handle from a
+  // region that will be converted to read-only. On this platform it is a safe
+  // operation, as the handle returned from this method will remain writable
+  // after the region is converted to read-only. However, it breaks chromium's
+  // WritableSharedMemoryRegion semantics and so should be use with care.
+  HANDLE UnsafeGetPlatformHandle() const { return handle_.GetPlatformHandle(); }
+#endif
+
  private:
   friend class SharedMemoryHooks;
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index eaa95bc..aae7732 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8912639898317211552
\ No newline at end of file
+8912607903722280080
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 46100a7..2644a8e 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8912640165921142128
\ No newline at end of file
+8912611050038160288
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index 95d5f58..221bcb7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=76
 MINOR=0
-BUILD=3804
+BUILD=3805
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java
index 7783593..e6d1dc8a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestContactDetailsSection.java
@@ -50,12 +50,14 @@
     }
 
     @Override
-    protected void createOrEditItem(@Nullable View oldFullView, @Nullable AutofillContact oldItem) {
+    protected void createOrEditItem(@Nullable AutofillContact oldItem) {
         if (mEditor != null) {
-            mIgnoreProfileChangeNotifications = true;
-            mEditor.edit(oldItem,
-                    editedOption -> onItemCreatedOrEdited(oldItem, oldFullView, editedOption));
-            mIgnoreProfileChangeNotifications = false;
+            mEditor.edit(oldItem, newItem -> {
+                assert (newItem != null && newItem.isComplete());
+                mIgnoreProfileChangeNotifications = true;
+                addOrUpdateItem(newItem, true);
+                mIgnoreProfileChangeNotifications = false;
+            }, cancel -> {});
         }
     }
 
@@ -144,7 +146,8 @@
     }
 
     @Override
-    protected void onItemAddedOrUpdated(AutofillContact contact) {
+    protected void addOrUpdateItem(AutofillContact contact, boolean select) {
+        super.addOrUpdateItem(contact, select);
         addAutocompleteInformationToEditor(contact);
     }
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java
index b1ac137..b1860c5 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestPaymentMethodSection.java
@@ -26,6 +26,7 @@
 public class AssistantPaymentRequestPaymentMethodSection
         extends AssistantPaymentRequestSection<AutofillPaymentInstrument> {
     private CardEditor mEditor;
+    private boolean mIgnorePaymentMethodsChangeNotifications;
 
     AssistantPaymentRequestPaymentMethodSection(Context context, ViewGroup parent) {
         super(context, parent, R.layout.autofill_assistant_payment_method_summary,
@@ -42,13 +43,16 @@
     }
 
     @Override
-    protected void createOrEditItem(
-            @Nullable View oldFullView, @Nullable AutofillPaymentInstrument oldItem) {
+    protected void createOrEditItem(@Nullable AutofillPaymentInstrument oldItem) {
         if (mEditor == null) {
             return;
         }
-        mEditor.edit(
-                oldItem, editedOption -> onItemCreatedOrEdited(oldItem, oldFullView, editedOption));
+        mEditor.edit(oldItem, newItem -> {
+            assert (newItem != null && newItem.isComplete());
+            mIgnorePaymentMethodsChangeNotifications = true;
+            addOrUpdateItem(newItem, true);
+            mIgnorePaymentMethodsChangeNotifications = false;
+        }, cancel -> {});
     }
 
     @Override
@@ -93,11 +97,6 @@
         methodIncompleteView.setVisibility(method.isComplete() ? View.GONE : View.VISIBLE);
     }
 
-    @Override
-    protected void onItemAddedOrUpdated(AutofillPaymentInstrument method) {
-        // Nothing to do
-    }
-
     void onProfilesChanged(List<PersonalDataManager.AutofillProfile> profiles) {
         for (PersonalDataManager.AutofillProfile profile : profiles) {
             // TODO(crbug.com/806868): replace suggested billing addresses (remove if necessary).
@@ -110,6 +109,9 @@
      * the new/changed set of payment methods, while keeping the selected item if possible.
      */
     void onAvailablePaymentMethodsChanged(List<AutofillPaymentInstrument> paymentMethods) {
+        if (mIgnorePaymentMethodsChangeNotifications) {
+            return;
+        }
         AutofillPaymentInstrument previouslySelectedMethod = mSelectedOption;
         int selectedMethodIndex = -1;
         for (int i = 0; i < paymentMethods.size(); i++) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java
index 9647500..b21bac2 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestSection.java
@@ -108,9 +108,9 @@
         titleAddButtonLabelView.setText(titleAddButton);
 
         mTitleAddButton = mSectionExpander.findViewById(R.id.section_title_add_button);
-        mTitleAddButton.setOnClickListener(unusedView -> createOrEditItem(null, null));
+        mTitleAddButton.setOnClickListener(unusedView -> createOrEditItem(null));
 
-        mItemsView.setOnAddButtonClickedListener(() -> createOrEditItem(null, null));
+        mItemsView.setOnAddButtonClickedListener(() -> createOrEditItem(null));
         parent.addView(mSectionExpander,
                 new ViewGroup.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
@@ -182,6 +182,12 @@
         return items;
     }
 
+    /**
+     * Adds a new item to the list, or updates an item in-place if it is already in the list.
+     *
+     * @param option The item to add or update.
+     * @param select Whether to select the new/updated item or not.
+     */
     void addOrUpdateItem(@Nullable T option, boolean select) {
         if (option == null) {
             return;
@@ -204,7 +210,6 @@
         } else {
             updateSummaryView(mSummaryView, item.mOption);
         }
-        onItemAddedOrUpdated(option);
 
         if (select) {
             mIgnoreItemSelectedNotifications = true;
@@ -219,7 +224,7 @@
         updatePaddings();
     }
 
-    void updatePaddings() {
+    private void updatePaddings() {
         if (isEmpty()) {
             // Section is empty, i.e., the title is the bottom-most widget.
             mSectionExpander.setTitlePadding(mTopPadding, mBottomPadding);
@@ -273,19 +278,18 @@
                 // radiobutton to not render properly.
                 mSectionExpander.post(() -> mSectionExpander.setExpanded(false));
             } else {
-                createOrEditItem(item.mFullView, item.mOption);
+                createOrEditItem(item.mOption);
             }
-        }, () -> createOrEditItem(item.mFullView, item.mOption));
+        }, () -> createOrEditItem(item.mOption));
         updateVisibility();
     }
 
     /**
      * Asks the subclass to edit an item or create a new one (if |oldItem| is null). Subclasses
-     * should call |onItemCreatedOrEdited| when they are done.
-     * @param oldFullView The view associated with |oldItem|.
+     * should call |addOrUpdateItem| when they are done.
      * @param oldItem The item to be edited (null if a new item should be created).
      */
-    protected abstract void createOrEditItem(@Nullable View oldFullView, @Nullable T oldItem);
+    protected abstract void createOrEditItem(@Nullable T oldItem);
 
     /**
      * Asks the subclass to update the contents of |fullView|, which was previously created by
@@ -299,40 +303,12 @@
     protected abstract void updateSummaryView(View summaryView, T option);
 
     /**
-     * An item was added to the list or updated in place. Subclasses may react to this event. A
-     * common use case is for subclasses to add the information of the new profile to the
-     * autocomplete fields of their editor.
-     */
-    protected abstract void onItemAddedOrUpdated(T option);
-
-    /**
      * For convenience. Hides |view| if it is empty.
      */
     void hideIfEmpty(TextView view) {
         view.setVisibility(view.length() == 0 ? View.GONE : View.VISIBLE);
     }
 
-    /**
-     * An old item was edited or a new item was created.
-     *
-     * @param oldItem The item that was edited. null to indicate that a new item was created.
-     * @param oldFullView The view associated with |oldItem|. Null if |oldItem| is null.
-     * @param newItem The new or edited item. Cancelling an 'edit' flow will yield the old item.
-     */
-    void onItemCreatedOrEdited(
-            @Nullable T oldItem, @Nullable View oldFullView, @Nullable T newItem) {
-        if (newItem == null) {
-            // User cancelled 'add' flow.
-            return;
-        } else if (!newItem.isComplete()) {
-            // User cancelled 'edit' flow.
-            return;
-            // TODO(crbug.com/806868) add special case for cancelling edit of incomplete item
-        } else {
-            addOrUpdateItem(newItem, /*select = */ true);
-        }
-    }
-
     private boolean isEmpty() {
         return mItems.isEmpty();
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java
index 963e55e..da9f1ef 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestShippingAddressSection.java
@@ -42,13 +42,15 @@
     }
 
     @Override
-    protected void createOrEditItem(@Nullable View oldFullView, @Nullable AutofillAddress oldItem) {
+    protected void createOrEditItem(@Nullable AutofillAddress oldItem) {
         if (mEditor == null) {
             return;
         }
         mIgnoreProfileChangeNotifications = true;
-        mEditor.edit(
-                oldItem, editedOption -> onItemCreatedOrEdited(oldItem, oldFullView, editedOption));
+        mEditor.edit(oldItem, newItem -> {
+            assert (newItem != null && newItem.isComplete());
+            addOrUpdateItem(newItem, true);
+        }, cancel -> {});
         mIgnoreProfileChangeNotifications = false;
     }
 
@@ -113,8 +115,13 @@
     }
 
     @Override
-    protected void onItemAddedOrUpdated(AutofillAddress address) {
+    protected void addOrUpdateItem(AutofillAddress address, boolean select) {
+        super.addOrUpdateItem(address, select);
+
         // Update autocomplete information in the editor.
+        if (mEditor == null) {
+            return;
+        }
         mEditor.addPhoneNumberIfValid(address.getProfile().getPhoneNumber());
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
index 177b7c3..e0cb230 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
@@ -139,6 +139,15 @@
     }
 
     /**
+     * Allows calling |edit| with a single callback used for both 'done' and 'cancel'.
+     * @see #edit(AutofillAddress, Callback, Callback)
+     */
+    public void edit(
+            @Nullable final AutofillAddress toEdit, final Callback<AutofillAddress> callback) {
+        edit(toEdit, callback, callback);
+    }
+
+    /**
      * Builds and shows an editor model with the following fields.
      *
      * [ country dropdown   ] <----- country dropdown is always present.
@@ -150,9 +159,10 @@
      * [ phone number field ] <----- phone is always present and required.
      */
     @Override
-    public void edit(
-            @Nullable final AutofillAddress toEdit, final Callback<AutofillAddress> callback) {
-        super.edit(toEdit, callback);
+    public void edit(@Nullable final AutofillAddress toEdit,
+            final Callback<AutofillAddress> doneCallback,
+            final Callback<AutofillAddress> cancelCallback) {
+        super.edit(toEdit, doneCallback, cancelCallback);
 
         if (mAutofillProfileBridge == null) mAutofillProfileBridge = new AutofillProfileBridge();
 
@@ -275,7 +285,7 @@
             // ever called when Cancel has already occurred.
             mAdminAreasLoaded = true;
             PersonalDataManager.getInstance().cancelPendingGetSubKeys();
-            callback.onResult(toEdit);
+            cancelCallback.onResult(toEdit);
         });
 
         // If the user clicks [Done], save changes on disk, mark the address "complete," and send it
@@ -285,7 +295,7 @@
             PersonalDataManager.getInstance().cancelPendingGetSubKeys();
             commitChanges(mProfile);
             address.completeAddress(mProfile);
-            callback.onResult(address);
+            doneCallback.onResult(address);
         });
 
         loadAdminAreasForCountry(mCountryField.getValue().toString());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index 1bed8d1..6d9e539a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -337,6 +337,15 @@
     }
 
     /**
+     * Allows calling |edit| with a single callback used for both 'done' and 'cancel'.
+     * @see #edit(AutofillPaymentInstrument, Callback, Callback)
+     */
+    public void edit(@Nullable final AutofillPaymentInstrument toEdit,
+            final Callback<AutofillPaymentInstrument> callback) {
+        edit(toEdit, callback, callback);
+    }
+
+    /**
      * Builds and shows an editor model with the following fields for local cards.
      *
      * [ accepted card types hint images     ]
@@ -354,8 +363,9 @@
      */
     @Override
     public void edit(@Nullable final AutofillPaymentInstrument toEdit,
-            final Callback<AutofillPaymentInstrument> callback) {
-        super.edit(toEdit, callback);
+            final Callback<AutofillPaymentInstrument> doneCallback,
+            final Callback<AutofillPaymentInstrument> cancelCallback) {
+        super.edit(toEdit, doneCallback, cancelCallback);
 
         // If |toEdit| is null, we're creating a new credit card.
         final boolean isNewCard = toEdit == null;
@@ -376,7 +386,7 @@
             try {
                 calendar = mCalendar.get();
             } catch (InterruptedException | ExecutionException e) {
-                mHandler.post(() -> callback.onResult(null));
+                mHandler.post(() -> cancelCallback.onResult(toEdit));
                 return;
             }
             assert calendar != null;
@@ -399,7 +409,7 @@
 
         // If the user clicks [Cancel], send |toEdit| card back to the caller (will return original
         // state, which could be null, a full card, or a partial card).
-        editor.setCancelCallback(() -> callback.onResult(toEdit));
+        editor.setCancelCallback(() -> cancelCallback.onResult(toEdit));
 
         // If the user clicks [Done], save changes on disk, mark the card "complete," and send it
         // back to the caller.
@@ -417,7 +427,7 @@
             assert billingAddress != null;
 
             instrument.completeInstrument(card, methodName, billingAddress);
-            callback.onResult(instrument);
+            doneCallback.onResult(instrument);
         });
 
         mEditorDialog.show(editor);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
index d726ecf..1bf60328 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
@@ -154,10 +154,20 @@
         mPayerErrors = errors;
     }
 
-    @Override
+    /**
+     * Allows calling |edit| with a single callback used for both 'done' and 'cancel'.
+     * @see #edit(AutofillContact, Callback, Callback)
+     */
     public void edit(
             @Nullable final AutofillContact toEdit, final Callback<AutofillContact> callback) {
-        super.edit(toEdit, callback);
+        edit(toEdit, callback, callback);
+    }
+
+    @Override
+    public void edit(@Nullable final AutofillContact toEdit,
+            final Callback<AutofillContact> doneCallback,
+            final Callback<AutofillContact> cancelCallback) {
+        super.edit(toEdit, doneCallback, cancelCallback);
 
         final AutofillContact contact = toEdit == null
                 ? new AutofillContact(mContext, new AutofillProfile(), null, null, null,
@@ -215,7 +225,7 @@
 
         // If the user clicks [Cancel], send |toEdit| contact back to the caller, which was the
         // original state (could be null, a complete contact, a partial contact).
-        editor.setCancelCallback(() -> callback.onResult(toEdit));
+        editor.setCancelCallback(() -> cancelCallback.onResult(toEdit));
 
         editor.setDoneCallback(() -> {
             String name = null;
@@ -251,7 +261,7 @@
 
             profile.setIsLocal(true);
             contact.completeContact(profile.getGUID(), name, phone, email);
-            callback.onResult(contact);
+            doneCallback.onResult(contact);
         });
 
         mEditorDialog.show(editor);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java
index a8bce3f..fa1a4d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ChromeSigninManagerDelegate.java
@@ -4,8 +4,27 @@
 
 package org.chromium.chrome.browser.signin;
 
+import android.app.Activity;
+import android.content.Context;
+
+import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
+import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
+
 /**
  * This implementation of {@link SigninManagerDelegate} provides {@link SigninManager} access to
  * //chrome/browser level dependencies.
  */
-public class ChromeSigninManagerDelegate implements SigninManagerDelegate {}
+public class ChromeSigninManagerDelegate implements SigninManagerDelegate {
+    @Override
+    public void handleGooglePlayServicesUnavailability(Activity activity, boolean cancelable) {
+        UserRecoverableErrorHandler errorHandler = activity != null
+                ? new UserRecoverableErrorHandler.ModalDialog(activity, cancelable)
+                : new UserRecoverableErrorHandler.SystemNotification();
+        ExternalAuthUtils.getInstance().canUseGooglePlayServices(errorHandler);
+    }
+
+    @Override
+    public boolean isGooglePlayServicesPresent(Context context) {
+        return !ExternalAuthUtils.getInstance().isGooglePlayServicesMissing(context);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index 5f8793d..e24c5f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -27,8 +27,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.task.PostTask;
-import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
-import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler;
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountTrackerService;
@@ -303,7 +301,7 @@
      */
     public boolean isSigninSupported() {
         return !ApiCompatibilityUtils.isDemoUser(mContext)
-                && !ExternalAuthUtils.getInstance().isGooglePlayServicesMissing(mContext);
+                && mDelegate.isGooglePlayServicesPresent(mContext);
     }
 
     /**
@@ -425,10 +423,7 @@
             mSignInState.mBlockedOnAccountSeeding = true;
         } else {
             Activity activity = mSignInState.mActivity;
-            UserRecoverableErrorHandler errorHandler = activity != null
-                    ? new UserRecoverableErrorHandler.ModalDialog(activity, !isForceSigninEnabled())
-                    : new UserRecoverableErrorHandler.SystemNotification();
-            ExternalAuthUtils.getInstance().canUseGooglePlayServices(errorHandler);
+            mDelegate.handleGooglePlayServicesUnavailability(activity, !isForceSigninEnabled());
             Log.w(TAG, "Cancelling the sign-in process as Google Play services is unavailable");
             abortSignIn();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java
index 9bf72c9..5cf548a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerDelegate.java
@@ -4,9 +4,25 @@
 
 package org.chromium.chrome.browser.signin;
 
+import android.app.Activity;
+import android.content.Context;
+
 /**
  * Interface providing SigninManager access to dependencies that are not part of the SignIn
  * component. This interface interacts with //chrome features such as Policy, Sync, data wiping,
  * Google Play services.
  */
-public interface SigninManagerDelegate {}
+public interface SigninManagerDelegate {
+    /**
+     * If there is no Google Play Services available, ask the user to fix by showing either a
+     * notification or a modal dialog
+     * @param activity The activity used to open the dialog, or null to use notifications
+     * @param cancelable Whether the dialog can be canceled
+     */
+    public void handleGooglePlayServicesUnavailability(Activity activity, boolean cancelable);
+
+    /**
+     * @return Whether the device has Google Play Services.
+     */
+    public boolean isGooglePlayServicesPresent(Context context);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorBase.java
index ee51bd4..33edd96 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorBase.java
@@ -23,7 +23,7 @@
     /**
      * Sets the user interface to be used for editing contact information.
      *
-     * @param editorView The user interface to be used.
+     * @param editorDialog The user interface to be used.
      */
     public void setEditorDialog(EditorDialog editorDialog) {
         assert editorDialog != null;
@@ -36,11 +36,16 @@
      *
      * @param toEdit   The information to edit. Can be null if the user is adding new information
      *                 instead of editing an existing one.
-     * @param callback The callback to invoke with the complete and valid information. Can be
-     *                 invoked with null if the user clicked Cancel.
+     * @param doneCallback The callback to invoke when confirming the edit dialog, with the complete
+     *                     and valid information.
+     * @param cancelCallback The callback to invoke when cancelling the edit dialog. Can be called
+     *         with null (|toEdit| was null), incomplete information (|toEdit| was incomplete),
+     *         invalid information (|toEdit| was invalid), or even with complete and valid
+     *         information (|toEdit| was both complete and valid to begin with).
      */
-    protected void edit(@Nullable T toEdit, Callback<T> callback) {
-        assert callback != null;
+    protected void edit(@Nullable T toEdit, Callback<T> doneCallback, Callback<T> cancelCallback) {
+        assert doneCallback != null;
+        assert cancelCallback != null;
         assert mEditorDialog != null;
         assert mContext != null;
     }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index e4bb0b7..fe779849 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-76.0.3803.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-76.0.3804.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index bccc024..39da66c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2612,6 +2612,9 @@
       "android/widget/thumbnail_generator.cc",
       "android/widget/thumbnail_generator.h",
       "autofill/accessory_controller.h",
+      "autofill/address_accessory_controller.h",
+      "autofill/address_accessory_controller_impl.cc",
+      "autofill/address_accessory_controller_impl.h",
       "autofill/android/personal_data_manager_android.cc",
       "autofill/android/personal_data_manager_android.h",
       "autofill/android/phone_number_util_android.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4503a31e..61529f28 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -452,13 +452,6 @@
 
 #endif  // OS_CHROMEOS
 
-const FeatureEntry::Choice kV8CacheOptionsChoices[] = {
-    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
-    {flags_ui::kGenericExperimentChoiceDisabled, switches::kV8CacheOptions,
-     "none"},
-    {flag_descriptions::kV8CacheOptionsCode, switches::kV8CacheOptions, "code"},
-};
-
 #if defined(OS_CHROMEOS)
 const FeatureEntry::Choice kCrosRegionsModeChoices[] = {
     {flag_descriptions::kCrosRegionsModeDefault, "", ""},
@@ -1777,9 +1770,6 @@
      flag_descriptions::kAcceleratedMjpegDecodeDescription, kOsCrOS,
      SINGLE_DISABLE_VALUE_TYPE(switches::kDisableAcceleratedMjpegDecode)},
 #endif  // OS_CHROMEOS
-    {"v8-cache-options", flag_descriptions::kV8CacheOptionsName,
-     flag_descriptions::kV8CacheOptionsDescription, kOsAll,
-     MULTI_VALUE_TYPE(kV8CacheOptionsChoices)},
     {"system-keyboard-lock", flag_descriptions::kSystemKeyboardLockName,
      flag_descriptions::kSystemKeyboardLockDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kSystemKeyboardLock)},
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 0cda26c..b908adf 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -3320,6 +3320,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewPluginTest, TestLoadPluginInternalResource) {
+  if (content::MimeHandlerViewMode::UsesCrossProcessFrame()) {
+    // Permissions are broken with frame-based MimeHandlerView as it never goes
+    // through the same plugin checks when attaching an <embed>. Fix this asap.
+    // (https://crbug.com/963694).
+    return;
+  }
   const char kTestMimeType[] = "application/pdf";
   const char kTestFileType[] = "pdf";
   content::WebPluginInfo plugin_info;
diff --git a/chrome/browser/autofill/address_accessory_controller.h b/chrome/browser/autofill/address_accessory_controller.h
new file mode 100644
index 0000000..b867127
--- /dev/null
+++ b/chrome/browser/autofill/address_accessory_controller.h
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
+#define CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/autofill/accessory_controller.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace autofill {
+
+// Interface for address-specific keyboard accessory controller between the
+// ManualFillingController and the autofill backend.
+//
+// There is a single instance per WebContents that can be accessed by calling:
+//     AddressAccessoryController::GetOrCreate(web_contents);
+// On the first call, an instance is attached to |web_contents|, so it can be
+// returned by subsequent calls.
+class AddressAccessoryController
+    : public base::SupportsWeakPtr<AddressAccessoryController>,
+      public AccessoryController {
+ public:
+  AddressAccessoryController() = default;
+  ~AddressAccessoryController() override = default;
+
+  // Returns true if the accessory controller may exist for |web_contents|.
+  // Otherwise it returns false.
+  static bool AllowedForWebContents(content::WebContents* web_contents);
+
+  // Returns a reference to the unique AddressAccessoryController associated
+  // with |web_contents|. A new instance is created if the first time this
+  // function is called. Only valid to be called if
+  // |AddressAccessoryController::AllowedForWebContents(web_contents)|.
+  static AddressAccessoryController* GetOrCreate(
+      content::WebContents* web_contents);
+
+  // Returns a reference to the unique AddressAccessoryController associated
+  // with |web_contents|. Returns null if no such instance exists.
+  static AddressAccessoryController* GetIfExisting(
+      content::WebContents* web_contents);
+
+  // Fetches suggestions and propagates them to the frontend.
+  virtual void RefreshSuggestions() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AddressAccessoryController);
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
diff --git a/chrome/browser/autofill/address_accessory_controller_impl.cc b/chrome/browser/autofill/address_accessory_controller_impl.cc
new file mode 100644
index 0000000..4d0995d
--- /dev/null
+++ b/chrome/browser/autofill/address_accessory_controller_impl.cc
@@ -0,0 +1,181 @@
+// 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/autofill/address_accessory_controller_impl.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/autofill/manual_filling_controller.h"
+#include "chrome/browser/autofill/manual_filling_utils.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/vr/vr_tab_helper.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+namespace {
+
+// Defines which types to load from the Personal data manager and add as field
+// to the address sheet. Order matters.
+constexpr ServerFieldType kTypesToInclude[] = {
+    // TODO(crbug.com/965494): Possibly, the names should be in a single chip.
+    ServerFieldType::NAME_FIRST,
+    ServerFieldType::NAME_MIDDLE,
+    ServerFieldType::NAME_LAST,
+    ServerFieldType::COMPANY_NAME,
+    ServerFieldType::ADDRESS_HOME_LINE1,
+    ServerFieldType::ADDRESS_HOME_LINE2,
+    ServerFieldType::ADDRESS_HOME_ZIP,
+    ServerFieldType::ADDRESS_HOME_CITY,
+    ServerFieldType::ADDRESS_HOME_STATE,
+    ServerFieldType::ADDRESS_HOME_COUNTRY,
+    ServerFieldType::PHONE_HOME_WHOLE_NUMBER,
+    ServerFieldType::EMAIL_ADDRESS,
+};
+
+void AddProfileInfoAsSelectableField(UserInfo* info,
+                                     const AutofillProfile* profile,
+                                     ServerFieldType type) {
+  base::string16 field = profile->GetRawInfo(type);
+  if (type == ServerFieldType::NAME_MIDDLE && field.empty()) {
+    field = profile->GetRawInfo(ServerFieldType::NAME_MIDDLE_INITIAL);
+  }
+  info->add_field(UserInfo::Field(field, field, /*is_password=*/false,
+                                  /*selectable=*/true));
+}
+
+UserInfo TranslateProfile(const AutofillProfile* profile) {
+  UserInfo info;
+  for (ServerFieldType server_field_type : kTypesToInclude) {
+    AddProfileInfoAsSelectableField(&info, profile, server_field_type);
+  }
+  return info;
+}
+
+std::vector<UserInfo> UserInfosForProfiles(
+    const std::vector<AutofillProfile*>& profiles) {
+  std::vector<UserInfo> infos(profiles.size());
+  std::transform(profiles.begin(), profiles.end(), infos.begin(),
+                 TranslateProfile);
+  return infos;
+}
+
+std::vector<FooterCommand> CreateManageAddressesFooter() {
+  return {FooterCommand(l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK))};
+}
+
+}  // namespace
+
+AddressAccessoryControllerImpl::~AddressAccessoryControllerImpl() = default;
+
+// static
+bool AddressAccessoryController::AllowedForWebContents(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
+  if (vr::VrTabHelper::IsInVr(web_contents)) {
+    return false;  // TODO(crbug.com/865749): Re-Enable if possible.
+  }
+  return base::FeatureList::IsEnabled(
+      autofill::features::kAutofillKeyboardAccessory);
+}
+
+// static
+AddressAccessoryController* AddressAccessoryController::GetOrCreate(
+    content::WebContents* web_contents) {
+  DCHECK(AddressAccessoryController::AllowedForWebContents(web_contents));
+
+  AddressAccessoryControllerImpl::CreateForWebContents(web_contents);
+  return AddressAccessoryControllerImpl::FromWebContents(web_contents);
+}
+
+// static
+AddressAccessoryController* AddressAccessoryController::GetIfExisting(
+    content::WebContents* web_contents) {
+  return AddressAccessoryControllerImpl::FromWebContents(web_contents);
+}
+
+void AddressAccessoryControllerImpl::OnFillingTriggered(
+    const UserInfo::Field& selection) {
+  // Since the data we fill is scoped to the profile and not to a frame, we can
+  // fill the focused frame - we basically behave like a keyboard here.
+  autofill::ContentAutofillDriver* driver =
+      autofill::ContentAutofillDriver::GetForRenderFrameHost(
+          web_contents_->GetFocusedFrame());
+  if (!driver)
+    return;
+  driver->RendererShouldFillFieldWithValue(selection.display_text());
+}
+
+void AddressAccessoryControllerImpl::RefreshSuggestions() {
+  std::vector<AutofillProfile*> profiles = GetProfiles();
+  base::string16 title_or_empty_message;
+  if (profiles.empty())
+    title_or_empty_message =
+        l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE);
+  GetManualFillingController()->RefreshSuggestionsForField(
+      mojom::FocusedFieldType::kFillableTextField,
+      autofill::CreateAccessorySheetData(
+          autofill::FallbackSheetType::ADDRESS, title_or_empty_message,
+          UserInfosForProfiles(profiles), CreateManageAddressesFooter()));
+}
+
+// static
+void AddressAccessoryControllerImpl::CreateForWebContentsForTesting(
+    content::WebContents* web_contents,
+    base::WeakPtr<ManualFillingController> mf_controller,
+    autofill::PersonalDataManager* personal_data_manager) {
+  DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
+  DCHECK(!FromWebContents(web_contents)) << "Controller already attached!";
+  DCHECK(mf_controller);
+
+  web_contents->SetUserData(
+      UserDataKey(),
+      base::WrapUnique(new AddressAccessoryControllerImpl(
+          web_contents, std::move(mf_controller), personal_data_manager)));
+}
+
+AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents),
+      personal_data_manager_for_testing_(nullptr) {}
+
+// Additional creation functions in unit tests only:
+AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
+    content::WebContents* web_contents,
+    base::WeakPtr<ManualFillingController> mf_controller,
+    autofill::PersonalDataManager* personal_data_manager)
+    : web_contents_(web_contents),
+      mf_controller_(std::move(mf_controller)),
+      personal_data_manager_for_testing_(personal_data_manager) {}
+
+std::vector<AutofillProfile*> AddressAccessoryControllerImpl::GetProfiles() {
+  const autofill::PersonalDataManager* data_manager =
+      personal_data_manager_for_testing_;
+  if (!data_manager)
+    data_manager = autofill::PersonalDataManagerFactory::GetForProfile(
+        Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
+  if (!data_manager)
+    return {};  // No data available yet or anymore!
+  return data_manager->GetProfilesToSuggest();
+}
+
+base::WeakPtr<ManualFillingController>
+AddressAccessoryControllerImpl::GetManualFillingController() {
+  if (!mf_controller_)
+    mf_controller_ = ManualFillingController::GetOrCreate(web_contents_);
+  DCHECK(mf_controller_);
+  return mf_controller_;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AddressAccessoryControllerImpl)
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/address_accessory_controller_impl.h b/chrome/browser/autofill/address_accessory_controller_impl.h
new file mode 100644
index 0000000..40c3c2b
--- /dev/null
+++ b/chrome/browser/autofill/address_accessory_controller_impl.h
@@ -0,0 +1,81 @@
+// 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_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
+
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/autofill/address_accessory_controller.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "url/gurl.h"
+
+class ManualFillingController;
+
+namespace autofill {
+class AutofillProfile;
+class PersonalDataManager;
+
+// Use either AddressAccessoryController::GetOrCreate or
+// AddressAccessoryController::GetIfExisting to obtain instances of this class.
+// This class exists for every tab and should never store state based on the
+// contents of one of its frames.
+class AddressAccessoryControllerImpl
+    : public AddressAccessoryController,
+      public content::WebContentsUserData<AddressAccessoryControllerImpl> {
+ public:
+  ~AddressAccessoryControllerImpl() override;
+
+  // AccessoryController:
+  void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
+
+  // AddressAccessoryController:
+  void RefreshSuggestions() override;
+
+  // Like |CreateForWebContents|, it creates the controller and attaches it to
+  // the given |web_contents|. Additionally, it allows inject a manual filling
+  // controller.
+  static void CreateForWebContentsForTesting(
+      content::WebContents* web_contents,
+      base::WeakPtr<ManualFillingController> mf_controller,
+      autofill::PersonalDataManager* personal_data_manager);
+
+ private:
+  friend class content::WebContentsUserData<AddressAccessoryControllerImpl>;
+
+  // Required for construction via |CreateForWebContents|:
+  explicit AddressAccessoryControllerImpl(content::WebContents* contents);
+
+  std::vector<autofill::AutofillProfile*> GetProfiles();
+
+  // Constructor that allows to inject a mock or fake view.
+  AddressAccessoryControllerImpl(
+      content::WebContents* web_contents,
+      base::WeakPtr<ManualFillingController> mf_controller,
+      autofill::PersonalDataManager* personal_data_manager);
+
+  // Lazy-initializes and returns the ManualFillingController for the current
+  // |web_contents_|. The lazy initialization allows injecting mocks for tests.
+  base::WeakPtr<ManualFillingController> GetManualFillingController();
+
+  // The tab for which this class is scoped.
+  content::WebContents* web_contents_;
+
+  // The password accessory controller object to forward client requests to.
+  base::WeakPtr<ManualFillingController> mf_controller_;
+
+  // The data manager used to retrieve the profiles in tests.
+  const autofill::PersonalDataManager* personal_data_manager_for_testing_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(AddressAccessoryControllerImpl);
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/autofill/address_accessory_controller_impl_unittest.cc b/chrome/browser/autofill/address_accessory_controller_impl_unittest.cc
new file mode 100644
index 0000000..7080e54
--- /dev/null
+++ b/chrome/browser/autofill/address_accessory_controller_impl_unittest.cc
@@ -0,0 +1,143 @@
+// 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/autofill/address_accessory_controller_impl.h"
+
+#include <algorithm>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/mock_callback.h"
+#include "chrome/browser/autofill/mock_manual_filling_controller.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/strings/grit/components_strings.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+namespace {
+using autofill::UserInfo;
+using base::ASCIIToUTF16;
+using testing::_;
+using testing::ByMove;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+using FillingSource = ManualFillingController::FillingSource;
+
+base::string16 addresses_empty_str() {
+  return l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE);
+}
+
+base::string16 manage_addresses_str() {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK);
+}
+
+// Creates a AccessorySheetData::Builder with a "Manage Addresses" footer.
+AccessorySheetData::Builder AddressAccessorySheetDataBuilder(
+    const base::string16& title) {
+  return AccessorySheetData::Builder(FallbackSheetType::ADDRESS, title)
+      .AppendFooterCommand(manage_addresses_str());
+}
+
+}  // namespace
+
+class AddressAccessoryControllerTest : public ChromeRenderViewHostTestHarness {
+ public:
+  AddressAccessoryControllerTest() {}
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    personal_data_manager_.SetAutofillProfileEnabled(true);
+
+    AddressAccessoryControllerImpl::CreateForWebContentsForTesting(
+        web_contents(), mock_manual_filling_controller_.AsWeakPtr(),
+        &personal_data_manager_);
+  }
+
+  void TearDown() override { personal_data_manager_.ClearProfiles(); }
+
+  AddressAccessoryController* controller() {
+    return AddressAccessoryControllerImpl::FromWebContents(web_contents());
+  }
+
+ protected:
+  StrictMock<MockManualFillingController> mock_manual_filling_controller_;
+  autofill::TestPersonalDataManager personal_data_manager_;
+};
+
+TEST_F(AddressAccessoryControllerTest, IsNotRecreatedForSameWebContents) {
+  AddressAccessoryControllerImpl* initial_controller =
+      AddressAccessoryControllerImpl::FromWebContents(web_contents());
+  EXPECT_NE(nullptr, initial_controller);
+  AddressAccessoryControllerImpl::CreateForWebContents(web_contents());
+  EXPECT_EQ(AddressAccessoryControllerImpl::FromWebContents(web_contents()),
+            initial_controller);
+}
+
+TEST_F(AddressAccessoryControllerTest, RefreshSuggestionsCallsUI) {
+  AutofillProfile canadian = test::GetFullValidProfileForCanada();
+  personal_data_manager_.AddProfile(canadian);
+
+  autofill::AccessorySheetData result(autofill::FallbackSheetType::PASSWORD,
+                                      base::string16());
+  EXPECT_CALL(mock_manual_filling_controller_,
+              RefreshSuggestionsForField(
+                  mojom::FocusedFieldType::kFillableTextField, _))
+      .WillOnce(SaveArg<1>(&result));
+
+  controller()->RefreshSuggestions();
+
+  ASSERT_EQ(
+      result,
+      AddressAccessorySheetDataBuilder(base::string16())
+          .AddUserInfo()
+          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_FIRST))
+          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_MIDDLE))
+          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_LAST))
+          .AppendSimpleField(canadian.GetRawInfo(ServerFieldType::COMPANY_NAME))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_LINE1))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_LINE2))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_ZIP))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_CITY))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_STATE))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_COUNTRY))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::PHONE_HOME_WHOLE_NUMBER))
+          .AppendSimpleField(
+              canadian.GetRawInfo(ServerFieldType::EMAIL_ADDRESS))
+          .Build());
+}
+
+TEST_F(AddressAccessoryControllerTest, ProvidesEmptySuggestionsMessage) {
+  autofill::AccessorySheetData result(autofill::FallbackSheetType::PASSWORD,
+                                      base::string16());
+  EXPECT_CALL(mock_manual_filling_controller_,
+              RefreshSuggestionsForField(
+                  mojom::FocusedFieldType::kFillableTextField, _))
+      .WillOnce(SaveArg<1>(&result));
+
+  controller()->RefreshSuggestions();
+
+  ASSERT_EQ(result,
+            AddressAccessorySheetDataBuilder(addresses_empty_str()).Build());
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
index bdc95f5..35f9c91 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/browser/autofill/credit_card_accessory_controller_impl.h"
 
+#include <algorithm>
 #include <iterator>
+#include <utility>
 #include <vector>
 
 #include "base/strings/utf_string_conversions.h"
@@ -28,7 +30,7 @@
                                        /*selectable=*/true));
 }
 
-UserInfo Translate(const CreditCard* data) {
+UserInfo TranslateCard(const CreditCard* data) {
   DCHECK(data);
 
   UserInfo user_info;
@@ -63,7 +65,7 @@
   const std::vector<CreditCard*> suggestions = GetSuggestions();
   std::vector<UserInfo> info_to_add;
   std::transform(suggestions.begin(), suggestions.end(),
-                 std::back_inserter(info_to_add), &Translate);
+                 std::back_inserter(info_to_add), &TranslateCard);
 
   // TODO(crbug.com/902425): Add "Manage payment methods" footer command
   std::vector<FooterCommand> footer_commands;
diff --git a/chrome/browser/browsing_data/browsing_data_cache_storage_helper.cc b/chrome/browser/browsing_data/browsing_data_cache_storage_helper.cc
index 750a3ec..63921f2 100644
--- a/chrome/browser/browsing_data/browsing_data_cache_storage_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_cache_storage_helper.cc
@@ -25,7 +25,7 @@
 void GetAllOriginsInfoForCacheStorageCallback(
     BrowsingDataCacheStorageHelper::FetchCallback callback,
     const std::vector<StorageUsageInfo>& origins) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
   std::list<content::StorageUsageInfo> result;
@@ -35,8 +35,7 @@
     result.push_back(origin);
   }
 
-  base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
-                           base::BindOnce(std::move(callback), result));
+  std::move(callback).Run(result);
 }
 
 }  // namespace
@@ -52,33 +51,12 @@
 void BrowsingDataCacheStorageHelper::StartFetching(FetchCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(
-          &BrowsingDataCacheStorageHelper::FetchCacheStorageUsageInfoOnIOThread,
-          this, std::move(callback)));
-}
-
-void BrowsingDataCacheStorageHelper::DeleteCacheStorage(const GURL& origin) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(
-          &BrowsingDataCacheStorageHelper::DeleteCacheStorageOnIOThread, this,
-          origin));
-}
-
-void BrowsingDataCacheStorageHelper::FetchCacheStorageUsageInfoOnIOThread(
-    FetchCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!callback.is_null());
   cache_storage_context_->GetAllOriginsInfo(base::BindOnce(
       &GetAllOriginsInfoForCacheStorageCallback, std::move(callback)));
 }
 
-void BrowsingDataCacheStorageHelper::DeleteCacheStorageOnIOThread(
-    const GURL& origin) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+void BrowsingDataCacheStorageHelper::DeleteCacheStorage(const GURL& origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   cache_storage_context_->DeleteForOrigin(origin);
 }
 
diff --git a/chrome/browser/browsing_data/browsing_data_cache_storage_helper.h b/chrome/browser/browsing_data/browsing_data_cache_storage_helper.h
index 8be5f29..60f98bc 100644
--- a/chrome/browser/browsing_data/browsing_data_cache_storage_helper.h
+++ b/chrome/browser/browsing_data/browsing_data_cache_storage_helper.h
@@ -54,12 +54,6 @@
  private:
   friend class base::RefCountedThreadSafe<BrowsingDataCacheStorageHelper>;
 
-  // Deletes Cache Storages for an origin the IO thread.
-  void DeleteCacheStorageOnIOThread(const GURL& origin);
-
-  // Enumerates all Cache Storage instances on the IO thread.
-  void FetchCacheStorageUsageInfoOnIOThread(FetchCallback callback);
-
   DISALLOW_COPY_AND_ASSIGN(BrowsingDataCacheStorageHelper);
 };
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 098d7e2..398abbd 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -324,6 +324,7 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "net/base/load_flags.h"
 #include "net/base/mime_util.h"
@@ -1462,12 +1463,13 @@
   mojo::PendingRemote<service_manager::mojom::Service> service;
   *service_request = service.InitWithNewPipeAndPassReceiver();
   service_manager::Identity renderer_identity = host->GetChildIdentity();
+  mojo::Remote<service_manager::mojom::ProcessMetadata> metadata;
   ChromeService::GetInstance()->connector()->RegisterServiceInstance(
       service_manager::Identity(chrome::mojom::kRendererServiceName,
                                 renderer_identity.instance_group(),
                                 renderer_identity.instance_id(),
                                 base::Token::CreateRandom()),
-      std::move(service), mojo::NullReceiver() /* metadata_receiver */);
+      std::move(service), metadata.BindNewPipeAndPassReceiver());
 }
 
 GURL ChromeContentBrowserClient::GetEffectiveURL(
@@ -5791,7 +5793,7 @@
 #endif
 
 base::flat_set<std::string>
-ChromeContentBrowserClient::GetMimeHandlerViewMimeTypes(
+ChromeContentBrowserClient::GetPluginMimeTypesWithExternalHandlers(
     content::ResourceContext* resource_context) {
   base::flat_set<std::string> mime_types;
 #if BUILDFLAG(ENABLE_PLUGINS)
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 5e41c50..f5354152 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -587,7 +587,7 @@
       const override;
 #endif
 
-  base::flat_set<std::string> GetMimeHandlerViewMimeTypes(
+  base::flat_set<std::string> GetPluginMimeTypesWithExternalHandlers(
       content::ResourceContext* resource_context) override;
 
   void AugmentNavigationDownloadPolicy(
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index dd170dd..eef8a1b 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -1445,7 +1445,8 @@
                      expected_entry) != synthetic_trials.end();
   }
 
-  const std::string kSyntheticTrialName = "SiteIsolationActive";
+  const std::string kSiteIsolationSyntheticTrialName = "SiteIsolationActive";
+  const std::string kOOPIFSyntheticTrialName = "OutOfProcessIframesActive";
   const std::string kSyntheticTrialGroup = "Enabled";
 
  protected:
@@ -1548,24 +1549,31 @@
           web_contents);
   recorder->EnableSiteIsolationSyntheticTrialForTesting();
 
-  EXPECT_FALSE(HasSyntheticTrial(kSyntheticTrialName));
+  EXPECT_FALSE(HasSyntheticTrial(kSiteIsolationSyntheticTrialName));
+  EXPECT_FALSE(HasSyntheticTrial(kOOPIFSyntheticTrialName));
 
   // Browse to a page with some iframes without involving any isolated origins.
   GURL unisolated_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b,c(a))"));
   ui_test_utils::NavigateToURL(browser(), unisolated_url);
-  EXPECT_FALSE(HasSyntheticTrial(kSyntheticTrialName));
+  EXPECT_FALSE(HasSyntheticTrial(kSiteIsolationSyntheticTrialName));
 
   // Now browse to an isolated origin.
   GURL isolated_url(
       embedded_test_server()->GetURL("isolated1.com", "/title1.html"));
   ui_test_utils::NavigateToURL(browser(), isolated_url);
-  EXPECT_TRUE(
-      IsInSyntheticTrialGroup(kSyntheticTrialName, kSyntheticTrialGroup));
+  EXPECT_TRUE(IsInSyntheticTrialGroup(kSiteIsolationSyntheticTrialName,
+                                      kSyntheticTrialGroup));
+
+  // The OOPIF synthetic trial shouldn't be activated, since the isolated
+  // oriign page doesn't have any OOPIFs.
+  EXPECT_FALSE(
+      IsInSyntheticTrialGroup(kOOPIFSyntheticTrialName, kSyntheticTrialGroup));
 }
 
-// This test checks that the synthetic field trial is activated properly after
-// a navigation to an isolated origin commits in a subframe.
+// This test checks that the synthetic field trials for both site isolation and
+// encountering OOPIFs are activated properly after a navigation to an isolated
+// origin commits in a subframe.
 IN_PROC_BROWSER_TEST_F(SiteIsolationForPasswordSitesBrowserTest,
                        SyntheticTrialFromSubframe) {
   content::WebContents* web_contents =
@@ -1576,14 +1584,17 @@
           web_contents);
   recorder->EnableSiteIsolationSyntheticTrialForTesting();
 
-  EXPECT_FALSE(HasSyntheticTrial(kSyntheticTrialName));
+  EXPECT_FALSE(HasSyntheticTrial(kSiteIsolationSyntheticTrialName));
+  EXPECT_FALSE(HasSyntheticTrial(kOOPIFSyntheticTrialName));
 
   // Browse to a page with an isolated origin on one of the iframes.
   GURL isolated_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b,c,isolated2,d)"));
   ui_test_utils::NavigateToURL(browser(), isolated_url);
+  EXPECT_TRUE(IsInSyntheticTrialGroup(kSiteIsolationSyntheticTrialName,
+                                      kSyntheticTrialGroup));
   EXPECT_TRUE(
-      IsInSyntheticTrialGroup(kSyntheticTrialName, kSyntheticTrialGroup));
+      IsInSyntheticTrialGroup(kOOPIFSyntheticTrialName, kSyntheticTrialGroup));
 }
 
 // Verifies that persistent isolated sites survive restarts.  Part 1.
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc
index 30af4e57..8f83d64 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc
@@ -12,14 +12,7 @@
 
 ModelConfig::ModelConfig() = default;
 
-ModelConfig::ModelConfig(const ModelConfig& config) {
-  auto_brightness_als_horizon_seconds =
-      config.auto_brightness_als_horizon_seconds;
-  log_lux = config.log_lux;
-  brightness = config.brightness;
-  metrics_key = config.metrics_key;
-  model_als_horizon_seconds = config.model_als_horizon_seconds;
-}
+ModelConfig::ModelConfig(const ModelConfig& config) = default;
 
 ModelConfig::~ModelConfig() = default;
 
@@ -29,6 +22,9 @@
                config.auto_brightness_als_horizon_seconds) >= kTol)
     return false;
 
+  if (enabled != config.enabled)
+    return false;
+
   if (log_lux.size() != config.log_lux.size())
     return false;
 
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h
index 5b47542..7d49d72 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h
@@ -16,6 +16,7 @@
 // Model customization config.
 struct ModelConfig {
   double auto_brightness_als_horizon_seconds = -1.0;
+  bool enabled = false;
   std::vector<double> log_lux;
   std::vector<double> brightness;
   std::string metrics_key;
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl.cc b/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl.cc
index d28acc7..3fa5587 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl.cc
@@ -58,12 +58,14 @@
 
 struct ModelConfigFromJson {
   double auto_brightness_als_horizon_seconds;
+  bool enabled;
   GlobalCurveFromJson global_curve;
   std::string metrics_key;
   double model_als_horizon_seconds;
 
   ModelConfigFromJson()
       : auto_brightness_als_horizon_seconds(-1.0),
+        enabled(false),
         model_als_horizon_seconds(-1.0) {}
 
   static void RegisterJSONConverter(
@@ -71,6 +73,7 @@
     converter->RegisterDoubleField(
         "auto_brightness_als_horizon_seconds",
         &ModelConfigFromJson::auto_brightness_als_horizon_seconds);
+    converter->RegisterBoolField("enabled", &ModelConfigFromJson::enabled);
     converter->RegisterNestedField("global_curve",
                                    &ModelConfigFromJson::global_curve);
     converter->RegisterStringField("metrics_key",
@@ -158,6 +161,9 @@
           "auto_brightness_als_horizon_seconds",
           model_config_.auto_brightness_als_horizon_seconds);
 
+  model_config_.enabled = GetFieldTrialParamByFeatureAsBool(
+      features::kAutoScreenBrightness, "enabled", model_config_.enabled);
+
   model_config_.model_als_horizon_seconds = GetFieldTrialParamByFeatureAsInt(
       features::kAutoScreenBrightness, "model_als_horizon_seconds",
       model_config_.model_als_horizon_seconds);
@@ -228,6 +234,8 @@
   model_config_.auto_brightness_als_horizon_seconds =
       loaded_model_configs.auto_brightness_als_horizon_seconds;
 
+  model_config_.enabled = loaded_model_configs.enabled;
+
   std::vector<double> log_lux;
   for (const auto& log_lux_val : loaded_model_configs.global_curve.log_lux) {
     DCHECK(log_lux_val);
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
index cc33291..3933935 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
@@ -109,24 +109,26 @@
 };
 
 TEST_F(ModelConfigLoaderImplTest, ValidModelParamsLoaded) {
-  const std::string model_params =
-      "{\n"
-      "  \"auto_brightness_als_horizon_seconds\": 2, \n"
-      "  \"global_curve\": { \n"
-      "  \"log_lux\": [ \n"
-      "      1.0, \n"
-      "      2.0, \n"
-      "      3.0 \n"
-      "    ], \n"
-      "  \"brightness\": [ \n"
-      "      10.0, \n"
-      "      20.0, \n"
-      "      30.0 \n"
-      "    ] \n"
-      "   }, \n"
-      "  \"metrics_key\": \"abc\", \n"
-      "  \"model_als_horizon_seconds\": 5 \n"
-      "}\n";
+  const std::string model_params = R"(
+      {
+        "auto_brightness_als_horizon_seconds": 2,
+        "enabled": true,
+        "global_curve": {
+        "log_lux": [
+            1.0,
+            2.0,
+            3.0
+          ],
+        "brightness": [
+            10.0,
+            20.0,
+            30.0
+          ]
+         },
+        "metrics_key": "abc",
+        "model_als_horizon_seconds": 5
+      }
+      )";
 
   Init(model_params);
   EXPECT_TRUE(test_observer_->model_config_loader_initialized());
@@ -136,6 +138,45 @@
 
   ModelConfig expected_model_config;
   expected_model_config.auto_brightness_als_horizon_seconds = 2.0;
+  expected_model_config.enabled = true;
+  expected_model_config.log_lux = expected_log_lux;
+  expected_model_config.brightness = expected_brightness;
+  expected_model_config.metrics_key = "abc";
+  expected_model_config.model_als_horizon_seconds = 5;
+  EXPECT_TRUE(test_observer_->model_config());
+  EXPECT_EQ(*test_observer_->model_config(), expected_model_config);
+}
+
+TEST_F(ModelConfigLoaderImplTest, MissingEnabledMeansFalse) {
+  const std::string model_params = R"(
+      {
+        "auto_brightness_als_horizon_seconds": 2,
+        "global_curve": {
+        "log_lux": [
+            1.0,
+            2.0,
+            3.0
+          ],
+        "brightness": [
+            10.0,
+            20.0,
+            30.0
+          ]
+         },
+        "metrics_key": "abc",
+        "model_als_horizon_seconds": 5
+      }
+      )";
+
+  Init(model_params);
+  EXPECT_TRUE(test_observer_->model_config_loader_initialized());
+
+  std::vector<double> expected_log_lux = {1.0, 2.0, 3.0};
+  std::vector<double> expected_brightness = {10.0, 20.0, 30.0};
+
+  ModelConfig expected_model_config;
+  expected_model_config.auto_brightness_als_horizon_seconds = 2.0;
+  expected_model_config.enabled = false;
   expected_model_config.log_lux = expected_log_lux;
   expected_model_config.brightness = expected_brightness;
   expected_model_config.metrics_key = "abc";
@@ -145,29 +186,32 @@
 }
 
 TEST_F(ModelConfigLoaderImplTest, ValidModelParamsLoadedThenOverriden) {
-  const std::string model_params =
-      "{\n"
-      "  \"auto_brightness_als_horizon_seconds\": 2, \n"
-      "  \"global_curve\": { \n"
-      "  \"log_lux\": [ \n"
-      "      1.0, \n"
-      "      2.0, \n"
-      "      3.0 \n"
-      "    ], \n"
-      "  \"brightness\": [ \n"
-      "      10.0, \n"
-      "      20.0, \n"
-      "      30.0 \n"
-      "    ] \n"
-      "   }, \n"
-      "  \"metrics_key\": \"abc\", \n"
-      "  \"model_als_horizon_seconds\": 5 \n"
-      "}\n";
+  const std::string model_params = R"(
+      {
+        "auto_brightness_als_horizon_seconds": 2,
+        "enabled": true,
+        "global_curve": {
+        "log_lux": [
+            1.0,
+            2.0,
+            3.0
+          ],
+        "brightness": [
+            10.0,
+            20.0,
+            30.0
+          ]
+         },
+        "metrics_key": "abc",
+        "model_als_horizon_seconds": 5
+      }
+      )";
 
   const std::string global_curve_spec("2:20,4:40,6:60");
 
   const std::map<std::string, std::string> experiment_params = {
       {"auto_brightness_als_horizon_seconds", "10"},
+      {"enabled", "false"},
       {"model_als_horizon_seconds", "20"},
       {"global_curve", global_curve_spec},
   };
@@ -180,6 +224,7 @@
 
   ModelConfig expected_model_config;
   expected_model_config.auto_brightness_als_horizon_seconds = 10.0;
+  expected_model_config.enabled = false;
   expected_model_config.log_lux = expected_log_lux;
   expected_model_config.brightness = expected_brightness;
   expected_model_config.metrics_key = "abc";
@@ -190,23 +235,24 @@
 
 TEST_F(ModelConfigLoaderImplTest, InvalidModelParamsLoaded) {
   // "auto_brightness_als_horizon_seconds" is missing.
-  const std::string model_params =
-      "{\n"
-      "  \"global_curve\": { \n"
-      "  \"log_lux\": [ \n"
-      "      1.0, \n"
-      "      2.0, \n"
-      "      3.0 \n"
-      "    ], \n"
-      "  \"brightness\": [ \n"
-      "      10.0, \n"
-      "      20.0, \n"
-      "      30.0 \n"
-      "    ] \n"
-      "   }, \n"
-      "  \"metrics_key\": \"abc\", \n"
-      "  \"model_als_horizon_seconds\": 5 \n"
-      "}\n";
+  const std::string model_params = R"(
+      {
+        "global_curve": {
+        "log_lux": [
+            1.0,
+            2.0,
+            3.0
+          ],
+        "brightness": [
+            10.0,
+            20.0,
+            30.0
+          ]
+         },
+        "metrics_key": "abc",
+        "model_als_horizon_seconds": 5
+      }
+      )";
 
   Init(model_params);
   EXPECT_TRUE(test_observer_->model_config_loader_initialized());
@@ -216,23 +262,24 @@
 TEST_F(ModelConfigLoaderImplTest, InvalidModelParamsLoadedThenOverriden) {
   // Same as InvalidModelParamsLoaded, but missing
   // "auto_brightness_als_horizon_seconds" is specified in the experiment flags.
-  const std::string model_params =
-      "{\n"
-      "  \"global_curve\": { \n"
-      "  \"log_lux\": [ \n"
-      "      1.0, \n"
-      "      2.0, \n"
-      "      3.0 \n"
-      "    ], \n"
-      "  \"brightness\": [ \n"
-      "      10.0, \n"
-      "      20.0, \n"
-      "      30.0 \n"
-      "    ] \n"
-      "   }, \n"
-      "  \"metrics_key\": \"abc\", \n"
-      "  \"model_als_horizon_seconds\": 5 \n"
-      "}\n";
+  const std::string model_params = R"(
+      {
+        "global_curve": {
+        "log_lux": [
+            1.0,
+            2.0,
+            3.0
+          ],
+        "brightness": [
+            10.0,
+            20.0,
+            30.0
+          ]
+         },
+        "metrics_key": "abc",
+        "model_als_horizon_seconds": 5
+      }
+      )";
 
   const std::map<std::string, std::string> experiment_params = {
       {"auto_brightness_als_horizon_seconds", "10"},
@@ -247,6 +294,7 @@
 
   ModelConfig expected_model_config;
   expected_model_config.auto_brightness_als_horizon_seconds = 10.0;
+  expected_model_config.enabled = false;
   expected_model_config.log_lux = expected_log_lux;
   expected_model_config.brightness = expected_brightness;
   expected_model_config.metrics_key = "abc";
@@ -269,23 +317,24 @@
 }
 
 TEST_F(ModelConfigLoaderImplTest, InvalidJsonFormat) {
-  const std::string model_params =
-      "{\n"
-      "  \"global_curve\": { \n"
-      "  \"log_lux\": [ \n"
-      "      1.0, \n"
-      "      2.0, \n"
-      "      3.0 \n"
-      "    ], \n"
-      "  \"brightness\": [ \n"
-      "      10.0, \n"
-      "      20.0, \n"
-      "      30.0 \n"
-      "    ] \n"
-      "   }, \n"
-      "  \"metrics_key\": 10, \n"
-      "  \"model_als_horizon_seconds\": 5 \n"
-      "}\n";
+  const std::string model_params = R"(
+      {
+        "global_curve": {
+        "log_lux": [
+            1.0,
+            2.0,
+            3.0
+          ],
+        "brightness": [
+            10.0,
+            20.0,
+            30.0
+          ]
+         },
+        "metrics_key": 10,
+        "model_als_horizon_seconds": 5
+      }
+      )";
 
   const std::map<std::string, std::string> experiment_params = {
       {"auto_brightness_als_horizon_seconds", "10"},
diff --git a/chrome/browser/content_settings/host_content_settings_map_factory.cc b/chrome/browser/content_settings/host_content_settings_map_factory.cc
index f82e1aa..eb91ba3 100644
--- a/chrome/browser/content_settings/host_content_settings_map_factory.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_factory.cc
@@ -84,7 +84,7 @@
 
   scoped_refptr<HostContentSettingsMap> settings_map(new HostContentSettingsMap(
       profile->GetPrefs(), profile->IsIncognitoProfile(),
-      profile->IsGuestProfile(),
+      profile->IsGuestSession(),
       /*store_last_modified=*/true,
       base::FeatureList::IsEnabled(features::kPermissionDelegation)));
 
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
index 101ad56b..c75ebb8 100644
--- a/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
+++ b/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
@@ -484,11 +484,8 @@
   // (for visibility reasons).
   ASSERT_TRUE(action);
 
-  // Ensure browser actions are hidden (so that the ShowAction() rule has an
-  // effect).
-  bool has_browser_action =
-      action_type && *action_type == ActionInfo::TYPE_BROWSER;
-  if (has_browser_action)
+  // Ensure actions are hidden (so that the ShowAction() rule has an effect).
+  if (action->default_state() == ActionInfo::STATE_DISABLED)
     action->SetIsVisible(ExtensionAction::kDefaultTabId, false);
 
   const char kScript[] =
@@ -514,12 +511,11 @@
   const int tab_id = SessionTabHelper::IdForTab(tab).id();
   EXPECT_TRUE(action->GetIsVisible(tab_id));
 
-  // Even an extension with no specified action with have a (synthesized) page
-  // action. Only browser action extensions are expected to not have a page
-  // action.
-  bool expect_page_action = !has_browser_action;
-  EXPECT_EQ(expect_page_action, action->action_type() == ActionInfo::TYPE_PAGE);
-  EXPECT_EQ(expect_page_action ? 1u : 0u,
+  // If an extension had no action specified in the manifest, it will get a
+  // synthesized page action.
+  ActionInfo::Type expected_type = action_type.value_or(ActionInfo::TYPE_PAGE);
+  EXPECT_EQ(expected_type, action->action_type());
+  EXPECT_EQ(expected_type == ActionInfo::TYPE_PAGE ? 1u : 0u,
             extension_action_test_util::GetVisiblePageActionCount(tab));
 }
 
@@ -538,12 +534,10 @@
   TestShowAction(ActionInfo::TYPE_BROWSER);
 }
 
-// TODO(https://crbug.com/893373): Enable this when ExtensionActionManager knows
-// about ActionInfo::Type::TYPE_ACTION.
-// IN_PROC_BROWSER_TEST_P(ParameterizedShowActionDeclarativeContentApiTest,
-//                        ActionInManifest) {
-//   TestShowAction(ActionInfo::TYPE_ACTION);
-// }
+IN_PROC_BROWSER_TEST_P(ParameterizedShowActionDeclarativeContentApiTest,
+                       ActionInManifest) {
+  TestShowAction(ActionInfo::TYPE_ACTION);
+}
 
 // Tests that rules from manifest are added and evaluated properly.
 IN_PROC_BROWSER_TEST_P(ParameterizedShowActionDeclarativeContentApiTest,
diff --git a/chrome/browser/extensions/background_xhr_browsertest.cc b/chrome/browser/extensions/background_xhr_browsertest.cc
index 4972c27..6285b64 100644
--- a/chrome/browser/extensions/background_xhr_browsertest.cc
+++ b/chrome/browser/extensions/background_xhr_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "base/test/scoped_feature_list.h"
@@ -427,6 +428,39 @@
   EXPECT_FALSE(CanFetch(extension, public_example_url));
 }
 
+IN_PROC_BROWSER_TEST_P(BackgroundXhrWebstoreTest, XHRAnyPortPermission) {
+  const Extension* extension = LoadXhrExtension("http://example.com:*/*");
+
+  GURL permitted_url_to_fetch =
+      embedded_test_server()->GetURL("example.com", "/simple.html");
+
+  EXPECT_TRUE(CanFetch(extension, permitted_url_to_fetch));
+}
+
+IN_PROC_BROWSER_TEST_P(BackgroundXhrWebstoreTest,
+                       XHRPortSpecificPermissionAllow) {
+  const Extension* extension = LoadXhrExtension(
+      "http://example.com:" +
+      base::NumberToString(embedded_test_server()->port()) + "/*");
+
+  GURL permitted_url_to_fetch =
+      embedded_test_server()->GetURL("example.com", "/simple.html");
+
+  EXPECT_TRUE(CanFetch(extension, permitted_url_to_fetch));
+}
+
+IN_PROC_BROWSER_TEST_P(BackgroundXhrWebstoreTest,
+                       XHRPortSpecificPermissionBlock) {
+  const Extension* extension = LoadXhrExtension(
+      "http://example.com:" +
+      base::NumberToString(embedded_test_server()->port() + 1) + "/*");
+
+  GURL not_permitted_url_to_fetch =
+      embedded_test_server()->GetURL("example.com", "/simple.html");
+
+  EXPECT_FALSE(CanFetch(extension, not_permitted_url_to_fetch));
+}
+
 INSTANTIATE_TEST_SUITE_P(WithoutAny,
                          BackgroundXhrWebstoreTest,
                          testing::Values(TestMode::kWithoutAny));
diff --git a/chrome/browser/extensions/extension_action.cc b/chrome/browser/extensions/extension_action.cc
index c2b9040..1d76297d 100644
--- a/chrome/browser/extensions/extension_action.cc
+++ b/chrome/browser/extensions/extension_action.cc
@@ -88,9 +88,10 @@
                                  const extensions::ActionInfo& manifest_data)
     : extension_id_(extension.id()),
       extension_name_(extension.name()),
-      action_type_(manifest_data.type) {
-  SetIsVisible(kDefaultTabId, manifest_data.default_state ==
-                                  extensions::ActionInfo::STATE_ENABLED);
+      action_type_(manifest_data.type),
+      default_state_(manifest_data.default_state) {
+  SetIsVisible(kDefaultTabId,
+               default_state_ == extensions::ActionInfo::STATE_ENABLED);
   Populate(extension, manifest_data);
 }
 
diff --git a/chrome/browser/extensions/extension_action.h b/chrome/browser/extensions/extension_action.h
index c0b0e7b..4ccaa91 100644
--- a/chrome/browser/extensions/extension_action.h
+++ b/chrome/browser/extensions/extension_action.h
@@ -64,6 +64,10 @@
     return action_type_;
   }
 
+  extensions::ActionInfo::DefaultState default_state() const {
+    return default_state_;
+  }
+
   // Set the url which the popup will load when the user clicks this action's
   // icon.  Setting an empty URL will disable the popup for a given tab.
   void SetPopupUrl(int tab_id, const GURL& url);
@@ -251,6 +255,8 @@
   const std::string extension_name_;
 
   const extensions::ActionInfo::Type action_type_;
+  // The default state of the action.
+  const extensions::ActionInfo::DefaultState default_state_;
 
   // Each of these data items can have both a global state (stored with the key
   // kDefaultTabId), or tab-specific state (stored with the tab_id as the key).
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 9c72ec4d..ecaa5d2 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2949,11 +2949,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "v8-cache-options",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "views-cast-dialog",
     "owners": [ "//chrome/browser/media/router/OWNERS" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e35c144..9db24c3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1870,11 +1870,6 @@
     "Enable simple user activation for APIs that are otherwise controlled by "
     "user gesture tokens.";
 
-const char kV8CacheOptionsName[] = "V8 caching mode.";
-const char kV8CacheOptionsDescription[] =
-    "Caching mode for the V8 JavaScript engine.";
-const char kV8CacheOptionsCode[] = "Cache V8 compiler data.";
-
 const char kV8VmFutureName[] = "Future V8 VM features";
 const char kV8VmFutureDescription[] =
     "This enables upcoming and experimental V8 VM features. "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a6b86a9..134857d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1109,11 +1109,6 @@
 extern const char kUserActivationV2Name[];
 extern const char kUserActivationV2Description[];
 
-extern const char kV8CacheOptionsName[];
-extern const char kV8CacheOptionsDescription[];
-extern const char kV8CacheOptionsParse[];
-extern const char kV8CacheOptionsCode[];
-
 extern const char kV8VmFutureName[];
 extern const char kV8VmFutureDescription[];
 
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest.cc b/chrome/browser/media/webrtc/webrtc_browsertest.cc
index afc9717ea..bda650e 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest.cc
@@ -232,11 +232,8 @@
   DetectVideoAndHangUp();
 }
 
-// TODO(https://crbug.com/webrtc/10453): Temporarily disabled to unblock a
-// webrtc roll.
-IN_PROC_BROWSER_TEST_F(
-    MAYBE_WebRtcBrowserTest,
-    DISABLED_RunsAudioVideoWebRTCCallInTwoTabsGetStatsPromise) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest,
+                       RunsAudioVideoWebRTCCallInTwoTabsGetStatsPromise) {
   StartServerAndOpenTabs();
   SetupPeerconnectionWithLocalStream(left_tab_);
   SetupPeerconnectionWithLocalStream(right_tab_);
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index 552d7e7..83974b83 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/password_manager/password_accessory_controller_impl.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -41,8 +42,9 @@
 
 namespace {
 
-autofill::UserInfo Translate(bool current_field_is_password,
-                             const PasswordAccessorySuggestion& data) {
+autofill::UserInfo TranslateCredentials(
+    bool current_field_is_password,
+    const PasswordAccessorySuggestion& data) {
   UserInfo user_info;
 
   user_info.add_field(UserInfo::Field(
@@ -198,7 +200,7 @@
     info_to_add.resize(suggestions.size());
     std::transform(suggestions.begin(), suggestions.end(), info_to_add.begin(),
                    [is_password_field](const auto& x) {
-                     return Translate(is_password_field, x);
+                     return TranslateCredentials(is_password_field, x);
                    });
   }
 
diff --git a/chrome/browser/resources/chromeos/camera/BUILD.gn b/chrome/browser/resources/chromeos/camera/BUILD.gn
index 2cc13dfd..57e3d082 100644
--- a/chrome/browser/resources/chromeos/camera/BUILD.gn
+++ b/chrome/browser/resources/chromeos/camera/BUILD.gn
@@ -165,6 +165,7 @@
     "src/js/views/camera/options.js",
     "src/js/views/camera/preview.js",
     "src/js/views/camera/recordtime.js",
+    "src/js/views/camera/resolution_preference.js",
     "src/js/views/camera/timertick.js",
   ]
 
@@ -199,6 +200,7 @@
 copy("chrome_camera_app_mojo_generated") {
   sources = [
     "$root_gen_dir/media/capture/mojom/image_capture.mojom-lite.js",
+    "$root_gen_dir/media/capture/video/chromeos/mojo/camera_common.mojom-lite.js",
     "$root_gen_dir/media/capture/video/chromeos/mojo/camera_metadata.mojom-lite.js",
     "$root_gen_dir/media/capture/video/chromeos/mojo/camera_metadata_tags.mojom-lite.js",
     "$root_gen_dir/media/capture/video/chromeos/mojo/cros_image_capture.mojom-lite.js",
diff --git a/chrome/browser/resources/chromeos/camera/Makefile b/chrome/browser/resources/chromeos/camera/Makefile
index 4c5e7c7..09db0f0 100644
--- a/chrome/browser/resources/chromeos/camera/Makefile
+++ b/chrome/browser/resources/chromeos/camera/Makefile
@@ -137,6 +137,7 @@
 	src/js/views/camera/options.js \
 	src/js/views/camera/preview.js \
 	src/js/views/camera/recordtime.js \
+	src/js/views/camera/resolution_preference.js \
 	src/js/views/camera/timertick.js \
 	src/js/views/dialog.js \
 	src/js/views/gallery_base.js \
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css
index 06f339a..f4f76d6 100644
--- a/chrome/browser/resources/chromeos/camera/src/css/main.css
+++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -1025,6 +1025,10 @@
   background-image: url(../images/settings_timer_duration.svg);
 }
 
+body.no-resolution-settings #settings-resolution {
+  display: none;
+}
+
 #settings-resolution .icon {
   background-image: url(../images/settings_resolution.svg);
 }
diff --git a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
index bc1d826..d8ce9e1 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
@@ -129,9 +129,10 @@
   return Promise
       .all([
         this.capture_.getPhotoCapabilities(),
-        this.mojoCapture_.getStaticMetadata(this.deviceId_),
+        this.mojoCapture_.getCameraInfo(this.deviceId_),
       ])
-      .then(([capabilities, {staticMetadata}]) => {
+      .then(([capabilities, {cameraInfo}]) => {
+        const staticMetadata = cameraInfo.staticCameraCharacteristics;
         let supportedEffects = [cros.mojom.Effect.NO_EFFECT];
         if (cca.mojo.getMetadataData_(staticMetadata, portraitModeTag).length >
             0) {
@@ -188,7 +189,8 @@
   const numElementPerEntry = 4;
 
   const mojoCapture = cros.mojom.CrosImageCapture.getProxy();
-  return mojoCapture.getStaticMetadata(deviceId).then(({staticMetadata}) => {
+  return mojoCapture.getCameraInfo(deviceId).then(({cameraInfo}) => {
+    const staticMetadata = cameraInfo.staticCameraCharacteristics;
     const streamConfigs = cca.mojo.getMetadataData_(
         staticMetadata,
         cros.mojom.CameraMetadataTag
@@ -235,7 +237,8 @@
   const numElementPerEntry = 4;
 
   var mojoCapture = cros.mojom.CrosImageCapture.getProxy();
-  return mojoCapture.getStaticMetadata(deviceId).then(({staticMetadata}) => {
+  return mojoCapture.getCameraInfo(deviceId).then(({cameraInfo}) => {
+    const staticMetadata = cameraInfo.staticCameraCharacteristics;
     const minFrameDurationConfigs = cca.mojo.getMetadataData_(
         staticMetadata,
         cros.mojom.CameraMetadataTag
@@ -264,3 +267,16 @@
     return supportedConfigs;
   });
 };
+
+/**
+ * Gets camera facing for given device.
+ * @param {string} deviceId The renderer-facing device Id of the target camera
+ *   which could be retrieved from MediaDeviceInfo.deviceId.
+ * @return {Promise<cros.mojom.CameraFacing>} Promise of device facing.
+ */
+cca.mojo.getCameraFacing = function(deviceId) {
+  var mojoCapture = cros.mojom.CrosImageCapture.getProxy();
+  return mojoCapture.getCameraInfo(deviceId).then(({cameraInfo}) => {
+    return cameraInfo.facing;
+  });
+};
diff --git a/chrome/browser/resources/chromeos/camera/src/js/resolution_event_broker.js b/chrome/browser/resources/chromeos/camera/src/js/resolution_event_broker.js
index dee6fd9c..32d0b8c 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/resolution_event_broker.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/resolution_event_broker.js
@@ -15,9 +15,9 @@
  */
 
 /**
- * A list of device id and the supported video, photo resolutions of its
- * corresponding video device.
- * @typedef {[number, ResolList, ResolList]} DeviceIdResols
+ * Tuple of device id, width, height of preferred capture resolution and all
+ * available resolutions for a specific video device.
+ * @typedef {[string, number, number, ResolList]} DeviceIdResols
  */
 
 /**
@@ -31,7 +31,7 @@
    * @type {function(string, number, number)}
    * @private
    */
-  this.photoChangeResolHandler_ = () => {};
+  this.photoChangePrefResolHandler_ = () => {};
 
   /**
    * Handler for requests of changing user-preferred resolution used in video
@@ -39,29 +39,39 @@
    * @type {function(string, number, number)}
    * @private
    */
-  this.videoChangeResolHandler_ = () => {};
+  this.videoChangePrefResolHandler_ = () => {};
 
   /**
-   * Listener for changes of resolution currently used in photo taking.
-   * @type {function(string, number, number)}
+   * Listener for changes of device ids of currently available front, back and
+   * external cameras and all of their supported photo resolutions.
+   * @type {function(?DeviceIdResols, ?DeviceIdResols, Array<DeviceIdResols>)}
    * @private
    */
   this.photoResolChangeListener_ = () => {};
 
   /**
-   * Listener for changes of resolution currently used in video recording.
-   * @type {function(string, number, number)}
+   * Listener for changes of device ids of currently available front, back and
+   * external cameras and all of their supported video resolutions.
+   * @type {function(?DeviceIdResols, ?DeviceIdResols, Array<DeviceIdResols>)}
    * @private
    */
   this.videoResolChangeListener_ = () => {};
 
   /**
-   * Listener for changes of currently available device-resolutions of front,
-   * back, external cameras.
-   * @type {function(?DeviceIdResols, ?DeviceIdResols, Array<DeviceIdResols>)}
+   * Listener for changes of preferred photo resolution used on particular video
+   * device.
+   * @type {function(string, number, number)}
    * @private
    */
-  this.deviceResolListener_ = () => {};
+  this.photoPrefResolChangeListener_ = () => {};
+
+  /**
+   * Listener for changes of preferred video resolution used on particular video
+   * device.
+   * @type {function(string, number, number)}
+   * @private
+   */
+  this.videoPrefResolChangeListener_ = () => {};
 };
 
 /**
@@ -70,9 +80,9 @@
  * @param {function(string, number, number)} handler Called with device id of
  *     video device to be changed and width, height of new resolution.
  */
-cca.ResolutionEventBroker.prototype.registerChangePhotoResolHandler = function(
-    handler) {
-  this.photoChangeResolHandler_ = handler;
+cca.ResolutionEventBroker.prototype.registerChangePhotoPrefResolHandler =
+    function(handler) {
+  this.photoChangePrefResolHandler_ = handler;
 };
 
 /**
@@ -81,9 +91,9 @@
  * @param {function(string, number, number)} handler Called with device id of
  *     video device to be changed and width, height of new resolution.
  */
-cca.ResolutionEventBroker.prototype.registerChangeVideoResolHandler = function(
-    handler) {
-  this.videoChangeResolHandler_ = handler;
+cca.ResolutionEventBroker.prototype.registerChangeVideoPrefResolHandler =
+    function(handler) {
+  this.videoChangePrefResolHandler_ = handler;
 };
 
 /**
@@ -92,9 +102,9 @@
  * @param {number} width Change to resolution width.
  * @param {number} height Change to resolution height.
  */
-cca.ResolutionEventBroker.prototype.requestChangePhotoResol = function(
+cca.ResolutionEventBroker.prototype.requestChangePhotoPrefResol = function(
     deviceId, width, height) {
-  this.photoChangeResolHandler_(deviceId, width, height);
+  this.photoChangePrefResolHandler_(deviceId, width, height);
 };
 
 /**
@@ -103,15 +113,16 @@
  * @param {number} width Change to resolution width.
  * @param {number} height Change to resolution height.
  */
-cca.ResolutionEventBroker.prototype.requestChangeVideoResol = function(
+cca.ResolutionEventBroker.prototype.requestChangeVideoPrefResol = function(
     deviceId, width, height) {
-  this.videoChangeResolHandler_(deviceId, width, height);
+  this.videoChangePrefResolHandler_(deviceId, width, height);
 };
 
 /**
-   Adds listener for changes of resolution currently used in photo taking.
- * @param {function(string, number, number)} listener Called with changed device
- *     id and new resolution width, height.
+ * Adds listener for changes of available photo resolutions of all cameras.
+ * @param {function(?DeviceIdResols, ?DeviceIdResols, Array<DeviceIdResols>)}
+ *     listener Called with device id, preferred photo capture resolution and
+ *     available photo resolutions of front, back and external cameras.
  */
 cca.ResolutionEventBroker.prototype.addPhotoResolChangeListener = function(
     listener) {
@@ -119,9 +130,10 @@
 };
 
 /**
- * Adds listener for changes of resolution currently used in video recording.
- * @param {function(string, number, number)} listener Called with changed device
- *     id and new resolution width, height.
+ * Adds listener for changes of available video resolutions of all cameras.
+ * @param {function(?DeviceIdResols, ?DeviceIdResols, Array<DeviceIdResols>)}
+ *     listener Called with device id, preferred video capture resolution and
+ *     available video resolutions of front, back and external cameras.
  */
 cca.ResolutionEventBroker.prototype.addVideoResolChangeListener = function(
     listener) {
@@ -129,47 +141,77 @@
 };
 
 /**
- * Notifies the change of resolution currently used in photo taking.
- * @param {string} deviceId Device id of changed video device.
- * @param {number} width New resolution width.
- * @param {number} height New resolution height.
+ * Notifies the change of available photo resolution of all cameras.
+ * @param {?DeviceIdResols} frontResolutions Device id of front camera and
+ *     all of its available supported photo resolutions.
+ * @param {?DeviceIdResols} backResolutions Device id of back camera and
+ *     all of its available supported photo resolutions.
+ * @param {Array<DeviceIdResols>} externalResolutions Device id of external
+ *     cameras and all of their available supported photo resolutions.
  */
 cca.ResolutionEventBroker.prototype.notifyPhotoResolChange = function(
-    deviceId, width, height) {
-  this.photoResolChangeListener_(deviceId, width, height);
+    frontResolutions, backResolutions, externalResolutions) {
+  this.photoResolChangeListener_(
+      frontResolutions, backResolutions, externalResolutions);
 };
 
 /**
- * Notifies the change of resolution currently used in video recording.
+ * Notifies the change of available video resolution of all cameras.
+ * @param {?DeviceIdResols} frontResolutions Device id of front camera and
+ *     all of its available supported video resolutions.
+ * @param {?DeviceIdResols} backResolutions Device id of back camera and
+ *     all of its available supported video resolutions.
+ * @param {Array<DeviceIdResols>} externalResolutions Device id of external
+ *     cameras and all of their available supported video resolutions.
+ */
+cca.ResolutionEventBroker.prototype.notifyVideoResolChange = function(
+    frontResolutions, backResolutions, externalResolutions) {
+  this.videoResolChangeListener_(
+      frontResolutions, backResolutions, externalResolutions);
+};
+
+/**
+ * Adds listener for changes of preferred resolution used in taking photo on
+ * particular video device.
+ * @param {function(string, number, number)} listener Called with changed video
+ *     device id and new preferred resolution width, height.
+ */
+cca.ResolutionEventBroker.prototype.addPhotoPrefResolChangeListener = function(
+    listener) {
+  this.photoPrefResolChangeListener_ = listener;
+};
+
+/**
+ * Adds listener for changes of preferred resolution used in video recording on
+ * particular video device.
+ * @param {function(string, number, number)} listener Called with changed video
+ *     device id and new preferred resolution width, height.
+ */
+cca.ResolutionEventBroker.prototype.addVideoPrefResolChangeListener = function(
+    listener) {
+  this.videoPrefResolChangeListener_ = listener;
+};
+
+/**
+ * Notifies the change of preferred resolution used in photo taking on
+ * particular video device.
  * @param {string} deviceId Device id of changed video device.
  * @param {number} width New resolution width.
  * @param {number} height New resolution height.
  */
-cca.ResolutionEventBroker.prototype.notifyVideoResolChange = function(
+cca.ResolutionEventBroker.prototype.notifyPhotoPrefResolChange = function(
     deviceId, width, height) {
-  this.videoResolChangeListener_(deviceId, width, height);
+  this.photoPrefResolChangeListener_(deviceId, width, height);
 };
 
 /**
- * Adds listener for changes of currently available device-resolutions of
- * all cameras.
- * @param {function(?DeviceIdResols, ?DeviceIdResols, Array<DeviceIdResols>)}
- *     listener Listener function called with deviceId-resolutions of front,
- *     back and external cameras.
+ * Notifies the change of preferred resolution used in video recording on
+ * particular video device.
+ * @param {string} deviceId Device id of changed video device.
+ * @param {number} width New resolution width.
+ * @param {number} height New resolution height.
  */
-cca.ResolutionEventBroker.prototype.addUpdateDeviceResolutionsListener =
-    function(listener) {
-  this.deviceResolListener_ = listener;
-};
-
-/**
- * Notifies the change of currently available device-resolutions of all cameras.
- * @param {?DeviceIdResols} front DeviceId-resolutions of front camera.
- * @param {?DeviceIdResols} back DeviceId-resolutions of back camera.
- * @param {Array<DeviceIdResols>} externals DeviceId-resolutions of external
- *     cameras.
- */
-cca.ResolutionEventBroker.prototype.notifyUpdateDeviceResolutions = function(
-    front, back, externals) {
-  this.deviceResolListener_(front, back, externals);
+cca.ResolutionEventBroker.prototype.notifyVideoPrefResolChange = function(
+    deviceId, width, height) {
+  this.videoPrefResolChangeListener_(deviceId, width, height);
 };
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
index 5804268..baf23e0 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
@@ -31,6 +31,20 @@
   this.model_ = model;
 
   /**
+   * @type {cca.views.camera.PhotoResolPreferrer}
+   * @private
+   */
+  this.photoResolPreferrer_ = new cca.views.camera.PhotoResolPreferrer(
+      resolBroker, this.stop_.bind(this));
+
+  /**
+   * @type {cca.views.camera.VideoResolPreferrer}
+   * @private
+   */
+  this.videoResolPreferrer_ = new cca.views.camera.VideoResolPreferrer(
+      resolBroker, this.stop_.bind(this));
+
+  /**
    * Layout handler for the camera view.
    * @type {cca.views.camera.Layout}
    * @private
@@ -49,8 +63,9 @@
    * @type {cca.views.camera.Options}
    * @private
    */
-  this.options_ =
-      new cca.views.camera.Options(resolBroker, this.stop_.bind(this));
+  this.options_ = new cca.views.camera.Options(
+      this.photoResolPreferrer_, this.videoResolPreferrer_,
+      this.stop_.bind(this));
 
   /**
    * Modes for the camera.
@@ -58,8 +73,8 @@
    * @private
    */
   this.modes_ = new cca.views.camera.Modes(
-      resolBroker, this.stop_.bind(this), this.stop_.bind(this),
-      async (blob, isMotionPicture, filename) => {
+      this.photoResolPreferrer_, this.videoResolPreferrer_,
+      this.stop_.bind(this), async (blob, isMotionPicture, filename) => {
         if (blob) {
           cca.metrics.log(
               cca.metrics.Type.CAPTURE, this.facingMode_, blob.mins);
@@ -225,10 +240,16 @@
 cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) {
   let supportedModes = null;
   for (const mode of this.modes_.getModeCandidates()) {
-    const [photoRs, videoRs] =
-        await this.options_.getDeviceResolutions(deviceId);
-    for (const [[width, height], previewCandidates] of this.modes_
-             .getResolutionCandidates(mode, deviceId, photoRs, videoRs)) {
+    try {
+      const previewRs = (await this.options_.getDeviceResolutions(deviceId))[1];
+      var resolCandidates =
+          this.modes_.getResolutionCandidates(mode, deviceId, previewRs);
+    } catch (e) {
+      // Assume the exception here is thrown from error of HALv1 not support
+      // resolution query, fallback to use v1 constraints-candidates.
+      resolCandidates = this.modes_.getResolutionCandidatesV1(mode, deviceId);
+    }
+    for (const [captureResolution, previewCandidates] of resolCandidates) {
       for (const constraints of previewCandidates) {
         try {
           const stream = await navigator.mediaDevices.getUserMedia(constraints);
@@ -242,7 +263,8 @@
           await this.preview_.start(stream);
           this.facingMode_ = this.options_.updateValues(constraints, stream);
           await this.modes_.updateModeSelectionUI(supportedModes);
-          await this.modes_.updateMode(mode, stream, deviceId, width, height);
+          await this.modes_.updateMode(
+              mode, stream, deviceId, captureResolution);
           cca.nav.close('warning', 'no-camera');
           return true;
         } catch (e) {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
index b48001c..ea3878a 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
@@ -21,16 +21,15 @@
 
 /**
  * Mode controller managing capture sequence of different camera mode.
- * @param {cca.ResolutionEventBroker} resolBroker
- * @param {function()} doSwitchResolution Callback to trigger resolution
- *     switching.
+ * @param {cca.views.camera.PhotoResolPreferrer} photoResolPreferrer
+ * @param {cca.views.camera.VideoResolPreferrer} videoResolPreferrer
  * @param {function()} doSwitchMode Callback to trigger mode switching.
  * @param {function(?Blob, boolean, string): Promise} doSavePicture Callback for
  *     saving picture.
  * @constructor
  */
 cca.views.camera.Modes = function(
-    resolBroker, doSwitchResolution, doSwitchMode, doSavePicture) {
+    photoResolPreferrer, videoResolPreferrer, doSwitchMode, doSavePicture) {
   /**
    * @type {function()}
    * @private
@@ -64,32 +63,10 @@
   this.modesGroup_ = document.querySelector('#modes-group');
 
   /**
-   * @type {cca.views.camera.PhotoResolutionConfig}
+   * @type {?[number, number]}
    * @private
    */
-  this.photoResConfig_ = new cca.views.camera.PhotoResolutionConfig(
-      resolBroker, doSwitchResolution);
-
-  /**
-   * @type {cca.views.camera.VideoResolutionConfig}
-   * @private
-   */
-  this.videoResConfig_ = new cca.views.camera.VideoResolutionConfig(
-      resolBroker, doSwitchResolution);
-
-  /**
-   * Captured resolution width.
-   * @type {number}
-   * @private
-   */
-  this.captureWidth_ = 0;
-
-  /**
-   * Captured resolution height.
-   * @type {number}
-   * @private
-   */
-  this.captureHeight_ = 0;
+  this.captureResolution_ = null;
 
   /**
    * Mode classname and related functions and attributes.
@@ -101,29 +78,29 @@
       captureFactory: () =>
           new cca.views.camera.Video(this.stream_, this.doSavePicture_),
       isSupported: async () => true,
-      resolutionConfig: this.videoResConfig_,
+      resolutionConfig: videoResolPreferrer,
+      v1Config: cca.views.camera.Modes.videoConstraits,
       nextMode: 'photo-mode',
     },
     'photo-mode': {
       captureFactory: () => new cca.views.camera.Photo(
-          this.stream_, this.doSavePicture_, this.captureWidth_,
-          this.captureHeight_),
+          this.stream_, this.doSavePicture_, this.captureResolution_),
       isSupported: async () => true,
-      resolutionConfig: this.photoResConfig_,
+      resolutionConfig: photoResolPreferrer,
+      v1Config: cca.views.camera.Modes.photoConstraits,
       nextMode: 'square-mode',
     },
     'square-mode': {
       captureFactory: () => new cca.views.camera.Square(
-          this.stream_, this.doSavePicture_, this.captureWidth_,
-          this.captureHeight_),
+          this.stream_, this.doSavePicture_, this.captureResolution_),
       isSupported: async () => true,
-      resolutionConfig: this.photoResConfig_,
+      resolutionConfig: photoResolPreferrer,
+      v1Config: cca.views.camera.Modes.photoConstraits,
       nextMode: 'portrait-mode',
     },
     'portrait-mode': {
       captureFactory: () => new cca.views.camera.Portrait(
-          this.stream_, this.doSavePicture_, this.captureWidth_,
-          this.captureHeight_),
+          this.stream_, this.doSavePicture_, this.captureResolution_),
       isSupported: async (stream) => {
         try {
           const imageCapture =
@@ -139,7 +116,8 @@
           return false;
         }
       },
-      resolutionConfig: this.photoResConfig_,
+      resolutionConfig: photoResolPreferrer,
+      v1Config: cca.views.camera.Modes.photoConstraits,
       nextMode: 'video-mode',
     },
   };
@@ -184,284 +162,47 @@
 };
 
 /**
- * Controller for generating suitable capture and preview resolution
- * combination.
- * @param {cca.ResolutionEventBroker} resolBroker
- * @param {function()} doSwitchResolution
- * @constructor
+ * Returns a set of available video constraints for HALv1 device.
+ * @param {?string} deviceId Id of video device.
+ * @return {Array<Object>} Result of constraints-candidates.
  */
-cca.views.camera.ModeResolutionConfig = function(
-    resolBroker, doSwitchResolution) {
-  /**
-   * @type {cca.ResolutionEventBroker}
-   * @private
-   */
-  this.resolBroker_ = resolBroker;
-
-  /**
-   * @type {function()}
-   * @private
-   */
-  this.doSwitchResolution_ = doSwitchResolution;
-
-  /**
-   * Object saving resolution preference for each of its key as device id and
-   * value to be preferred width, height of resolution of that video device.
-   * @type {Object<string, {width: number, height: number}>}
-   * @private
-   */
-  this.prefResolution_ = null;
-
-  /**
-   * Device id of currently working video device.
-   * @type {string}
-   * @private
-   */
-  this.deviceId_ = null;
-
-  // End of properties, seal the object.
-  Object.seal(this);
+cca.views.camera.Modes.videoConstraits = function(deviceId) {
+  return [
+    {
+      aspectRatio: {ideal: 1.7777777778},
+      width: {min: 1280},
+      frameRate: {min: 24},
+    },
+    {
+      width: {min: 640},
+      frameRate: {min: 24},
+    },
+  ].map((constraint) => {
+    constraint.deviceId = {exact: deviceId};
+    return {audio: true, video: constraint};
+  });
 };
 
 /**
- * Gets all available resolutions candidates for capturing under this controller
- * and its corresponding preview constraints from all available resolutions of
- * different format. Returned resolutions and constraints candidates are both
- * sorted in desired trying order.
- * @param {string} deviceId
- * @param {ResolList} photoResolutions
- * @param {ResolList} videoResolutions
- * @return {Array<[number, number, Array<Object>]>} Result capture resolution
- *     width, height and constraints-candidates for its preview.
+ * Returns a set of available photo constraints for HALv1 device.
+ * @param {?string} deviceId Id of video device.
+ * @return {Array<Object>} Result of constraints-candidates.
  */
-cca.views.camera.ModeResolutionConfig.prototype.getSortedCandidates = function(
-    deviceId, photoResolutions, videoResolutions) {
-  return null;
-};
-
-/**
- * Updates selected resolution.
- * @param {string} deviceId Device id of selected video device.
- * @param {number} width Width of selected resolution.
- * @param {number} height Height of selected resolution.
- */
-cca.views.camera.ModeResolutionConfig.prototype.updateSelectedResolution =
-    function(deviceId, width, height) {};
-
-/**
- * Controller for generating suitable video recording and preview resolution
- * combination.
- * @param {cca.ResolutionEventBroker} resolBroker
- * @param {function()} doSwitchResolution
- * @constructor
- */
-cca.views.camera.VideoResolutionConfig = function(
-    resolBroker, doSwitchResolution) {
-  cca.views.camera.ModeResolutionConfig.call(
-      this, resolBroker, doSwitchResolution);
-
-  // Restore saved preferred video resolution per video device.
-  chrome.storage.local.get(
-      {deviceVideoResolution: {}},
-      (values) => this.prefResolution_ = values.deviceVideoResolution);
-
-  this.resolBroker_.registerChangeVideoResolHandler(
-      (deviceId, width, height) => {
-        this.prefResolution_[deviceId] = {width, height};
-        chrome.storage.local.set({deviceVideoResolution: this.prefResolution_});
-        if (cca.state.get('video-mode') && deviceId == this.deviceId_) {
-          this.doSwitchResolution_();
-        }
-      });
-};
-
-cca.views.camera.VideoResolutionConfig.prototype = {
-  __proto__: cca.views.camera.ModeResolutionConfig.prototype,
-};
-
-/**
- * @param {string} deviceId
- * @param {number} width
- * @param {number} height
- * @override
- */
-cca.views.camera.VideoResolutionConfig.prototype.updateSelectedResolution =
-    function(deviceId, width, height) {
-  this.deviceId_ = deviceId;
-  this.prefResolution_[deviceId] = {width, height};
-  chrome.storage.local.set({deviceVideoResolution: this.prefResolution_});
-  this.resolBroker_.notifyVideoResolChange(deviceId, width, height);
-};
-
-/**
- * @param {string} deviceId
- * @param {ResolList} photoResolutions
- * @param {ResolList} videoResolutions
- * @return {Array<[number, number, Array<Object>]>}
- * @override
- */
-cca.views.camera.VideoResolutionConfig.prototype.getSortedCandidates = function(
-    deviceId, photoResolutions, videoResolutions) {
-  // Due to the limitation of MediaStream API, preview stream is used directly
-  // to do video recording.
-  const prefR = this.prefResolution_[deviceId] || {width: 0, height: -1};
-  const preferredOrder = ([w, h], [w2, h2]) => {
-    if (w == w2 && h == h2) {
-      return 0;
-    }
-    // Exactly the preferred resolution.
-    if (w == prefR.width && h == prefR.height) {
-      return -1;
-    }
-    if (w2 == prefR.width && h2 == prefR.height) {
-      return 1;
-    }
-    // Aspect ratio same as preferred resolution.
-    if (w * h2 != w2 * h) {
-      if (w * prefR.height == prefR.width * h) {
-        return -1;
-      }
-      if (w2 * prefR.height == prefR.width * h2) {
-        return 1;
-      }
-    }
-    return w2 * h2 - w * h;
-  };
-  return videoResolutions.sort(preferredOrder).map(([width, height]) => ([
-                                                     [width, height],
-                                                     [{
-                                                       audio: true,
-                                                       video: {
-                                                         deviceId:
-                                                             {exact: deviceId},
-                                                         frameRate: {min: 24},
-                                                         width,
-                                                         height,
-                                                       },
-                                                     }],
-                                                   ]));
-};
-
-/**
- * Controller for generating suitable photo taking and preview resolution
- * combination.
- * @param {cca.ResolutionEventBroker} resolBroker
- * @param {function()} doSwitchResolution
- * @constructor
- */
-cca.views.camera.PhotoResolutionConfig = function(
-    resolBroker, doSwitchResolution) {
-  cca.views.camera.ModeResolutionConfig.call(
-      this, resolBroker, doSwitchResolution);
-
-  // Restore saved preferred photo resolution per video device.
-  chrome.storage.local.get(
-      {devicePhotoResolution: {}},
-      (values) => this.prefResolution_ = values.devicePhotoResolution);
-
-  this.resolBroker_.registerChangePhotoResolHandler(
-      (deviceId, width, height) => {
-        this.prefResolution_[deviceId] = {width, height};
-        chrome.storage.local.set({devicePhotoResolution: this.prefResolution_});
-        if (!cca.state.get('video-mode') && deviceId == this.deviceId_) {
-          this.doSwitchResolution_();
-        }
-      });
-};
-
-/**
- * @param {string} deviceId
- * @param {number} width
- * @param {number} height
- * @override
- */
-cca.views.camera.PhotoResolutionConfig.prototype.updateSelectedResolution =
-    function(deviceId, width, height) {
-  this.deviceId_ = deviceId;
-  this.prefResolution_[deviceId] = {width, height};
-  chrome.storage.local.set({devicePhotoResolution: this.prefResolution_});
-  this.resolBroker_.notifyPhotoResolChange(deviceId, width, height);
-};
-
-/**
- * Finds and pairs photo resolutions and preview resolutions with the same
- * aspect ratio.
- * @param {ResolList} captureResolutions Available photo capturing resolutions.
- * @param {ResolList} previewResolutions Available preview resolutions.
- * @return {Array<[ResolList, ResolList]>} Each item of returned array is a pair
- *     of capture and preview resolutions of same aspect ratio.
- */
-cca.views.camera.PhotoResolutionConfig.prototype
-    .pairCapturePreviewResolutions_ = function(
-    captureResolutions, previewResolutions) {
-  const toAspectRatio = (w, h) => (w / h).toFixed(4);
-  const previewRatios = previewResolutions.reduce((rs, [w, h]) => {
-    const key = toAspectRatio(w, h);
-    rs[key] = rs[key] || [];
-    rs[key].push([w, h]);
-    return rs;
-  }, {});
-  const captureRatios = captureResolutions.reduce((rs, [w, h]) => {
-    const key = toAspectRatio(w, h);
-    if (key in previewRatios) {
-      rs[key] = rs[key] || [];
-      rs[key].push([w, h]);
-    }
-    return rs;
-  }, {});
-  return Object.entries(captureRatios)
-      .map(([aspectRatio,
-             captureRs]) => [captureRs, previewRatios[aspectRatio]]);
-};
-
-/**
- * @param {string} deviceId
- * @param {ResolList} photoResolutions
- * @param {ResolList} videoResolutions
- * @return {Array<[number, number, Array<Object>]>}
- * @override
- */
-cca.views.camera.PhotoResolutionConfig.prototype.getSortedCandidates = function(
-    deviceId, photoResolutions, videoResolutions) {
-  const prefR = this.prefResolution_[deviceId] || {width: 0, height: -1};
-  return this.pairCapturePreviewResolutions_(photoResolutions, videoResolutions)
-      .map(([captureRs, previewRs]) => {
-        if (captureRs.some(([w, h]) => w == prefR.width && h == prefR.height)) {
-          var captureR = [prefR.width, prefR.height];
-        } else {
-          var captureR = captureRs.reduce(
-              (captureR, r) => (r[0] > captureR[0] ? r : captureR), [0, -1]);
-        }
-
-        const candidates = previewRs.sort(([w, h], [w2, h2]) => w2 - w)
-                               .map(([width, height]) => ({
-                                      audio: false,
-                                      video: {
-                                        deviceId: {exact: deviceId},
-                                        frameRate: {min: 24},
-                                        width,
-                                        height,
-                                      },
-                                    }));
-        // Format of map result:
-        // [
-        //   [[CaptureW 1, CaptureH 1], [CaptureW 2, CaptureH 2], ...],
-        //   [PreviewConstraint 1, PreviewConstraint 2, ...]
-        // ]
-        return [captureR, candidates];
-      })
-      .sort(([[w, h]], [[w2, h2]]) => {
-        if (w == w2 && h == h2) {
-          return 0;
-        }
-        if (w == prefR.width && h == prefR.height) {
-          return -1;
-        }
-        if (w2 == prefR.width && h2 == prefR.height) {
-          return 1;
-        }
-        return w2 * h2 - w * h;
-      });
+cca.views.camera.Modes.photoConstraits = function(deviceId) {
+  return [
+    {
+      aspectRatio: {ideal: 1.3333333333},
+      width: {min: 1280},
+      frameRate: {min: 24},
+    },
+    {
+      width: {min: 640},
+      frameRate: {min: 24},
+    },
+  ].map((constraint) => {
+    constraint.deviceId = {exact: deviceId};
+    return {audio: false, video: constraint};
+  });
 };
 
 /**
@@ -500,15 +241,28 @@
  * constraints for the given mode.
  * @param {string} mode
  * @param {string} deviceId
- * @param {ResolList} photoResolutions
- * @param {ResolList} videoResolutions
- * @return {Array<[number, number, Array<Object>]>} Result capture resolution
+ * @param {ResolList} previewResolutions
+ * @return {Array<[?[number, number], Array<Object>]>} Result capture resolution
  *     width, height and constraints-candidates for its preview.
  */
 cca.views.camera.Modes.prototype.getResolutionCandidates = function(
-    mode, deviceId, photoResolutions, videoResolutions) {
+    mode, deviceId, previewResolutions) {
   return this.allModes_[mode].resolutionConfig.getSortedCandidates(
-      deviceId, photoResolutions, videoResolutions);
+      deviceId, previewResolutions);
+};
+
+/**
+ * Gets capture resolution and its corresponding preview constraints for the
+ * given mode on camera HALv1 device.
+ * @param {string} mode
+ * @param {string} deviceId
+ * @return {Array<[?[number, number], Array<Object>]>} Result capture resolution
+ *     width, height and constraints-candidates for its preview.
+ */
+cca.views.camera.Modes.prototype.getResolutionCandidatesV1 = function(
+    mode, deviceId) {
+  return this.allModes_[mode].v1Config(deviceId).map(
+      (constraints) => [null, [constraints]]);
 };
 
 /**
@@ -546,33 +300,33 @@
  * @param {string} mode Classname of mode to be updated.
  * @param {MediaStream} stream Stream of the new switching mode.
  * @param {string} deviceId Device id of currently working video device.
- * @param {number} captureWidth Capturing resolution width.
- * @param {number} captureHeight Capturing resolution height.
+ * @param {?[number, number]} captureResolution Capturing resolution width and
+ *     height.
  */
 cca.views.camera.Modes.prototype.updateMode =
-    async function(mode, stream, deviceId, captureWidth, captureHeight) {
+    async function(mode, stream, deviceId, captureResolution) {
   if (this.current != null) {
     await this.current.stopCapture();
   }
   this.updateModeUI_(mode);
   this.stream_ = stream;
-  this.captureWidth_ = captureWidth;
-  this.captureHeight_ = captureHeight;
+  this.captureResolution_ = captureResolution;
   this.current = this.allModes_[mode].captureFactory();
-  this.allModes_[mode].resolutionConfig.updateSelectedResolution(
-      deviceId, captureWidth, captureHeight);
+  if (this.captureResolution_) {
+    this.allModes_[mode].resolutionConfig.updateCurrentResolution(
+        deviceId, ...this.captureResolution_);
+  }
 };
 
 /**
  * Base class for controlling capture sequence in different camera modes.
  * @param {MediaStream} stream
  * @param {function(?Blob, boolean, string): Promise} doSavePicture
- * @param {number} captureWidth Capturing resolution width.
- * @param {number} captureHeight Capturing resolution height.
+ * @param {?[number, number]} captureResolution Capturing resolution width and
+ *     height.
  * @constructor
  */
-cca.views.camera.Mode = function(
-    stream, doSavePicture, captureWidth, captureHeight) {
+cca.views.camera.Mode = function(stream, doSavePicture, captureResolution) {
   /**
    * Stream of current mode.
    * @type {?Promise}
@@ -588,16 +342,12 @@
   this.doSavePicture_ = doSavePicture;
 
   /**
-   * @type {number}
+   * Width, height of capture resolution. May be null on device not supporting
+   * setting resolution.
+   * @type {?[number, number]}
    * @private
    */
-  this.captureWidth_ = captureWidth;
-
-  /**
-   * @type {number}
-   * @private
-   */
-  this.captureHeight_ = captureHeight;
+  this.captureResolution_ = captureResolution;
 
   /**
    * Promise for ongoing capture operation.
@@ -648,7 +398,7 @@
  * @constructor
  */
 cca.views.camera.Video = function(stream, doSavePicture) {
-  cca.views.camera.Mode.call(this, stream, doSavePicture, -1, -1);
+  cca.views.camera.Mode.call(this, stream, doSavePicture, null);
 
   /**
    * Promise for play start sound delay.
@@ -788,14 +538,11 @@
  * Photo mode capture controller.
  * @param {MediaStream} stream
  * @param {function(?Blob, boolean, string): Promise} doSavePicture
- * @param {number} captureWidth
- * @param {number} captureHeight
+ * @param {?[number, number]} captureResolution
  * @constructor
  */
-cca.views.camera.Photo = function(
-    stream, doSavePicture, captureWidth, captureHeight) {
-  cca.views.camera.Mode.call(
-      this, stream, doSavePicture, captureWidth, captureHeight);
+cca.views.camera.Photo = function(stream, doSavePicture, captureResolution) {
+  cca.views.camera.Mode.call(this, stream, doSavePicture, captureResolution);
 
   /**
    * ImageCapture object to capture still photos.
@@ -841,10 +588,18 @@
  * @private
  */
 cca.views.camera.Photo.prototype.createPhotoBlob_ = async function() {
-  const photoSettings = {
-    imageWidth: this.captureWidth_,
-    imageHeight: this.captureHeight_,
-  };
+  if (this.captureResolution_) {
+    var photoSettings = {
+      imageWidth: this.captureResolution_[0],
+      imageHeight: this.captureResolution_[1],
+    };
+  } else {
+    const caps = await this.imageCapture_.getPhotoCapabilities();
+    photoSettings = {
+      imageWidth: caps.imageWidth.max,
+      imageHeight: caps.imageHeight.max,
+    };
+  }
   return await this.imageCapture_.takePhoto(photoSettings);
 };
 
@@ -852,14 +607,11 @@
  * Square mode capture controller.
  * @param {MediaStream} stream
  * @param {function(?Blob, boolean, string): Promise} doSavePicture
- * @param {number} captureWidth
- * @param {number} captureHeight
+ * @param {?[number, number]} captureResolution
  * @constructor
  */
-cca.views.camera.Square = function(
-    stream, doSavePicture, captureWidth, captureHeight) {
-  cca.views.camera.Photo.call(
-      this, stream, doSavePicture, captureWidth, captureHeight);
+cca.views.camera.Square = function(stream, doSavePicture, captureResolution) {
+  cca.views.camera.Photo.call(this, stream, doSavePicture, captureResolution);
 
   /**
    * Picture saving callback from parent.
@@ -915,14 +667,11 @@
  * Portrait mode capture controller.
  * @param {MediaStream} stream
  * @param {function(?Blob, boolean): Promise} doSavePicture
- * @param {number} captureWidth
- * @param {number} captureHeight
+ * @param {?[number, number]} captureResolution
  * @constructor
  */
-cca.views.camera.Portrait = function(
-    stream, doSavePicture, captureWidth, captureHeight) {
-  cca.views.camera.Mode.call(
-      this, stream, doSavePicture, captureWidth, captureHeight);
+cca.views.camera.Portrait = function(stream, doSavePicture, captureResolution) {
+  cca.views.camera.Mode.call(this, stream, doSavePicture, captureResolution);
 
   /**
    * ImageCapture object to capture still photos.
@@ -952,10 +701,18 @@
       throw e;
     }
   }
-  const photoSettings = {
-    imageWidth: this.captureWidth_,
-    imageHeight: this.captureHeight_,
-  };
+  if (this.captureResolution_) {
+    var photoSettings = {
+      imageWidth: this.captureResolution_[0],
+      imageHeight: this.captureResolution_[1],
+    };
+  } else {
+    const caps = await this.imageCapture_.getPhotoCapabilities();
+    photoSettings = {
+      imageWidth: caps.imageWidth.max,
+      imageHeight: caps.imageHeight.max,
+    };
+  }
   try {
     var [reference, portrait] = this.crosImageCapture_.takePhoto(
         photoSettings, [cros.mojom.Effect.PORTRAIT_MODE]);
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
index bcfdacb3..341d4670 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/options.js
@@ -21,16 +21,24 @@
 
 /**
  * Creates a controller for the options of Camera view.
- * @param {cca.ResolutionEventBroker} resolBroker
+ * @param {cca.views.camera.PhotoResolPreferrer} photoResolPreferrer
+ * @param {cca.views.camera.VideoResolPreferrer} videoResolPreferrer
  * @param {function()} doSwitchDevice Callback to trigger device switching.
  * @constructor
  */
-cca.views.camera.Options = function(resolBroker, doSwitchDevice) {
+cca.views.camera.Options = function(
+    photoResolPreferrer, videoResolPreferrer, doSwitchDevice) {
   /**
-   * @type {cca.ResolutionEventBroker}
+   * @type {cca.views.camera.PhotoResolPreferrer}
    * @private
    */
-  this.resolBroker_ = resolBroker;
+  this.photoResolPreferrer_ = photoResolPreferrer;
+
+  /**
+   * @type {cca.views.camera.VideoResolPreferrer}
+   * @private
+   */
+  this.videoResolPreferrer_ = videoResolPreferrer;
 
   /**
    * @type {function()}
@@ -133,13 +141,6 @@
 cca.views.camera.Options.BACK_CAMERA_LABEL = 'Back Camera';
 
 /**
- * Label of external facing camera from MediaDeviceInfo.
- * @type {string}
- * @const
- */
-cca.views.camera.Options.EXTERNAL_CAMERA_LABEL = 'External Camera';
-
-/**
  * Switches to the next available camera device.
  * @private
  */
@@ -277,17 +278,29 @@
     this.refreshingVideoDeviceIds_ = false;
   });
 
-  this.deviceResolutions_ = this.videoDevices_.then((devices) => {
-    return Promise.all(devices.map((d) => Promise.all([
-      d,
-      cca.mojo.getPhotoResolutions(d.deviceId),
-      cca.mojo.getVideoConfigs(d.deviceId)
-          .then((v) => v.filter(([, , fps]) => fps >= 24).map(([w,
-                                                                h]) => [w, h])),
-    ])));
-  });
+  this.deviceResolutions_ =
+      this.videoDevices_
+          .then((devices) => {
+            return Promise.all(devices.map((d) => Promise.all([
+              d,
+              cca.mojo.getPhotoResolutions(d.deviceId),
+              cca.mojo.getVideoConfigs(d.deviceId)
+                  .then(
+                      (v) => v.filter(([, , fps]) => fps >= 24)
+                                 .map(([w, h]) => [w, h])),
+            ])));
+          })
+          .catch((e) => {
+            cca.state.set('no-resolution-settings', true);
+            throw e;
+          });
 
-  this.deviceResolutions_.then((deviceResolutions) => {
+  (async () => {
+    try {
+      var deviceResolutions = await this.deviceResolutions_;
+    } catch (e) {
+      return;
+    }
     let frontSetting = null;
     let backSetting = null;
     let externalSettings = [];
@@ -300,16 +313,20 @@
         case cca.views.camera.Options.BACK_CAMERA_LABEL:
           backSetting = setting;
           break;
-        case cca.views.camera.Options.EXTERNAL_CAMERA_LABEL:
-          externalSettings.push(setting);
-          break;
         default:
-          console.error(`Ignore device of unknown label: ${label}`);
+          // TODO(inker): Use private API to get camera facing information.
+          externalSettings.push(setting);
       }
     });
-    this.resolBroker_.notifyUpdateDeviceResolutions(
-        frontSetting, backSetting, externalSettings);
-  });
+    this.photoResolPreferrer_.updateResolutions(
+        frontSetting && [frontSetting[0], frontSetting[1]],
+        backSetting && [backSetting[0], backSetting[1]],
+        externalSettings.map(([deviceId, photoRs]) => [deviceId, photoRs]));
+    this.videoResolPreferrer_.updateResolutions(
+        frontSetting && [frontSetting[0], frontSetting[2]],
+        backSetting && [backSetting[0], backSetting[2]],
+        externalSettings.map(([deviceId, , videoRs]) => [deviceId, videoRs]));
+  })();
 };
 
 /**
@@ -343,6 +360,8 @@
  * @async
  * @param {string} deviceId Device id of the video device.
  * @return {[ResolList, ResolList]} Supported photo and video resolutions.
+ * @throws {Error} May fail on HALv1 device without capability of querying
+ *     supported resolutions.
  */
 cca.views.camera.Options.prototype.getDeviceResolutions =
     async function(deviceId) {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/resolution_preference.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/resolution_preference.js
new file mode 100644
index 0000000..50283ed
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/resolution_preference.js
@@ -0,0 +1,382 @@
+// 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.
+
+'use strict';
+
+/**
+ * Namespace for the Camera app.
+ */
+var cca = cca || {};
+
+/**
+ * Namespace for views.
+ */
+cca.views = cca.views || {};
+
+/**
+ * Namespace for Camera view.
+ */
+cca.views.camera = cca.views.camera || {};
+
+/**
+ * Controller for handling resolution preference.
+ * @param {cca.ResolutionEventBroker} resolBroker
+ * @param {function()} doSwitchResolution
+ * @constructor
+ */
+cca.views.camera.ResolutionPreferrer = function(
+    resolBroker, doSwitchResolution) {
+  /**
+   * @type {cca.ResolutionEventBroker}
+   * @protected
+   */
+  this.resolBroker_ = resolBroker;
+
+  /**
+   * @type {function()}
+   * @protected
+   */
+  this.doSwitchResolution_ = doSwitchResolution;
+
+  /**
+   * Object saving resolution preference that each of its key as device id and
+   * value to be preferred width, height of resolution of that video device.
+   * @type {?Object<string, {width: number, height: number}>}
+   * @protected
+   */
+  this.prefResolution_ = null;
+
+  /**
+   * Device id of currently working video device.
+   * @type {?string}
+   * @protected
+   */
+  this.deviceId_ = null;
+
+  /**
+   * Object of device id as its key and all of available capture resolutions
+   * supported by that video device as its value.
+   * @type {Object<string, ResolList>}
+   * @protected
+   */
+  this.deviceResolutions_ = null;
+
+  // End of properties, seal the object.
+  Object.seal(this);
+};
+
+/**
+ * Updates resolution preference based on newly updated available resolutions.
+ * @param {?[string, ResolList]} frontResolutions Device id and available
+ *     resolutions of front camera.
+ * @param {?[string, ResolList]} backResolutions Device id and available
+ *     resolutions of back camera.
+ * @param {Array<[string, ResolList]>} externalResolutions Device ids and
+ *     available resolutions of all external cameras.
+ */
+cca.views.camera.ResolutionPreferrer.prototype.updateResolutions = function(
+    frontResolutions, backResolutions, externalResolutions) {};
+
+/**
+ * Updates preferred resolution of currently working video device.
+ * @param {string} deviceId Device id of video device to be updated.
+ * @param {number} width Width of resolution to be updated to.
+ * @param {number} height Height of resolution to be updated to.
+ */
+cca.views.camera.ResolutionPreferrer.prototype.updateCurrentResolution =
+    function(deviceId, width, height) {};
+
+/**
+ * Gets all available resolutions candidates for capturing under this controller
+ * and its corresponding preview constraints for the specified video device.
+ * Returned resolutions and constraints candidates are both sorted in desired
+ * trying order.
+ * @param {string} deviceId Device id of video device.
+ * @param {ResolList} previewResolutions Available preview resolutions for the
+ *     video device.
+ * @return {Array<[number, number, Array<Object>]>} Result capture resolution
+ *     width, height and constraints-candidates for its preview.
+ */
+cca.views.camera.ResolutionPreferrer.prototype.getSortedCandidates = function(
+    deviceId, previewResolutions) {
+  return null;
+};
+
+/**
+ * Controller for handling video resolution preference.
+ * @param {cca.ResolutionEventBroker} resolBroker
+ * @param {function()} doSwitchResolution
+ * @constructor
+ */
+cca.views.camera.VideoResolPreferrer = function(
+    resolBroker, doSwitchResolution) {
+  cca.views.camera.ResolutionPreferrer.call(
+      this, resolBroker, doSwitchResolution);
+
+  // Restore saved preferred video resolution per video device.
+  chrome.storage.local.get(
+      {deviceVideoResolution: {}},
+      (values) => this.prefResolution_ = values.deviceVideoResolution);
+
+  this.resolBroker_.registerChangeVideoPrefResolHandler(
+      (deviceId, width, height) => {
+        this.prefResolution_[deviceId] = {width, height};
+        chrome.storage.local.set({deviceVideoResolution: this.prefResolution_});
+        if (cca.state.get('video-mode') && deviceId == this.deviceId_) {
+          this.doSwitchResolution_();
+        } else {
+          this.resolBroker_.notifyVideoPrefResolChange(deviceId, width, height);
+        }
+      });
+};
+
+cca.views.camera.VideoResolPreferrer.prototype = {
+  __proto__: cca.views.camera.ResolutionPreferrer.prototype,
+};
+
+/**
+ * @param {?[string, ResolList]} frontResolutions
+ * @param {?[string, ResolList]} backResolutions
+ * @param {Array<[string, ResolList]>} externalResolutions
+ * @override
+ */
+cca.views.camera.VideoResolPreferrer.prototype.updateResolutions = function(
+    frontResolutions, backResolutions, externalResolutions) {
+  this.deviceResolutions_ = {};
+
+  const toDeviceIdResols = (deviceId, resolutions) => {
+    this.deviceResolutions_[deviceId] = resolutions;
+    let {width = -1, height = -1} = this.prefResolution_[deviceId] || {};
+    if (!resolutions.find(([w, h]) => w == width && h == height)) {
+      [width, height] = resolutions.reduce(
+          (maxR, R) => (maxR[0] * maxR[1] < R[0] * R[1] ? R : maxR), [0, 0]);
+    }
+    this.prefResolution_[deviceId] = {width, height};
+    return [deviceId, width, height, resolutions];
+  };
+
+  this.resolBroker_.notifyVideoResolChange(
+      frontResolutions && toDeviceIdResols(...frontResolutions),
+      backResolutions && toDeviceIdResols(...backResolutions),
+      externalResolutions.map((ext) => toDeviceIdResols(...ext)));
+  chrome.storage.local.set({deviceVideoResolution: this.prefResolution_});
+};
+
+/**
+ * @param {string} deviceId
+ * @param {number} width
+ * @param {number} height
+ * @override
+ */
+cca.views.camera.VideoResolPreferrer.prototype.updateCurrentResolution =
+    function(deviceId, width, height) {
+  this.deviceId_ = deviceId;
+  this.prefResolution_[deviceId] = {width, height};
+  chrome.storage.local.set({deviceVideoResolution: this.prefResolution_});
+  this.resolBroker_.notifyVideoPrefResolChange(deviceId, width, height);
+};
+
+/**
+ * @param {string} deviceId
+ * @return {Array<[number, number, Array<Object>]>}
+ * @override
+ */
+cca.views.camera.VideoResolPreferrer.prototype.getSortedCandidates = function(
+    deviceId, previewResolutions) {
+  // Due to the limitation of MediaStream API, preview stream is used directly
+  // to do video recording.
+  const prefR = this.prefResolution_[deviceId] || {width: 0, height: -1};
+  const preferredOrder = ([w, h], [w2, h2]) => {
+    if (w == w2 && h == h2) {
+      return 0;
+    }
+    // Exactly the preferred resolution.
+    if (w == prefR.width && h == prefR.height) {
+      return -1;
+    }
+    if (w2 == prefR.width && h2 == prefR.height) {
+      return 1;
+    }
+    // Aspect ratio same as preferred resolution.
+    if (w * h2 != w2 * h) {
+      if (w * prefR.height == prefR.width * h) {
+        return -1;
+      }
+      if (w2 * prefR.height == prefR.width * h2) {
+        return 1;
+      }
+    }
+    return w2 * h2 - w * h;
+  };
+  const toConstraints = (width, height) => ({
+    audio: true,
+    video: {
+      deviceId: {exact: deviceId},
+      frameRate: {min: 24},
+      width,
+      height,
+    },
+  });
+  const videoResolutions = this.deviceResolutions_[deviceId];
+  return videoResolutions.sort(preferredOrder).map(([width, height]) => ([
+                                                     [width, height],
+                                                     [toConstraints(
+                                                         width, height)],
+                                                   ]));
+};
+
+/**
+ * Controller for handling photo resolution preference.
+ * @param {cca.ResolutionEventBroker} resolBroker
+ * @param {function()} doSwitchResolution
+ * @constructor
+ */
+cca.views.camera.PhotoResolPreferrer = function(
+    resolBroker, doSwitchResolution) {
+  cca.views.camera.ResolutionPreferrer.call(
+      this, resolBroker, doSwitchResolution);
+
+  // Restore saved preferred photo resolution per video device.
+  chrome.storage.local.get(
+      {devicePhotoResolution: {}},
+      (values) => this.prefResolution_ = values.devicePhotoResolution);
+
+  this.resolBroker_.registerChangePhotoPrefResolHandler(
+      (deviceId, width, height) => {
+        this.prefResolution_[deviceId] = {width, height};
+        chrome.storage.local.set({devicePhotoResolution: this.prefResolution_});
+        if (!cca.state.get('video-mode') && deviceId == this.deviceId_) {
+          this.doSwitchResolution_();
+        } else {
+          this.resolBroker_.notifyPhotoPrefResolChange(deviceId, width, height);
+        }
+      });
+};
+
+cca.views.camera.PhotoResolPreferrer.prototype = {
+  __proto__: cca.views.camera.ResolutionPreferrer.prototype,
+};
+
+/**
+ * @param {?[string, ResolList]} frontResolutions
+ * @param {?[string, ResolList]} backResolutions
+ * @param {Array<[string, ResolList]>} externalResolutions
+ * @override
+ */
+cca.views.camera.PhotoResolPreferrer.prototype.updateResolutions = function(
+    frontResolutions, backResolutions, externalResolutions) {
+  this.deviceResolutions_ = {};
+
+  const toDeviceIdResols = (deviceId, resolutions) => {
+    this.deviceResolutions_[deviceId] = resolutions;
+    let {width = -1, height = -1} = this.prefResolution_[deviceId] || {};
+    if (!resolutions.find(([w, h]) => w == width && h == height)) {
+      [width, height] = resolutions.reduce(
+          (maxR, R) => (maxR[0] * maxR[1] < R[0] * R[1] ? R : maxR), [0, 0]);
+    }
+    this.prefResolution_[deviceId] = {width, height};
+    return [deviceId, width, height, resolutions];
+  };
+
+  this.resolBroker_.notifyPhotoResolChange(
+      frontResolutions && toDeviceIdResols(...frontResolutions),
+      backResolutions && toDeviceIdResols(...backResolutions),
+      externalResolutions.map((ext) => toDeviceIdResols(...ext)));
+  chrome.storage.local.set({devicePhotoResolution: this.prefResolution_});
+};
+
+/**
+ * @param {string} deviceId
+ * @param {number} width
+ * @param {number} height
+ * @override
+ */
+cca.views.camera.PhotoResolPreferrer.prototype.updateCurrentResolution =
+    function(deviceId, width, height) {
+  this.deviceId_ = deviceId;
+  this.prefResolution_[deviceId] = {width, height};
+  chrome.storage.local.set({devicePhotoResolution: this.prefResolution_});
+  this.resolBroker_.notifyPhotoPrefResolChange(deviceId, width, height);
+};
+
+/**
+ * Finds and pairs photo resolutions and preview resolutions with the same
+ * aspect ratio.
+ * @param {ResolList} captureResolutions Available photo capturing resolutions.
+ * @param {ResolList} previewResolutions Available preview resolutions.
+ * @return {Array<[ResolList, ResolList]>} Each item of returned array is a pair
+ *     of capture and preview resolutions of same aspect ratio.
+ */
+cca.views.camera.PhotoResolPreferrer.prototype.pairCapturePreviewResolutions_ =
+    function(captureResolutions, previewResolutions) {
+  const toAspectRatio = (w, h) => (w / h).toFixed(4);
+  const previewRatios = previewResolutions.reduce((rs, [w, h]) => {
+    const key = toAspectRatio(w, h);
+    rs[key] = rs[key] || [];
+    rs[key].push([w, h]);
+    return rs;
+  }, {});
+  const captureRatios = captureResolutions.reduce((rs, [w, h]) => {
+    const key = toAspectRatio(w, h);
+    if (key in previewRatios) {
+      rs[key] = rs[key] || [];
+      rs[key].push([w, h]);
+    }
+    return rs;
+  }, {});
+  return Object.entries(captureRatios)
+      .map(([aspectRatio,
+             captureRs]) => [captureRs, previewRatios[aspectRatio]]);
+};
+
+/**
+ * @param {string} deviceId
+ * @param {ResolList} previewResolutions
+ * @return {Array<[number, number, Array<Object>]>}
+ * @override
+ */
+cca.views.camera.PhotoResolPreferrer.prototype.getSortedCandidates = function(
+    deviceId, previewResolutions) {
+  const photoResolutions = this.deviceResolutions_[deviceId];
+  const prefR = this.prefResolution_[deviceId] || {width: 0, height: -1};
+  return this
+      .pairCapturePreviewResolutions_(photoResolutions, previewResolutions)
+      .map(([captureRs, previewRs]) => {
+        if (captureRs.some(([w, h]) => w == prefR.width && h == prefR.height)) {
+          var captureR = [prefR.width, prefR.height];
+        } else {
+          var captureR = captureRs.reduce(
+              (captureR, r) => (r[0] > captureR[0] ? r : captureR), [0, -1]);
+        }
+
+        const candidates = previewRs.sort(([w, h], [w2, h2]) => w2 - w)
+                               .map(([width, height]) => ({
+                                      audio: false,
+                                      video: {
+                                        deviceId: {exact: deviceId},
+                                        frameRate: {min: 24},
+                                        width,
+                                        height,
+                                      },
+                                    }));
+        // Format of map result:
+        // [
+        //   [[CaptureW 1, CaptureH 1], [CaptureW 2, CaptureH 2], ...],
+        //   [PreviewConstraint 1, PreviewConstraint 2, ...]
+        // ]
+        return [captureR, candidates];
+      })
+      .sort(([[w, h]], [[w2, h2]]) => {
+        if (w == w2 && h == h2) {
+          return 0;
+        }
+        if (w == prefR.width && h == prefR.height) {
+          return -1;
+        }
+        if (w2 == prefR.width && h2 == prefR.height) {
+          return 1;
+        }
+        return w2 * h2 - w * h;
+      });
+};
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
index 1c1c916..98cc3d7 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
@@ -114,29 +114,25 @@
     'settings-front-photores': () => {
       const element = document.querySelector('#settings-front-photores');
       if (element.classList.contains('multi-option')) {
-        this.openPhotoResSettings_(
-            this.frontSetting_[0], this.frontSetting_[1], element);
+        this.openPhotoResSettings_(this.frontPhotoSetting_, element);
       }
     },
     'settings-front-videores': () => {
       const element = document.querySelector('#settings-front-videores');
       if (element.classList.contains('multi-option')) {
-        this.openVideoResSettings_(
-            this.frontSetting_[0], this.frontSetting_[2], element);
+        this.openVideoResSettings_(this.frontVideoSetting_, element);
       }
     },
     'settings-back-photores': () => {
       const element = document.querySelector('#settings-back-photores');
       if (element.classList.contains('multi-option')) {
-        this.openPhotoResSettings_(
-            this.backSetting_[0], this.backSetting_[1], element);
+        this.openPhotoResSettings_(this.backPhotoSetting_, element);
       }
     },
     'settings-back-videores': () => {
       const element = document.querySelector('#settings-back-videores');
       if (element.classList.contains('multi-option')) {
-        this.openVideoResSettings_(
-            this.backSetting_[0], this.backSetting_[2], element);
+        this.openVideoResSettings_(this.backVideoSetting_, element);
       }
     },
   });
@@ -205,34 +201,81 @@
       document.querySelector('#extcam-resolution-item-template');
 
   /**
-   * Device id and resolutions of front camera. Null for no front camera.
+   * Device id and photo resolutions of front camera. Null if no front camera.
    * @type {?DeviceIdResols}
    * @private
    */
-  this.frontSetting_ = null;
+  this.frontPhotoSetting_ = null;
 
   /**
-   * Device id and resolutions of back camera. Null for no back camera.
+   * Device id and video resolutions of front camera. Null if no front camera.
    * @type {?DeviceIdResols}
    * @private
    */
-  this.backSetting_ = null;
+  this.frontVideoSetting_ = null;
 
   /**
-   * Device id and resolutions of external cameras.
+   * Device id and photo resolutions of back camera. Null if no back camera.
+   * @type {?DeviceIdResols}
+   * @private
+   */
+  this.backPhotoSetting_ = null;
+
+  /**
+   * Device id and video resolutions of back camera. Null if no back camera.
+   * @type {?DeviceIdResols}
+   * @private
+   */
+  this.backVideoSetting_ = null;
+
+  /**
+   * Device id and photo resolutions of external cameras.
    * @type {Array<DeviceIdResols>}
    * @private
    */
-  this.externalSettings_ = [];
+  this.externalPhotoSettings_ = [];
+
+  /**
+   * Device id and video resolutions of external cameras.
+   * @type {Array<DeviceIdResols>}
+   * @private
+   */
+  this.externalVideoSettings_ = [];
+
+  /**
+   * Device id of currently opened resolution setting view.
+   * @type {?string}
+   * @private
+   */
+  this.openedSettingDeviceId_ = null;
 
   // End of properties, seal the object.
   Object.seal(this);
 
-  this.resolBroker_.addUpdateDeviceResolutionsListener(
-      this.updateResolutions.bind(this));
-  this.resolBroker_.addPhotoResolChangeListener(
+  this.resolBroker_.addPhotoResolChangeListener((front, back, externals) => {
+    // Filter out resolutions of megapixels < 0.1 i.e. megapixels 0.0
+    const zeroMPFilter = (s) => {
+      s = [...s];
+      s.splice(3, 1, s[3].filter(([w, h]) => w * h >= 100000));
+      return s;
+    };
+    this.frontPhotoSetting_ = zeroMPFilter(front);
+    this.backPhotoSetting_ = zeroMPFilter(back);
+    this.externalPhotoSettings_ = externals.map(zeroMPFilter);
+    this.updateResolutions_();
+  });
+
+  this.resolBroker_.addVideoResolChangeListener((front, back, externals) => {
+    this.frontVideoSetting_ = front;
+    this.backVideoSetting_ = back;
+    this.externalVideoSettings_ = externals;
+    this.updateResolutions_();
+  });
+
+
+  this.resolBroker_.addPhotoPrefResolChangeListener(
       this.updateSelectedPhotoResolution_.bind(this));
-  this.resolBroker_.addVideoResolChangeListener(
+  this.resolBroker_.addVideoPrefResolChangeListener(
       this.updateSelectedVideoResolution_.bind(this));
 };
 
@@ -248,7 +291,13 @@
  * @private
  */
 cca.views.ResolutionSettings.prototype.photoOptTextTempl_ = function(w, h) {
-  return `${Math.round(w * h / 100000) / 10} megapixels (${w}:${h})`;
+  const gcd = (a, b) => (a <= 0 ? b : gcd(b % a, a));
+  if (h * w >= 100000) {
+    return `${Math.round(w * h / 100000) / 10} megapixels (${w}:${h})`;
+  } else {
+    const d = gcd(w, h);
+    return `(${w / d}:${h / d}) ${w} x ${h}px`;
+  }
 };
 
 /**
@@ -263,51 +312,68 @@
 };
 
 /**
- * Updates resolutions of front, back camera and external cameras.
- * @param {?DeviceIdResols} frontSetting Resolutions of front camera.
- * @param {?DeviceIdResols} backSetting Resolutions of back camera.
- * @param {Array<DeviceIdResols>} externalSettings Resolutions of external
- *     cameras.
+ * Finds resolution setting of target device id.
+ * @param {string} deviceId
+ * @return {?DeviceIdResols}
+ * @private
  */
-cca.views.ResolutionSettings.prototype.updateResolutions = function(
-    frontSetting, backSetting, externalSettings) {
-  const checkMulti = (item, resolutions, optTextTempl) => {
-    // Filter out resolutions of megapixels < 0.1 i.e. megapixels 0.0
-    resolutions = resolutions.filter(([w, h]) => w * h >= 100000);
+cca.views.ResolutionSettings.prototype.getDeviceSetting_ = function(deviceId) {
+  if (this.frontPhotoSetting_ && this.frontPhotoSetting_[0] === deviceId) {
+    return [this.frontPhotoSetting_, this.frontVideoSetting_];
+  }
+  if (this.backPhotoSetting_ && this.backPhotoSetting_[0] === deviceId) {
+    return [this.backPhotoSetting_, this.backVideoSetting_];
+  }
+  const idx = this.externalPhotoSettings_.findIndex(([id]) => id === deviceId);
+  return idx == -1 ?
+      null :
+      [this.externalPhotoSettings_[idx], this.externalVideoSettings_[idx]];
+};
+
+/**
+ * Updates resolution information of front, back camera and external cameras.
+ * @private
+ */
+cca.views.ResolutionSettings.prototype.updateResolutions_ = function() {
+  // Since photo/video resolutions may be updated independently and updating
+  // setting menu requires both information, check if their device ids are
+  // matched before update.
+  const compareId = (setting, setting2) =>
+      (setting && setting[0]) === (setting2 && setting2[0]);
+  if (!compareId(this.frontPhotoSetting_, this.frontVideoSetting_) ||
+      !compareId(this.backPhotoItem_, this.backVideoItem_) ||
+      this.externalPhotoSettings_.length !=
+          this.externalVideoSettings_.length ||
+      this.externalPhotoSettings_.some(
+          (s, index) => !compareId(s, this.externalVideoSettings_[index]))) {
+    return;
+  }
+
+  const prepItem = (item, [id, w, h, resolutions], optTextTempl) => {
+    item.dataset.deviceId = id;
     item.classList.toggle('multi-option', resolutions.length > 1);
-    if (resolutions.length == 1) {
-      const [w, h] = resolutions;
-      item.dataset.sWidth = w;
-      item.dataset.sHeight = h;
-      item.querySelector('.description>span').textContent = optTextTempl(w, h);
-    }
+    item.querySelector('.description>span').textContent = optTextTempl(w, h);
   };
 
   // Update front camera setting
-  this.frontSetting_ = frontSetting;
-  cca.state.set('has-front-camera', this.frontSetting_);
-  if (this.frontSetting_) {
-    const [deviceId, photoRs, videoRs] = this.frontSetting_;
-    this.frontPhotoItem_.dataset.deviceId =
-        this.frontVideoItem_.dataset.deviceId = deviceId;
-    checkMulti(this.frontPhotoItem_, photoRs, this.photoOptTextTempl_);
-    checkMulti(this.frontVideoItem_, videoRs, this.videoOptTextTempl_);
+  cca.state.set('has-front-camera', !!this.frontPhotoSetting_);
+  if (this.frontPhotoSetting_) {
+    prepItem(
+        this.frontPhotoItem_, this.frontPhotoSetting_, this.photoOptTextTempl_);
+    prepItem(
+        this.frontVideoItem_, this.frontVideoSetting_, this.videoOptTextTempl_);
   }
 
   // Update back camera setting
-  this.backSetting_ = backSetting;
-  cca.state.set('has-back-camera', this.backSetting_);
-  if (this.backSetting_) {
-    const [deviceId, photoRs, videoRs] = this.backSetting_;
-    this.backPhotoItem_.dataset.deviceId =
-        this.backVideoItem_.dataset.deviceId = deviceId;
-    checkMulti(this.backPhotoItem_, photoRs, this.photoOptTextTempl_);
-    checkMulti(this.backVideoItem_, videoRs, this.videoOptTextTempl_);
+  cca.state.set('has-back-camera', !!this.backPhotoSetting_);
+  if (this.backPhotoSetting_) {
+    prepItem(
+        this.backPhotoItem_, this.backPhotoSetting_, this.photoOptTextTempl_);
+    prepItem(
+        this.backVideoItem_, this.backVideoSetting_, this.videoOptTextTempl_);
   }
 
   // Update external camera settings
-  this.externalSettings_ = externalSettings;
-
   // To prevent losing focus on item already exist before update, locate
   // focused item in both previous and current list, pop out all items in
   // previous list except those having same deviceId as focused one and
@@ -315,7 +381,8 @@
   const prevFocus =
       this.resMenu_.querySelector('.menu-item.external-camera:focus');
   const prevFId = prevFocus && prevFocus.dataset.deviceId;
-  const focusIdx = externalSettings.findIndex(([id]) => id === prevFId);
+  const focusIdx =
+      this.externalPhotoSettings_.findIndex(([id]) => id === prevFId);
   const fTitle =
       this.resMenu_.querySelector(`.menu-item[data-device-id="${prevFId}"]`);
   const focusedId = focusIdx === -1 ? null : prevFId;
@@ -325,34 +392,45 @@
           (element) => element.dataset.deviceId !== focusedId &&
               element.parentNode.removeChild(element));
 
-  externalSettings.forEach(([deviceId, photoRs, videoRs], index) => {
-    if (deviceId === focusedId) {
-      return;
-    }
-    const extItem = document.importNode(this.extcamItemTempl_.content, true);
-    const [titleItem, photoItem, videoItem] =
-        extItem.querySelectorAll('.menu-item');
-    titleItem.dataset.deviceId = photoItem.dataset.deviceId =
-        videoItem.dataset.deviceId = deviceId;
-    checkMulti(photoItem, photoRs, this.photoOptTextTempl_);
-    checkMulti(videoItem, videoRs, this.videoOptTextTempl_);
+  this.externalPhotoSettings_.forEach((photoSetting, index) => {
+    const deviceId = photoSetting[0];
+    const videoSetting = this.externalVideoSettings_[index];
+    if (deviceId !== focusedId) {
+      const extItem = document.importNode(this.extcamItemTempl_.content, true);
+      var [titleItem, photoItem, videoItem] =
+          extItem.querySelectorAll('.menu-item');
 
-    photoItem.addEventListener('click', () => {
-      if (photoItem.classList.contains('multi-option')) {
-        this.openPhotoResSettings_(deviceId, photoRs, photoItem);
+      photoItem.addEventListener('click', () => {
+        if (photoItem.classList.contains('multi-option')) {
+          this.openPhotoResSettings_(photoSetting, photoItem);
+        }
+      });
+      videoItem.addEventListener('click', () => {
+        if (videoItem.classList.contains('multi-option')) {
+          this.openVideoResSettings_(videoSetting, videoItem);
+        }
+      });
+      if (index < focusIdx) {
+        this.resMenu_.insertBefore(extItem, fTitle);
+      } else {
+        this.resMenu_.appendChild(extItem);
       }
-    });
-    videoItem.addEventListener('click', () => {
-      if (videoItem.classList.contains('multi-option')) {
-        this.openVideoResSettings_(deviceId, videoRs, videoItem);
-      }
-    });
-    if (index < focusIdx) {
-      this.resMenu_.insertBefore(extItem, fTitle);
     } else {
-      this.resMenu_.appendChild(extItem);
+      titleItem = fTitle;
+      photoItem = fTitle.nextElementSibling;
+      videoItem = photoItem.nextElementSibling;
     }
+    titleItem.dataset.deviceId = deviceId;
+    prepItem(photoItem, photoSetting, this.photoOptTextTempl_);
+    prepItem(videoItem, videoSetting, this.videoOptTextTempl_);
   });
+  if ((cca.state.get('photoresolutionsettings') ||
+       cca.state.get('videoresolutionsettings')) &&
+      !this.getDeviceSetting_(this.openedSettingDeviceId_)) {
+    cca.nav.close(
+        cca.state.get('photoresolutionsettings') ? 'photoresolutionsettings' :
+                                                   'videoresolutionsettings');
+  }
 };
 
 /**
@@ -364,15 +442,23 @@
  */
 cca.views.ResolutionSettings.prototype.updateSelectedPhotoResolution_ =
     function(deviceId, width, height) {
-  const [photoItem] = this.resMenu_.querySelectorAll(
-      `.resol-item[data-device-id="${deviceId}"]`);
-  photoItem.dataset.sWidth = width;
-  photoItem.dataset.sHeight = height;
+  const [photoSetting] = this.getDeviceSetting_(deviceId);
+  photoSetting[1] = width;
+  photoSetting[2] = height;
+  if (this.frontPhotoSetting_ && deviceId === this.frontPhotoSetting_[0]) {
+    var photoItem = this.frontPhotoItem_;
+  } else if (this.backPhotoSetting_ && deviceId === this.backPhotoSetting_[0]) {
+    photoItem = this.backPhotoItem_;
+  } else {
+    photoItem = this.resMenu_.querySelectorAll(
+        `.menu-item[data-device-id="${deviceId}"]`)[1];
+  }
   photoItem.querySelector('.description>span').textContent =
       this.photoOptTextTempl_(width, height);
 
   // Update setting option if it's opened.
-  if (cca.state.get('photoresolutionsettings')) {
+  if (cca.state.get('photoresolutionsettings') &&
+      this.openedSettingDeviceId_ === deviceId) {
     this.photoResMenu_
         .querySelector(`input[data-width="${width}"][data-height="${height}"]`)
         .checked = true;
@@ -388,15 +474,23 @@
  */
 cca.views.ResolutionSettings.prototype.updateSelectedVideoResolution_ =
     function(deviceId, width, height) {
-  const [, videoItem] = this.resMenu_.querySelectorAll(
-      `.resol-item[data-device-id="${deviceId}"]`);
-  videoItem.dataset.sWidth = width;
-  videoItem.dataset.sHeight = height;
+  const [, videoSetting] = this.getDeviceSetting_(deviceId);
+  videoSetting[1] = width;
+  videoSetting[2] = height;
+  if (this.frontVideoSetting_ && deviceId === this.frontVideoSetting_[0]) {
+    var videoItem = this.frontVideoItem_;
+  } else if (this.backVideoSetting_ && deviceId === this.backVideoSetting_[0]) {
+    videoItem = this.backVideoItem_;
+  } else {
+    videoItem = this.resMenu_.querySelectorAll(
+        `.menu-item[data-device-id="${deviceId}"]`)[2];
+  }
   videoItem.querySelector('.description>span').textContent =
       this.videoOptTextTempl_(width, height);
 
   // Update setting option if it's opened.
-  if (cca.state.get('videoresolutionsettings')) {
+  if (cca.state.get('videoresolutionsettings') &&
+      this.openedSettingDeviceId_ === deviceId) {
     this.videoResMenu_
         .querySelector(`input[data-width="${width}"][data-height="${height}"]`)
         .checked = true;
@@ -405,37 +499,37 @@
 
 /**
  * Opens photo resolution setting view.
- * @param {string} deviceId Device id of the opened view.
- * @param {ResolList} resolutions Resolutions to be shown in opened view.
- * @param {HTMLElement} resolItem Dom element from upper layer holding the
- *     selected resolution width, height.
+ * @param {DeviceIdResols} setting Photo resolution setting.
+ * @param {HTMLElement} resolItem Dom element from upper layer menu item showing
+ *     title of the selected resolution.
  * @private
  */
 cca.views.ResolutionSettings.prototype.openPhotoResSettings_ = function(
-    deviceId, resolutions, resolItem) {
+    setting, resolItem) {
+  const [deviceId, width, height, resolutions] = setting;
+  this.openedSettingDeviceId_ = deviceId;
   this.updateMenu_(
       resolItem, this.photoResMenu_, this.photoOptTextTempl_,
-      (w, h) => this.resolBroker_.requestChangePhotoResol(deviceId, w, h),
-      resolutions, parseInt(resolItem.dataset.sWidth),
-      parseInt(resolItem.dataset.sHeight));
+      (w, h) => this.resolBroker_.requestChangePhotoPrefResol(deviceId, w, h),
+      resolutions, width, height);
   this.openSubSettings('photoresolutionsettings');
 };
 
 /**
  * Opens video resolution setting view.
- * @param {string} deviceId Device id of the opened view.
- * @param {ResolList} resolutions Resolutions to be shown in opened view.
- * @param {HTMLElement} resolItem Dom element from upper layer holding the
- *     selected resolution width, height.
+ * @param {DeviceIdResols} setting Video resolution setting.
+ * @param {HTMLElement} resolItem Dom element from upper layer menu item showing
+ *     title of the selected resolution.
  * @private
  */
 cca.views.ResolutionSettings.prototype.openVideoResSettings_ = function(
-    deviceId, resolutions, resolItem) {
+    setting, resolItem) {
+  const [deviceId, width, height, resolutions] = setting;
+  this.openedSettingDeviceId_ = deviceId;
   this.updateMenu_(
       resolItem, this.videoResMenu_, this.videoOptTextTempl_,
-      (w, h) => this.resolBroker_.requestChangeVideoResol(deviceId, w, h),
-      resolutions, parseInt(resolItem.dataset.sWidth),
-      parseInt(resolItem.dataset.sHeight));
+      (w, h) => this.resolBroker_.requestChangeVideoPrefResol(deviceId, w, h),
+      resolutions, width, height);
   this.openSubSettings('videoresolutionsettings');
 };
 
@@ -458,11 +552,6 @@
     resolItem, menu, optTextTempl, onChange, resolutions, selectedWidth,
     selectedHeight) {
   const captionText = resolItem.querySelector('.description>span');
-  const updateSelection = (w, h) => {
-    resolItem.dataset.sWidth = w;
-    resolItem.dataset.sHeight = h;
-    captionText.textContent = optTextTempl(w, h);
-  };
   captionText.textContent = '';
   menu.querySelectorAll('.menu-item')
       .forEach((element) => element.parentNode.removeChild(element));
@@ -476,7 +565,7 @@
     inputElement.dataset.width = w;
     inputElement.dataset.height = h;
     if (w == selectedWidth && h == selectedHeight) {
-      updateSelection(w, h);
+      captionText.textContent = optTextTempl(w, h);
       inputElement.checked = true;
     }
     inputElement.addEventListener('click', (event) => {
@@ -486,7 +575,7 @@
     });
     inputElement.addEventListener('change', (event) => {
       if (inputElement.checked) {
-        updateSelection(w, h);
+        captionText.textContent = optTextTempl(w, h);
         onChange(w, h);
       }
     });
diff --git a/chrome/browser/resources/chromeos/camera/src/views/main.html b/chrome/browser/resources/chromeos/camera/src/views/main.html
index 1cb3f53..232dd7b 100644
--- a/chrome/browser/resources/chromeos/camera/src/views/main.html
+++ b/chrome/browser/resources/chromeos/camera/src/views/main.html
@@ -25,6 +25,7 @@
     <script src="../js/mojo/mojo_bindings_lite.js"></script>
     <script src="../js/mojo/camera_metadata_tags.mojom-lite.js"></script>
     <script src="../js/mojo/camera_metadata.mojom-lite.js"></script>
+    <script src="../js/mojo/camera_common.mojom-lite.js"></script>
     <script src="../js/mojo/image_capture.mojom-lite.js"></script>
     <script src="../js/mojo/cros_image_capture.mojom-lite.js"></script>
     <script src="../js/mojo/imagecapture.js"></script>
@@ -35,6 +36,7 @@
     <script src="../js/views/camera/options.js"></script>
     <script src="../js/views/camera/preview.js"></script>
     <script src="../js/views/camera/recordtime.js"></script>
+    <script src="../js/views/camera/resolution_preference.js"></script>
     <script src="../js/views/camera/timertick.js"></script>
     <script src="../js/views/camera/modes.js"></script>
     <script src="../js/views/dialog.js"></script>
diff --git a/chrome/browser/resources/extensions/error_page.html b/chrome/browser/resources/extensions/error_page.html
index 5067418..d5254f4 100644
--- a/chrome/browser/resources/extensions/error_page.html
+++ b/chrome/browser/resources/extensions/error_page.html
@@ -171,8 +171,7 @@
                   <cr-icon-button class="icon-delete-gray"
                       on-click="onDeleteErrorAction_"
                       aria-describedby$="[[item.id]]"
-                      aria-label="$i18n{clearEntry}"
-                      on-keydown="onDeleteErrorAction_"></cr-icon-button>
+                      aria-label="$i18n{clearEntry}"></cr-icon-button>
                 </div>
                 <iron-collapse opened="[[isOpened_(index, selectedEntry_)]]">
                   <div class="devtools-controls">
diff --git a/chrome/browser/resources/extensions/error_page.js b/chrome/browser/resources/extensions/error_page.js
index 9b910d7f1..fc0e44f0 100644
--- a/chrome/browser/resources/extensions/error_page.js
+++ b/chrome/browser/resources/extensions/error_page.js
@@ -198,10 +198,6 @@
      * @private
      */
     onDeleteErrorAction_: function(e) {
-      if (e.type == 'keydown' && !((e.code == 'Space' || e.code == 'Enter'))) {
-        return;
-      }
-
       this.delegate.deleteErrors(
           this.data.id, [(/** @type {!{model:Object}} */ (e)).model.item.id]);
       e.stopPropagation();
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 82b6128..d31b232 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -17,45 +17,51 @@
   <template>
     <style include="settings-shared"></style>
 <if expr="chromeos">
-    <settings-animated-pages id="pages" current-route="{{currentRoute}}"
-        section="a11y" focus-config="[[focusConfig_]]">
-      <div route-path="default">
-        <settings-toggle-button
-            id="a11yImageLabels"
-            hidden$="[[!showAccessibilityLabelsSetting_]]"
-            pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
-            on-change="onToggleAccessibilityImageLabels_"
-            label="$i18n{accessibleImageLabelsTitle}"
-            sub-label="$i18n{accessibleImageLabelsSubtitle}">
-        </settings-toggle-button>
-        <settings-toggle-button id="optionsInMenuToggle"
-            label="$i18n{optionsInMenuLabel}"
-            pref="{{prefs.settings.a11y.enable_menu}}">
-        </settings-toggle-button>
-        <cr-link-row class="hr" id="subpage-trigger"
-            label="$i18n{manageAccessibilityFeatures}"
-            on-click="onManageAccessibilityFeaturesTap_"
-            sub-label="$i18n{moreFeaturesLinkDescription}">
-        </cr-link-row>
-      </div>
+    <template is="dom-if" if="[[pageVisibility.webstoreLink]]">
+      <settings-animated-pages id="pages" current-route="{{currentRoute}}"
+          section="a11y" focus-config="[[focusConfig_]]">
+        <div route-path="default">
+          <settings-toggle-button
+              id="a11yImageLabels"
+              hidden$="[[!showAccessibilityLabelsSetting_]]"
+              pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
+              on-change="onToggleAccessibilityImageLabels_"
+              label="$i18n{accessibleImageLabelsTitle}"
+              sub-label="$i18n{accessibleImageLabelsSubtitle}">
+          </settings-toggle-button>
+          <settings-toggle-button id="optionsInMenuToggle"
+              label="$i18n{optionsInMenuLabel}"
+              pref="{{prefs.settings.a11y.enable_menu}}">
+          </settings-toggle-button>
+          <cr-link-row class="hr" id="subpage-trigger"
+              label="$i18n{manageAccessibilityFeatures}"
+              on-click="onManageAccessibilityFeaturesTap_"
+              sub-label="$i18n{moreFeaturesLinkDescription}">
+          </cr-link-row>
+        </div>
 
-      <template is="dom-if" route-path="/manageAccessibility">
-        <settings-subpage
-            associated-control="[[$$('#subpage-trigger')]]"
-            page-title="$i18n{manageAccessibilityFeatures}">
-          <settings-manage-a11y-page prefs="{{prefs}}">
-          </settings-manage-a11y-page>
-        </settings-subpage>
-      </template>
-      <template is="dom-if" route-path="/manageAccessibility/tts">
-        <settings-subpage
-            associated-control="[[$$('#subpage-trigger')]]"
-            page-title="$i18n{manageTtsSettings}">
-          <settings-tts-subpage prefs="{{prefs}}">
-          </settings-tts-subpage>
-        </settings-subpage>
-      </template>
-    </settings-animated-pages>
+        <template is="dom-if" route-path="/manageAccessibility">
+          <settings-subpage
+              associated-control="[[$$('#subpage-trigger')]]"
+              page-title="$i18n{manageAccessibilityFeatures}">
+            <settings-manage-a11y-page prefs="{{prefs}}">
+            </settings-manage-a11y-page>
+          </settings-subpage>
+        </template>
+        <template is="dom-if" route-path="/manageAccessibility/tts">
+          <settings-subpage
+              associated-control="[[$$('#subpage-trigger')]]"
+              page-title="$i18n{manageTtsSettings}">
+            <settings-tts-subpage prefs="{{prefs}}">
+            </settings-tts-subpage>
+          </settings-subpage>
+        </template>
+      </settings-animated-pages>
+    </template>
+    <cr-link-row class="hr" label="$i18n{moreFeaturesLink}"
+        on-click="onMoreFeaturesLinkClick_" sub-label="$i18n{a11yWebStore}"
+        hidden="[[pageVisibility.webstoreLink]]"
+        external></cr-link-row>
 </if>
 
 <if expr="not chromeos">
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index dccd90c..8e63bac 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -307,7 +307,9 @@
                 restamp>
               <settings-section page-title="$i18n{a11yPageTitle}"
                   section="a11y">
-                <settings-a11y-page prefs="{{prefs}}"></settings-a11y-page>
+                <settings-a11y-page prefs="{{prefs}}"
+                    page-visibility="[[pageVisibility.a11y]]">
+                </settings-a11y-page>
               </settings-section>
             </template>
 <if expr="not chromeos">
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 2903a42e..4991d67 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -62,6 +62,7 @@
 
 group("closure_compile") {
   deps = [
+    "os_a11y_page:closure_compile",
     "os_people_page:closure_compile",
     "os_privacy_page:closure_compile",
     "os_reset_page:closure_compile",
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.html b/chrome/browser/resources/settings/chromeos/lazy_load.html
index 7afc1763..5ef4a0c 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.html
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.html
@@ -1,7 +1,7 @@
 <html>
 <head></head>
 <body>
-  <link rel="import" href="../a11y_page/a11y_page.html">
+  <link rel="import" href="os_a11y_page/os_a11y_page.html">
   <link rel="import" href="../date_time_page/date_time_page.html">
   <link rel="import" href="../languages_page/languages_page.html">
   <link rel="import" href="../printing_page/printing_page.html">
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_a11y_page/BUILD.gn
new file mode 100644
index 0000000..a87a220
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":os_a11y_page",
+    "../../a11y_page:externs",
+    "../../a11y_page:manage_a11y_page",
+    "../../a11y_page:tts_subpage",
+  ]
+}
+
+js_library("os_a11y_page") {
+  deps = [
+    "../../:route",
+    "../../settings_page:settings_animated_pages",
+    "//ui/webui/resources/js:load_time_data",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
+  ]
+}
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.html
new file mode 100644
index 0000000..eacfd34
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.html
@@ -0,0 +1,57 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="../../i18n_setup.html">
+<link rel="import" href="../../settings_shared_css.html">
+<link rel="import" href="../../a11y_page/manage_a11y_page.html">
+<link rel="import" href="../../controls/settings_toggle_button.html">
+<link rel="import" href="../../settings_page/settings_animated_pages.html">
+<link rel="import" href="../../settings_page/settings_subpage.html">
+<link rel="import" href="../../a11y_page/tts_subpage.html">
+
+<dom-module id="os-settings-a11y-page">
+  <template>
+    <style include="settings-shared"></style>
+    <settings-animated-pages id="pages" current-route="{{currentRoute}}"
+        section="a11y" focus-config="[[focusConfig_]]">
+      <div route-path="default">
+        <settings-toggle-button
+            id="a11yImageLabels"
+            hidden$="[[!showAccessibilityLabelsSetting_]]"
+            pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
+            on-change="onToggleAccessibilityImageLabels_"
+            label="$i18n{accessibleImageLabelsTitle}"
+            sub-label="$i18n{accessibleImageLabelsSubtitle}">
+        </settings-toggle-button>
+        <settings-toggle-button id="optionsInMenuToggle"
+            label="$i18n{optionsInMenuLabel}"
+            pref="{{prefs.settings.a11y.enable_menu}}">
+        </settings-toggle-button>
+        <cr-link-row class="hr" id="subpage-trigger"
+            label="$i18n{manageAccessibilityFeatures}"
+            on-click="onManageAccessibilityFeaturesTap_"
+            sub-label="$i18n{moreFeaturesLinkDescription}">
+        </cr-link-row>
+      </div>
+
+      <template is="dom-if" route-path="/manageAccessibility">
+        <settings-subpage
+            associated-control="[[$$('#subpage-trigger')]]"
+            page-title="$i18n{manageAccessibilityFeatures}">
+          <settings-manage-a11y-page prefs="{{prefs}}">
+          </settings-manage-a11y-page>
+        </settings-subpage>
+      </template>
+      <template is="dom-if" route-path="/manageAccessibility/tts">
+        <settings-subpage
+            associated-control="[[$$('#subpage-trigger')]]"
+            page-title="$i18n{manageTtsSettings}">
+          <settings-tts-subpage prefs="{{prefs}}">
+          </settings-tts-subpage>
+        </settings-subpage>
+      </template>
+    </settings-animated-pages>
+  </template>
+  <script src="os_a11y_page.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
new file mode 100644
index 0000000..0f35fb1
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
@@ -0,0 +1,99 @@
+// 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
+ * 'os-settings-a11y-page' is the small section of advanced settings containing
+ * a subpage with Accessibility settings for ChromeOS.
+ */
+Polymer({
+  is: 'os-settings-a11y-page',
+
+  behaviors: [WebUIListenerBehavior],
+
+  properties: {
+    /**
+     * The current active route.
+     */
+    currentRoute: {
+      type: Object,
+      notify: true,
+    },
+
+    /**
+     * Preferences state.
+     */
+    prefs: {
+      type: Object,
+      notify: true,
+    },
+
+    /**
+     * Whether to show accessibility labels settings.
+     */
+    showAccessibilityLabelsSetting_: {
+      type: Boolean,
+      value: false,
+    },
+
+    /** @private {!Map<string, string>} */
+    focusConfig_: {
+      type: Object,
+      value: function() {
+        const map = new Map();
+        if (settings.routes.MANAGE_ACCESSIBILITY) {
+          map.set(
+              settings.routes.MANAGE_ACCESSIBILITY.path, '#subpage-trigger');
+        }
+        return map;
+      },
+    },
+
+    /**
+     * Whether to show experimental accessibility features.
+     * @private {boolean}
+     */
+    showExperimentalFeatures_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('showExperimentalA11yFeatures');
+      },
+    },
+  },
+
+  /** @override */
+  ready: function() {
+    this.addWebUIListener(
+        'screen-reader-state-changed',
+        this.onScreenReaderStateChanged_.bind(this));
+    chrome.send('getScreenReaderState');
+  },
+
+  /**
+   * @private
+   * @param {boolean} hasScreenReader Whether a screen reader is enabled.
+   */
+  onScreenReaderStateChanged_: function(hasScreenReader) {
+    // TODO(katie): Remove showExperimentalA11yLabels flag before launch.
+    this.showAccessibilityLabelsSetting_ = hasScreenReader &&
+        loadTimeData.getBoolean('showExperimentalA11yLabels');
+  },
+
+  /** @private */
+  onToggleAccessibilityImageLabels_: function() {
+    const a11yImageLabelsOn = this.$.a11yImageLabels.checked;
+    if (a11yImageLabelsOn) {
+      chrome.send('confirmA11yImageLabels');
+    }
+    chrome.metricsPrivate.recordBoolean(
+        'Accessibility.ImageLabels.FromSettings.ToggleSetting',
+        a11yImageLabelsOn);
+  },
+
+  /** @private */
+  onManageAccessibilityFeaturesTap_: function() {
+    settings.navigateTo(settings.routes.MANAGE_ACCESSIBILITY);
+  },
+
+});
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index e337e9f..efa1e3b 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="../os_a11y_page/os_a11y_page.html">
 <link rel="import" href="../os_people_page/os_people_page.html">
 <link rel="import" href="../os_search_page/os_search_page.html">
 <link rel="import" href="../personalization_page/personalization_page.html">
@@ -259,7 +260,8 @@
                 restamp>
               <settings-section page-title="$i18n{a11yPageTitle}"
                   section="a11y">
-                <settings-a11y-page prefs="{{prefs}}"></settings-a11y-page>
+                <os-settings-a11y-page prefs="{{prefs}}">
+                </os-settings-a11y-page>
               </settings-section>
             </template>
             <template is="dom-if" if="[[showPage_(pageVisibility.reset)]]"
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 1f18102..052920b 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -13,11 +13,11 @@
   <release seq="1">
     <structures>
       <structure name="IDR_OS_SETTINGS_A11Y_PAGE_JS"
-                 file="a11y_page/a11y_page.js"
+                 file="chromeos/os_a11y_page/os_a11y_page.js"
                  preprocess="true"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_A11Y_PAGE_HTML"
-                 file="a11y_page/a11y_page.html"
+                 file="chromeos/os_a11y_page/os_a11y_page.html"
                  type="chrome_html"
                  preprocess="true"
                  allowexternalscript="true" />
diff --git a/chrome/browser/resources/settings/page_visibility.js b/chrome/browser/resources/settings/page_visibility.js
index 4c2b571..bcf816b 100644
--- a/chrome/browser/resources/settings/page_visibility.js
+++ b/chrome/browser/resources/settings/page_visibility.js
@@ -8,6 +8,7 @@
  * project is completed this can be changed to only consider incognito and
  * guest mode. https://crbug.com/950007
  * @typedef {{
+ *   a11y: (boolean|undefined|A11yPageVisibility),
  *   advancedSettings: (boolean|undefined),
  *   appearance: (boolean|undefined|AppearancePageVisibility),
  *   autofill: (boolean|undefined),
@@ -29,6 +30,13 @@
 
 /**
  * @typedef {{
+ *   webstoreLink: boolean,
+ * }}
+ */
+let A11yPageVisibility;
+
+/**
+ * @typedef {{
  *   bookmarksBar: boolean,
  *   homeButton: boolean,
  *   pageZoom: boolean,
@@ -129,6 +137,9 @@
         googleDrive: false,
         smbShares: false,
       },
+      a11y: {
+        webstoreLink: showOSSettings,
+      },
       extensions: false,
       printing: showOSSettings,
     };
@@ -172,6 +183,9 @@
         googleDrive: showOSSettings,
         smbShares: showOSSettings,
       },
+      a11y: {
+        webstoreLink: showOSSettings,
+      },
       extensions: true,
       printing: showOSSettings,
     };
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chrome/browser/resources/settings/settings_page/settings_subpage.html
index 56e73c80..d29eb8f 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage.html
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage.html
@@ -17,10 +17,10 @@
     <style include="settings-shared">
       :host {
         background-color: var(--cr-card-background-color);
-        bottom: 0;
+        box-sizing: border-box;
         display: block;
         left: 0;
-        min-height: fit-content;
+        min-height: calc(100vh - var(--cr-toolbar-height));
         padding-bottom: 60px;
         position: absolute;
         right: 0;
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 58d76f3c..d1dc39c 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -87,8 +87,6 @@
 
       #main {
         flex-basis: var(--settings-main-basis);
-        height: 100%;
-        min-height: fit-content;
       }
 
       /* The breakpoint of 980px was decided on by the rounded sum of
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
new file mode 100644
index 0000000..a6bda83a
--- /dev/null
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -0,0 +1,64 @@
+// 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/base64.h"
+#include "base/macros.h"
+#include "chrome/browser/sync/test/integration/encryption_helper.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "components/sync/base/nigori.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace {
+
+using encryption_helper::GetServerNigori;
+
+syncer::KeyParams KeystoreKeyParams(const std::string& key) {
+  // Due to mis-encode of keystore keys to base64 we have to always encode such
+  // keys to provide backward compatibility.
+  std::string encoded_key;
+  base::Base64Encode(key, &encoded_key);
+  return {syncer::KeyDerivationParams::CreateForPbkdf2(),
+          std::move(encoded_key)};
+}
+
+MATCHER_P(IsDataEncryptedWith, key_params, "") {
+  const sync_pb::EncryptedData& encrypted_data = arg;
+  syncer::Nigori nigori;
+  nigori.InitByDerivation(key_params.derivation_params, key_params.password);
+  std::string nigori_name;
+  EXPECT_TRUE(nigori.Permute(syncer::Nigori::Type::Password,
+                             syncer::kNigoriKeyName, &nigori_name));
+  return encrypted_data.key_name() == nigori_name;
+}
+
+class SingleClientNigoriSyncTest : public SyncTest {
+ public:
+  SingleClientNigoriSyncTest() : SyncTest(SINGLE_CLIENT) {}
+  ~SingleClientNigoriSyncTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SingleClientNigoriSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientNigoriSyncTest,
+                       ShouldCommitKeystoreNigoriWhenReceivedDefault) {
+  // SetupSync() should make FakeServer send default NigoriSpecifics.
+  ASSERT_TRUE(SetupSync());
+  // TODO(crbug/922900): we may want to actually wait for specifics update in
+  // fake server. Due to implementation details it's not currently needed.
+  sync_pb::NigoriSpecifics specifics;
+  EXPECT_TRUE(GetServerNigori(GetFakeServer(), &specifics));
+
+  const std::vector<std::string>& keystore_keys =
+      GetFakeServer()->GetKeystoreKeys();
+  ASSERT_TRUE(keystore_keys.size() == 1);
+  EXPECT_THAT(specifics.encryption_keybag(),
+              IsDataEncryptedWith(KeystoreKeyParams(keystore_keys.back())));
+  EXPECT_EQ(specifics.passphrase_type(),
+            sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
+  EXPECT_TRUE(specifics.keybag_is_frozen());
+  EXPECT_TRUE(specifics.has_keystore_migration_time());
+}
+
+}  // namespace
diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder.cc b/chrome/browser/tab_contents/navigation_metrics_recorder.cc
index 10fc571..58464f9 100644
--- a/chrome/browser/tab_contents/navigation_metrics_recorder.cc
+++ b/chrome/browser/tab_contents/navigation_metrics_recorder.cc
@@ -60,6 +60,14 @@
         "SiteIsolationActive", "Enabled");
   }
 
+  // Also register a synthetic field trial when we encounter a navigation to an
+  // OOPIF.
+  if (is_synthetic_isolation_trial_enabled_ &&
+      navigation_handle->GetRenderFrameHost()->IsCrossProcessSubframe()) {
+    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+        "OutOfProcessIframesActive", "Enabled");
+  }
+
   if (!navigation_handle->IsInMainFrame())
     return;
 
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index f4aad672..efb0113 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -183,6 +183,11 @@
     return true;
   }
 
+  if (IsForSystemWebApp()) {
+    DCHECK_EQ(last_committed_url.scheme_piece(), content::kChromeUIScheme);
+    return false;
+  }
+
   // Page URLs that are not within scope
   // (https://www.w3.org/TR/appmanifest/#dfn-within-scope) of the app
   // corresponding to |launch_url| show the toolbar.
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 0cd7fe3..9479195 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -65,6 +65,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/context_menu_params.h"
+#include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
@@ -1613,6 +1614,43 @@
   NavigateAndCheckForToolbar(app_browser_, app_url, false);
 }
 
+// Check the toolbar is not shown for system web apps for pages on the chrome://
+// scheme.
+IN_PROC_BROWSER_TEST_P(HostedAppPWAOnlyTest,
+                       ShouldNotShowToolbarForSystemWebAppInChrome) {
+  const GURL app_url(chrome::kChromeUISettingsURL);
+
+  SetupSystemAppWithURL(app_url);
+
+  // Navigate to the app's launch page; the toolbar should be hidden.
+  NavigateAndCheckForToolbar(app_browser_, app_url, false);
+
+  // Because the first part of the url is on a different origin (settings vs.
+  // foo) a toolbar would normally be shown. However, because settings is a
+  // SystemWebApp and foo is served via chrome:// it is okay not to show the
+  // toolbar.
+  GURL out_of_scope_chrome_page(app_url.spec() + "://foo");
+  LOG(ERROR) << out_of_scope_chrome_page;
+  NavigateAndCheckForToolbar(app_browser_, out_of_scope_chrome_page, false);
+}
+
+// Check the toolbar is shown for system web apps on pages not on the chrome://
+// scheme. Note: We should never normally get into this scenario.
+IN_PROC_BROWSER_TEST_P(HostedAppPWAOnlyTest,
+                       ShouldShowToolbarForSystemWebAppOnInternet) {
+  const GURL app_url(chrome::kChromeUISettingsURL);
+
+  SetupSystemAppWithURL(app_url);
+
+  // Navigate to the app's launch page; the toolbar should be hidden.
+  NavigateAndCheckForToolbar(app_browser_, app_url, false);
+
+  // Even though the url is secure it is not being served over chrome:// so a
+  // toolbar should be shown.
+  GURL off_scheme_page("https://example.com");
+  NavigateAndCheckForToolbar(app_browser_, off_scheme_page, true);
+}
+
 // Common app manifest for HostedAppProcessModelTests.
 constexpr const char kHostedAppProcessModelManifest[] =
     R"( { "name": "Hosted App Process Model Test",
diff --git a/chrome/browser/ui/views/extensions/pwa_confirmation.cc b/chrome/browser/ui/views/extensions/pwa_confirmation.cc
index 1e6489990..6e9b206 100644
--- a/chrome/browser/ui/views/extensions/pwa_confirmation.cc
+++ b/chrome/browser/ui/views/extensions/pwa_confirmation.cc
@@ -128,7 +128,7 @@
 
 views::View* PWAConfirmation::GetInitiallyFocusedView(
     views::DialogDelegateView* dialog) {
-  return dialog->GetDialogClientView()->cancel_button();
+  return nullptr;
 }
 
 void PWAConfirmation::Accept() {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index beeed98..4d98d30 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -55,6 +55,7 @@
 #include "ui/events/gestures/gesture_recognizer.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/views/controls/label.h"
@@ -300,7 +301,10 @@
 
 gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
     const gfx::Rect& client_bounds) const {
-  return client_bounds;
+  const int top_inset = GetTopInset(false);
+  return gfx::Rect(client_bounds.x(),
+                   std::max(0, client_bounds.y() - top_inset),
+                   client_bounds.width(), client_bounds.height() + top_inset);
 }
 
 int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
index c5594b43d..165c65a9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -41,7 +41,7 @@
 }
 
 class OmniboxResultView : public views::View,
-                          private gfx::AnimationDelegate,
+                          public gfx::AnimationDelegate,
                           public views::ButtonListener,
                           public views::ContextMenuController,
                           public ui::SimpleMenuModel::Delegate {
diff --git a/chrome/common/extensions/chrome_extensions_client.cc b/chrome/common/extensions/chrome_extensions_client.cc
index 222392e..6827e12 100644
--- a/chrome/common/extensions/chrome_extensions_client.cc
+++ b/chrome/common/extensions/chrome_extensions_client.cc
@@ -231,7 +231,6 @@
   // conservative. Components shouldn't be subject to enterprise policy controls
   // or blocking access to the webstore so they get the highest priority
   // allowlist entry.
-  // TODO(crbug.com/936900): Specify the port argument.
   if (extensions::Manifest::IsComponentLocation(extension.location()) &&
       is_extension_active) {
     origin_patterns->push_back(network::mojom::CorsOriginPattern::New(
@@ -244,7 +243,6 @@
   // TODO(jstritar): We should try to remove this special case. Also, these
   // whitelist entries need to be updated when the kManagement permission
   // changes.
-  // TODO(crbug.com/936900): Specify the port argument.
   if (is_extension_active && extension.permissions_data()->HasAPIPermission(
                                  extensions::APIPermission::kManagement)) {
     origin_patterns->push_back(network::mojom::CorsOriginPattern::New(
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 8bcf321..cb3666d 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -14,7 +14,7 @@
 
 // Please keep this file in the same order as the header.
 
-// Note: Add hosts to |kChromePaths| in browser_about_handler.cc to be listed by
+// Note: Add hosts to |kChromeHostURLs| at the bottom of this file to be listed by
 // chrome://chrome-urls (about:about) and the built-in AutocompleteProvider.
 
 const char kChromeUIAboutHost[] = "about";
@@ -455,6 +455,7 @@
     kChromeUIPasswordManagerInternalsHost,
     kChromeUIPolicyHost,
     kChromeUIPredictorsHost,
+    kChromeUIPrefsInternalsHost,
     kChromeUIQuotaInternalsHost,
     kChromeUISignInInternalsHost,
     kChromeUISiteEngagementHost,
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 37a8483a..29601c5f 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -589,7 +589,7 @@
                                    .ToSkBitmap());
 }
 
-bool ChromeContentRendererClient::MaybeCreateMimeHandlerView(
+bool ChromeContentRendererClient::IsPluginHandledExternally(
     content::RenderFrame* render_frame,
     const blink::WebElement& plugin_element,
     const GURL& original_url,
@@ -608,14 +608,7 @@
       render_frame->GetRoutingID(), original_url,
       render_frame->GetWebFrame()->Top()->GetSecurityOrigin(), mime_type,
       &plugin_info);
-  // TODO(ekaramad): Not continuing here due to a disallowed status should take
-  // us to CreatePlugin. See if more in depths investigation of |status| is
-  // necessary here (see https://crbug.com/965747). For now, returning false
-  // should take us to CreatePlugin after HTMLPlugInElement which is called
-  // through HTMLPlugInElement::LoadPlugin code path.
-  if ((plugin_info->status != chrome::mojom::PluginStatus::kAllowed &&
-       plugin_info->status !=
-           chrome::mojom::PluginStatus::kPlayImportantContent) ||
+  if (plugin_info->status == chrome::mojom::PluginStatus::kNotFound ||
       !ChromeExtensionsRendererClient::MaybeCreateMimeHandlerView(
           plugin_element, original_url, plugin_info->actual_mime_type,
           plugin_info->plugin)) {
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 1a71531..ac59181 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -90,10 +90,10 @@
   void RenderViewCreated(content::RenderView* render_view) override;
   SkBitmap* GetSadPluginBitmap() override;
   SkBitmap* GetSadWebViewBitmap() override;
-  bool MaybeCreateMimeHandlerView(content::RenderFrame* render_frame,
-                                  const blink::WebElement& plugin_element,
-                                  const GURL& original_url,
-                                  const std::string& mime_type) override;
+  bool IsPluginHandledExternally(content::RenderFrame* render_frame,
+                                 const blink::WebElement& plugin_element,
+                                 const GURL& original_url,
+                                 const std::string& mime_type) override;
   v8::Local<v8::Object> GetScriptableObject(
       const blink::WebElement& plugin_element,
       v8::Isolate* isolate) override;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f4f2cc96..b769470 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1187,7 +1187,7 @@
       "//mojo/public/cpp/system",
       "//net",
       "//net:test_support",
-      "//services/device/public/cpp/test:test_support",
+      "//services/device/public/cpp:test_support",
       "//services/device/public/mojom",
       "//services/network/public/cpp",
       "//services/service_manager/public/cpp",
@@ -3230,7 +3230,7 @@
     "//net:test_support",
     "//ppapi/buildflags",
     "//services/data_decoder/public/cpp:test_support",
-    "//services/device/public/cpp/test:test_support",
+    "//services/device/public/cpp:test_support",
     "//services/network:test_support",
     "//services/network/public/cpp",
     "//skia",
@@ -3324,6 +3324,7 @@
     sources += [
       "../browser/android/customtabs/dynamicmodule/module_metrics_unittest.cc",
       "../browser/android/search_permissions/search_permissions_service_unittest.cc",
+      "../browser/autofill/address_accessory_controller_impl_unittest.cc",
       "../browser/autofill/autofill_credit_card_filling_infobar_delegate_mobile_unittest.cc",
       "../browser/autofill/autofill_keyboard_accessory_adapter_unittest.cc",
       "../browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc",
@@ -3628,7 +3629,6 @@
       "//components/send_tab_to_self:test_support",
       "//components/signin/core/browser:signin_buildflags",
       "//components/sync:test_support",
-      "//services/device/public/cpp/test:test_support",
       "//services/metrics/public/cpp:ukm_builders",
       "//third_party/libaddressinput",
       "//ui/native_theme:test_support",
@@ -5640,6 +5640,7 @@
       "../browser/sync/test/integration/single_client_directory_sync_test.cc",
       "../browser/sync/test/integration/single_client_extensions_sync_test.cc",
       "../browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc",
+      "../browser/sync/test/integration/single_client_nigori_sync_test.cc",
       "../browser/sync/test/integration/single_client_passwords_sync_test.cc",
       "../browser/sync/test/integration/single_client_polling_sync_test.cc",
       "../browser/sync/test/integration/single_client_preferences_sync_test.cc",
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index fd0cd5f1..5fce174 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -710,10 +710,8 @@
 }
 
 Profile::ProfileType TestingProfile::GetProfileType() const {
-  if (guest_session_)
-    return GUEST_PROFILE;
   if (original_profile_)
-    return INCOGNITO_PROFILE;
+    return guest_session_ ? GUEST_PROFILE : INCOGNITO_PROFILE;
   return REGULAR_PROFILE;
 }
 
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
index a1e2ff1f..881b179 100644
--- a/chrome/test/data/webrtc/peerconnection_getstats.js
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -169,6 +169,7 @@
  */
 let kRTCOutboundRtpStreamStats = new RTCStats(kRTCSentRtpStreamStats, {
   trackId: 'string',
+  mediaSourceId: 'string',
   senderId: 'string',
   remoteId: 'string',
   lastPacketSentTimestamp: 'number',
@@ -205,6 +206,40 @@
 addRTCStatsToWhitelist(
     Presence.OPTIONAL, 'remote-outbound-rtp', kRTCRemoteOutboundRtpStreamStats);
 
+/**
+ * RTCMediaSourceStats
+ * https://w3c.github.io/webrtc-stats/#dom-rtcmediasourcestats
+ * @private
+ */
+const kRTCMediaSourceStats = new RTCStats(null, {
+  trackIdentifier: 'string',
+  kind: 'string',
+});
+
+/**
+ * RTCAudioSourceStats
+ * https://w3c.github.io/webrtc-stats/#dom-rtcaudiosourcestats
+ * @private
+ */
+const kRTCAudioSourceStats = new RTCStats(kRTCMediaSourceStats, {
+});
+addRTCStatsToWhitelist(
+    Presence.MANDATORY, 'media-source', kRTCAudioSourceStats);
+
+/**
+ * RTCVideoSourceStats
+ * https://w3c.github.io/webrtc-stats/#dom-rtcvideosourcestats
+ * @private
+ */
+const kRTCVideoSourceStats = new RTCStats(kRTCMediaSourceStats, {
+  width: 'number',
+  height: 'number',
+  frames: 'number',
+  framesPerSecond: 'number',
+});
+addRTCStatsToWhitelist(
+    Presence.MANDATORY, 'media-source', kRTCVideoSourceStats);
+
 /*
  * RTCRtpContributingSourceStats
  * https://w3c.github.io/webrtc-stats/#dom-rtcrtpcontributingsourcestats
@@ -294,6 +329,7 @@
  * @private
  */
 let kRTCVideoSenderStats = new RTCStats(kRTCVideoHandlerStats, {
+  mediaSourceId: 'string',
   framesCaptured: 'number',
   framesSent: 'number',
   hugeFramesSent: 'number',
@@ -361,6 +397,7 @@
  * @private
  */
 let kRTCAudioSenderStats = new RTCStats(kRTCAudioHandlerStats, {
+  mediaSourceId: 'string',
   echoReturnLoss: 'number',
   echoReturnLossEnhancement: 'number',
   totalSamplesSent: 'number',
diff --git a/chromeos/components/power/BUILD.gn b/chromeos/components/power/BUILD.gn
index 3c1fbff..672c440 100644
--- a/chromeos/components/power/BUILD.gn
+++ b/chromeos/components/power/BUILD.gn
@@ -31,7 +31,7 @@
     ":power",
     "//base/test:test_support",
     "//chromeos/dbus/power",
-    "//services/device/public/cpp/test:test_support",
+    "//services/device/public/cpp:test_support",
     "//services/device/public/mojom",
     "//services/service_manager/public/cpp/test:test_support",
     "//testing/gtest",
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 677dbf8..a82c5bd 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -160,7 +160,7 @@
     deps += [
       "//chromeos/dbus",
       "//libassistant/shared/public",
-      "//services/device/public/cpp/test:test_support",
+      "//services/device/public/cpp:test_support",
     ]
   }
 }
diff --git a/components/BUILD.gn b/components/BUILD.gn
index db1c3f3..93461ad 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -517,7 +517,7 @@
       "//ipc:test_support",
       "//net:test_support",
       "//printing/buildflags",
-      "//services/device/public/cpp/test:test_support",
+      "//services/device/public/cpp:test_support",
       "//services/device/public/mojom",
       "//services/service_manager/public/cpp",
       "//services/service_manager/public/mojom",
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 44f802f..0d48e3c1 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -376,7 +376,7 @@
     "//content/test:test_support",
     "//device/bluetooth",
     "//mojo/public/cpp/system:system",
-    "//services/device/public/cpp/test:test_support",
+    "//services/device/public/cpp:test_support",
     "//services/device/public/mojom",
     "//services/service_manager/public/cpp/test:test_support",
     "//testing/gmock",
diff --git a/components/autofill/core/browser/ui/accessory_sheet_data.cc b/components/autofill/core/browser/ui/accessory_sheet_data.cc
index 1eb934c3..aee02a65 100644
--- a/components/autofill/core/browser/ui/accessory_sheet_data.cc
+++ b/components/autofill/core/browser/ui/accessory_sheet_data.cc
@@ -8,12 +8,12 @@
 
 namespace autofill {
 
-UserInfo::Field::Field(const base::string16& display_text,
-                       const base::string16& a11y_description,
+UserInfo::Field::Field(base::string16 display_text,
+                       base::string16 a11y_description,
                        bool is_obfuscated,
                        bool selectable)
-    : display_text_(display_text),
-      a11y_description_(a11y_description),
+    : display_text_(std::move(display_text)),
+      a11y_description_(std::move(a11y_description)),
       is_obfuscated_(is_obfuscated),
       selectable_(selectable) {}
 
@@ -66,8 +66,8 @@
   return os << "]";
 }
 
-FooterCommand::FooterCommand(const base::string16& display_text)
-    : display_text_(display_text) {}
+FooterCommand::FooterCommand(base::string16 display_text)
+    : display_text_(std::move(display_text)) {}
 
 FooterCommand::FooterCommand(const FooterCommand& footer_command) = default;
 
@@ -102,8 +102,8 @@
 }
 
 AccessorySheetData::AccessorySheetData(FallbackSheetType sheet_type,
-                                       const base::string16& title)
-    : sheet_type_(sheet_type), title_(title) {}
+                                       base::string16 title)
+    : sheet_type_(sheet_type), title_(std::move(title)) {}
 
 AccessorySheetData::AccessorySheetData(const AccessorySheetData& data) =
     default;
@@ -138,8 +138,8 @@
 }
 
 AccessorySheetData::Builder::Builder(FallbackSheetType type,
-                                     const base::string16& title)
-    : accessory_sheet_data_(type, title) {}
+                                     base::string16 title)
+    : accessory_sheet_data_(type, std::move(title)) {}
 
 AccessorySheetData::Builder::~Builder() = default;
 
@@ -154,46 +154,50 @@
 }
 
 AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendSimpleField(
-    const base::string16& text) && {
+    base::string16 text) && {
   // Calls AppendSimpleField(...)& since |this| is an lvalue.
-  return std::move(AppendSimpleField(text));
+  return std::move(AppendSimpleField(std::move(text)));
 }
 
 AccessorySheetData::Builder& AccessorySheetData::Builder::AppendSimpleField(
-    const base::string16& text) & {
-  return AppendField(text, text, false, true);
+    base::string16 text) & {
+  base::string16 display_text = text;
+  base::string16 a11y_description = std::move(text);
+  return AppendField(display_text, a11y_description, false, true);
 }
 
 AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendField(
-    const base::string16& display_text,
-    const base::string16& a11y_description,
+    base::string16 display_text,
+    base::string16 a11y_description,
     bool is_obfuscated,
     bool selectable) && {
   // Calls AppendField(...)& since |this| is an lvalue.
-  return std::move(
-      AppendField(display_text, a11y_description, is_obfuscated, selectable));
+  return std::move(AppendField(std::move(display_text),
+                               std::move(a11y_description), is_obfuscated,
+                               selectable));
 }
 
 AccessorySheetData::Builder& AccessorySheetData::Builder::AppendField(
-    const base::string16& display_text,
-    const base::string16& a11y_description,
+    base::string16 display_text,
+    base::string16 a11y_description,
     bool is_obfuscated,
     bool selectable) & {
   accessory_sheet_data_.mutable_user_info_list().back().add_field(
-      UserInfo::Field(display_text, a11y_description, is_obfuscated,
-                      selectable));
+      UserInfo::Field(std::move(display_text), std::move(a11y_description),
+                      is_obfuscated, selectable));
   return *this;
 }
 
 AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendFooterCommand(
-    const base::string16& display_text) && {
+    base::string16 display_text) && {
   // Calls AppendFooterCommand(...)& since |this| is an lvalue.
-  return std::move(AppendFooterCommand(display_text));
+  return std::move(AppendFooterCommand(std::move(display_text)));
 }
 
 AccessorySheetData::Builder& AccessorySheetData::Builder::AppendFooterCommand(
-    const base::string16& display_text) & {
-  accessory_sheet_data_.add_footer_command(FooterCommand(display_text));
+    base::string16 display_text) & {
+  accessory_sheet_data_.add_footer_command(
+      FooterCommand(std::move(display_text)));
   return *this;
 }
 
diff --git a/components/autofill/core/browser/ui/accessory_sheet_data.h b/components/autofill/core/browser/ui/accessory_sheet_data.h
index fc77e6360..f0de34f 100644
--- a/components/autofill/core/browser/ui/accessory_sheet_data.h
+++ b/components/autofill/core/browser/ui/accessory_sheet_data.h
@@ -20,8 +20,8 @@
   // number.
   class Field {
    public:
-    Field(const base::string16& display_text,
-          const base::string16& a11y_description,
+    Field(base::string16 display_text,
+          base::string16 a11y_description,
           bool is_obfuscated,
           bool selectable);
     Field(const Field& field);
@@ -74,7 +74,7 @@
 // Represents a command below the suggestions, such as "Manage password...".
 class FooterCommand {
  public:
-  explicit FooterCommand(const base::string16& display_text);
+  explicit FooterCommand(base::string16 display_text);
   FooterCommand(const FooterCommand& footer_command);
   FooterCommand(FooterCommand&& footer_command);
 
@@ -110,7 +110,7 @@
   class Builder;
 
   explicit AccessorySheetData(FallbackSheetType sheet_type,
-                              const base::string16& title);
+                              base::string16 title);
   AccessorySheetData(const AccessorySheetData& data);
   AccessorySheetData(AccessorySheetData&& data);
 
@@ -166,7 +166,7 @@
 //       .Build();
 class AccessorySheetData::Builder {
  public:
-  Builder(FallbackSheetType type, const base::string16& title);
+  Builder(FallbackSheetType type, base::string16 title);
   ~Builder();
 
   // Adds a new UserInfo object to |accessory_sheet_data_|.
@@ -174,22 +174,22 @@
   Builder& AddUserInfo() &;
 
   // Appends a selectable, non-obfuscated field to the last UserInfo object.
-  Builder&& AppendSimpleField(const base::string16& text) &&;
-  Builder& AppendSimpleField(const base::string16& text) &;
+  Builder&& AppendSimpleField(base::string16 text) &&;
+  Builder& AppendSimpleField(base::string16 text) &;
 
   // Appends a field to the last UserInfo object.
-  Builder&& AppendField(const base::string16& display_text,
-                        const base::string16& a11y_description,
+  Builder&& AppendField(base::string16 display_text,
+                        base::string16 a11y_description,
                         bool is_obfuscated,
                         bool selectable) &&;
-  Builder& AppendField(const base::string16& display_text,
-                       const base::string16& a11y_description,
+  Builder& AppendField(base::string16 display_text,
+                       base::string16 a11y_description,
                        bool is_obfuscated,
                        bool selectable) &;
 
   // Appends a new footer command to |accessory_sheet_data_|.
-  Builder&& AppendFooterCommand(const base::string16& display_text) &&;
-  Builder& AppendFooterCommand(const base::string16& display_text) &;
+  Builder&& AppendFooterCommand(base::string16 display_text) &&;
+  Builder& AppendFooterCommand(base::string16 display_text) &;
 
   // This class returns the constructed AccessorySheetData object. Since this
   // would render the builder unusable, it's required to destroy the object
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 6a6886e..ff297d9 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -256,6 +256,12 @@
     <message name="IDS_MANUAL_FILLING_CREDIT_CARD_SHEET_TITLE" desc="Title shown at the top of a sheet where users can select indiviual data pieces (e.g., the cardholder name) related to their stored payment methods (e.g., credit cards) to fill that data piece into the focused form field. Sentence-cased.">
       Payment methods
     </message>
+    <message name="IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE" desc="Message indicating that no addresses are available for filling.">
+      No saved addresses
+    </message>
+    <message name="IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK" desc="The text of the link in the address sheet that opens the address management section.">
+      Manage addresses
+    </message>
   </if>
 
 </grit-part>
diff --git a/components/chromeos_camera/BUILD.gn b/components/chromeos_camera/BUILD.gn
index a60b8e2fc..a9677b1 100644
--- a/components/chromeos_camera/BUILD.gn
+++ b/components/chromeos_camera/BUILD.gn
@@ -142,6 +142,7 @@
       "//base",
       "//base/test:test_support",
       "//media:test_support",
+      "//media/capture:chromeos_test_utils",
       "//media/gpu:buildflags",
       "//media/gpu/test:helpers",
       "//media/parsers",
diff --git a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
index f5bb1607..8077bfb 100644
--- a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
+++ b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
@@ -36,40 +36,37 @@
   // Encodes the given buffer that contains one I420 image.
   // |input_fd| and |output_fd| are file descriptors of shared memory.
   // The image is encoded from memory of |input_fd|
-  // with size |input_buffer_size|. The output buffer is associated with
-  // |buffer_id| and the dimension of I420 image is |coded_size_width| and
+  // with size |input_buffer_size|. |task_id| is used to distinguish different
+  // tasks. The dimension of I420 image is |coded_size_width| and
   // |coded_size_height|.
   // |exif_fd| is the shared memory buffer, with |exif_buffer_size| as size,
   // containing Exif data which will be put onto APP1 segment in the output
   // JPEG image.
   // Encoded JPEG image will be put onto memory associated with |output_fd|
   // with allocated size |output_buffer_size|.
-  // Returns |buffer_id| and |error| in a callback to notify the
-  // encode status. |buffer_id| is the id of |output_buffer| and |status| is the
-  // status code. |encoded_buffer_size| is the actual size of the encoded JPEG.
-  EncodeWithFD(int32 buffer_id, handle input_fd, uint32 input_buffer_size,
+  // Returns |task_id| and |error| in a callback to notify the
+  // encode status. |status| is the status code. |encoded_buffer_size| is the
+  // actual size of the encoded JPEG.
+  EncodeWithFD(int32 task_id, handle input_fd, uint32 input_buffer_size,
                int32 coded_size_width, int32 coded_size_height,
                handle exif_fd, uint32 exif_buffer_size,
                handle output_fd, uint32 output_buffer_size)
-      => (int32 buffer_id, uint32 encoded_buffer_size, EncodeStatus status);
+      => (int32 task_id, uint32 encoded_buffer_size, EncodeStatus status);
 
-  // Encodes the given DMA-buf. The buffer id of output is |buffer_id|. The
-  // size of input image defined in |coded_size_width| and |coded_size_height|
-  // and the format |input_format| represents by its fourcc value. The plane
-  // information of input DMA-buf and output DMA-buf is stored in |input_planes|
-  // and |output_planes| respectively. Although the actual amount of buffers
-  // could be equal to or less than the number of planes, the amount of plane
-  // information |input_planes| and |output_planes| should be same as the number
-  // of planes. |exif_handle| is the shared memory buffer, with
-  // |exif_buffer_size| as size, containing Exif data which will be put onto
-  // APP1 segment in the output JPEG image. When the task ends, it returns
+  // Encodes the given DMA-buf. |task_id| is used to distinguish different
+  // tasks. The size of input image defined in |coded_size_width| and
+  // |coded_size_height| and the format |input_format| represents by its fourcc
+  // value. The plane information of input DMA-buf and output DMA-buf is stored
+  // in |input_planes| and |output_planes| respectively. Although the actual
+  // amount of buffers could be equal to or less than the number of planes, the
+  // amount of plane information |input_planes| and |output_planes| should be
+  // same as the number of planes. |exif_handle| is the shared memory buffer,
+  // with |exif_buffer_size| as size, containing Exif data which will be put
+  // onto APP1 segment in the output JPEG image. When the task ends, it returns
   // |status| as the status code and |encoded_buffer_size| is the actual size of
   // the encoded JPEG.
-  //
-  // TODO(crbug.com/957469): Consider passing more unique identifier rather than
-  // |buffer_id| for JpegEocdeAccelerator
   EncodeWithDmaBuf(
-      int32 buffer_id,
+      int32 task_id,
       uint32 input_format,
       array<DmaBufPlane> input_planes,
       array<DmaBufPlane> output_planes,
diff --git a/components/chromeos_camera/jpeg_encode_accelerator.h b/components/chromeos_camera/jpeg_encode_accelerator.h
index 2f7ca99..0cca112 100644
--- a/components/chromeos_camera/jpeg_encode_accelerator.h
+++ b/components/chromeos_camera/jpeg_encode_accelerator.h
@@ -105,21 +105,22 @@
                       media::BitstreamBuffer output_buffer) = 0;
 
   // Encodes the given |video_frame| that contains a YUV image. Client will
-  // receive the encoded result in Client::VideoFrameReady() callback with the
-  // corresponding |output_buffer.id()|, or receive
-  // Client::NotifyError() callback.
+  // receive the encoded result in Client::VideoFrameReady() callback, or
+  // receive Client::NotifyError() callback.
   // Parameters:
   //  |input_frame| contains the YUV image to be encoded.
   //  |output_frame| is used to represent the output Dma-buf layout.
   //  |quality| of JPEG image. The range is from 1~100. High value means high
   //  quality.
+  //  |task_id| is an identifier started from zero that passed from the client
+  //  side. Could be used to identify different encode tasks.
   //  |exif_buffer| contains Exif data to be inserted into JPEG image. If it's
   //  nullptr, the JFIF APP0 segment will be inserted.
   virtual void EncodeWithDmaBuf(scoped_refptr<media::VideoFrame> input_frame,
                                 scoped_refptr<media::VideoFrame> output_frame,
                                 int quality,
-                                int32_t buffer_id,
-                                const media::BitstreamBuffer* exif_buffer) = 0;
+                                int32_t task_id,
+                                media::BitstreamBuffer* exif_buffer) = 0;
 };
 
 }  // namespace chromeos_camera
diff --git a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
index cea3b569..9b26ac1 100644
--- a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
+++ b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
@@ -26,6 +26,7 @@
 #include "components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h"
 #include "components/chromeos_camera/jpeg_encode_accelerator.h"
 #include "media/base/test_data_util.h"
+#include "media/capture/video/chromeos/local_gpu_memory_buffer_manager.h"
 #include "media/gpu/buildflags.h"
 #include "media/gpu/test/video_accelerator_unittest_helpers.h"
 #include "media/parsers/jpeg_parser.h"
@@ -81,6 +82,31 @@
   ERROR,
 };
 
+scoped_refptr<media::VideoFrame> GetVideoFrameFromGpuMemoryBuffer(
+    gfx::GpuMemoryBuffer* buffer,
+    gfx::Size size,
+    media::VideoPixelFormat format) {
+  auto buffer_handle = buffer->CloneHandle().native_pixmap_handle;
+
+  size_t num_planes = media::VideoFrame::NumPlanes(format);
+  std::vector<media::VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<size_t> buffer_sizes(num_planes);
+  std::vector<base::ScopedFD> fds(num_planes);
+  for (size_t i = 0; i < num_planes; i++) {
+    auto& plane = buffer_handle.planes[i];
+    fds[i] = std::move(plane.fd);
+    planes[i].stride = plane.stride;
+    planes[i].offset = plane.offset;
+    buffer_sizes[i] = plane.size;
+  }
+
+  gfx::Rect visible_rect(size);
+  auto layout = media::VideoFrameLayout::CreateWithPlanes(
+      format, size, std::move(planes), std::move(buffer_sizes));
+  return media::VideoFrame::WrapExternalDmabufs(
+      *layout, visible_rect, size, std::move(fds), base::TimeDelta());
+}
+
 class JpegEncodeAcceleratorTestEnvironment : public ::testing::Environment {
  public:
   JpegEncodeAcceleratorTestEnvironment(
@@ -247,6 +273,7 @@
   void CreateJpegEncoder();
   void DestroyJpegEncoder();
   void StartEncode(int32_t bitstream_buffer_id);
+  void StartEncodeDmaBuf(int32_t bitstream_buffer_id);
 
   // JpegEncodeAccelerator::Client implementation.
   void VideoFrameReady(int32_t buffer_id, size_t encoded_picture_size) override;
@@ -305,6 +332,9 @@
   // Mapped memory of output buffer from software encoder.
   std::unique_ptr<base::SharedMemory> sw_out_shm_;
 
+  // Used to create Gpu memory buffer for DMA-buf encoding tests.
+  std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(JpegClient);
 };
 
@@ -314,7 +344,8 @@
     : test_aligned_images_(test_aligned_images),
       test_images_(test_images),
       state_(ClientState::CREATED),
-      note_(note) {}
+      note_(note),
+      gpu_memory_buffer_manager_(new media::LocalGpuMemoryBufferManager()) {}
 
 JpegClient::~JpegClient() {}
 
@@ -576,6 +607,34 @@
                    std::move(encoded_buffer_));
 }
 
+void JpegClient::StartEncodeDmaBuf(int32_t bitstream_buffer_id) {
+  TestImage* test_image = GetTestImage(bitstream_buffer_id);
+  test_image->output_size =
+      encoder_->GetMaxCodedBufferSize(test_image->visible_size);
+
+  auto input_buffer = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
+      test_image->visible_size, gfx::BufferFormat::YUV_420_BIPLANAR,
+      gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gpu::kNullSurfaceHandle);
+  ASSERT_EQ(input_buffer->Map(), true);
+  memcpy(input_buffer->memory(0), test_image->image_data.data(),
+         test_image->image_data.size());
+  auto input_frame = GetVideoFrameFromGpuMemoryBuffer(
+      input_buffer.get(), test_image->visible_size, media::PIXEL_FORMAT_NV12);
+  LOG_ASSERT(input_frame.get());
+
+  auto output_buffer = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
+      test_image->visible_size, gfx::BufferFormat::R_8,
+      gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE, gpu::kNullSurfaceHandle);
+  ASSERT_EQ(output_buffer->Map(), true);
+  auto output_frame = GetVideoFrameFromGpuMemoryBuffer(
+      output_buffer.get(), test_image->visible_size, media::PIXEL_FORMAT_MJPEG);
+  LOG_ASSERT(output_frame.get());
+
+  buffer_id_to_start_time_[bitstream_buffer_id] = base::TimeTicks::Now();
+  encoder_->EncodeWithDmaBuf(input_frame, output_frame, kJpegDefaultQuality,
+                             bitstream_buffer_id, nullptr);
+}
+
 class JpegEncodeAcceleratorTest : public ::testing::Test {
  protected:
   JpegEncodeAcceleratorTest() {}
@@ -628,6 +687,14 @@
     for (size_t i = 0; i < num_concurrent_encoders; i++) {
       ASSERT_EQ(notes[i]->Wait(), ClientState::ENCODE_PASS);
     }
+    for (size_t i = 0; i < num_concurrent_encoders; i++) {
+      encoder_thread.task_runner()->PostTask(
+          FROM_HERE, base::BindOnce(&JpegClient::StartEncodeDmaBuf,
+                                    base::Unretained(clients[i].get()), index));
+    }
+    for (size_t i = 0; i < num_concurrent_encoders; i++) {
+      ASSERT_EQ(notes[i]->Wait(), ClientState::ENCODE_PASS);
+    }
   }
 
   for (size_t index = 0; index < test_images_.size(); index++) {
@@ -653,6 +720,24 @@
       ASSERT_EQ(notes[i]->Wait(), ClientState::ENCODE_PASS);
 #endif
     }
+
+    for (size_t i = 0; i < num_concurrent_encoders; i++) {
+      encoder_thread.task_runner()->PostTask(
+          FROM_HERE,
+          base::BindOnce(&JpegClient::StartEncode,
+                         base::Unretained(clients[i].get()), buffer_id));
+    }
+
+    for (size_t i = 0; i < num_concurrent_encoders; i++) {
+// For unaligned images, V4L2 may not be able to encode them.
+#if BUILDFLAG(USE_V4L2_CODEC) && defined(ARCH_CPU_ARM_FAMILY)
+      ClientState status = notes[i]->Wait();
+      ASSERT_TRUE(status == ClientState::ENCODE_PASS ||
+                  status == ClientState::ERROR);
+#else
+      ASSERT_EQ(notes[i]->Wait(), ClientState::ENCODE_PASS);
+#endif
+    }
   }
 
   for (size_t i = 0; i < num_concurrent_encoders; i++) {
diff --git a/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
index 0b3c4772..4324d5c 100644
--- a/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
+++ b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
@@ -107,19 +107,19 @@
 }
 
 void MojoJpegEncodeAcceleratorService::VideoFrameReady(
-    int32_t bitstream_buffer_id,
+    int32_t task_id,
     size_t encoded_picture_size) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   NotifyEncodeStatus(
-      bitstream_buffer_id, encoded_picture_size,
+      task_id, encoded_picture_size,
       ::chromeos_camera::JpegEncodeAccelerator::Status::ENCODE_OK);
 }
 
 void MojoJpegEncodeAcceleratorService::NotifyError(
-    int32_t bitstream_buffer_id,
+    int32_t task_id,
     ::chromeos_camera::JpegEncodeAccelerator::Status error) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  NotifyEncodeStatus(bitstream_buffer_id, 0, error);
+  NotifyEncodeStatus(task_id, 0, error);
 }
 
 void MojoJpegEncodeAcceleratorService::Initialize(InitializeCallback callback) {
@@ -151,7 +151,7 @@
 }
 
 void MojoJpegEncodeAcceleratorService::EncodeWithFD(
-    int32_t buffer_id,
+    int32_t task_id,
     mojo::ScopedHandle input_handle,
     uint32_t input_buffer_size,
     int32_t coded_size_width,
@@ -169,7 +169,7 @@
 
   if (coded_size_width <= 0 || coded_size_height <= 0) {
     std::move(callback).Run(
-        buffer_id, 0,
+        task_id, 0,
         ::chromeos_camera::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
     return;
   }
@@ -177,7 +177,7 @@
   result = mojo::UnwrapPlatformFile(std::move(input_handle), &input_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, 0,
+        task_id, 0,
         ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
@@ -185,7 +185,7 @@
   result = mojo::UnwrapPlatformFile(std::move(exif_handle), &exif_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, 0,
+        task_id, 0,
         ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
@@ -193,7 +193,7 @@
   result = mojo::UnwrapPlatformFile(std::move(output_handle), &output_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, 0,
+        task_id, 0,
         ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
@@ -209,33 +209,33 @@
       base::FileDescriptor(output_fd, true), output_buffer_size, output_guid);
 
   media::BitstreamBuffer output_buffer(
-      buffer_id, output_shm_handle, false /* read_only */, output_buffer_size);
+      task_id, output_shm_handle, false /* read_only */, output_buffer_size);
   std::unique_ptr<media::BitstreamBuffer> exif_buffer;
   if (exif_buffer_size > 0) {
     exif_buffer = std::make_unique<media::BitstreamBuffer>(
-        buffer_id, exif_shm_handle, false /* read_only */, exif_buffer_size);
+        task_id, exif_shm_handle, false /* read_only */, exif_buffer_size);
   }
   gfx::Size coded_size(coded_size_width, coded_size_height);
 
-  if (encode_cb_map_.find(buffer_id) != encode_cb_map_.end()) {
-    mojo::ReportBadMessage("buffer_id is already registered in encode_cb_map_");
+  if (encode_cb_map_.find(task_id) != encode_cb_map_.end()) {
+    mojo::ReportBadMessage("task_id is already registered in encode_cb_map_");
     return;
   }
   auto wrapped_callback = base::BindOnce(
-      [](int32_t buffer_id, EncodeWithFDCallback callback,
+      [](int32_t task_id, EncodeWithFDCallback callback,
          uint32_t encoded_picture_size,
          ::chromeos_camera::JpegEncodeAccelerator::Status error) {
-        std::move(callback).Run(buffer_id, encoded_picture_size, error);
+        std::move(callback).Run(task_id, encoded_picture_size, error);
       },
-      buffer_id, std::move(callback));
-  encode_cb_map_.emplace(buffer_id, std::move(wrapped_callback));
+      task_id, std::move(callback));
+  encode_cb_map_.emplace(task_id, std::move(wrapped_callback));
 
   auto input_shm = std::make_unique<base::SharedMemory>(input_shm_handle, true);
   if (!input_shm->Map(input_buffer_size)) {
     DLOG(ERROR) << "Could not map input shared memory for buffer id "
-                << buffer_id;
+                << task_id;
     NotifyEncodeStatus(
-        buffer_id, 0,
+        task_id, 0,
         ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
@@ -253,9 +253,9 @@
           0,                         // data_offset
           base::TimeDelta());        // timestamp
   if (!frame.get()) {
-    LOG(ERROR) << "Could not create VideoFrame for buffer id " << buffer_id;
+    LOG(ERROR) << "Could not create VideoFrame for buffer id " << task_id;
     NotifyEncodeStatus(
-        buffer_id, 0,
+        task_id, 0,
         ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
@@ -270,7 +270,7 @@
 }
 
 void MojoJpegEncodeAcceleratorService::EncodeWithDmaBuf(
-    int32_t buffer_id,
+    int32_t task_id,
     uint32_t input_format,
     std::vector<chromeos_camera::mojom::DmaBufPlanePtr> input_planes,
     std::vector<chromeos_camera::mojom::DmaBufPlanePtr> output_planes,
@@ -285,8 +285,8 @@
         0, ::chromeos_camera::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
     return;
   }
-  if (encode_cb_map_.find(buffer_id) != encode_cb_map_.end()) {
-    mojo::ReportBadMessage("buffer_id is already registered in encode_cb_map_");
+  if (encode_cb_map_.find(task_id) != encode_cb_map_.end()) {
+    mojo::ReportBadMessage("task_id is already registered in encode_cb_map_");
     return;
   }
 
@@ -319,23 +319,25 @@
                                            exif_buffer_size, exif_guid);
   std::unique_ptr<media::BitstreamBuffer> exif_buffer;
   if (exif_buffer_size > 0) {
+    // Currently we use our zero-based |task_id| as id of |exif_buffer| to track
+    // the encode task process from both Chrome OS and Chrome side.
     exif_buffer = std::make_unique<media::BitstreamBuffer>(
-        buffer_id, exif_shm_handle, false /* read_only */, exif_buffer_size);
+        task_id, exif_shm_handle, false /* read_only */, exif_buffer_size);
   }
-  encode_cb_map_.emplace(buffer_id, std::move(callback));
+  encode_cb_map_.emplace(task_id, std::move(callback));
 
   DCHECK(accelerator_);
   accelerator_->EncodeWithDmaBuf(input_video_frame, output_video_frame,
-                                 kJpegQuality, buffer_id, exif_buffer.get());
+                                 kJpegQuality, task_id, exif_buffer.get());
 }
 
 void MojoJpegEncodeAcceleratorService::NotifyEncodeStatus(
-    int32_t bitstream_buffer_id,
+    int32_t task_id,
     size_t encoded_picture_size,
     ::chromeos_camera::JpegEncodeAccelerator::Status error) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  auto iter = encode_cb_map_.find(bitstream_buffer_id);
+  auto iter = encode_cb_map_.find(task_id);
   DCHECK(iter != encode_cb_map_.end());
   EncodeWithDmaBufCallback encode_cb = std::move(iter->second);
   encode_cb_map_.erase(iter);
diff --git a/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h
index 147de0d8..7db5439 100644
--- a/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h
+++ b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h
@@ -30,9 +30,9 @@
   ~MojoJpegEncodeAcceleratorService() override;
 
   // JpegEncodeAccelerator::Client implementation.
-  void VideoFrameReady(int32_t buffer_id, size_t encoded_picture_size) override;
+  void VideoFrameReady(int32_t task_id, size_t encoded_picture_size) override;
   void NotifyError(
-      int32_t buffer_id,
+      int32_t task_id,
       ::chromeos_camera::JpegEncodeAccelerator::Status status) override;
 
  private:
@@ -48,7 +48,7 @@
   void Initialize(InitializeCallback callback) override;
 
   // TODO(wtlee): To be deprecated. (crbug.com/944705)
-  void EncodeWithFD(int32_t buffer_id,
+  void EncodeWithFD(int32_t task_id,
                     mojo::ScopedHandle input_fd,
                     uint32_t input_buffer_size,
                     int32_t coded_size_width,
@@ -60,7 +60,7 @@
                     EncodeWithFDCallback callback) override;
 
   void EncodeWithDmaBuf(
-      int32_t buffer_id,
+      int32_t task_id,
       uint32_t input_format,
       std::vector<chromeos_camera::mojom::DmaBufPlanePtr> input_planes,
       std::vector<chromeos_camera::mojom::DmaBufPlanePtr> output_planes,
@@ -71,14 +71,14 @@
       EncodeWithDmaBufCallback callback) override;
 
   void NotifyEncodeStatus(
-      int32_t bitstream_buffer_id,
+      int32_t task_id,
       size_t encoded_picture_size,
       ::chromeos_camera::JpegEncodeAccelerator::Status status);
 
   const std::vector<GpuJpegEncodeAcceleratorFactory::CreateAcceleratorCB>
       accelerator_factory_functions_;
 
-  // A map from bitstream_buffer_id to EncodeCallback.
+  // A map from task_id to EncodeCallback.
   EncodeCallbackMap encode_cb_map_;
 
   std::unique_ptr<::chromeos_camera::JpegEncodeAccelerator> accelerator_;
diff --git a/components/exo/gaming_seat.cc b/components/exo/gaming_seat.cc
index 7b34453..8a8a3dbd 100644
--- a/components/exo/gaming_seat.cc
+++ b/components/exo/gaming_seat.cc
@@ -63,7 +63,7 @@
 // ui::GamepadObserver overrides:
 
 void GamingSeat::OnGamepadDevicesUpdated() {
-  std::vector<ui::InputDevice> gamepad_devices =
+  std::vector<ui::GamepadDevice> gamepad_devices =
       ui::GamepadProviderOzone::GetInstance()->GetGamepadDevices();
 
   base::flat_map<int, GamepadDelegate*> new_gamepads;
diff --git a/components/exo/gaming_seat_delegate.h b/components/exo/gaming_seat_delegate.h
index b9650723..61d6f53 100644
--- a/components/exo/gaming_seat_delegate.h
+++ b/components/exo/gaming_seat_delegate.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_EXO_GAMING_SEAT_DELEGATE_H_
 
 namespace ui {
-struct InputDevice;
+struct GamepadDevice;
 }  // namespace ui
 
 namespace exo {
@@ -27,7 +27,7 @@
 
   // When a new gamepad is connected, gaming seat call this to get the
   // gamepad delegate.
-  virtual GamepadDelegate* GamepadAdded(const ui::InputDevice& device) = 0;
+  virtual GamepadDelegate* GamepadAdded(const ui::GamepadDevice& device) = 0;
 
  protected:
   virtual ~GamingSeatDelegate() {}
diff --git a/components/exo/gaming_seat_unittest.cc b/components/exo/gaming_seat_unittest.cc
index df35551d..dd153f39 100644
--- a/components/exo/gaming_seat_unittest.cc
+++ b/components/exo/gaming_seat_unittest.cc
@@ -25,7 +25,7 @@
 class MockGamingSeatDelegate : public GamingSeatDelegate {
  public:
   MOCK_CONST_METHOD1(CanAcceptGamepadEventsForSurface, bool(Surface*));
-  MOCK_METHOD1(GamepadAdded, GamepadDelegate*(const ui::InputDevice&));
+  MOCK_METHOD1(GamepadAdded, GamepadDelegate*(const ui::GamepadDevice&));
   MOCK_METHOD0(Die, void());
   void OnGamingSeatDestroying(GamingSeat*) override { delete this; }
   ~MockGamingSeatDelegate() { Die(); }
@@ -55,10 +55,11 @@
   }
 
   void UpdateGamepadDevice(const std::vector<int>& gamepad_device_ids) {
-    std::vector<ui::InputDevice> gamepad_devices;
+    std::vector<ui::GamepadDevice> gamepad_devices;
     for (auto& id : gamepad_device_ids) {
-      gamepad_devices.push_back(ui::InputDevice(
-          id, ui::InputDeviceType::INPUT_DEVICE_USB, "gamepad"));
+      gamepad_devices.emplace_back(
+          ui::InputDevice(id, ui::InputDeviceType::INPUT_DEVICE_USB, "gamepad"),
+          std::vector<ui::GamepadDevice::Axis>());
     }
     ui::GamepadProviderOzone::GetInstance()->DispatchGamepadDevicesUpdated(
         gamepad_devices);
diff --git a/components/exo/wayland/zcr_gaming_input.cc b/components/exo/wayland/zcr_gaming_input.cc
index e7af947..47f9ef9 100644
--- a/components/exo/wayland/zcr_gaming_input.cc
+++ b/components/exo/wayland/zcr_gaming_input.cc
@@ -14,7 +14,7 @@
 #include "components/exo/gaming_seat.h"
 #include "components/exo/gaming_seat_delegate.h"
 #include "components/exo/wayland/server_util.h"
-#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/gamepad_device.h"
 
 namespace exo {
 namespace wayland {
@@ -73,9 +73,18 @@
     if (!gamepad_resource_) {
       return;
     }
-    // TODO(tetsui): Send raw axis index when kRawGamepadInfoFeature is enabled.
-    zcr_gamepad_v2_send_axis(gamepad_resource_, NowInMilliseconds(), axis,
-                             wl_fixed_from_double(value));
+    // There are two types of axis events: Web Gamepad remapped and raw. When
+    // |axis| is not WG_ABS_COUNT, it's Web Gamepad remapped. ExoRawGamepadInfo
+    // expects raw events to be forawrded, so we should discard remapped ones.
+    // TODO(tetsui): Remove this when Web Gamepad mapping in Ozone is removed.
+    if (base::FeatureList::IsEnabled(kRawGamepadInfoFeature) &&
+        axis != ui::WG_ABS_COUNT) {
+      return;
+    }
+    zcr_gamepad_v2_send_axis(
+        gamepad_resource_, NowInMilliseconds(),
+        base::FeatureList::IsEnabled(kRawGamepadInfoFeature) ? raw_axis : axis,
+        wl_fixed_from_double(value));
   }
   void OnButton(int button,
                 int raw_button,
@@ -136,7 +145,7 @@
            wl_resource_get_client(surface_resource) ==
                wl_resource_get_client(gaming_seat_resource_);
   }
-  GamepadDelegate* GamepadAdded(const ui::InputDevice& device) override {
+  GamepadDelegate* GamepadAdded(const ui::GamepadDevice& device) override {
     wl_resource* gamepad_resource =
         wl_resource_create(wl_resource_get_client(gaming_seat_resource_),
                            &zcr_gamepad_v2_interface,
@@ -155,7 +164,13 @@
           GetGamepadBusType(device.type), device.vendor_id, device.product_id,
           device.version);
 
-      // TODO(tetsui): Send joystick motion range.
+      for (size_t i = 0; i < device.axes.size(); ++i) {
+        const auto& axis = device.axes[i];
+        zcr_gamepad_v2_send_axis_added(gamepad_resource, i, axis.min_value,
+                                       axis.max_value, axis.flat, axis.fuzz,
+                                       axis.resolution);
+      }
+      zcr_gamepad_v2_send_activated(gamepad_resource);
     } else {
       zcr_gaming_seat_v2_send_gamepad_added(gaming_seat_resource_,
                                             gamepad_resource);
diff --git a/components/flags_ui/resources/flags.css b/components/flags_ui/resources/flags.css
index 9a855e4..589c80a 100644
--- a/components/flags_ui/resources/flags.css
+++ b/components/flags_ui/resources/flags.css
@@ -313,7 +313,7 @@
   border: 1px solid var(--link-color);
   color: var(--link-color);
   font-size: .8125rem;
-  height: 26px;
+  height: 1.625rem;
   letter-spacing: .01em;
   max-width: 150px;
   text-align-last: center;
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 719fde8..ce2b6d6 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -168,6 +168,8 @@
     "omnibox_pref_names.h",
     "omnibox_view.cc",
     "omnibox_view.h",
+    "on_device_head_provider.cc",
+    "on_device_head_provider.h",
     "on_device_head_serving.cc",
     "on_device_head_serving.h",
     "scored_history_match.cc",
@@ -400,6 +402,7 @@
     "omnibox_pedal_unittest.cc",
     "omnibox_popup_model_unittest.cc",
     "omnibox_view_unittest.cc",
+    "on_device_head_provider_unittest.cc",
     "on_device_head_serving_unittest.cc",
     "scored_history_match_unittest.cc",
     "search_suggestion_parser_unittest.cc",
diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc
index 86c2f984..98a94d5 100644
--- a/components/omnibox/browser/base_search_provider.cc
+++ b/components/omnibox/browser/base_search_provider.cc
@@ -179,8 +179,8 @@
       /*subtype_identifier=*/271, /*from_keyword_provider=*/false, relevance,
       /*relevance_from_server=*/false,
       base::CollapseWhitespace(input.text(), false));
-  // On device providers are synchronous.
-  suggest_result.set_received_after_last_keystroke(false);
+  // On device providers are asynchronous.
+  suggest_result.set_received_after_last_keystroke(true);
   return CreateSearchSuggestion(
       autocomplete_provider, input, /*in_keyword_mode=*/false, suggest_result,
       template_url, search_terms_data, accepted_suggestion,
diff --git a/components/omnibox/browser/on_device_head_provider.cc b/components/omnibox/browser/on_device_head_provider.cc
new file mode 100644
index 0000000..95f942a
--- /dev/null
+++ b/components/omnibox/browser/on_device_head_provider.cc
@@ -0,0 +1,206 @@
+// 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/omnibox/browser/on_device_head_provider.h"
+
+#include <limits>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/i18n/case_conversion.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/trace_event/trace_event.h"
+#include "components/omnibox/browser/autocomplete_provider_listener.h"
+#include "components/omnibox/browser/base_search_provider.h"
+#include "components/search_engines/search_terms_data.h"
+#include "components/search_engines/template_url_service.h"
+
+namespace {
+const int kBaseRelevance = 99;
+const size_t kMaxRequestId = std::numeric_limits<size_t>::max() - 1;
+
+bool IsDefaultSearchProviderGoogle(
+    const TemplateURLService* template_url_service) {
+  if (!template_url_service)
+    return false;
+
+  const TemplateURL* default_provider =
+      template_url_service->GetDefaultSearchProvider();
+  return default_provider &&
+         default_provider->GetEngineType(
+             template_url_service->search_terms_data()) == SEARCH_ENGINE_GOOGLE;
+}
+
+}  // namespace
+
+struct OnDeviceHeadProvider::OnDeviceHeadProviderParams {
+  // The id assigned during request creation, which is used to trace this
+  // request and determine whether it is current or obsolete.
+  const size_t request_id;
+
+  // AutocompleteInput provided by OnDeviceHeadProvider::Start.
+  AutocompleteInput input;
+
+  // The suggestions fetched from the on device model which matches the input.
+  std::vector<std::string> suggestions;
+
+  // Indicates whether this request failed or not.
+  bool failed = false;
+
+  OnDeviceHeadProviderParams(size_t request_id, const AutocompleteInput& input)
+      : request_id(request_id), input(input) {}
+
+  ~OnDeviceHeadProviderParams() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OnDeviceHeadProviderParams);
+};
+
+// static
+OnDeviceHeadProvider* OnDeviceHeadProvider::Create(
+    AutocompleteProviderClient* client,
+    AutocompleteProviderListener* listener) {
+  return new OnDeviceHeadProvider(client, listener);
+}
+
+OnDeviceHeadProvider::OnDeviceHeadProvider(
+    AutocompleteProviderClient* client,
+    AutocompleteProviderListener* listener)
+    : AutocompleteProvider(AutocompleteProvider::TYPE_ON_DEVICE_HEAD),
+      client_(client),
+      listener_(listener),
+      serving_(nullptr),
+      task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      on_device_search_request_id_(0),
+      weak_ptr_factory_(this) {}
+
+OnDeviceHeadProvider::~OnDeviceHeadProvider() {
+  serving_.reset();
+}
+
+bool OnDeviceHeadProvider::IsOnDeviceHeadProviderAllowed(
+    const AutocompleteInput& input) {
+  // Only accept asynchronous request.
+  if (!input.want_asynchronous_matches() ||
+      input.type() == metrics::OmniboxInputType::INVALID)
+    return false;
+
+  // Make sure search suggest is enabled and user is not in incognito.
+  if (client()->IsOffTheRecord() || !client()->SearchSuggestEnabled())
+    return false;
+
+  // Do not proceed if default search provider is not Google.
+  return IsDefaultSearchProviderGoogle(client()->GetTemplateURLService());
+}
+
+void OnDeviceHeadProvider::Start(const AutocompleteInput& input,
+                                 bool minimal_changes) {
+  TRACE_EVENT0("omnibox", "OnDeviceHeadProvider::Start");
+
+  // Cancel any in-progress request.
+  Stop(!minimal_changes, false);
+
+  if (!IsOnDeviceHeadProviderAllowed(input)) {
+    matches_.clear();
+    return;
+  }
+
+  // If the input text has not changed, the result can be reused.
+  if (minimal_changes)
+    return;
+
+  matches_.clear();
+  if (!input.text().empty() && serving_) {
+    done_ = false;
+    // Note |on_device_search_request_id_| has already been changed in |Stop|
+    // so we don't need to change it again here to get a new id for this
+    // request.
+    std::unique_ptr<OnDeviceHeadProviderParams> params = base::WrapUnique(
+        new OnDeviceHeadProviderParams(on_device_search_request_id_, input));
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&OnDeviceHeadProvider::DoSearch,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(params)));
+  }
+}
+
+void OnDeviceHeadProvider::Stop(bool clear_cached_results,
+                                bool due_to_user_inactivity) {
+  // Increase the request_id so that any in-progress requests will become
+  // obsolete.
+  on_device_search_request_id_ =
+      (on_device_search_request_id_ + 1) % kMaxRequestId;
+
+  if (clear_cached_results)
+    matches_.clear();
+
+  done_ = true;
+}
+
+bool OnDeviceHeadProvider::CreateOnDeviceHeadServingInstance() {
+  // TODO(crbug.com/925072): A placeholder later will be used to create
+  // serving instance from downloaded model.
+  return serving_ ? true : false;
+}
+
+void OnDeviceHeadProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
+  provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo());
+  metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back();
+  new_entry.set_provider(metrics::OmniboxEventProto::ON_DEVICE_HEAD);
+  new_entry.set_provider_done(done_);
+}
+
+void OnDeviceHeadProvider::DoSearch(
+    std::unique_ptr<OnDeviceHeadProviderParams> params) {
+  if (serving_ && params &&
+      params->request_id == on_device_search_request_id_) {
+    // TODO(crbug.com/925072): Add model search time to UMA.
+    base::string16 trimmed_input;
+    base::TrimWhitespace(params->input.text(), base::TRIM_ALL, &trimmed_input);
+    auto results = serving_->GetSuggestionsForPrefix(
+        base::UTF16ToUTF8(base::i18n::ToLower(trimmed_input)));
+    params->suggestions.clear();
+    for (const auto& item : results) {
+      // The second member is the score which is not useful for provider.
+      params->suggestions.push_back(item.first);
+    }
+  } else {
+    params->failed = true;
+  }
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&OnDeviceHeadProvider::SearchDone,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(params)));
+}
+
+void OnDeviceHeadProvider::SearchDone(
+    std::unique_ptr<OnDeviceHeadProviderParams> params) {
+  TRACE_EVENT0("omnibox", "OnDeviceHeadProvider::SearchDone");
+  // Ignore this request if it has been stopped or a new one has already been
+  // created.
+  if (!params || params->request_id != on_device_search_request_id_)
+    return;
+
+  const TemplateURLService* template_url_service =
+      client()->GetTemplateURLService();
+
+  if (IsDefaultSearchProviderGoogle(template_url_service) && !params->failed) {
+    matches_.clear();
+    int relevance = kBaseRelevance;
+    for (const auto& item : params->suggestions) {
+      matches_.push_back(BaseSearchProvider::CreateOnDeviceSearchSuggestion(
+          /*autocomplete_provider=*/this, /*input=*/params->input,
+          /*suggestion=*/base::UTF8ToUTF16(item), /*relevance=*/relevance--,
+          /*template_url=*/
+          template_url_service->GetDefaultSearchProvider(),
+          /*search_terms_data=*/
+          template_url_service->search_terms_data(),
+          /*accepted_suggestion=*/TemplateURLRef::NO_SUGGESTION_CHOSEN));
+    }
+  }
+
+  done_ = true;
+  listener_->OnProviderUpdate(true);
+}
diff --git a/components/omnibox/browser/on_device_head_provider.h b/components/omnibox/browser/on_device_head_provider.h
new file mode 100644
index 0000000..396e620
--- /dev/null
+++ b/components/omnibox/browser/on_device_head_provider.h
@@ -0,0 +1,79 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OMNIBOX_BROWSER_ON_DEVICE_HEAD_PROVIDER_H_
+#define COMPONENTS_OMNIBOX_BROWSER_ON_DEVICE_HEAD_PROVIDER_H_
+
+#include <memory>
+
+#include "components/omnibox/browser/autocomplete_provider.h"
+#include "components/omnibox/browser/autocomplete_provider_client.h"
+#include "components/omnibox/browser/on_device_head_serving.h"
+
+class AutocompleteProviderListener;
+
+// An asynchronous autocomplete provider which receives input string and tries
+// to find the matches in an on device head model. This provider is designed to
+// help users get suggestions when they are in poor network.
+// All matches provided by this provider will have a relevance no greater than
+// 99, such that its matches will not show before any other providers.
+class OnDeviceHeadProvider : public AutocompleteProvider {
+ public:
+  static OnDeviceHeadProvider* Create(AutocompleteProviderClient* client,
+                                      AutocompleteProviderListener* listener);
+
+  void Start(const AutocompleteInput& input, bool minimal_changes) override;
+  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void AddProviderInfo(ProvidersInfo* provider_info) const override;
+
+  // Creates the on device head serving service from a local head model.
+  // Returns true if the creation is successful.
+  bool CreateOnDeviceHeadServingInstance();
+
+  AutocompleteProviderClient* client() { return client_; }
+
+ private:
+  friend class OnDeviceHeadProviderTest;
+
+  // A useful data structure to store Autocomplete input and suggestions fetched
+  // from the on device head model for a search request to the model.
+  struct OnDeviceHeadProviderParams;
+
+  OnDeviceHeadProvider(AutocompleteProviderClient* client,
+                       AutocompleteProviderListener* listener);
+  ~OnDeviceHeadProvider() override;
+
+  bool IsOnDeviceHeadProviderAllowed(const AutocompleteInput& input);
+
+  // Helper functions used for asynchronous search to the on device head model.
+  // The Autocomplete input and output from the model will be passed from
+  // DoSearch to SearchDone via the OnDeviceHeadProviderParams object.
+  // DoSearch: searches the on device model and returns the tops suggestions
+  // matches the given AutocompleteInput.
+  void DoSearch(std::unique_ptr<OnDeviceHeadProviderParams> params);
+  // SearchDone: called after DoSearch, fills |matches_| with the suggestions
+  // fetches by DoSearch and then calls OnProviderUpdate.
+  void SearchDone(std::unique_ptr<OnDeviceHeadProviderParams> params);
+
+  AutocompleteProviderClient* client_;
+  AutocompleteProviderListener* listener_;
+
+  // The instance which does the search in the head model and returns top
+  // suggestions matching the Autocomplete input.
+  std::unique_ptr<OnDeviceHeadServing> serving_;
+
+  // The task runner instance where asynchronous searches to the head model will
+  // be run.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // The request id used to trace current request to the on device head model.
+  // The id will be increased whenever a new request is received from the
+  // AutocompleteController.
+  size_t on_device_search_request_id_;
+  base::WeakPtrFactory<OnDeviceHeadProvider> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OnDeviceHeadProvider);
+};
+
+#endif  // COMPONENTS_OMNIBOX_BROWSER_ON_DEVICE_HEAD_PROVIDER_H_
diff --git a/components/omnibox/browser/on_device_head_provider_unittest.cc b/components/omnibox/browser/on_device_head_provider_unittest.cc
new file mode 100644
index 0000000..ef636c9
--- /dev/null
+++ b/components/omnibox/browser/on_device_head_provider_unittest.cc
@@ -0,0 +1,181 @@
+// 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/omnibox/browser/on_device_head_provider.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_provider_listener.h"
+#include "components/omnibox/browser/fake_autocomplete_provider_client.h"
+#include "components/omnibox/browser/on_device_head_serving.h"
+#include "components/omnibox/browser/test_scheme_classifier.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+class OnDeviceHeadProviderTest : public testing::Test,
+                                 public AutocompleteProviderListener {
+ protected:
+  void SetUp() override {
+    client_.reset(new FakeAutocompleteProviderClient());
+    provider_ = OnDeviceHeadProvider::Create(client_.get(), this);
+  }
+
+  void TearDown() override {
+    provider_ = nullptr;
+    client_.reset();
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  // AutocompleteProviderListener:
+  void OnProviderUpdate(bool updated_matches) override {
+    // No action required.
+  }
+
+  void SetTestOnDeviceHeadServing() {
+    base::FilePath file_path;
+    base::PathService::Get(base::DIR_SOURCE_ROOT, &file_path);
+    // The same test model also used in ./on_device_head_serving_unittest.cc.
+    file_path = file_path.AppendASCII(
+        "components/test/data/omnibox/on_device_head_test_model.bin");
+    ASSERT_TRUE(base::PathExists(file_path));
+#if defined(OS_WIN)
+    provider_->serving_ =
+        OnDeviceHeadServing::Create(base::WideToUTF8(file_path.value()), 3);
+#else
+    provider_->serving_ = OnDeviceHeadServing::Create(file_path.value(), 3);
+#endif
+    ASSERT_TRUE(provider_->serving_);
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<FakeAutocompleteProviderClient> client_;
+  scoped_refptr<OnDeviceHeadProvider> provider_;
+};
+
+TEST_F(OnDeviceHeadProviderTest, ServingInstanceNotCreated) {
+  AutocompleteInput input(base::UTF8ToUTF16("a"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  input.set_want_asynchronous_matches(true);
+
+  EXPECT_CALL(*client_.get(), IsOffTheRecord()).WillOnce(Return(false));
+  EXPECT_CALL(*client_.get(), SearchSuggestEnabled()).WillOnce(Return(true));
+
+  provider_->Start(input, false);
+  if (!provider_->done())
+    base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(provider_->matches().empty());
+  EXPECT_TRUE(provider_->done());
+}
+
+TEST_F(OnDeviceHeadProviderTest, RejectSynchronousRequest) {
+  AutocompleteInput input(base::UTF8ToUTF16("a"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  input.set_want_asynchronous_matches(false);
+
+  SetTestOnDeviceHeadServing();
+  provider_->Start(input, false);
+  if (!provider_->done())
+    base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(provider_->matches().empty());
+  EXPECT_TRUE(provider_->done());
+}
+
+TEST_F(OnDeviceHeadProviderTest, RejectIncognito) {
+  AutocompleteInput input(base::UTF8ToUTF16("a"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  input.set_want_asynchronous_matches(true);
+
+  EXPECT_CALL(*client_.get(), IsOffTheRecord()).WillOnce(Return(true));
+
+  SetTestOnDeviceHeadServing();
+  provider_->Start(input, false);
+  if (!provider_->done())
+    base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(provider_->matches().empty());
+  EXPECT_TRUE(provider_->done());
+}
+
+TEST_F(OnDeviceHeadProviderTest, NoMatches) {
+  AutocompleteInput input(base::UTF8ToUTF16("b"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  input.set_want_asynchronous_matches(true);
+
+  EXPECT_CALL(*client_.get(), IsOffTheRecord()).WillOnce(Return(false));
+  EXPECT_CALL(*client_.get(), SearchSuggestEnabled()).WillOnce(Return(true));
+
+  SetTestOnDeviceHeadServing();
+  provider_->Start(input, false);
+  if (!provider_->done())
+    base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(provider_->matches().empty());
+  EXPECT_TRUE(provider_->done());
+}
+
+TEST_F(OnDeviceHeadProviderTest, HasMatches) {
+  AutocompleteInput input(base::UTF8ToUTF16("M"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+  input.set_want_asynchronous_matches(true);
+
+  EXPECT_CALL(*client_.get(), IsOffTheRecord()).WillOnce(Return(false));
+  EXPECT_CALL(*client_.get(), SearchSuggestEnabled()).WillOnce(Return(true));
+
+  SetTestOnDeviceHeadServing();
+  provider_->Start(input, false);
+  if (!provider_->done())
+    base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(provider_->done());
+  ASSERT_EQ(3U, provider_->matches().size());
+  EXPECT_EQ(base::UTF8ToUTF16("maps"), provider_->matches()[0].contents);
+  EXPECT_EQ(base::UTF8ToUTF16("mail"), provider_->matches()[1].contents);
+  EXPECT_EQ(base::UTF8ToUTF16("map"), provider_->matches()[2].contents);
+}
+
+TEST_F(OnDeviceHeadProviderTest, CancelInProgressRequest) {
+  AutocompleteInput input1(base::UTF8ToUTF16("g"),
+                           metrics::OmniboxEventProto::OTHER,
+                           TestSchemeClassifier());
+  input1.set_want_asynchronous_matches(true);
+  AutocompleteInput input2(base::UTF8ToUTF16("m"),
+                           metrics::OmniboxEventProto::OTHER,
+                           TestSchemeClassifier());
+  input2.set_want_asynchronous_matches(true);
+
+  EXPECT_CALL(*client_.get(), IsOffTheRecord()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*client_.get(), SearchSuggestEnabled())
+      .WillRepeatedly(Return(true));
+
+  SetTestOnDeviceHeadServing();
+  provider_->Start(input1, false);
+  EXPECT_FALSE(provider_->done());
+  provider_->Start(input2, false);
+
+  if (!provider_->done())
+    base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(provider_->done());
+  ASSERT_EQ(3U, provider_->matches().size());
+  EXPECT_EQ(base::UTF8ToUTF16("maps"), provider_->matches()[0].contents);
+  EXPECT_EQ(base::UTF8ToUTF16("mail"), provider_->matches()[1].contents);
+  EXPECT_EQ(base::UTF8ToUTF16("map"), provider_->matches()[2].contents);
+}
diff --git a/components/omnibox/browser/on_device_head_serving.cc b/components/omnibox/browser/on_device_head_serving.cc
index fde794f..eed3a5a9 100644
--- a/components/omnibox/browser/on_device_head_serving.cc
+++ b/components/omnibox/browser/on_device_head_serving.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 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.
 
diff --git a/components/omnibox/browser/on_device_head_serving.h b/components/omnibox/browser/on_device_head_serving.h
index fc98895..5cf7a8b 100644
--- a/components/omnibox/browser/on_device_head_serving.h
+++ b/components/omnibox/browser/on_device_head_serving.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 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.
 
diff --git a/components/omnibox/browser/on_device_head_serving_unittest.cc b/components/omnibox/browser/on_device_head_serving_unittest.cc
index 5761d88..ef043ac 100644
--- a/components/omnibox/browser/on_device_head_serving_unittest.cc
+++ b/components/omnibox/browser/on_device_head_serving_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 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.
 
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index cc2d5776..ff564c0 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -31,6 +31,7 @@
 #include "components/autofill/core/common/password_form.h"
 #include "components/os_crypt/os_crypt.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_manager_util.h"
@@ -233,6 +234,10 @@
   LogDynamicUMAStat(name, sample, 0, 32, 6);
 }
 
+void LogAccountStatHiRes(const std::string& name, int sample) {
+  LogDynamicUMAStat(name, sample, 0, 1000, 100);
+}
+
 void LogTimesUsedStat(const std::string& name, int sample) {
   LogDynamicUMAStat(name, sample, 0, 100, 10);
 }
@@ -759,10 +764,9 @@
   if (!s.is_valid())
     return;
 
-  std::string custom_passphrase = "WithoutCustomPassphrase";
-  if (custom_passphrase_sync_enabled) {
-    custom_passphrase = "WithCustomPassphrase";
-  }
+  std::string custom_passphrase = custom_passphrase_sync_enabled
+                                      ? "WithCustomPassphrase"
+                                      : "WithoutCustomPassphrase";
 
   int total_user_created_accounts = 0;
   int total_generated_accounts = 0;
@@ -787,18 +791,27 @@
                              custom_passphrase.c_str()),
           accounts_per_site);
     }
+    // PasswordManager.AccountsPerSite.Overall is not recorded here even though
+    // the histogram suffixes suggest it. We may add it if we need it.
   }
-  LogAccountStat(
-      base::StringPrintf("PasswordManager.TotalAccounts.UserCreated.%s",
-                         custom_passphrase.c_str()),
+  LogAccountStatHiRes(
+      base::StringPrintf(
+          "PasswordManager.TotalAccountsHiRes.ByType.UserCreated.%s",
+          custom_passphrase.c_str()),
       total_user_created_accounts);
-  LogAccountStat(
-      base::StringPrintf("PasswordManager.TotalAccounts.AutoGenerated.%s",
-                         custom_passphrase.c_str()),
+  LogAccountStatHiRes(
+      base::StringPrintf(
+          "PasswordManager.TotalAccountsHiRes.ByType.AutoGenerated.%s",
+          custom_passphrase.c_str()),
       total_generated_accounts);
-  LogAccountStat(base::StringPrintf("PasswordManager.BlacklistedSites.%s",
-                                    custom_passphrase.c_str()),
-                 blacklisted_sites);
+  LogAccountStatHiRes(
+      base::StringPrintf("PasswordManager.TotalAccountsHiRes.ByType.Overall.%s",
+                         custom_passphrase.c_str()),
+      total_user_created_accounts + total_generated_accounts);
+  LogAccountStatHiRes(
+      base::StringPrintf("PasswordManager.BlacklistedSitesHiRes.%s",
+                         custom_passphrase.c_str()),
+      blacklisted_sites);
 
   sql::Statement usage_statement(db_.GetCachedStatement(
       SQL_FROM_HERE, "SELECT password_type, times_used FROM logins"));
@@ -821,6 +834,8 @@
                              custom_passphrase.c_str()),
           usage_statement.ColumnInt(1));
     }
+    // PasswordManager.TimesPasswordUsed.Overall is not implemented even though
+    // the histogram suffixes forsee it. We can add it if necessary.
   }
 
   bool syncing_account_saved = false;
@@ -907,6 +922,22 @@
   LogNumberOfAccountsForScheme("Https", https_logins);
   LogNumberOfAccountsForScheme("Other", other_logins);
 
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  // Number of times the user needs to dismiss a save/update bubble for it to
+  // not be shown again. This happens only on desktop platforms.
+  int threshold =
+      password_bubble_experiment::GetSmartBubbleDismissalThreshold();
+  LogAccountStatHiRes(
+      "PasswordManager.BubbleSuppression.DomainsWithSuppressedBubble",
+      stats_table_.GetNumDomainsWithAtLeastNDismissals(threshold));
+  LogAccountStatHiRes(
+      "PasswordManager.BubbleSuppression.AccountsWithSuppressedBubble",
+      stats_table_.GetNumAccountsWithAtLeastNDismissals(threshold));
+  LogAccountStatHiRes(
+      "PasswordManager.BubbleSuppression.AccountsInStatisticsTable",
+      stats_table_.GetNumAccounts());
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
+
   sql::Statement saved_passwords_statement(
       db_.GetUniqueStatement("SELECT signon_realm, password_value, scheme "
                              "FROM logins WHERE blacklisted_by_user = 0"));
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index a62b732d..f272765a8 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -1465,12 +1465,30 @@
   EXPECT_EQ(AddChangeForForm(password_form),
             db().AddBlacklistedLoginForTesting(password_form));
 
+  StatisticsTable& stats_table = db().stats_table();
+  InteractionsStats stats;
+  stats.origin_domain = GURL("https://example.com");
+  stats.username_value = base::ASCIIToUTF16("user1");
+  stats.dismissal_count = 10;
+  stats.update_time = base::Time::FromTimeT(1);
+  EXPECT_TRUE(stats_table.AddRow(stats));
+  stats.username_value = base::ASCIIToUTF16("user2");
+  stats.dismissal_count = 1;
+  EXPECT_TRUE(stats_table.AddRow(stats));
+  stats.username_value = base::ASCIIToUTF16("user3");
+  stats.dismissal_count = 10;
+  EXPECT_TRUE(stats_table.AddRow(stats));
+  stats.origin_domain = GURL("https://foo.com");
+  stats.dismissal_count = 10;
+  EXPECT_TRUE(stats_table.AddRow(stats));
+
   base::HistogramTester histogram_tester;
   db().ReportMetrics("", false);
 
   histogram_tester.ExpectUniqueSample(
-      "PasswordManager.TotalAccounts.UserCreated.WithoutCustomPassphrase", 9,
-      1);
+      "PasswordManager.TotalAccountsHiRes.ByType.UserCreated."
+      "WithoutCustomPassphrase",
+      9, 1);
   histogram_tester.ExpectBucketCount(
       "PasswordManager.AccountsPerSite.UserCreated.WithoutCustomPassphrase", 1,
       2);
@@ -1487,8 +1505,9 @@
       "PasswordManager.TimesPasswordUsed.UserCreated.WithoutCustomPassphrase",
       3, 1);
   histogram_tester.ExpectUniqueSample(
-      "PasswordManager.TotalAccounts.AutoGenerated.WithoutCustomPassphrase", 2,
-      1);
+      "PasswordManager.TotalAccountsHiRes.ByType.AutoGenerated."
+      "WithoutCustomPassphrase",
+      2, 1);
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.TotalAccountsHiRes.WithScheme.Android", 2, 1);
   histogram_tester.ExpectUniqueSample(
@@ -1514,6 +1533,14 @@
       "PasswordManager.EmptyUsernames.WithoutCorrespondingNonempty", 1, 1);
   histogram_tester.ExpectUniqueSample("PasswordManager.InaccessiblePasswords",
                                       0, 1);
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.BubbleSuppression.DomainsWithSuppressedBubble", 2, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.BubbleSuppression.AccountsWithSuppressedBubble", 3, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.BubbleSuppression.AccountsInStatisticsTable", 4, 1);
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
 }
 
 TEST_F(LoginDatabaseTest, PasswordReuseMetrics) {
diff --git a/components/password_manager/core/browser/statistics_table.cc b/components/password_manager/core/browser/statistics_table.cc
index d9105d4..1cf0b6d7 100644
--- a/components/password_manager/core/browser/statistics_table.cc
+++ b/components/password_manager/core/browser/statistics_table.cc
@@ -187,4 +187,28 @@
   return success;
 }
 
+int StatisticsTable::GetNumDomainsWithAtLeastNDismissals(int64_t n) {
+  sql::Statement select_statement(
+      db_->GetCachedStatement(SQL_FROM_HERE,
+                              "SELECT COUNT(DISTINCT origin_domain) FROM stats "
+                              "WHERE dismissal_count >= ?"));
+  select_statement.BindInt64(0, n);
+  return select_statement.Step() ? select_statement.ColumnInt(0) : 0u;
+}
+
+int StatisticsTable::GetNumAccountsWithAtLeastNDismissals(int64_t n) {
+  sql::Statement select_statement(
+      db_->GetCachedStatement(SQL_FROM_HERE,
+                              "SELECT COUNT(1) FROM stats "
+                              "WHERE dismissal_count >= ?"));
+  select_statement.BindInt64(0, n);
+  return select_statement.Step() ? select_statement.ColumnInt(0) : 0u;
+}
+
+int StatisticsTable::GetNumAccounts() {
+  sql::Statement select_statement(
+      db_->GetCachedStatement(SQL_FROM_HERE, "SELECT COUNT(1) FROM stats"));
+  return select_statement.Step() ? select_statement.ColumnInt(0) : 0u;
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/statistics_table.h b/components/password_manager/core/browser/statistics_table.h
index b24389e0..c47ed74a 100644
--- a/components/password_manager/core/browser/statistics_table.h
+++ b/components/password_manager/core/browser/statistics_table.h
@@ -80,6 +80,17 @@
       base::Time delete_begin,
       base::Time delete_end);
 
+  // Returns the number of distinct domains for which at least one account has
+  // |n| or more dismissals.
+  int GetNumDomainsWithAtLeastNDismissals(int64_t n);
+
+  // Returns the number of distinct accounts for which have at least |n| or more
+  // dismissals.
+  int GetNumAccountsWithAtLeastNDismissals(int64_t n);
+
+  // Returns the number of rows (origin/username pairs) in the table.
+  int GetNumAccounts();
+
  private:
   sql::Database* db_;
 
diff --git a/components/password_manager/core/browser/statistics_table_unittest.cc b/components/password_manager/core/browser/statistics_table_unittest.cc
index 59abc66..b6808151 100644
--- a/components/password_manager/core/browser/statistics_table_unittest.cc
+++ b/components/password_manager/core/browser/statistics_table_unittest.cc
@@ -21,6 +21,7 @@
 const char kTestDomain4[] = "http://localhost";
 const char kUsername1[] = "user1";
 const char kUsername2[] = "user2";
+const char kUsername3[] = "user3";
 
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
@@ -183,5 +184,35 @@
   EXPECT_FALSE(db()->RemoveRow(test_data().origin_domain));
 }
 
+TEST_F(StatisticsTableTest, GetDomainsAndAccountsDomainsWithNDismissals) {
+  struct {
+    const char* origin;
+    const char* username;
+    int dismissal_count;
+  } const stats_database_entries[] = {
+      {kTestDomain, kUsername1, 10},   // A
+      {kTestDomain, kUsername2, 10},   // B
+      {kTestDomain, kUsername3, 1},    // C
+      {kTestDomain2, kUsername1, 1},   // D
+      {kTestDomain3, kUsername1, 10},  // E
+  };
+  for (const auto& entry : stats_database_entries) {
+    EXPECT_TRUE(db()->AddRow({
+        .origin_domain = GURL(entry.origin),
+        .username_value = base::ASCIIToUTF16(entry.username),
+        .dismissal_count = entry.dismissal_count,
+        .update_time = base::Time::FromTimeT(1),
+    }));
+  }
+
+  EXPECT_EQ(5, db()->GetNumAccounts());  // A,B,C,D,E
+
+  EXPECT_EQ(3, db()->GetNumDomainsWithAtLeastNDismissals(1));   // (A,B,C), D, E
+  EXPECT_EQ(2, db()->GetNumDomainsWithAtLeastNDismissals(10));  // (A,B), E
+
+  EXPECT_EQ(5, db()->GetNumAccountsWithAtLeastNDismissals(1));   // A,B,C,D,E
+  EXPECT_EQ(3, db()->GetNumAccountsWithAtLeastNDismissals(10));  // A,B,E
+}
+
 }  // namespace
 }  // namespace password_manager
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index 368c3cc..b33e0a0 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -325,7 +325,10 @@
   }
 
   if (is_android) {
-    sources += [ "consistency_cookie_manager_unittest.cc" ]
+    sources += [
+      "consistency_cookie_manager_unittest.cc",
+      "oauth2_token_service_delegate_android_unittest.cc",
+    ]
   }
 
   if (!enable_dice_support) {
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java
index eb30f20..c05c6502 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java
@@ -348,7 +348,12 @@
     }
 
     @CalledByNative
-    private static void saveStoredAccounts(String[] accounts) {
+    /**
+     * Called by native to save the account IDs that have associated OAuth2 refresh tokens.
+     * This is called during updateAccountList to sync with getSystemAccountNames.
+     * @param accounts IDs to save.
+     */
+    private static void setAccounts(String[] accounts) {
         Set<String> set = new HashSet<>(Arrays.asList(accounts));
         ContextUtils.getAppSharedPreferences()
                 .edit()
diff --git a/components/signin/core/browser/oauth2_token_service_delegate_android.cc b/components/signin/core/browser/oauth2_token_service_delegate_android.cc
index 575b6123..386c6dc 100644
--- a/components/signin/core/browser/oauth2_token_service_delegate_android.cc
+++ b/components/signin/core/browser/oauth2_token_service_delegate_android.cc
@@ -150,16 +150,14 @@
   if (account_tracker_service_->GetMigrationState() ==
       AccountTrackerService::MIGRATION_IN_PROGRESS) {
     std::vector<std::string> accounts = GetAccounts();
-    std::vector<std::string> accounts_id;
+    std::vector<CoreAccountId> accounts_id;
     for (auto account_name : accounts) {
       AccountInfo account_info =
           account_tracker_service_->FindAccountInfoByEmail(account_name);
       DCHECK(!account_info.gaia.empty());
       accounts_id.push_back(account_info.gaia);
     }
-    ScopedJavaLocalRef<jobjectArray> java_accounts(
-        base::android::ToJavaArrayOfStrings(env, accounts_id));
-    Java_OAuth2TokenService_saveStoredAccounts(env, java_accounts);
+    SetAccounts(accounts_id);
   }
 
   if (!disable_interaction_with_system_accounts_) {
@@ -244,6 +242,36 @@
   return account_names;
 }
 
+std::vector<CoreAccountId>
+OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() {
+  std::vector<CoreAccountId> ids;
+  for (const std::string& name : GetSystemAccountNames()) {
+    CoreAccountId id(MapAccountNameToAccountId(name));
+    if (!id.empty())
+      ids.push_back(std::move(id));
+  }
+  return ids;
+}
+
+std::vector<CoreAccountId>
+OAuth2TokenServiceDelegateAndroid::GetValidAccounts() {
+  std::vector<CoreAccountId> ids;
+  for (const std::string& id : GetAccounts()) {
+    if (ValidateAccountId(id))
+      ids.emplace_back(id);
+  }
+  return ids;
+}
+
+void OAuth2TokenServiceDelegateAndroid::SetAccounts(
+    const std::vector<CoreAccountId>& accounts) {
+  JNIEnv* env = AttachCurrentThread();
+  std::vector<std::string> str_ids(accounts.begin(), accounts.end());
+  ScopedJavaLocalRef<jobjectArray> java_accounts(
+      base::android::ToJavaArrayOfStrings(env, str_ids));
+  Java_OAuth2TokenService_setAccounts(env, java_accounts);
+}
+
 OAuth2AccessTokenFetcher*
 OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher(
     const std::string& account_id,
@@ -284,52 +312,33 @@
   // Clear any auth errors so that client can retry to get access tokens.
   errors_.clear();
 
-  UpdateAccountList(MapAccountNameToAccountId(signed_in_account_name));
+  UpdateAccountList(MapAccountNameToAccountId(signed_in_account_name),
+                    GetValidAccounts(), GetSystemAccounts());
 }
 
 void OAuth2TokenServiceDelegateAndroid::UpdateAccountList(
-    const std::string& signed_in_account_id) {
-  std::vector<std::string> curr_ids;
-  for (const std::string& curr_name : GetSystemAccountNames()) {
-    std::string curr_id(MapAccountNameToAccountId(curr_name));
-    if (!curr_id.empty())
-      curr_ids.push_back(curr_id);
-  }
-
-  std::vector<std::string> prev_ids;
-  for (const std::string& prev_id : GetAccounts()) {
-    if (ValidateAccountId(prev_id))
-      prev_ids.push_back(prev_id);
-  }
-
+    const CoreAccountId& signed_in_account_id,
+    const std::vector<CoreAccountId>& prev_ids,
+    const std::vector<CoreAccountId>& curr_ids) {
   DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:"
            << " sigined_in_account_id=" << signed_in_account_id
            << " prev_ids=" << prev_ids.size()
            << " curr_ids=" << curr_ids.size();
 
-  std::vector<std::string> refreshed_ids;
-  std::vector<std::string> revoked_ids;
+  std::vector<CoreAccountId> refreshed_ids;
+  std::vector<CoreAccountId> revoked_ids;
   bool keep_accounts = UpdateAccountList(
       signed_in_account_id, prev_ids, curr_ids, &refreshed_ids, &revoked_ids);
 
   ScopedBatchChange batch(this);
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobjectArray> java_accounts;
-  if (keep_accounts) {
-    java_accounts = base::android::ToJavaArrayOfStrings(env, curr_ids);
-  } else {
-    DCHECK(!base::FeatureList::IsEnabled(signin::kMiceFeature));
-    java_accounts =
-        base::android::ToJavaArrayOfStrings(env, std::vector<std::string>());
-  }
 
   // Save the current accounts in the token service before calling
   // FireRefreshToken* methods.
-  Java_OAuth2TokenService_saveStoredAccounts(env, java_accounts);
+  SetAccounts(keep_accounts ? curr_ids : std::vector<CoreAccountId>());
 
-  for (const std::string& refreshed_id : refreshed_ids)
+  for (const CoreAccountId& refreshed_id : refreshed_ids)
     FireRefreshTokenAvailable(refreshed_id);
-  for (const std::string& revoked_id : revoked_ids)
+  for (const CoreAccountId& revoked_id : revoked_ids)
     FireRefreshTokenRevoked(revoked_id);
   if (fire_refresh_token_loaded_ == RT_WAIT_FOR_VALIDATION) {
     fire_refresh_token_loaded_ = RT_LOADED;
@@ -338,7 +347,7 @@
     fire_refresh_token_loaded_ = RT_HAS_BEEN_VALIDATED;
   }
 
-  // Clear accounts no longer exist on device from AccountTrackerService.
+  // Clear accounts that no longer exist on device from AccountTrackerService.
   std::vector<AccountInfo> accounts_info =
       account_tracker_service_->GetAccounts();
   for (const AccountInfo& info : accounts_info) {
@@ -361,16 +370,16 @@
 }
 
 bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList(
-    const std::string& signed_in_id,
-    const std::vector<std::string>& prev_ids,
-    const std::vector<std::string>& curr_ids,
-    std::vector<std::string>* refreshed_ids,
-    std::vector<std::string>* revoked_ids) {
+    const CoreAccountId& signed_in_id,
+    const std::vector<CoreAccountId>& prev_ids,
+    const std::vector<CoreAccountId>& curr_ids,
+    std::vector<CoreAccountId>* refreshed_ids,
+    std::vector<CoreAccountId>* revoked_ids) {
   bool keep_accounts = base::FeatureList::IsEnabled(signin::kMiceFeature) ||
                        base::ContainsValue(curr_ids, signed_in_id);
   if (keep_accounts) {
     // Revoke token for ids that have been removed from the device.
-    for (const std::string& prev_id : prev_ids) {
+    for (const CoreAccountId& prev_id : prev_ids) {
       if (prev_id == signed_in_id)
         continue;
       if (!base::ContainsValue(curr_ids, prev_id)) {
@@ -386,7 +395,7 @@
                << "refreshed=" << signed_in_id;
       refreshed_ids->push_back(signed_in_id);
     }
-    for (const std::string& curr_id : curr_ids) {
+    for (const CoreAccountId& curr_id : curr_ids) {
       if (curr_id == signed_in_id)
         continue;
       DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:"
@@ -400,7 +409,7 @@
                << "revoked=" << signed_in_id;
       revoked_ids->push_back(signed_in_id);
     }
-    for (const std::string& prev_id : prev_ids) {
+    for (const CoreAccountId& prev_id : prev_ids) {
       if (prev_id == signed_in_id)
         continue;
       DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:"
@@ -469,10 +478,7 @@
 
   // Clear accounts in the token service before calling
   // |FireRefreshTokenRevoked|.
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobjectArray> java_accounts(
-      base::android::ToJavaArrayOfStrings(env, std::vector<std::string>()));
-  Java_OAuth2TokenService_saveStoredAccounts(env, java_accounts);
+  SetAccounts(std::vector<CoreAccountId>());
 
   for (const std::string& account : accounts_to_revoke)
     FireRefreshTokenRevoked(account);
@@ -499,17 +505,18 @@
     const std::string& primary_account_id) {
   // UpdateAccountList() effectively synchronizes the accounts in the Token
   // Service with those present at the system level.
-  UpdateAccountList(primary_account_id);
+  UpdateAccountList(primary_account_id, GetValidAccounts(),
+                    GetSystemAccounts());
 }
 
 std::string OAuth2TokenServiceDelegateAndroid::MapAccountIdToAccountName(
-    const std::string& account_id) const {
+    const CoreAccountId& account_id) const {
   return account_tracker_service_->GetAccountInfo(account_id).email;
 }
 
-std::string OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId(
+CoreAccountId OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId(
     const std::string& account_name) const {
-  std::string account_id =
+  CoreAccountId account_id =
       account_tracker_service_->FindAccountInfoByEmail(account_name).account_id;
   DCHECK(!account_id.empty() || account_name.empty())
       << "Can't find account id, account_name=" << account_name;
diff --git a/components/signin/core/browser/oauth2_token_service_delegate_android.h b/components/signin/core/browser/oauth2_token_service_delegate_android.h
index 8808eea..d360324 100644
--- a/components/signin/core/browser/oauth2_token_service_delegate_android.h
+++ b/components/signin/core/browser/oauth2_token_service_delegate_android.h
@@ -55,9 +55,6 @@
                        const GoogleServiceAuthError& error) override;
   std::vector<std::string> GetAccounts() override;
 
-  // Lists account names at the OS level.
-  std::vector<std::string> GetSystemAccountNames();
-
   void UpdateAccountList(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
@@ -67,7 +64,9 @@
   // android account ids and check the token status of each.
   // NOTE: TokenAvailable notifications will be sent for all accounts, even if
   // they were already known. See https://crbug.com/939470 for details.
-  void UpdateAccountList(const std::string& signed_in_account_id);
+  void UpdateAccountList(const CoreAccountId& signed_in_account_id,
+                         const std::vector<CoreAccountId>& prev_ids,
+                         const std::vector<CoreAccountId>& curr_ids);
 
   // Overridden from OAuth2TokenService to complete signout of all
   // OA2TService aware accounts.
@@ -98,8 +97,9 @@
   void FireRefreshTokensLoaded() override;
 
  private:
-  std::string MapAccountIdToAccountName(const std::string& account_id) const;
-  std::string MapAccountNameToAccountId(const std::string& account_name) const;
+  std::string MapAccountIdToAccountName(const CoreAccountId& account_id) const;
+  CoreAccountId MapAccountNameToAccountId(
+      const std::string& account_name) const;
 
   enum RefreshTokenLoadStatus {
     RT_LOAD_NOT_START,
@@ -110,11 +110,20 @@
 
   // Return whether accounts are valid and we have access to all the tokens in
   // |curr_ids|.
-  bool UpdateAccountList(const std::string& signed_in_id,
-                         const std::vector<std::string>& prev_ids,
-                         const std::vector<std::string>& curr_ids,
-                         std::vector<std::string>* refreshed_ids,
-                         std::vector<std::string>* revoked_ids);
+  bool UpdateAccountList(const CoreAccountId& signed_in_id,
+                         const std::vector<CoreAccountId>& prev_ids,
+                         const std::vector<CoreAccountId>& curr_ids,
+                         std::vector<CoreAccountId>* refreshed_ids,
+                         std::vector<CoreAccountId>* revoked_ids);
+
+  // Lists account names at the OS level.
+  std::vector<std::string> GetSystemAccountNames();
+  // As |GetSystemAccountNames| but returning account IDs.
+  std::vector<CoreAccountId> GetSystemAccounts();
+  // As |GetAccounts| but with only validated account IDs.
+  std::vector<CoreAccountId> GetValidAccounts();
+  // Set accounts using Java's Oauth2TokenService.setAccounts.
+  virtual void SetAccounts(const std::vector<CoreAccountId>& accounts);
 
   base::android::ScopedJavaGlobalRef<jobject> java_ref_;
 
diff --git a/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc b/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc
new file mode 100644
index 0000000..6fe207a
--- /dev/null
+++ b/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 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/signin/core/browser/oauth2_token_service_delegate_android.h"
+
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::Sequence;
+using ::testing::StrictMock;
+
+namespace signin {
+
+namespace {
+const std::vector<CoreAccountId> kEmptyVector;
+class OAuth2TokenServiceDelegateAndroidForTest
+    : public OAuth2TokenServiceDelegateAndroid {
+ public:
+  OAuth2TokenServiceDelegateAndroidForTest(
+      AccountTrackerService* account_tracker_service)
+      : OAuth2TokenServiceDelegateAndroid(account_tracker_service) {}
+  MOCK_METHOD1(SetAccounts, void(const std::vector<CoreAccountId>&));
+};
+
+class TestObserver : public OAuth2TokenService::Observer {
+ public:
+  MOCK_METHOD1(OnRefreshTokenAvailable, void(const std::string&));
+  MOCK_METHOD1(OnRefreshTokenRevoked, void(const std::string&));
+  MOCK_METHOD0(OnRefreshTokensLoaded, void());
+};
+}  // namespace
+
+class OAuth2TokenServiceDelegateAndroidTest : public testing::Test {
+ public:
+  OAuth2TokenServiceDelegateAndroidTest() {}
+  ~OAuth2TokenServiceDelegateAndroidTest() override = default;
+
+ protected:
+  void SetUp() override {
+    testing::Test::SetUp();
+    AccountTrackerService::RegisterPrefs(pref_service_.registry());
+    account_tracker_service_.Initialize(&pref_service_, base::FilePath());
+    OAuth2TokenServiceDelegateAndroid::
+        set_disable_interaction_with_system_accounts();
+    delegate_ = std::make_unique<OAuth2TokenServiceDelegateAndroidForTest>(
+        &account_tracker_service_);
+    delegate_->AddObserver(&observer_);
+    CreateAndSeedAccounts();
+  }
+
+  void TearDown() override {
+    delegate_->RemoveObserver(&observer_);
+    testing::Test::TearDown();
+  }
+
+  AccountInfo CreateAccountInfo(const std::string& gaia_id,
+                                const std::string& email) {
+    AccountInfo account_info;
+
+    account_info.gaia = gaia_id;
+    account_info.email = email;
+    account_info.full_name = "fullname";
+    account_info.given_name = "givenname";
+    account_info.hosted_domain = "example.com";
+    account_info.locale = "en";
+    account_info.picture_url = "https://example.com";
+    account_info.is_child_account = false;
+    account_info.account_id = account_tracker_service_.PickAccountIdForAccount(
+        account_info.gaia, account_info.email);
+
+    DCHECK(account_info.IsValid());
+
+    return account_info;
+  }
+
+  void CreateAndSeedAccounts() {
+    account1_ = CreateAccountInfo("gaia-id-user-1", "user-1@example.com");
+    account2_ = CreateAccountInfo("gaia-id-user-2", "user-2@example.com");
+    // SeedAccountInfo is required for
+    // OAuth2TokenServiceDelegateAndrod::MapAccountNameToAccountId
+    account_tracker_service_.SeedAccountInfo(account1_);
+    account_tracker_service_.SeedAccountInfo(account2_);
+  }
+
+  AccountTrackerService account_tracker_service_;
+  sync_preferences::TestingPrefServiceSyncable pref_service_;
+  std::unique_ptr<OAuth2TokenServiceDelegateAndroidForTest> delegate_;
+  StrictMock<TestObserver> observer_;
+
+  AccountInfo account1_;
+  AccountInfo account2_;
+};
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith0SystemAccount0AccountAndNotSignedIn) {
+  EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)).WillOnce(Return());
+  // No observer call expected
+  delegate_->UpdateAccountList(std::string(), {}, {});
+  EXPECT_TRUE(account_tracker_service_.GetAccounts().empty());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount0AccountAndNotSignedIn) {
+  EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)).WillOnce(Return());
+  // No observer call expected
+  delegate_->UpdateAccountList(std::string(), {}, {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>{account1_},
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount1AccountAndNotSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector))
+      .InSequence(seq)
+      .WillOnce(Return());
+  // Stored account from |GetAccounts| must fire a revoked event
+  EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(std::string(), {account1_.account_id},
+                               {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>{account1_},
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount0AccountAndSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_,
+              SetAccounts(std::vector<CoreAccountId>({account1_.account_id})))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(account1_.account_id, {},
+                               {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>{account1_},
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount1AccountAndSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_,
+              SetAccounts(std::vector<CoreAccountId>({account1_.account_id})))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(account1_.account_id, {account1_.account_id},
+                               {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>{account1_},
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount1AccountDifferentAndSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_,
+              SetAccounts(std::vector<CoreAccountId>({account1_.account_id})))
+      .InSequence(seq)
+      .WillOnce(Return());
+  // Previously stored account is removed, new account is available
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(account1_.account_id, {account2_.account_id},
+                               {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>{account1_},
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith0SystemAccount1AccountSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(account1_.account_id, {account1_.account_id},
+                               {});
+  EXPECT_TRUE(account_tracker_service_.GetAccounts().empty());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount0AccountAndSignedInDifferent) {
+  EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)).WillOnce(Return());
+
+  delegate_->UpdateAccountList(account2_.account_id, {},
+                               {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>{account1_},
+            account_tracker_service_.GetAccounts());
+}
+
+// Test Getsysaccounts return a user != from signed user while GetAccounts not
+// empty
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount1AccountAndSignedInDifferent) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(account2_.account_id, {account1_.account_id},
+                               {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>{account1_},
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith2SystemAccount0AccountAndSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_, SetAccounts(std::vector<CoreAccountId>(
+                              {account1_.account_id, account2_.account_id})))
+      .InSequence(seq)
+      .WillOnce(Return());
+  // OnRefreshTokenAvailable fired, signed in account should go first.
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(account2_.account_id, {},
+                               {account1_.account_id, account2_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>({account1_, account2_}),
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith2SystemAccount1AccountAndSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_, SetAccounts(std::vector<CoreAccountId>(
+                              {account1_.account_id, account2_.account_id})))
+      .InSequence(seq)
+      .WillOnce(Return());
+  // OnRefreshTokenAvailable fired, signed in account should go first.
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+  delegate_->UpdateAccountList(account1_.account_id, {account2_.account_id},
+                               {account1_.account_id, account2_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>({account1_, account2_}),
+            account_tracker_service_.GetAccounts());
+}
+
+TEST_F(OAuth2TokenServiceDelegateAndroidTest,
+       UpdateAccountListWith1SystemAccount2AccountAndSignedIn) {
+  Sequence seq;
+  EXPECT_CALL(*delegate_,
+              SetAccounts(std::vector<CoreAccountId>({account1_.account_id})))
+      .InSequence(seq)
+      .WillOnce(Return());
+  // OnRefreshTokenAvailable fired, signed in account should go first.
+  EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+  EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id.id))
+      .InSequence(seq)
+      .WillOnce(Return());
+
+  delegate_->UpdateAccountList(account1_.account_id,
+                               {account1_.account_id, account2_.account_id},
+                               {account1_.account_id});
+  EXPECT_EQ(std::vector<AccountInfo>({account1_}),
+            account_tracker_service_.GetAccounts());
+}
+
+}  // namespace signin
diff --git a/components/sync/base/cryptographer.cc b/components/sync/base/cryptographer.cc
index 3b77dc2..e308c53 100644
--- a/components/sync/base/cryptographer.cc
+++ b/components/sync/base/cryptographer.cc
@@ -18,12 +18,6 @@
 
 const char kNigoriTag[] = "google_chrome_nigori";
 
-// We name a particular Nigori instance (ie. a triplet consisting of a hostname,
-// a username, and a password) by calling Permute on this string. Since the
-// output of Permute is always the same for a given triplet, clients will always
-// assign the same name to a particular triplet.
-const char kNigoriKeyName[] = "nigori-key";
-
 KeyParams::KeyParams(KeyDerivationParams derivation_params,
                      const std::string& password)
     : derivation_params(derivation_params), password(password) {}
diff --git a/components/sync/base/nigori.cc b/components/sync/base/nigori.cc
index a1b3560..eee629f 100644
--- a/components/sync/base/nigori.cc
+++ b/components/sync/base/nigori.cc
@@ -35,6 +35,8 @@
 
 namespace syncer {
 
+const char kNigoriKeyName[] = "nigori-key";
+
 namespace {
 
 // NigoriStream simplifies the concatenation operation of the Nigori protocol.
diff --git a/components/sync/base/nigori.h b/components/sync/base/nigori.h
index 96d9acf..619ab41 100644
--- a/components/sync/base/nigori.h
+++ b/components/sync/base/nigori.h
@@ -21,6 +21,9 @@
 
 class Nigori;
 
+// TODO(crbug.com/922900): inline kNigoriKeyName into Nigori::Permute().
+extern const char kNigoriKeyName[];
+
 class KeyDerivationParams {
  public:
   static KeyDerivationParams CreateForPbkdf2();
diff --git a/components/sync/engine/sync_encryption_handler.h b/components/sync/engine/sync_encryption_handler.h
index 0a0837d..df23cb9 100644
--- a/components/sync/engine/sync_encryption_handler.h
+++ b/components/sync/engine/sync_encryption_handler.h
@@ -150,14 +150,15 @@
   // Notifies observers of the result of the operation via OnPassphraseAccepted
   // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
   // appropriate. If an explicit password has been set previously, we drop
-  // subsequent requests to set a passphrase.
+  // subsequent requests to set a passphrase. |passphrase| shouldn't be empty.
   virtual void SetEncryptionPassphrase(const std::string& passphrase) = 0;
 
   // Provides a passphrase for decrypting the user's existing sync data.
   // Notifies observers of the result of the operation via OnPassphraseAccepted
   // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
   // appropriate if there is a previously cached encryption passphrase. It is an
-  // error to call this when we don't have pending keys.
+  // error to call this when we don't have pending keys. |passphrase| shouldn't
+  // be empty.
   virtual void SetDecryptionPassphrase(const std::string& passphrase) = 0;
 
   // Enables encryption of all datatypes.
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.h b/components/sync/engine_impl/loopback_server/loopback_server.h
index a6129de68f..0ca6570 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server.h
+++ b/components/sync/engine_impl/loopback_server/loopback_server.h
@@ -73,6 +73,10 @@
     }
   }
 
+  const std::vector<std::string>& GetKeystoreKeysForTesting() const {
+    return keystore_keys_;
+  }
+
  private:
   // Allow the FakeServer decorator to inspect the internals of this class.
   friend class fake_server::FakeServer;
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index b9330db..9b789db 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -21,6 +21,8 @@
 
 using sync_pb::NigoriSpecifics;
 
+const char kNigoriNonUniqueName[] = "Nigori";
+
 // Attempts to decrypt |keystore_decryptor_token| with |keystore_keys|. Returns
 // serialized Nigori key if successful and base::nullopt otherwise.
 base::Optional<std::string> DecryptKeystoreDecryptor(
@@ -253,6 +255,45 @@
 void NigoriSyncBridgeImpl::SetDecryptionPassphrase(
     const std::string& passphrase) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // |passphrase| should be a valid one already (verified by the UI part, using
+  // pending keys exposed by OnPassphraseRequired()).
+  DCHECK(!passphrase.empty());
+  DCHECK(cryptographer_.has_pending_keys());
+  // has_pending_keys() should mean it's an explicit passphrase user.
+  DCHECK(passphrase_type_ == NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE ||
+         passphrase_type_ == NigoriSpecifics::CUSTOM_PASSPHRASE);
+
+  KeyParams key_params = {KeyDerivationParams::CreateForPbkdf2(), passphrase};
+  // The line below should set given |passphrase| as default key and cause
+  // decryption of pending keys.
+  if (!cryptographer_.AddKey(key_params)) {
+    processor_->ReportError(ModelError(
+        FROM_HERE, "Failed to add decryption passphrase to cryptographer."));
+    return;
+  }
+  if (cryptographer_.has_pending_keys()) {
+    // TODO(crbug.com/922900): old implementation assumes that pending keys
+    // encryption key may change in between of OnPassphraseRequired() and
+    // SetDecryptionPassphrase() calls, verify whether it's really possible.
+    // Hypothetical cases are transition from FROZEN_IMPLICIT_PASSPHRASE to
+    // CUSTOM_PASSPHRASE and changing of passphrase due to conflict resolution.
+    processor_->ReportError(ModelError(
+        FROM_HERE,
+        "Failed to decrypt pending keys with provided explicit passphrase."));
+    return;
+  }
+  for (auto& observer : observers_) {
+    observer.OnCryptographerStateChanged(&cryptographer_);
+  }
+  for (auto& observer : observers_) {
+    observer.OnPassphraseAccepted();
+  }
+  // TODO(crbug.com/922900): persist |passphrase| in corresponding storage.
+  // TODO(crbug.com/922900): support SCRYPT key derivation method and
+  // corresponding migration code.
+  // TODO(crbug.com/922900): we may need to rewrite encryption_keybag in Nigori
+  // node in case we have some keys in |cryptographer_| which is not stored in
+  // encryption_keybag yet.
   NOTIMPLEMENTED();
 }
 
@@ -437,6 +478,20 @@
   for (auto& observer : observers_) {
     observer.OnCryptographerStateChanged(&cryptographer_);
   }
+  if (cryptographer_.has_pending_keys()) {
+    // Update with keystore Nigori shouldn't reach this point, since it should
+    // report model error if it has pending keys.
+    DCHECK(passphrase_type_ == NigoriSpecifics::CUSTOM_PASSPHRASE ||
+           passphrase_type_ == NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE);
+    for (auto& observer : observers_) {
+      // TODO(crbug.com/922900): pass correct key_derivation_params once SCRYPT
+      // support is added.
+      observer.OnPassphraseRequired(
+          /*reason=*/REASON_DECRYPTION,
+          /*key_derivation_params=*/KeyDerivationParams::CreateForPbkdf2(),
+          /*pending_keys=*/cryptographer_.GetPendingKeys());
+    }
+  }
   return base::nullopt;
 }
 
@@ -469,17 +524,54 @@
 }
 
 void NigoriSyncBridgeImpl::UpdateCryptographerFromExplicitPassphraseNigori(
-    const sync_pb::EncryptedData& keybag) {
+    const sync_pb::EncryptedData& encryption_keybag) {
   // TODO(crbug.com/922900): support the case when client knows passphrase.
   NOTIMPLEMENTED();
-  DCHECK(!keybag.blob().empty());
-  cryptographer_.SetPendingKeys(keybag);
+  DCHECK(!encryption_keybag.blob().empty());
+  if (!cryptographer_.CanDecrypt(encryption_keybag)) {
+    // This will lead to OnPassphraseRequired() call later.
+    cryptographer_.SetPendingKeys(encryption_keybag);
+    return;
+  }
+  // |cryptographer_| can already have explicit passphrase, in that case it
+  // should be able to decrypt |encryption_keybag|. We need to take keys from
+  // |encryption_keybag| since some other client can write old keys to
+  // |encryption_keybag| and could encrypt some data with them.
+  // TODO(crbug.com/922900): find and document at least one real case
+  // corresponding to the sentence above.
+  // TODO(crbug.com/922900): we may also need to rewrite Nigori with keys
+  // currently stored in cryptographer, in case it doesn't have them already.
+  cryptographer_.InstallKeys(encryption_keybag);
 }
 
 std::unique_ptr<EntityData> NigoriSyncBridgeImpl::GetData() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(cryptographer_.is_ready());
+  DCHECK_NE(passphrase_type_, NigoriSpecifics::UNKNOWN);
+
+  NigoriSpecifics specifics;
+  cryptographer_.GetKeys(specifics.mutable_encryption_keybag());
+  specifics.set_keybag_is_frozen(true);
+  specifics.set_encrypt_everything(encrypt_everything_);
+  specifics.set_passphrase_type(passphrase_type_);
+  if (passphrase_type_ == NigoriSpecifics::KEYSTORE_PASSPHRASE) {
+    cryptographer_.EncryptString(cryptographer_.GetDefaultNigoriKeyData(),
+                                 specifics.mutable_keystore_decryptor_token());
+  }
+  if (!keystore_migration_time_.is_null()) {
+    specifics.set_keystore_migration_time(
+        TimeToProtoTime(keystore_migration_time_));
+  }
+  if (!custom_passphrase_time_.is_null()) {
+    specifics.set_custom_passphrase_time(
+        TimeToProtoTime(custom_passphrase_time_));
+  }
+  // TODO(crbug.com/922900): add other fields support.
   NOTIMPLEMENTED();
-  return nullptr;
+  auto entity_data = std::make_unique<EntityData>();
+  *entity_data->specifics.mutable_nigori() = std::move(specifics);
+  entity_data->non_unique_name = kNigoriNonUniqueName;
+  return entity_data;
 }
 
 ConflictResolution NigoriSyncBridgeImpl::ResolveConflict(
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index c33926e..eb2e41aa 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -77,6 +77,12 @@
   return decrypted == unencrypted;
 }
 
+MATCHER_P(EncryptedDataEq, expected, "") {
+  const sync_pb::EncryptedData& given = arg;
+  return given.key_name() == expected.key_name() &&
+         given.blob() == expected.blob();
+}
+
 KeyParams Pbkdf2KeyParams(std::string key) {
   return {KeyDerivationParams::CreateForPbkdf2(), std::move(key)};
 }
@@ -125,7 +131,8 @@
 class NigoriSyncBridgeImplTest : public testing::Test {
  protected:
   NigoriSyncBridgeImplTest() {
-    auto processor = std::make_unique<MockNigoriLocalChangeProcessor>();
+    auto processor =
+        std::make_unique<testing::NiceMock<MockNigoriLocalChangeProcessor>>();
     processor_ = processor.get();
     bridge_ = std::make_unique<NigoriSyncBridgeImpl>(std::move(processor),
                                                      &encryptor_);
@@ -174,12 +181,24 @@
     return specifics;
   }
 
+  // Builds NigoriSpecifics with following fields:
+  // 1. encryption_keybag contains keys derived from |passphrase_key_params|
+  // and |*old_key_params| (if |old_key_params| isn't nullopt). Encrypted with
+  // key derived from |passphrase_key_params|.
+  // 2. custom_passphrase_time is current time.
+  // 3. passphrase_type is CUSTOM_PASSPHRASE.
+  // 4. encrypt_everything is true.
+  // 5. Other fields are default.
   sync_pb::NigoriSpecifics BuildCustomPassphraseNigoriSpecifics(
-      const KeyParams& key_params) {
+      const KeyParams& passphrase_key_params,
+      const base::Optional<KeyParams>& old_key_params = base::nullopt) {
     sync_pb::NigoriSpecifics specifics;
 
     Cryptographer cryptographer(&encryptor_);
-    cryptographer.AddKey(key_params);
+    cryptographer.AddKey(passphrase_key_params);
+    if (old_key_params) {
+      cryptographer.AddNonDefaultKey(*old_key_params);
+    }
     EXPECT_TRUE(cryptographer.GetKeys(specifics.mutable_encryption_keybag()));
 
     specifics.set_custom_passphrase_time(TimeToProtoTime(base::Time::Now()));
@@ -194,7 +213,7 @@
   FakeEncryptor encryptor_;
   std::unique_ptr<NigoriSyncBridgeImpl> bridge_;
   // Ownership transferred to |bridge_|.
-  MockNigoriLocalChangeProcessor* processor_;
+  testing::NiceMock<MockNigoriLocalChangeProcessor>* processor_;
   testing::NiceMock<MockObserver> observer_;
 };
 
@@ -302,8 +321,9 @@
   EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kCurrentKeyParams));
 }
 
-// Tests that we build keystore Nigori, put it to processor and initialize
-// the cryptographer, when the default Nigori is received.
+// Tests that we build keystore Nigori, put it to processor, initialize the
+// cryptographer and expose a valid entity through GetData(), when the default
+// Nigori is received.
 TEST_F(NigoriSyncBridgeImplTest,
        ShouldPutAndMakeCryptographerReadyOnDefaultNigori) {
   const std::string kRawKeystoreKey = "raw_keystore_key";
@@ -319,6 +339,7 @@
   EXPECT_CALL(*processor(), Put(HasKeystoreNigori()));
   EXPECT_THAT(bridge()->MergeSyncData(std::move(default_entity_data)),
               Eq(base::nullopt));
+  EXPECT_THAT(bridge()->GetData(), HasKeystoreNigori());
 
   const Cryptographer& cryptographer = bridge()->GetCryptographerForTesting();
   EXPECT_THAT(cryptographer, CanDecryptWith(kKeystoreKeyParams));
@@ -401,6 +422,39 @@
               Ne(base::nullopt));
 }
 
+// Tests decryption logic for explicit passphrase. In order to check that we're
+// able to decrypt the data encrypted with old key (i.e. keystore keys or old
+// GAIA passphrase) we add one extra key to the encryption keybag.
+TEST_F(NigoriSyncBridgeImplTest,
+       ShouldDecryptWithCustomPassphraseAndUpdateDefaultKey) {
+  const KeyParams kOldKeyParams = Pbkdf2KeyParams("old_key");
+  const KeyParams kPassphraseKeyParams = Pbkdf2KeyParams("passphrase");
+  EntityData entity_data;
+  *entity_data.specifics.mutable_nigori() =
+      BuildCustomPassphraseNigoriSpecifics(kPassphraseKeyParams, kOldKeyParams);
+
+  ASSERT_TRUE(bridge()->SetKeystoreKeys({"keystore_key"}));
+
+  EXPECT_CALL(
+      *observer(),
+      OnPassphraseRequired(
+          /*reason=*/REASON_DECRYPTION,
+          /*key_derivation_params=*/KeyDerivationParams::CreateForPbkdf2(),
+          /*pending_keys=*/
+          EncryptedDataEq(entity_data.specifics.nigori().encryption_keybag())));
+  ASSERT_THAT(bridge()->MergeSyncData(std::move(entity_data)),
+              Eq(base::nullopt));
+
+  EXPECT_CALL(*observer(), OnPassphraseAccepted());
+  EXPECT_CALL(*observer(), OnCryptographerStateChanged(NotNull()));
+  bridge()->SetDecryptionPassphrase(kPassphraseKeyParams.password);
+
+  const Cryptographer& cryptographer = bridge()->GetCryptographerForTesting();
+  EXPECT_THAT(cryptographer, CanDecryptWith(kOldKeyParams));
+  EXPECT_THAT(cryptographer, CanDecryptWith(kPassphraseKeyParams));
+  EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kPassphraseKeyParams));
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync/test/fake_server/fake_server.cc b/components/sync/test/fake_server/fake_server.cc
index 8959f362..3e16786 100644
--- a/components/sync/test/fake_server/fake_server.cc
+++ b/components/sync/test/fake_server/fake_server.cc
@@ -309,6 +309,11 @@
   return loopback_server_->GetTopLevelPermanentItemId(model_type);
 }
 
+const std::vector<std::string>& FakeServer::GetKeystoreKeys() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return loopback_server_->GetKeystoreKeysForTesting();
+}
+
 void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(entity->GetModelType() != syncer::AUTOFILL_WALLET_DATA)
diff --git a/components/sync/test/fake_server/fake_server.h b/components/sync/test/fake_server/fake_server.h
index e7eeb4c..cd3d59f 100644
--- a/components/sync/test/fake_server/fake_server.h
+++ b/components/sync/test/fake_server/fake_server.h
@@ -95,6 +95,9 @@
   // was created.
   std::string GetTopLevelPermanentItemId(syncer::ModelType model_type);
 
+  // Returns all keystore keys from the server.
+  const std::vector<std::string>& GetKeystoreKeys() const;
+
   // Adds |entity| to the server's collection of entities. This method makes no
   // guarantees that the added entity will result in successful server
   // operations.
@@ -211,9 +214,6 @@
   // If set, the server will return HTTP errors.
   base::Optional<net::HttpStatusCode> http_error_status_code_;
 
-  // All Keystore keys known to the server.
-  std::vector<std::string> keystore_keys_;
-
   // All URLs received via history sync (powered by SESSIONS).
   std::set<std::string> committed_history_urls_;
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 01caac95..56273974 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1880,8 +1880,8 @@
     "web_contents/aura/gesture_nav_simple.h",
     "web_contents/aura/types.cc",
     "web_contents/aura/types.h",
-    "web_contents/web_contents_getter_registry.cc",
-    "web_contents/web_contents_getter_registry.h",
+    "web_contents/frame_tree_node_id_registry.cc",
+    "web_contents/frame_tree_node_id_registry.h",
     "web_contents/web_contents_impl.cc",
     "web_contents/web_contents_impl.h",
     "web_contents/web_contents_view.h",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index 26dd7e3..a237eaf 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -75,6 +75,8 @@
       return ui::ToString(static_cast<ax::mojom::DefaultActionVerb>(value));
     case ax::mojom::IntAttribute::kDescriptionFrom:
       return ui::ToString(static_cast<ax::mojom::DescriptionFrom>(value));
+    case ax::mojom::IntAttribute::kDropeffect:
+      return node.GetData().DropeffectBitfieldToString();
     case ax::mojom::IntAttribute::kHasPopup:
       return ui::ToString(static_cast<ax::mojom::HasPopup>(value));
     case ax::mojom::IntAttribute::kInvalidState:
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 9794721..74da28ea6 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -209,10 +209,12 @@
     case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
     case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
+    case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED:
     case ui::AXEventGenerator::Event::EXPANDED:
     case ui::AXEventGenerator::Event::ENABLED_CHANGED:
     case ui::AXEventGenerator::Event::FLOW_FROM_CHANGED:
     case ui::AXEventGenerator::Event::FLOW_TO_CHANGED:
+    case ui::AXEventGenerator::Event::GRABBED_CHANGED:
     case ui::AXEventGenerator::Event::HASPOPUP_CHANGED:
     case ui::AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED:
     case ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 8ccb8d2..e02f5f9 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -381,9 +381,11 @@
     case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
     case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
+    case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED:
     case ui::AXEventGenerator::Event::ENABLED_CHANGED:
     case ui::AXEventGenerator::Event::FLOW_FROM_CHANGED:
     case ui::AXEventGenerator::Event::FLOW_TO_CHANGED:
+    case ui::AXEventGenerator::Event::GRABBED_CHANGED:
     case ui::AXEventGenerator::Event::HASPOPUP_CHANGED:
     case ui::AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED:
     case ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index f479c6a..9c8fd53 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -220,6 +220,10 @@
         FireWinAccessibilityEvent(IA2_EVENT_TEXT_CARET_MOVED, focus_object);
       break;
     }
+    // aria-dropeffect is deprecated in WAI-ARIA 1.1.
+    case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED:
+      aria_properties_events_.insert(node);
+      break;
     case ui::AXEventGenerator::Event::ENABLED_CHANGED:
       FireUiaPropertyChangedEvent(UIA_IsEnabledPropertyId, node);
       aria_properties_events_.insert(node);
@@ -230,6 +234,8 @@
     case ui::AXEventGenerator::Event::FLOW_TO_CHANGED:
       FireUiaPropertyChangedEvent(UIA_FlowsToPropertyId, node);
       break;
+    // aria-grabbed is deprecated in WAI-ARIA 1.1.
+    case ui::AXEventGenerator::Event::GRABBED_CHANGED:
     case ui::AXEventGenerator::Event::HASPOPUP_CHANGED:
       aria_properties_events_.insert(node);
       break;
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index db1f1bfc..99070d8 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -261,6 +261,16 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
+                       AccessibilityEventsAriaDropeffectChanged) {
+  RunEventTest(FILE_PATH_LITERAL("aria-dropeffect-changed.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
+                       AccessibilityEventsAriaGrabbedChanged) {
+  RunEventTest(FILE_PATH_LITERAL("aria-grabbed-changed.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
                        AccessibilityEventsAriaHasPopupChanged) {
   RunEventTest(FILE_PATH_LITERAL("aria-haspopup-changed.html"));
 }
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 5f22028..5d72dd9 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1420,11 +1420,6 @@
           switches::kDisableGpuProcessForDX12VulkanInfoCollection)) {
     GpuDataManagerImpl::GetInstance()->RequestGpuSupportedRuntimeVersion();
   }
-
-  if (base::FeatureList::IsEnabled(features::kFontSrcLocalMatching)) {
-    content::DWriteFontLookupTableBuilder::GetInstance()
-        ->SchedulePrepareFontUniqueNameTableIfNeeded();
-  }
 #endif
 
   if (MediaKeysListenerManager::IsMediaKeysListenerManagerEnabled()) {
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc
index b2c3b6b2..bbb1e00 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.cc
+++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -93,24 +93,45 @@
 
 void CacheStorageContextImpl::GetAllOriginsInfo(
     CacheStorageContext::GetUsageInfoCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  // Can be called on any sequence.
 
-  if (!cache_manager_) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(std::move(callback), std::vector<StorageUsageInfo>()));
-    return;
-  }
+  callback = base::BindOnce(
+      [](scoped_refptr<base::SequencedTaskRunner> reply_task_runner,
+         GetUsageInfoCallback inner,
+         const std::vector<StorageUsageInfo>& entries) {
+        reply_task_runner->PostTask(FROM_HERE,
+                                    base::BindOnce(std::move(inner), entries));
+      },
+      base::SequencedTaskRunnerHandle::Get(), std::move(callback));
 
-  cache_manager_->GetAllOriginsUsage(CacheStorageOwner::kCacheAPI,
-                                     std::move(callback));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](scoped_refptr<CacheStorageContextImpl> context,
+             GetUsageInfoCallback callback) {
+            if (!context->cache_manager()) {
+              std::move(callback).Run(std::vector<StorageUsageInfo>());
+              return;
+            }
+            context->cache_manager()->GetAllOriginsUsage(
+                CacheStorageOwner::kCacheAPI, std::move(callback));
+          },
+          base::RetainedRef(this), std::move(callback)));
 }
 
 void CacheStorageContextImpl::DeleteForOrigin(const GURL& origin) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (cache_manager_)
-    cache_manager_->DeleteOriginData(url::Origin::Create(origin),
-                                     CacheStorageOwner::kCacheAPI);
+  // Can be called on any sequence.
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(
+                             [](scoped_refptr<CacheStorageContextImpl> context,
+                                const GURL& origin) {
+                               if (!context->cache_manager())
+                                 return;
+                               context->cache_manager()->DeleteOriginData(
+                                   url::Origin::Create(origin),
+                                   CacheStorageOwner::kCacheAPI);
+                             },
+                             base::RetainedRef(this), origin));
 }
 
 void CacheStorageContextImpl::AddObserver(
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 5c196a34..529de11 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1611,6 +1611,7 @@
                                 info.load_timing.request_start_time)
           .Build();
   response->SetFromServiceWorker(info.was_fetched_via_service_worker);
+  response->SetFromPrefetchCache(info.was_in_prefetch_cache);
   network::HttpRawRequestResponseInfo* raw_info =
       info.raw_request_response_info.get();
   if (raw_info) {
diff --git a/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc b/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
index afef87c9..c79d06e 100644
--- a/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
+++ b/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "base/stl_util.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "content/browser/devtools/protocol/devtools_protocol_test_support.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -131,6 +132,7 @@
 #endif
 
   void LoadAndWait(const std::string& url) {
+    base::ScopedAllowBlockingForTesting blocking_for_load;
     ASSERT_TRUE(embedded_test_server()->Start());
     TestNavigationObserver navigation_observer(
         static_cast<WebContentsImpl*>(shell()->web_contents()));
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index e56464e9..06108f11 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3472,6 +3472,8 @@
   // of this RenderFrameHost is being tracked.
   if (is_active())
     frame_tree_node_->DidStopLoading();
+
+  UpdateFrameFrozenState();
 }
 
 void RenderFrameHostImpl::OnDidChangeLoadProgress(double load_progress) {
@@ -6923,6 +6925,9 @@
 }
 
 void RenderFrameHostImpl::UpdateFrameFrozenState() {
+  // If the document is in the loading state keep it still loading.
+  if (is_loading_)
+    return;
 
   if (!IsFeatureEnabled(
           blink::mojom::FeaturePolicyFeature::kExecutionWhileNotRendered) &&
diff --git a/content/browser/loader/prefetched_signed_exchange_cache.cc b/content/browser/loader/prefetched_signed_exchange_cache.cc
index 36f40dc3..2564bdd 100644
--- a/content/browser/loader/prefetched_signed_exchange_cache.cc
+++ b/content/browser/loader/prefetched_signed_exchange_cache.cc
@@ -79,6 +79,7 @@
     network::ResourceResponseHead response_head =
         signed_exchange_utils::CreateRedirectResponseHead(
             outer_response, false /* is_fallback_redirect */);
+    response_head.was_in_prefetch_cache = true;
     UpdateRequestResponseStartTime(&response_head);
     client_->OnReceiveRedirect(signed_exchange_utils::CreateRedirectInfo(
                                    inner_url, url_request, outer_response,
diff --git a/content/browser/loader/prefetched_signed_exchange_cache_adapter.cc b/content/browser/loader/prefetched_signed_exchange_cache_adapter.cc
index 2d6db764..4bbfaa1a 100644
--- a/content/browser/loader/prefetched_signed_exchange_cache_adapter.cc
+++ b/content/browser/loader/prefetched_signed_exchange_cache_adapter.cc
@@ -47,8 +47,10 @@
 
 void PrefetchedSignedExchangeCacheAdapter::OnReceiveInnerResponse(
     const network::ResourceResponseHead& response) {
-  cached_exchange_->SetInnerResponse(
-      std::make_unique<network::ResourceResponseHead>(response));
+  std::unique_ptr<network::ResourceResponseHead> inner_response =
+      std::make_unique<network::ResourceResponseHead>(response);
+  inner_response->was_in_prefetch_cache = true;
+  cached_exchange_->SetInnerResponse(std::move(inner_response));
 }
 
 void PrefetchedSignedExchangeCacheAdapter::OnStartLoadingResponseBody(
diff --git a/content/browser/loader/resource_request_info_impl.cc b/content/browser/loader/resource_request_info_impl.cc
index 2a166f3..149f229 100644
--- a/content/browser/loader/resource_request_info_impl.cc
+++ b/content/browser/loader/resource_request_info_impl.cc
@@ -8,7 +8,7 @@
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/loader/resource_message_filter.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
-#include "content/browser/web_contents/web_contents_getter_registry.h"
+#include "content/browser/web_contents/frame_tree_node_id_registry.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/net/url_request_service_worker_data.h"
 #include "content/common/net/url_request_user_data.h"
@@ -194,10 +194,12 @@
 ResourceRequestInfoImpl::GetWebContentsGetterForRequest() {
   // If we have a window id, try to use that.
   if (fetch_window_id_) {
-    ResourceRequestInfo::WebContentsGetter getter =
-        WebContentsGetterRegistry::GetInstance()->Get(fetch_window_id_);
-    if (getter)
-      return getter;
+    int frame_tree_node_id =
+        FrameTreeNodeIdRegistry::GetInstance()->Get(fetch_window_id_);
+    if (frame_tree_node_id != FrameTreeNode::kFrameTreeNodeInvalidId) {
+      return base::BindRepeating(&WebContents::FromFrameTreeNodeId,
+                                 frame_tree_node_id);
+    }
   }
 
   // Navigation requests are created with a valid FrameTreeNode ID and invalid
diff --git a/content/browser/media/session/media_session_browsertest.cc b/content/browser/media/session/media_session_browsertest.cc
index add1dd5..4c8ff9ca 100644
--- a/content/browser/media/session/media_session_browsertest.cc
+++ b/content/browser/media/session/media_session_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -132,6 +133,7 @@
   }
 
   bool WasURLVisited(const GURL& url) {
+    base::AutoLock lock(visited_urls_lock_);
     return base::ContainsKey(visited_urls_, url);
   }
 
@@ -201,9 +203,15 @@
   };
 
   void OnServerRequest(const net::test_server::HttpRequest& request) {
+    // Note this method is called on the EmbeddedTestServer's background thread.
+    base::AutoLock lock(visited_urls_lock_);
     visited_urls_.insert(request.GetURL());
   }
 
+  // visited_urls_ is accessed both on the main thread and on the
+  // EmbeddedTestServer's background thread via OnServerRequest(), so it must be
+  // locked.
+  base::Lock visited_urls_lock_;
   std::set<GURL> visited_urls_;
 
   base::test::ScopedFeatureList disabled_feature_list_;
diff --git a/content/browser/net/accept_header_browsertest.cc b/content/browser/net/accept_header_browsertest.cc
index f89d6e7..c4ab85a 100644
--- a/content/browser/net/accept_header_browsertest.cc
+++ b/content/browser/net/accept_header_browsertest.cc
@@ -165,9 +165,13 @@
 IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, PluginAcceptHeader) {
   net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTP);
   server.ServeFilesFromSourceDirectory("ppapi/tests");
+  base::Lock plugin_accept_header_lock;
   std::string plugin_accept_header;
   server.RegisterRequestMonitor(base::BindLambdaForTesting(
       [&](const net::test_server::HttpRequest& request) {
+        // Note this callback runs on the EmbeddedTestServer's background
+        // thread.
+        base::AutoLock lock(plugin_accept_header_lock);
         if (request.relative_url == "/test_url_loader_data/hello.txt") {
           auto it = request.headers.find("Accept");
           if (it != request.headers.end())
@@ -179,7 +183,10 @@
   RunTestURL(
       server.GetURL(BuildQuery("/test_case.html?", "URLLoader_BasicGET")));
 
-  ASSERT_EQ("*/*", plugin_accept_header);
+  {
+    base::AutoLock lock(plugin_accept_header_lock);
+    ASSERT_EQ("*/*", plugin_accept_header);
+  }
 
   // Since the server uses local variables.
   ASSERT_TRUE(server.ShutdownAndWaitUntilComplete());
diff --git a/content/browser/network_service_client.cc b/content/browser/network_service_client.cc
index 5de9edc..6a23939 100644
--- a/content/browser/network_service_client.cc
+++ b/content/browser/network_service_client.cc
@@ -16,7 +16,7 @@
 #include "content/browser/ssl/ssl_error_handler.h"
 #include "content/browser/ssl/ssl_manager.h"
 #include "content/browser/ssl_private_key_impl.h"
-#include "content/browser/web_contents/web_contents_getter_registry.h"
+#include "content/browser/web_contents/frame_tree_node_id_registry.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -325,7 +325,13 @@
 
 base::RepeatingCallback<WebContents*(void)> GetWebContentsFromRegistry(
     const base::UnguessableToken& window_id) {
-  return WebContentsGetterRegistry::GetInstance()->Get(window_id);
+  int frame_tree_node_id =
+      FrameTreeNodeIdRegistry::GetInstance()->Get(window_id);
+  if (frame_tree_node_id == FrameTreeNode::kFrameTreeNodeInvalidId) {
+    return base::NullCallback();
+  }
+  return base::BindRepeating(&WebContents::FromFrameTreeNodeId,
+                             frame_tree_node_id);
 }
 
 WebContents* GetWebContents(int process_id, int routing_id) {
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
index e15f8ab..36ff518 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
@@ -17,8 +17,10 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/memory/singleton.h"
 #include "base/optional.h"
+#include "base/sequenced_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
@@ -44,6 +46,13 @@
  public:
   static DWriteFontLookupTableBuilder* GetInstance();
 
+  // Configure the task runner that will be used for posting tasks to when
+  // executing Mojo result callbacks that were passed from DWriteFontProxyImpl.
+  void SetCallbackTaskRunner(
+      scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
+    callback_task_runner_ = callback_task_runner;
+  }
+
   // Retrieve the prepared memory region if it is available.
   // EnsureFontUniqueNameTable() must be checked before.
   base::ReadOnlySharedMemoryRegion DuplicateMemoryRegion();
@@ -191,6 +200,7 @@
   bool caching_enabled_ = true;
   base::Optional<base::WaitableEvent> hang_event_for_testing_;
   base::CancelableOnceCallback<void()> timeout_callback_;
+  scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(DWriteFontLookupTableBuilder);
 };
diff --git a/content/browser/renderer_host/plugin_registry_impl.cc b/content/browser/renderer_host/plugin_registry_impl.cc
index 427e1cb8..8dd463f 100644
--- a/content/browser/renderer_host/plugin_registry_impl.cc
+++ b/content/browser/renderer_host/plugin_registry_impl.cc
@@ -58,7 +58,7 @@
   PluginServiceFilter* filter = PluginServiceImpl::GetInstance()->GetFilter();
   std::vector<blink::mojom::PluginInfoPtr> plugins;
   base::flat_set<std::string> mime_handler_view_mime_types =
-      GetContentClient()->browser()->GetMimeHandlerViewMimeTypes(
+      GetContentClient()->browser()->GetPluginMimeTypesWithExternalHandlers(
           resource_context_);
 
   const int child_process_id = -1;
@@ -76,15 +76,15 @@
       plugin_blink->description = plugin.desc;
       plugin_blink->filename = plugin.path.BaseName();
       plugin_blink->background_color = plugin.background_color;
-      plugin_blink->may_use_mime_handler_view = false;
+      plugin_blink->may_use_external_handler = false;
       for (const auto& mime_type : plugin.mime_types) {
         auto mime_type_blink = blink::mojom::PluginMimeType::New();
         mime_type_blink->mime_type = mime_type.mime_type;
         mime_type_blink->description = mime_type.description;
         mime_type_blink->file_extensions = mime_type.file_extensions;
         plugin_blink->mime_types.push_back(std::move(mime_type_blink));
-        if (!plugin_blink->may_use_mime_handler_view) {
-          plugin_blink->may_use_mime_handler_view = base::ContainsKey(
+        if (!plugin_blink->may_use_external_handler) {
+          plugin_blink->may_use_external_handler = base::ContainsKey(
               mime_handler_view_mime_types, mime_type.mime_type);
         }
       }
diff --git a/content/browser/service_manager/common_browser_interfaces.cc b/content/browser/service_manager/common_browser_interfaces.cc
index 91716b6..c6b09c6 100644
--- a/content/browser/service_manager/common_browser_interfaces.cc
+++ b/content/browser/service_manager/common_browser_interfaces.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/memory/ref_counted.h"
 #include "base/task/post_task.h"
 #include "base/task_runner.h"
@@ -23,6 +24,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/connection_filter.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
@@ -45,10 +47,19 @@
   ConnectionFilterImpl() {
 #if defined(OS_WIN)
     registry_.AddInterface(base::BindRepeating(&FontCacheDispatcher::Create));
-    registry_.AddInterface(
-        base::BindRepeating(&DWriteFontProxyImpl::Create),
+
+    auto dwrite_font_proxy_task_runner =
         base::CreateSequencedTaskRunnerWithTraits(
-            {base::TaskPriority::USER_BLOCKING, base::MayBlock()}));
+            {base::TaskPriority::USER_BLOCKING, base::MayBlock()});
+    registry_.AddInterface(base::BindRepeating(&DWriteFontProxyImpl::Create),
+                           dwrite_font_proxy_task_runner);
+
+    if (base::FeatureList::IsEnabled(features::kFontSrcLocalMatching)) {
+      content::DWriteFontLookupTableBuilder::GetInstance()
+          ->SetCallbackTaskRunner(dwrite_font_proxy_task_runner);
+      content::DWriteFontLookupTableBuilder::GetInstance()
+          ->SchedulePrepareFontUniqueNameTableIfNeeded();
+    }
 #elif defined(OS_MACOSX)
     registry_.AddInterface(
         base::BindRepeating(&SandboxSupportMacImpl::BindRequest,
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 2962095..12863b8 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/stl_util.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/fake_embedded_worker_instance_client.h"
@@ -1855,7 +1856,8 @@
   base::WeakPtr<ServiceWorkerProviderHost> reserved_client =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
           helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */,
-          {} /* web_contents_getter */, &provider_info);
+          FrameTreeNode::kFrameTreeNodeInvalidId, {} /* web_contents_getter */,
+          &provider_info);
   reserved_client->UpdateUrls(in_scope, in_scope);
 
   // Make an out-scope client.
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 5c7c687..e99a521 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -29,7 +29,7 @@
 #include "content/browser/service_worker/service_worker_type_converters.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/url_loader_factory_getter.h"
-#include "content/browser/web_contents/web_contents_getter_registry.h"
+#include "content/browser/web_contents/frame_tree_node_id_registry.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
@@ -163,6 +163,7 @@
 ServiceWorkerProviderHost::PreCreateNavigationHost(
     base::WeakPtr<ServiceWorkerContextCore> context,
     bool are_ancestors_secure,
+    int frame_tree_node_id,
     WebContentsGetter web_contents_getter,
     blink::mojom::ServiceWorkerProviderInfoForWindowPtr* out_provider_info) {
   DCHECK(context);
@@ -170,6 +171,7 @@
   (*out_provider_info)->client_request = mojo::MakeRequest(&client_ptr_info);
   auto host = base::WrapUnique(new ServiceWorkerProviderHost(
       blink::mojom::ServiceWorkerProviderType::kForWindow, are_ancestors_secure,
+      frame_tree_node_id,
       mojo::MakeRequest(&((*out_provider_info)->host_ptr_info)),
       std::move(client_ptr_info), context));
   host->web_contents_getter_ = std::move(web_contents_getter);
@@ -190,7 +192,7 @@
   (*out_provider_info)->client_request = mojo::MakeRequest(&client_ptr_info);
   auto host = base::WrapUnique(new ServiceWorkerProviderHost(
       blink::mojom::ServiceWorkerProviderType::kForServiceWorker,
-      true /* is_parent_frame_secure */,
+      true /* is_parent_frame_secure */, FrameTreeNode::kFrameTreeNodeInvalidId,
       mojo::MakeRequest(&((*out_provider_info)->host_ptr_info)),
       std::move(client_ptr_info), context));
   host->running_hosted_version_ = std::move(version);
@@ -210,7 +212,7 @@
   (*out_provider_info)->client_request = mojo::MakeRequest(&client_ptr_info);
   auto host = base::WrapUnique(new ServiceWorkerProviderHost(
       blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-      true /* is_parent_frame_secure */,
+      true /* is_parent_frame_secure */, FrameTreeNode::kFrameTreeNodeInvalidId,
       mojo::MakeRequest(&((*out_provider_info)->host_ptr_info)),
       std::move(client_ptr_info), context));
   host->SetRenderProcessId(process_id);
@@ -234,6 +236,7 @@
 ServiceWorkerProviderHost::ServiceWorkerProviderHost(
     blink::mojom::ServiceWorkerProviderType type,
     bool is_parent_frame_secure,
+    int frame_tree_node_id,
     blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request,
     blink::mojom::ServiceWorkerContainerAssociatedPtrInfo client_ptr_info,
     base::WeakPtr<ServiceWorkerContextCore> context)
@@ -245,6 +248,7 @@
       render_thread_id_(kDocumentMainThreadId),
       frame_id_(MSG_ROUTING_NONE),
       is_parent_frame_secure_(is_parent_frame_secure),
+      frame_tree_node_id_(frame_tree_node_id),
       context_(context),
       binding_(this),
       interface_provider_binding_(this) {
@@ -272,7 +276,7 @@
   if (controller_)
     controller_->RemoveControllee(client_uuid_);
   if (fetch_request_window_id_)
-    WebContentsGetterRegistry::GetInstance()->Remove(fetch_request_window_id_);
+    FrameTreeNodeIdRegistry::GetInstance()->Remove(fetch_request_window_id_);
 
   // Remove |this| as an observer of ServiceWorkerRegistrations.
   // TODO(falken): Use ScopedObserver instead of this explicit call.
@@ -401,10 +405,10 @@
     // may no longer be the potential controller of this frame and shouldn't
     // have the power to display SSL dialogs for it.
     if (type_ == blink::mojom::ServiceWorkerProviderType::kForWindow) {
-      auto* registry = WebContentsGetterRegistry::GetInstance();
+      auto* registry = FrameTreeNodeIdRegistry::GetInstance();
       registry->Remove(fetch_request_window_id_);
       fetch_request_window_id_ = base::UnguessableToken::Create();
-      registry->Add(fetch_request_window_id_, web_contents_getter());
+      registry->Add(fetch_request_window_id_, frame_tree_node_id_);
     }
   }
 
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 4634471..76560adc 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -114,8 +114,9 @@
   // ServiceWorkerProviderContext will later be created in the renderer, should
   // the navigation succeed. |are_ancestors_secure| should be true for main
   // frames. Otherwise it is true iff all ancestor frames of this frame have a
-  // secure origin. |web_contents_getter| indicates the tab where the navigation
-  // is occurring.
+  // secure origin. |frame_tree_node_id| is FrameTreeNode
+  // id. |web_contents_getter| indicates the tab where the navigation is
+  // occurring.
   //
   // The returned host stays alive as long as the filled |out_provider_info|
   // stays alive (namely, as long as |out_provider_info->host_ptr_info| stays
@@ -124,6 +125,7 @@
   static base::WeakPtr<ServiceWorkerProviderHost> PreCreateNavigationHost(
       base::WeakPtr<ServiceWorkerContextCore> context,
       bool are_ancestors_secure,
+      int frame_tree_node_id,
       WebContentsGetter web_contents_getter,
       blink::mojom::ServiceWorkerProviderInfoForWindowPtr* out_provider_info);
 
@@ -462,6 +464,7 @@
   ServiceWorkerProviderHost(
       blink::mojom::ServiceWorkerProviderType type,
       bool is_parent_frame_secure,
+      int frame_tree_node_id,
       blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request,
       blink::mojom::ServiceWorkerContainerAssociatedPtrInfo client_ptr_info,
       base::WeakPtr<ServiceWorkerContextCore> context);
@@ -625,8 +628,14 @@
   // change its value.
   bool is_parent_frame_secure_;
 
+  // FrameTreeNode id if this is a service worker window client.
+  // Otherwise, |FrameTreeNode::kFrameTreeNodeInvalidId|.
+  const int frame_tree_node_id_;
+
   // Only set when this object is pre-created for a navigation. It indicates the
   // tab where the navigation occurs.
+  // TODO(hayato): Remove |web_contents_getter_| since we can create
+  // WebContentsGetter from |frame_tree_node_id_|.
   WebContentsGetter web_contents_getter_;
 
   // For service worker clients. See comments for the getter functions.
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index 85a7408..927e30d 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_register_job.h"
@@ -457,7 +458,8 @@
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
           helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */,
-          base::NullCallback(), &provider_info);
+          FrameTreeNode::kFrameTreeNodeInvalidId, base::NullCallback(),
+          &provider_info);
   remote_endpoints_.emplace_back();
   remote_endpoints_.back().BindForWindow(std::move(provider_info));
   auto container = std::make_unique<MockServiceWorkerContainer>(
@@ -493,7 +495,8 @@
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
           helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */,
-          base::NullCallback(), &provider_info);
+          FrameTreeNode::kFrameTreeNodeInvalidId, base::NullCallback(),
+          &provider_info);
   remote_endpoints_.emplace_back();
   remote_endpoints_.back().BindForWindow(std::move(provider_info));
   auto container = std::make_unique<MockServiceWorkerContainer>(
@@ -870,6 +873,7 @@
     base::WeakPtr<ServiceWorkerProviderHost> host =
         ServiceWorkerProviderHost::PreCreateNavigationHost(
             helper_->context()->AsWeakPtr(), true,
+            FrameTreeNode::kFrameTreeNodeInvalidId,
             base::RepeatingCallback<WebContents*(void)>(), &provider_info);
     ServiceWorkerRemoteProviderEndpoint remote_endpoint;
     remote_endpoint.BindForWindow(std::move(provider_info));
@@ -890,6 +894,7 @@
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
           helper_->context()->AsWeakPtr(), true,
+          FrameTreeNode::kFrameTreeNodeInvalidId,
           base::RepeatingCallback<WebContents*(void)>(), &provider_info);
   EXPECT_FALSE(host->is_response_committed());
   EXPECT_FALSE(host->is_execution_ready());
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index f08c13a..bdf62b7 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/debug/crash_logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
@@ -38,6 +37,33 @@
   return version->GetInfo();
 }
 
+void CrashBecauseIsController(
+    scoped_refptr<ServiceWorkerVersion> activating_version,
+    scoped_refptr<ServiceWorkerVersion> exiting_version,
+    ServiceWorkerProviderHost* provider_host) {
+  std::string debug_log = base::StringPrintf(
+      "sw:%d/ex_regid:%" PRId64 "/ac_regid:%" PRId64 "/pr_id:%d/ca:%d",
+      activating_version->skip_waiting(), exiting_version->registration_id(),
+      activating_version->registration_id(), provider_host->provider_id(),
+      provider_host->IsSetControllerRegistrationAllowed());
+  DEBUG_ALIAS_FOR_CSTR(debug_log_copy, debug_log.c_str(), 128);
+  CHECK(false) << "A controllee has a controller which became redundant.";
+}
+
+void CrashBecauseHasControllee(
+    scoped_refptr<ServiceWorkerVersion> activating_version,
+    scoped_refptr<ServiceWorkerVersion> exiting_version) {
+  std::string debug_log = base::StringPrintf(
+      "sw:%d/ex_regid:%" PRId64 "/ac_regid:%" PRId64 "/cl_ids:",
+      activating_version->skip_waiting(), exiting_version->registration_id(),
+      activating_version->registration_id());
+  for (auto& pair : exiting_version->controllee_map()) {
+    debug_log += pair.second->client_uuid() + ",";
+  }
+  DEBUG_ALIAS_FOR_CSTR(debug_log_copy, debug_log.c_str(), 1024);
+  CHECK(false) << "Redundant service worker has controllee(s).";
+}
+
 }  // namespace
 
 ServiceWorkerRegistration::ServiceWorkerRegistration(
@@ -442,11 +468,6 @@
   // TODO(crbug.com/951571): Remove this instrumentation logic once the bug is
   // debugged.
   if (exiting_version.get()) {
-    int skip_waiting = activating_version->skip_waiting();
-    static auto* key = base::debug::AllocateCrashKeyString(
-        "swv_skip_waiting", base::debug::CrashKeySize::Size32);
-    base::debug::ScopedCrashKeyString(key, base::NumberToString(skip_waiting));
-    CHECK(!exiting_version->HasControllee());
     for (std::unique_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
              context_->GetClientProviderHostIterator(
                  scope_.GetOrigin(), false /* include_reserved_clients */);
@@ -459,7 +480,14 @@
       ServiceWorkerVersion* controller = host->controller();
       if (!controller)
         continue;
-      CHECK(controller != exiting_version);
+      if (controller == exiting_version) {
+        CrashBecauseIsController(activating_version, exiting_version, host);
+        return;
+      }
+    }
+    if (exiting_version->HasControllee()) {
+      CrashBecauseHasControllee(activating_version, exiting_version);
+      return;
     }
   }
 
diff --git a/content/browser/service_worker/service_worker_request_handler.cc b/content/browser/service_worker/service_worker_request_handler.cc
index 409ee08..aac22927 100644
--- a/content/browser/service_worker/service_worker_request_handler.cc
+++ b/content/browser/service_worker/service_worker_request_handler.cc
@@ -65,7 +65,8 @@
   // Initialize the SWProviderHost.
   *out_provider_host = ServiceWorkerProviderHost::PreCreateNavigationHost(
       context->AsWeakPtr(), request_info.are_ancestors_secure,
-      std::move(web_contents_getter), &provider_info);
+      request_info.frame_tree_node_id, std::move(web_contents_getter),
+      &provider_info);
   navigation_handle_core->OnCreatedProviderHost(*out_provider_host,
                                                 std::move(provider_info));
 
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
index e7367dd..02201ba 100644
--- a/content/browser/service_worker/service_worker_test_utils.cc
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -10,6 +10,7 @@
 #include "base/barrier_closure.h"
 #include "base/run_loop.h"
 #include "base/time/time.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_database.h"
@@ -240,7 +241,8 @@
   auto provider_info = blink::mojom::ServiceWorkerProviderInfoForWindow::New();
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
-          context, is_parent_frame_secure, base::NullCallback(),
+          context, is_parent_frame_secure,
+          FrameTreeNode::kFrameTreeNodeInvalidId, base::NullCallback(),
           &provider_info);
   output_endpoint->BindForWindow(std::move(provider_info));
 
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index fa8ab95..af701dc 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/fake_embedded_worker_instance_client.h"
@@ -1282,7 +1283,8 @@
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateNavigationHost(
           helper_->context()->AsWeakPtr(), true /* is_parent_frame_secure */,
-          base::NullCallback(), &provider_info);
+          FrameTreeNode::kFrameTreeNodeInvalidId, base::NullCallback(),
+          &provider_info);
   remote_endpoint.BindForWindow(std::move(provider_info));
   host->UpdateUrls(registration_->scope(), registration_->scope());
   host->SetControllerRegistration(registration_,
diff --git a/content/browser/web_contents/frame_tree_node_id_registry.cc b/content/browser/web_contents/frame_tree_node_id_registry.cc
new file mode 100644
index 0000000..4f1bc04
--- /dev/null
+++ b/content/browser/web_contents/frame_tree_node_id_registry.cc
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_contents/frame_tree_node_id_registry.h"
+
+#include "content/browser/frame_host/frame_tree_node.h"
+
+namespace content {
+
+// static
+FrameTreeNodeIdRegistry* FrameTreeNodeIdRegistry::GetInstance() {
+  static base::NoDestructor<FrameTreeNodeIdRegistry> instance;
+  return instance.get();
+}
+
+void FrameTreeNodeIdRegistry::Add(const base::UnguessableToken& id,
+                                  const int frame_tree_node_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  bool inserted = (map_.emplace(id, frame_tree_node_id)).second;
+  CHECK(inserted);
+}
+
+void FrameTreeNodeIdRegistry::Remove(const base::UnguessableToken& id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  map_.erase(id);
+}
+
+int FrameTreeNodeIdRegistry::Get(const base::UnguessableToken& id) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto iter = map_.find(id);
+  if (iter == map_.end())
+    return FrameTreeNode::kFrameTreeNodeInvalidId;
+  return iter->second;
+}
+
+FrameTreeNodeIdRegistry::FrameTreeNodeIdRegistry() = default;
+
+FrameTreeNodeIdRegistry::~FrameTreeNodeIdRegistry() = default;
+
+}  // namespace content
diff --git a/content/browser/web_contents/frame_tree_node_id_registry.h b/content/browser/web_contents/frame_tree_node_id_registry.h
new file mode 100644
index 0000000..f456f58
--- /dev/null
+++ b/content/browser/web_contents/frame_tree_node_id_registry.h
@@ -0,0 +1,55 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEB_CONTENTS_FRAME_TREE_NODE_ID_REGISTRY_H_
+#define CONTENT_BROWSER_WEB_CONTENTS_FRAME_TREE_NODE_ID_REGISTRY_H_
+
+#include <map>
+
+#include "base/no_destructor.h"
+#include "base/sequence_checker.h"
+#include "base/unguessable_token.h"
+
+namespace content {
+
+// A global map of UnguessableToken to FrameTreeNode id. This registry lives
+// and is used only on the IO thread, as it's convenient for the current user
+// of the class (ServiceWorkerProviderHost, which should move to the UI thread
+// eventually).  This is currently used to map a network request to a frame so
+// that the network service can tell the browser to display tab-level UI
+// required for the request in certain cases, including client certificates and
+// basic HTTP authentication.
+//
+// It uses FrameTreeNode rather than RenderFrameHost because the lookup can
+// happen for browser-initiated navigation requests, where a RenderFrameHost
+// might not have been created yet.
+//
+// Warning: A corresponding frame may have changed security contexts since it
+// was added.  It's useful for looking up a WebContents or determining if it's a
+// main frame or not, but callers should not make assumptions that it's in the
+// same renderer process or origin as when it was added to the registry.
+class FrameTreeNodeIdRegistry {
+ public:
+  static FrameTreeNodeIdRegistry* GetInstance();
+
+  void Add(const base::UnguessableToken& id, const int frame_tree_node_id);
+  void Remove(const base::UnguessableToken&);
+  // Returns FrameTreeNodeId::kFrameTreeNodeIdInvalid if not found.
+  int Get(const base::UnguessableToken& id) const;
+
+ private:
+  friend class base::NoDestructor<FrameTreeNodeIdRegistry>;
+
+  FrameTreeNodeIdRegistry();
+  ~FrameTreeNodeIdRegistry();
+
+  std::map<base::UnguessableToken, int> map_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  DISALLOW_COPY_AND_ASSIGN(FrameTreeNodeIdRegistry);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_CONTENTS_FRAME_TREE_NODE_ID_REGISTRY_H_
diff --git a/content/browser/web_contents/web_contents_getter_registry.cc b/content/browser/web_contents/web_contents_getter_registry.cc
deleted file mode 100644
index ec36306..0000000
--- a/content/browser/web_contents/web_contents_getter_registry.cc
+++ /dev/null
@@ -1,51 +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 "content/browser/web_contents/web_contents_getter_registry.h"
-
-#include "base/no_destructor.h"
-
-namespace content {
-
-using WebContentsGetter = WebContentsGetterRegistry::WebContentsGetter;
-
-// static
-WebContentsGetterRegistry* WebContentsGetterRegistry::GetInstance() {
-  static base::NoDestructor<WebContentsGetterRegistry> instance;
-  return instance.get();
-}
-
-void WebContentsGetterRegistry::Add(
-    const base::UnguessableToken& id,
-    const WebContentsGetter& web_contents_getter) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  bool inserted = (map_.emplace(id, web_contents_getter)).second;
-  CHECK(inserted);
-}
-
-void WebContentsGetterRegistry::Remove(const base::UnguessableToken& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  map_.erase(id);
-}
-
-const WebContentsGetter& WebContentsGetterRegistry::Get(
-    const base::UnguessableToken& id) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto iter = map_.find(id);
-  if (iter == map_.end())
-    return GetNullGetter();
-  return iter->second;
-}
-
-WebContentsGetterRegistry::WebContentsGetterRegistry() = default;
-
-WebContentsGetterRegistry::~WebContentsGetterRegistry() = default;
-
-// static
-const WebContentsGetter& WebContentsGetterRegistry::GetNullGetter() {
-  static const base::NoDestructor<WebContentsGetter> null_getter;
-  return *null_getter;
-}
-
-}  // namespace content
diff --git a/content/browser/web_contents/web_contents_getter_registry.h b/content/browser/web_contents/web_contents_getter_registry.h
deleted file mode 100644
index de6ad44..0000000
--- a/content/browser/web_contents/web_contents_getter_registry.h
+++ /dev/null
@@ -1,49 +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 CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_GETTER_REGISTRY_H_
-#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_GETTER_REGISTRY_H_
-
-#include "base/callback_forward.h"
-#include "base/containers/flat_map.h"
-#include "base/sequence_checker.h"
-#include "base/unguessable_token.h"
-#include "content/public/browser/web_contents.h"
-
-namespace content {
-
-// A global map of UnguessableToken to WebContentsGetter. Unlike most other
-// WebContents related objects, this registry lives and is used only on the IO
-// thread, as it's convenient for the current user of the class
-// (ServiceWorkerProviderHost, which should move to the UI thread eventually).
-// However, the WebContentsGetter callbacks must only be run on the UI thread.
-class WebContentsGetterRegistry {
- public:
-  using WebContentsGetter = base::RepeatingCallback<WebContents*()>;
-
-  static WebContentsGetterRegistry* GetInstance();
-
-  void Add(const base::UnguessableToken& id,
-           const WebContentsGetter& web_contents_getter);
-  void Remove(const base::UnguessableToken&);
-  // Returns null getter if not found.
-  const WebContentsGetter& Get(const base::UnguessableToken& id) const;
-
- private:
-  friend class base::NoDestructor<WebContentsGetterRegistry>;
-
-  WebContentsGetterRegistry();
-  ~WebContentsGetterRegistry();
-
-  static const WebContentsGetter& GetNullGetter();
-
-  base::flat_map<base::UnguessableToken, WebContentsGetter> map_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  DISALLOW_COPY_AND_ASSIGN(WebContentsGetterRegistry);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_GETTER_REGISTRY_H_
diff --git a/content/browser/web_package/signed_exchange_cert_fetcher.cc b/content/browser/web_package/signed_exchange_cert_fetcher.cc
index c314a94..8537b8b 100644
--- a/content/browser/web_package/signed_exchange_cert_fetcher.cc
+++ b/content/browser/web_package/signed_exchange_cert_fetcher.cc
@@ -5,17 +5,20 @@
 #include "content/browser/web_package/signed_exchange_cert_fetcher.h"
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/format_macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
+#include "content/browser/data_url_loader_factory.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/web_package/signed_exchange_consts.h"
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
 #include "content/browser/web_package/signed_exchange_reporter.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
+#include "content/common/single_request_url_loader_factory.h"
 #include "content/common/throttling_url_loader.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/common/url_loader_throttle.h"
@@ -25,6 +28,7 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "services/network/loader_util.h"
+#include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace content {
@@ -139,6 +143,15 @@
     devtools_proxy_->CertificateRequestSent(*cert_request_id_,
                                             *resource_request_);
   }
+  // When NetworkService enabled, data URL is not handled by the passed
+  // URLRequestContext's SharedURLLoaderFactory.
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
+      resource_request_->url.SchemeIs(url::kDataScheme)) {
+    shared_url_loader_factory_ =
+        base::MakeRefCounted<SingleRequestURLLoaderFactory>(
+            base::BindOnce(&SignedExchangeCertFetcher::OnDataURLRequest,
+                           base::Unretained(this)));
+  }
   url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
       std::move(shared_url_loader_factory_), std::move(throttles_),
       0 /* routing_id */,
@@ -318,6 +331,17 @@
     Abort();
 }
 
+void SignedExchangeCertFetcher::OnDataURLRequest(
+    const network::ResourceRequest& resource_request,
+    network::mojom::URLLoaderRequest url_loader_request,
+    network::mojom::URLLoaderClientPtr url_loader_client_ptr) {
+  data_url_loader_factory_ = std::make_unique<DataURLLoaderFactory>();
+  data_url_loader_factory_->CreateLoaderAndStart(
+      std::move(url_loader_request), 0, 0, 0, resource_request,
+      std::move(url_loader_client_ptr),
+      net::MutableNetworkTrafficAnnotationTag(kCertFetcherTrafficAnnotation));
+}
+
 void SignedExchangeCertFetcher::MaybeNotifyCompletionToDevtools(
     const network::URLLoaderCompletionStatus& status) {
   if (!devtools_proxy_ || has_notified_completion_to_devtools_)
diff --git a/content/browser/web_package/signed_exchange_cert_fetcher.h b/content/browser/web_package/signed_exchange_cert_fetcher.h
index f4168db..6e46cc4 100644
--- a/content/browser/web_package/signed_exchange_cert_fetcher.h
+++ b/content/browser/web_package/signed_exchange_cert_fetcher.h
@@ -20,6 +20,9 @@
 
 namespace network {
 class SharedURLLoaderFactory;
+namespace mojom {
+class URLLoaderFactory;
+}  // namespace mojom
 }  // namespace network
 
 namespace mojo {
@@ -100,6 +103,10 @@
       mojo::ScopedDataPipeConsumerHandle body) override;
   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
 
+  void OnDataURLRequest(const network::ResourceRequest& resource_request,
+                        network::mojom::URLLoaderRequest,
+                        network::mojom::URLLoaderClientPtr);
+
   scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
   std::vector<std::unique_ptr<URLLoaderThrottle>> throttles_;
   std::unique_ptr<network::ResourceRequest> resource_request_;
@@ -118,6 +125,8 @@
   SignedExchangeReporter* reporter_;
   base::Optional<base::UnguessableToken> cert_request_id_;
 
+  std::unique_ptr<network::mojom::URLLoaderFactory> data_url_loader_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeCertFetcher);
 };
 
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index d47ab9f..a6012af 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -907,13 +908,17 @@
 
   base::Optional<std::string> GetInterceptedAcceptHeader(
       const GURL& url) const {
+    base::AutoLock lock(url_accept_header_map_lock_);
     const auto it = url_accept_header_map_.find(url);
     if (it == url_accept_header_map_.end())
       return base::nullopt;
     return it->second;
   }
 
-  void ClearInterceptedAcceptHeaders() { url_accept_header_map_.clear(); }
+  void ClearInterceptedAcceptHeaders() {
+    base::AutoLock lock(url_accept_header_map_lock_);
+    url_accept_header_map_.clear();
+  }
 
   net::EmbeddedTestServer https_server_;
 
@@ -963,6 +968,8 @@
     const auto it = request.headers.find(std::string(network::kAcceptHeader));
     if (it == request.headers.end())
       return;
+    // Note this method is called on the EmbeddedTestServer's background thread.
+    base::AutoLock lock(url_accept_header_map_lock_);
     url_accept_header_map_[request.base_url.Resolve(request.relative_url)] =
         it->second;
   }
@@ -970,6 +977,10 @@
   base::test::ScopedFeatureList feature_list_;
   base::test::ScopedFeatureList feature_list_for_accept_header_;
 
+  // url_accept_header_map_ is accessed both on the main thread and on the
+  // EmbeddedTestServer's background thread via MonitorRequest(), so it must be
+  // locked.
+  mutable base::Lock url_accept_header_map_lock_;
   std::map<GURL, std::string> url_accept_header_map_;
 };
 
diff --git a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
index 3877337..8442067 100644
--- a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
@@ -199,6 +199,68 @@
 }
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
+                       PrefetchedSignedExchangeCache_CrossOrigin) {
+  int target_sxg_fetch_count = 0;
+  const char* prefetch_path = "/prefetch.html";
+  const char* target_sxg_path = "/target.sxg";
+  const char* target_path = "/target.html";
+
+  base::RunLoop target_sxg_prefetch_waiter;
+  RegisterRequestMonitor(embedded_test_server(), target_sxg_path,
+                         &target_sxg_fetch_count, &target_sxg_prefetch_waiter);
+  RegisterRequestHandler(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_EQ(0, prefetch_url_loader_called_);
+
+  const GURL target_sxg_url =
+      embedded_test_server()->GetURL("sxg.example.com", target_sxg_path);
+  const GURL target_url =
+      embedded_test_server()->GetURL("publisher.example.com", target_path);
+
+  RegisterResponse(prefetch_path,
+                   ResponseEntry(base::StringPrintf(
+                       "<body><link rel='prefetch' href='%s'></body>",
+                       target_sxg_url.spec().c_str())));
+  RegisterResponse(
+      target_sxg_path,
+      // We mock the SignedExchangeHandler, so just return a HTML content
+      // as "application/signed-exchange;v=b3".
+      ResponseEntry("<head><title>Prefetch Target (SXG)</title></head>",
+                    "application/signed-exchange;v=b3",
+                    {{"x-content-type-options", "nosniff"}}));
+
+  const net::SHA256HashValue target_header_integrity = {{0x01}};
+  MockSignedExchangeHandlerFactory factory({MockSignedExchangeHandlerParams(
+      target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK, target_url,
+      "text/html", {}, target_header_integrity)});
+  ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
+
+  NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
+  target_sxg_prefetch_waiter.Run();
+  EXPECT_EQ(1, target_sxg_fetch_count);
+
+  WaitUntilLoaded(target_sxg_url);
+
+  EXPECT_EQ(1, prefetch_url_loader_called_);
+
+  const auto cached_exchanges = GetCachedExchanges();
+  EXPECT_EQ(1u, cached_exchanges.size());
+  const auto it = cached_exchanges.find(target_sxg_url);
+  ASSERT_TRUE(it != cached_exchanges.end());
+  EXPECT_EQ(target_sxg_url, it->second->outer_url());
+  EXPECT_EQ(target_url, it->second->inner_url());
+  EXPECT_EQ(target_header_integrity, *it->second->header_integrity());
+
+  // Shutdown the server.
+  EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+
+  // Subsequent navigation to the target URL wouldn't hit the network for
+  // the target URL. The target content should still be read correctly.
+  // The content is loaded from PrefetchedSignedExchangeCache.
+  NavigateToURLAndWaitTitle(target_sxg_url, "Prefetch Target (SXG)");
+}
+
+IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
                        PrefetchedSignedExchangeCache_SameUrl) {
   int target_sxg_fetch_count = 0;
   const char* prefetch_path = "/prefetch.html";
@@ -414,6 +476,116 @@
 }
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
+                       PrefetchAlternativeSubresourceSXG_CrossOrigin) {
+  int script_sxg_fetch_count = 0;
+  int script_fetch_count = 0;
+  const char* prefetch_path = "/prefetch.html";
+  const char* target_sxg_path = "/target.sxg";
+  const char* target_path = "/target.html";
+  const char* script_sxg_path = "/script_js.sxg";
+  const char* script_path = "/script.js";
+
+  base::RunLoop script_sxg_prefetch_waiter;
+  RegisterRequestMonitor(embedded_test_server(), script_sxg_path,
+                         &script_sxg_fetch_count, &script_sxg_prefetch_waiter);
+  base::RunLoop script_prefetch_waiter;
+  RegisterRequestMonitor(embedded_test_server(), script_path,
+                         &script_fetch_count, &script_prefetch_waiter);
+  RegisterRequestHandler(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_EQ(0, prefetch_url_loader_called_);
+
+  const GURL target_sxg_url =
+      embedded_test_server()->GetURL("sxg.example.com", target_sxg_path);
+  const GURL target_url =
+      embedded_test_server()->GetURL("publisher.example.com", target_path);
+  const GURL script_sxg_url =
+      embedded_test_server()->GetURL("sxg.example.com", script_sxg_path);
+  const GURL script_url =
+      embedded_test_server()->GetURL("publisher.example.com", script_path);
+
+  const net::SHA256HashValue target_header_integrity = {{0x01}};
+  const net::SHA256HashValue script_header_integrity = {{0x02}};
+  const std::string script_header_integrity_string =
+      GetHeaderIntegrityString(script_header_integrity);
+
+  const std::string outer_link_header = base::StringPrintf(
+      "<%s>;"
+      "rel=\"alternate\";"
+      "type=\"application/signed-exchange;v=b3\";"
+      "anchor=\"%s\"",
+      script_sxg_url.spec().c_str(), script_url.spec().c_str());
+  const std::string inner_link_headers = base::StringPrintf(
+      "Link: "
+      "<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
+      "<%s>;rel=\"preload\";as=\"script\"",
+      script_url.spec().c_str(), script_header_integrity_string.c_str(),
+      script_url.spec().c_str());
+
+  RegisterResponse(prefetch_path,
+                   ResponseEntry(base::StringPrintf(
+                       "<body><link rel='prefetch' href='%s'></body>",
+                       target_sxg_url.spec().c_str())));
+  RegisterResponse(script_path, ResponseEntry("document.title=\"from server\";",
+                                              "text/javascript"));
+  RegisterResponse(target_sxg_path,
+                   // We mock the SignedExchangeHandler, so just return a HTML
+                   // content as "application/signed-exchange;v=b3".
+                   ResponseEntry("<head><title>Prefetch Target (SXG)</title>"
+                                 "<script src=\"./script.js\"></script></head>",
+                                 "application/signed-exchange;v=b3",
+                                 {{"x-content-type-options", "nosniff"},
+                                  {"link", outer_link_header}}));
+  RegisterResponse(script_sxg_path,
+                   // We mock the SignedExchangeHandler, so just return a JS
+                   // content as "application/signed-exchange;v=b3".
+                   ResponseEntry("document.title=\"done\";",
+                                 "application/signed-exchange;v=b3",
+                                 {{"x-content-type-options", "nosniff"}}));
+  MockSignedExchangeHandlerFactory factory(
+      {MockSignedExchangeHandlerParams(
+           target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
+           target_url, "text/html", {inner_link_headers},
+           target_header_integrity),
+       MockSignedExchangeHandlerParams(
+           script_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
+           script_url, "text/javascript", {}, script_header_integrity)});
+  ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
+
+  NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
+  script_sxg_prefetch_waiter.Run();
+  EXPECT_EQ(1, script_sxg_fetch_count);
+  EXPECT_EQ(0, script_fetch_count);
+
+  WaitUntilLoaded(target_sxg_url);
+  WaitUntilLoaded(script_sxg_url);
+
+  const auto cached_exchanges = GetCachedExchanges();
+  EXPECT_EQ(2u, cached_exchanges.size());
+
+  const auto target_it = cached_exchanges.find(target_sxg_url);
+  ASSERT_TRUE(target_it != cached_exchanges.end());
+  EXPECT_EQ(target_sxg_url, target_it->second->outer_url());
+  EXPECT_EQ(target_url, target_it->second->inner_url());
+  EXPECT_EQ(target_header_integrity, *target_it->second->header_integrity());
+
+  const auto script_it = cached_exchanges.find(script_sxg_url);
+  ASSERT_TRUE(script_it != cached_exchanges.end());
+  EXPECT_EQ(script_sxg_url, script_it->second->outer_url());
+  EXPECT_EQ(script_url, script_it->second->inner_url());
+  EXPECT_EQ(script_header_integrity, *script_it->second->header_integrity());
+
+  // Subsequent navigation to the target URL wouldn't hit the network for
+  // the target URL. The target content should still be read correctly.
+  // The content is loaded from PrefetchedSignedExchangeCache. And the script
+  // is also loaded from PrefetchedSignedExchangeCache.
+  NavigateToURLAndWaitTitle(target_sxg_url, "done");
+
+  EXPECT_EQ(1, script_sxg_fetch_count);
+  EXPECT_EQ(0, script_fetch_count);
+}
+
+IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
                        PrefetchAlternativeSubresourceSXG_DifferentOrigin) {
   int script_sxg_fetch_count = 0;
   int script_fetch_count = 0;
diff --git a/content/public/browser/cache_storage_context.h b/content/public/browser/cache_storage_context.h
index 81205ba..50d2820 100644
--- a/content/public/browser/cache_storage_context.h
+++ b/content/public/browser/cache_storage_context.h
@@ -24,7 +24,7 @@
       base::OnceCallback<void(const std::vector<StorageUsageInfo>& usage_info)>;
 
   // Methods used in response to browsing data and quota manager requests.
-  // Must be called on the IO thread.
+  // May be called on any sequence.
   virtual void GetAllOriginsInfo(GetUsageInfoCallback callback) = 0;
   virtual void DeleteForOrigin(const GURL& origin_url) = 0;
 
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index da482a1a..5e23081 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -979,7 +979,8 @@
 }
 #endif
 
-base::flat_set<std::string> ContentBrowserClient::GetMimeHandlerViewMimeTypes(
+base::flat_set<std::string>
+ContentBrowserClient::GetPluginMimeTypesWithExternalHandlers(
     ResourceContext* resource_context) {
   return base::flat_set<std::string>();
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 5e79a99..847b6c2 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1556,8 +1556,8 @@
   virtual WideColorGamutHeuristic GetWideColorGamutHeuristic() const;
 #endif
 
-  // Obtains the list of MIME types that are handled by a MimeHandlerView.
-  virtual base::flat_set<std::string> GetMimeHandlerViewMimeTypes(
+  // Obtains the list of MIME types that are for plugins with external handlers.
+  virtual base::flat_set<std::string> GetPluginMimeTypesWithExternalHandlers(
       ResourceContext* resource_context);
 
   // Possibly augment |download_policy| based on the status of |frame_host| as
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 1b53f29..e9382cd 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -87,6 +87,11 @@
 const base::Feature kBlockCredentialedSubresources{
     "BlockCredentialedSubresources", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Controls whether Bundled HTTP Exchanges is enabled.
+// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html
+const base::Feature kBundledHTTPExchanges{"BundledHTTPExchanges",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables code caching for inline scripts.
 const base::Feature kCacheInlineScriptCode{"CacheInlineScriptCode",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 9cf79b7c..44b242b 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -32,6 +32,7 @@
 CONTENT_EXPORT extern const base::Feature kBlinkHeapUnifiedGCScheduling;
 CONTENT_EXPORT extern const base::Feature kBloatedRendererDetection;
 CONTENT_EXPORT extern const base::Feature kBlockCredentialedSubresources;
+CONTENT_EXPORT extern const base::Feature kBundledHTTPExchanges;
 CONTENT_EXPORT extern const base::Feature kCacheInlineScriptCode;
 CONTENT_EXPORT extern const base::Feature kCanvas2DImageChromium;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition;
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index 41d7d15..1ac134a 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -23,7 +23,7 @@
   return nullptr;
 }
 
-bool ContentRendererClient::MaybeCreateMimeHandlerView(
+bool ContentRendererClient::IsPluginHandledExternally(
     RenderFrame* embedder_frame,
     const blink::WebElement& owner_element,
     const GURL& original_url,
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 5bcf6f9..fa2e153 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -87,17 +87,17 @@
   // none.
   virtual SkBitmap* GetSadWebViewBitmap();
 
-  // Returns true if the embedder renders the contents of the |plugin_element|
-  // in a cross-process frame using MimeHandlerView.
-  virtual bool MaybeCreateMimeHandlerView(
+  // Returns true if the embedder renders the contents of the |plugin_element|,
+  // using external handlers, in a cross-process frame.
+  virtual bool IsPluginHandledExternally(
       RenderFrame* embedder_frame,
       const blink::WebElement& plugin_element,
       const GURL& original_url,
       const std::string& original_mime_type);
 
   // Returns a scriptable object which implements custom javascript API for the
-  // given element. This is used for MimeHandlerView in providing API such as
-  // |postMessage| for <embed> and <object>.
+  // given element. This is used for external plugin handlers for providing
+  // custom API such as|postMessage| for <embed> and <object>.
   virtual v8::Local<v8::Object> GetScriptableObject(
       const blink::WebElement& plugin_element,
       v8::Isolate* isolate);
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 9b756cf..48e5cf6 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -633,7 +633,7 @@
     "//third_party/libyuv",
     "//third_party/opus",
     "//third_party/sqlite",
-    "//third_party/webrtc/api:create_peerconnection_factory",
+    "//third_party/webrtc/api:callfactory_api",
     "//third_party/webrtc/api:libjingle_logging_api",
     "//third_party/webrtc/api:libjingle_peerconnection_api",
     "//third_party/webrtc/api:rtc_stats_api",
@@ -654,12 +654,14 @@
     "//third_party/webrtc/api/audio_codecs/opus:audio_decoder_opus",
     "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_multiopus",
     "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_opus",
+    "//third_party/webrtc/api/rtc_event_log:rtc_event_log_factory",
     "//third_party/webrtc/api/video:video_bitrate_allocation",
     "//third_party/webrtc/api/video:video_frame",
     "//third_party/webrtc/api/video:video_frame_i420",
     "//third_party/webrtc/api/video_codecs:rtc_software_fallback_wrappers",
     "//third_party/webrtc/api/video_codecs:video_codecs_api",
     "//third_party/webrtc/common_video:common_video",
+    "//third_party/webrtc/media:rtc_audio_video",
     "//third_party/webrtc/media:rtc_internal_video_codecs",
     "//third_party/webrtc/media:rtc_media",
     "//third_party/webrtc/media:rtc_media_base",
diff --git a/content/renderer/accessibility/blink_ax_enum_conversion.cc b/content/renderer/accessibility/blink_ax_enum_conversion.cc
index cca4b0d..023b603 100644
--- a/content/renderer/accessibility/blink_ax_enum_conversion.cc
+++ b/content/renderer/accessibility/blink_ax_enum_conversion.cc
@@ -31,6 +31,11 @@
   if (o.IsDefault())
     dst->AddState(ax::mojom::State::kDefault);
 
+  // aria-grabbed is deprecated in WAI-ARIA 1.1.
+  if (o.IsGrabbed() != blink::kWebAXGrabbedStateUndefined)
+    dst->AddBoolAttribute(ax::mojom::BoolAttribute::kGrabbed,
+                          o.IsGrabbed() == blink::kWebAXGrabbedStateTrue);
+
   if (o.IsHovered())
     dst->AddState(ax::mojom::State::kHovered);
 
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 4b8e094..37fc22b 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -1106,6 +1106,15 @@
     dst->AddStringAttribute(ax::mojom::StringAttribute::kImageDataUrl,
                             src.ImageDataUrl(max_image_data_size_).Utf8());
   }
+
+  // aria-dropeffect is deprecated in WAI-ARIA 1.1.
+  WebVector<ax::mojom::Dropeffect> src_dropeffects;
+  src.Dropeffects(src_dropeffects);
+  if (!src_dropeffects.empty()) {
+    for (auto&& dropeffect : src_dropeffects) {
+      dst->AddDropeffect(dropeffect);
+    }
+  }
 }
 
 blink::WebDocument BlinkAXTreeSource::GetMainDocument() const {
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 5a2dfec5..c6d68e1 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -1123,6 +1123,7 @@
   response->SetRequestId(request_id);
   response->SetIsSignedExchangeInnerResponse(
       info.is_signed_exchange_inner_response);
+  response->SetWasInPrefetchCache(info.was_in_prefetch_cache);
 
   SetSecurityStyleAndDetails(url, info, response, report_security_info);
 
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
index 393926e..f9edc70 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
@@ -59,13 +59,18 @@
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_local_frame.h"
-#include "third_party/webrtc/api/create_peerconnection_factory.h"
+#include "third_party/webrtc/api/call/call_factory_interface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/rtc_event_log/rtc_event_log_factory.h"
 #include "third_party/webrtc/api/video_track_source_proxy.h"
 #include "third_party/webrtc/media/engine/fake_video_codec_factory.h"
 #include "third_party/webrtc/media/engine/multiplex_codec_factory.h"
+#include "third_party/webrtc/media/engine/webrtc_media_engine.h"
+#include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
 #include "third_party/webrtc/modules/video_coding/codecs/h264/include/h264.h"
 #include "third_party/webrtc/rtc_base/ref_counted_object.h"
 #include "third_party/webrtc/rtc_base/ssl_adapter.h"
+#include "third_party/webrtc_overrides/task_queue_factory.h"
 
 namespace content {
 
@@ -313,12 +318,24 @@
         std::make_unique<webrtc::FakeVideoDecoderFactory>();
   }
 
-  pc_factory_ = webrtc::CreatePeerConnectionFactory(
-      worker_thread_ /* network thread */, worker_thread_, signaling_thread_,
-      audio_device_.get(), CreateWebrtcAudioEncoderFactory(),
-      CreateWebrtcAudioDecoderFactory(), std::move(webrtc_encoder_factory),
-      std::move(webrtc_decoder_factory), nullptr /* audio_mixer */,
-      nullptr /* audio_processing */);
+  webrtc::PeerConnectionFactoryDependencies pcf_deps;
+  pcf_deps.worker_thread = worker_thread_;
+  pcf_deps.network_thread = worker_thread_;
+  pcf_deps.signaling_thread = signaling_thread_;
+  pcf_deps.task_queue_factory = CreateWebRtcTaskQueueFactory();
+  pcf_deps.call_factory = webrtc::CreateCallFactory();
+  pcf_deps.event_log_factory = std::make_unique<webrtc::RtcEventLogFactory>(
+      pcf_deps.task_queue_factory.get());
+  cricket::MediaEngineDependencies media_deps;
+  media_deps.task_queue_factory = pcf_deps.task_queue_factory.get();
+  media_deps.adm = audio_device_.get();
+  media_deps.audio_encoder_factory = CreateWebrtcAudioEncoderFactory();
+  media_deps.audio_decoder_factory = CreateWebrtcAudioDecoderFactory();
+  media_deps.video_encoder_factory = std::move(webrtc_encoder_factory);
+  media_deps.video_decoder_factory = std::move(webrtc_decoder_factory);
+  media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create();
+  pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
+  pc_factory_ = webrtc::CreateModularPeerConnectionFactory(std::move(pcf_deps));
   CHECK(pc_factory_.get());
 
   webrtc::PeerConnectionFactoryInterface::Options factory_options;
diff --git a/content/renderer/media/webrtc/rtc_rtp_source.cc b/content/renderer/media/webrtc/rtc_rtp_source.cc
index 0a4ace3..9b7bee96 100644
--- a/content/renderer/media/webrtc/rtc_rtp_source.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_source.cc
@@ -49,4 +49,8 @@
   return std::pow(10.0, -(double)rfc_level / 20.0);
 }
 
+uint32_t RTCRtpSource::RtpTimestamp() const {
+  return source_.rtp_timestamp();
+}
+
 }  // namespace content
diff --git a/content/renderer/media/webrtc/rtc_rtp_source.h b/content/renderer/media/webrtc/rtc_rtp_source.h
index 353d4de..8821d2d 100644
--- a/content/renderer/media/webrtc/rtc_rtp_source.h
+++ b/content/renderer/media/webrtc/rtc_rtp_source.h
@@ -22,6 +22,7 @@
   double TimestampMs() const override;
   uint32_t Source() const override;
   base::Optional<double> AudioLevel() const override;
+  uint32_t RtpTimestamp() const override;
 
  private:
   const webrtc::RtpSource source_;
diff --git a/content/renderer/media/webrtc/rtc_stats.cc b/content/renderer/media/webrtc/rtc_stats.cc
index 0d8bde55..7686d75d 100644
--- a/content/renderer/media/webrtc/rtc_stats.cc
+++ b/content/renderer/media/webrtc/rtc_stats.cc
@@ -35,6 +35,9 @@
     whitelisted_stats_types_.insert(webrtc::RTCRTPStreamStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCInboundRTPStreamStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCOutboundRTPStreamStats::kType);
+    whitelisted_stats_types_.insert(webrtc::RTCMediaSourceStats::kType);
+    whitelisted_stats_types_.insert(webrtc::RTCAudioSourceStats::kType);
+    whitelisted_stats_types_.insert(webrtc::RTCVideoSourceStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCTransportStats::kType);
   }
 
diff --git a/content/renderer/navigation_client.cc b/content/renderer/navigation_client.cc
index 36593d8..9780522 100644
--- a/content/renderer/navigation_client.cc
+++ b/content/renderer/navigation_client.cc
@@ -58,8 +58,8 @@
 
 void NavigationClient::Bind(mojom::NavigationClientAssociatedRequest request) {
   navigation_client_binding_.Bind(
-      std::move(request),
-      render_frame_->GetTaskRunner(blink::TaskType::kInternalNavigation));
+      std::move(request), render_frame_->GetTaskRunner(
+                              blink::TaskType::kInternalNavigationAssociated));
   SetDisconnectionHandler();
 }
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 0985b34..c0db728 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3963,13 +3963,13 @@
 // blink::WebLocalFrameClient implementation
 // ----------------------------------------
 
-bool RenderFrameImpl::MaybeCreateMimeHandlerView(
+bool RenderFrameImpl::IsPluginHandledExternally(
     const blink::WebElement& plugin_element,
     const blink::WebURL& url,
     const blink::WebString& suggested_mime_type) {
   DCHECK(content::MimeHandlerViewMode::UsesCrossProcessFrame());
 #if BUILDFLAG(ENABLE_PLUGINS)
-  return GetContentClient()->renderer()->MaybeCreateMimeHandlerView(
+  return GetContentClient()->renderer()->IsPluginHandledExternally(
       this, plugin_element, GURL(url), suggested_mime_type.Utf8());
 #else
   return false;
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index e17758e..0c561c5 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1358,7 +1358,7 @@
 
   std::unique_ptr<blink::WebSocketHandshakeThrottle>
   CreateWebSocketHandshakeThrottle() override;
-  bool MaybeCreateMimeHandlerView(
+  bool IsPluginHandledExternally(
       const blink::WebElement& plugin_element,
       const blink::WebURL& url,
       const blink::WebString& suggested_mime_type) override;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index ee84cf4e2..3561a4f 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1069,6 +1069,10 @@
   if (!frame)
     return;
 
+  GetChannel()->AddListenerTaskRunner(
+      routing_id,
+      frame->GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
+
   scoped_refptr<PendingFrameCreate> create(it->second);
   frame->BindFrame(it->second->browser_info(), it->second->TakeFrameRequest());
   pending_frame_creates_.erase(it);
@@ -1076,6 +1080,7 @@
 
 void RenderThreadImpl::RemoveRoute(int32_t routing_id) {
   ChildThreadImpl::GetRouter()->RemoveRoute(routing_id);
+  GetChannel()->RemoveListenerTaskRunner(routing_id);
 }
 
 void RenderThreadImpl::RegisterPendingFrameCreate(
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 18d525e..8f51921 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -323,7 +323,7 @@
     "//net:test_support",
     "//ppapi/buildflags",
     "//sandbox",
-    "//services/device/public/cpp/test:test_support",
+    "//services/device/public/cpp:test_support",
     "//services/device/public/mojom",
     "//services/network/public/cpp",
     "//services/service_manager/embedder:embedder_result_codes",
@@ -663,7 +663,6 @@
   mac_xib_bundle_data("content_shell_framework_xibs") {
     sources = [
       "app/English.lproj/HttpAuth.xib",
-      "app/English.lproj/MainMenu.xib",
     ]
     output_path = "{{bundle_resources_dir}}/English.lproj"
   }
diff --git a/content/shell/app/English.lproj/MainMenu.xib b/content/shell/app/English.lproj/MainMenu.xib
deleted file mode 100644
index 63147be..0000000
--- a/content/shell/app/English.lproj/MainMenu.xib
+++ /dev/null
@@ -1,344 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
-    <dependencies>
-        <deployment version="1090" identifier="macosx"/>
-        <development version="5100" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application"/>
-        <menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
-            <items>
-                <menuItem title="Content Shell" id="56">
-                    <menu key="submenu" title="Content Shell" systemMenu="apple" id="57">
-                        <items>
-                            <menuItem title="About Content Shell" id="58">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="236">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121"/>
-                            <menuItem isSeparatorItem="YES" id="143">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Services" id="131">
-                                <menu key="submenu" title="Services" systemMenu="services" id="130"/>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="144">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Hide Content Shell" keyEquivalent="h" id="134">
-                                <connections>
-                                    <action selector="hide:" target="-1" id="367"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Hide Others" keyEquivalent="h" id="145">
-                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
-                                <connections>
-                                    <action selector="hideOtherApplications:" target="-1" id="368"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Show All" id="150">
-                                <connections>
-                                    <action selector="unhideAllApplications:" target="-1" id="370"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="149">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Quit Content Shell" keyEquivalent="q" id="136" userLabel="1111">
-                                <connections>
-                                    <action selector="terminate:" target="-1" id="369"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="File" id="83">
-                    <menu key="submenu" title="File" id="81">
-                        <items>
-                            <menuItem title="New" keyEquivalent="n" id="82" userLabel="9">
-                                <connections>
-                                    <action selector="newDocument:" target="-1" id="373"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Open…" keyEquivalent="o" id="72">
-                                <connections>
-                                    <action selector="openDocument:" target="-1" id="374"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Open Recent" id="124">
-                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
-                                    <items>
-                                        <menuItem title="Clear Menu" id="126">
-                                            <connections>
-                                                <action selector="clearRecentDocuments:" target="-1" id="127"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="79" userLabel="7">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Close" keyEquivalent="w" id="73" userLabel="1">
-                                <connections>
-                                    <action selector="performClose:" target="-1" id="193"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Save" keyEquivalent="s" id="75" userLabel="3">
-                                <connections>
-                                    <action selector="saveDocument:" target="-1" id="362"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Save As…" keyEquivalent="S" id="80" userLabel="8">
-                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
-                                <connections>
-                                    <action selector="saveDocumentAs:" target="-1" id="363"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Revert to Saved" id="112" userLabel="10">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="revertDocumentToSaved:" target="-1" id="364"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="74" userLabel="2">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Page Setup..." keyEquivalent="P" id="77" userLabel="5">
-                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
-                                <connections>
-                                    <action selector="runPageLayout:" target="-1" id="87"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Print…" keyEquivalent="p" id="78" userLabel="6">
-                                <connections>
-                                    <action selector="print:" target="-1" id="86"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Edit" id="217">
-                    <menu key="submenu" title="Edit" id="205">
-                        <items>
-                            <menuItem title="Undo" keyEquivalent="z" id="207">
-                                <connections>
-                                    <action selector="undo:" target="-1" id="223"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Redo" keyEquivalent="Z" id="215">
-                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
-                                <connections>
-                                    <action selector="redo:" target="-1" id="231"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="206">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Cut" keyEquivalent="x" id="199">
-                                <connections>
-                                    <action selector="cut:" target="-1" id="228"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Copy" keyEquivalent="c" id="197">
-                                <connections>
-                                    <action selector="copy:" target="-1" id="224"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Paste" keyEquivalent="v" id="203">
-                                <connections>
-                                    <action selector="paste:" target="-1" id="226"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Delete" id="202">
-                                <connections>
-                                    <action selector="delete:" target="-1" id="235"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Select All" keyEquivalent="a" id="198">
-                                <connections>
-                                    <action selector="selectAll:" target="-1" id="232"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="214">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Find" id="218">
-                                <menu key="submenu" title="Find" id="220">
-                                    <items>
-                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
-                                            <connections>
-                                                <action selector="performFindPanelAction:" target="-1" id="241"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
-                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
-                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
-                                        </menuItem>
-                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
-                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="210">
-                                            <connections>
-                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem title="Spelling and Grammar" id="216">
-                                <menu key="submenu" title="Spelling and Grammar" id="200">
-                                    <items>
-                                        <menuItem title="Show Spelling…" keyEquivalent=":" id="204">
-                                            <connections>
-                                                <action selector="showGuessPanel:" target="-1" id="230"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Check Spelling" keyEquivalent=";" id="201">
-                                            <connections>
-                                                <action selector="checkSpelling:" target="-1" id="225"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Check Spelling While Typing" id="219">
-                                            <connections>
-                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Check Grammar With Spelling" id="346">
-                                            <connections>
-                                                <action selector="toggleGrammarChecking:" target="-1" id="347"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem title="Substitutions" id="348">
-                                <menu key="submenu" title="Substitutions" id="349">
-                                    <items>
-                                        <menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
-                                            <connections>
-                                                <action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
-                                            <connections>
-                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
-                                            <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
-                                            <connections>
-                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem title="Speech" id="211">
-                                <menu key="submenu" title="Speech" id="212">
-                                    <items>
-                                        <menuItem title="Start Speaking" id="196">
-                                            <connections>
-                                                <action selector="startSpeaking:" target="-1" id="233"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Stop Speaking" id="195">
-                                            <connections>
-                                                <action selector="stopSpeaking:" target="-1" id="227"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Format" id="299">
-                    <menu key="submenu" title="Format" id="300">
-                        <items>
-                            <menuItem title="Show Fonts" keyEquivalent="t" id="344"/>
-                            <menuItem title="Show Colors" keyEquivalent="C" id="345">
-                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
-                                <connections>
-                                    <action selector="orderFrontColorPanel:" target="-1" id="361"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="View" id="295">
-                    <menu key="submenu" title="View" id="296">
-                        <items>
-                            <menuItem title="Show Toolbar" keyEquivalent="t" id="297">
-                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
-                                <connections>
-                                    <action selector="toggleToolbarShown:" target="-1" id="366"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Customize Toolbar…" id="298">
-                                <connections>
-                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Debug" id="440">
-                    <modifierMask key="keyEquivalentModifierMask"/>
-                    <menu key="submenu" title="Debug" id="441">
-                        <items>
-                            <menuItem title="Show Developer Tools" id="442">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="showDevTools:" target="-1" id="443"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Window" id="19">
-                    <menu key="submenu" title="Window" systemMenu="window" id="24">
-                        <items>
-                            <menuItem title="Minimize" keyEquivalent="m" id="23">
-                                <connections>
-                                    <action selector="performMiniaturize:" target="-1" id="37"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Zoom" id="239">
-                                <connections>
-                                    <action selector="performZoom:" target="-1" id="240"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="92">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Bring All to Front" id="5">
-                                <connections>
-                                    <action selector="arrangeInFront:" target="-1" id="39"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Help" id="103" userLabel="1">
-                    <menu key="submenu" title="Help" id="106" userLabel="2">
-                        <items>
-                            <menuItem title="Content Shell Help" keyEquivalent="?" id="111">
-                                <connections>
-                                    <action selector="showHelp:" target="-1" id="360"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-            </items>
-        </menu>
-        <userDefaultsController representsSharedInstance="YES" id="389"/>
-    </objects>
-</document>
diff --git a/content/shell/app/app-Info.plist b/content/shell/app/app-Info.plist
index 6fcaf9f..af17b42 100644
--- a/content/shell/app/app-Info.plist
+++ b/content/shell/app/app-Info.plist
@@ -20,8 +20,6 @@
 	<string>APPL</string>
 	<key>CFBundleVersion</key>
 	<string>1.0</string>
-	<key>NSMainNibFile</key>
-	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
 	<string>NSApplication</string>
 	<key>LSFileQuarantineEnabled</key>
diff --git a/content/shell/browser/shell_browser_main_parts_mac.mm b/content/shell/browser/shell_browser_main_parts_mac.mm
index 04c7f4d..311f2a9 100644
--- a/content/shell/browser/shell_browser_main_parts_mac.mm
+++ b/content/shell/browser/shell_browser_main_parts_mac.mm
@@ -10,16 +10,128 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/mac/sdk_forward_declarations.h"
 
+namespace {
+
+base::scoped_nsobject<NSMenuItem> CreateMenuItem(NSString* title,
+                                                 SEL action,
+                                                 NSString* key_equivalent) {
+  return base::scoped_nsobject<NSMenuItem>([[NSMenuItem alloc]
+      initWithTitle:title
+             action:action
+      keyEquivalent:key_equivalent]);
+}
+
+// The App Menu refers to the dropdown titled "Content Shell".
+base::scoped_nsobject<NSMenu> BuildAppMenu() {
+  // The title is not used, as the title will always be the name of the App.
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]);
+
+  base::scoped_nsobject<NSMenuItem> item =
+      CreateMenuItem(@"Hide Content Shell", @selector(hide:), @"h");
+  [menu addItem:item];
+
+  item =
+      CreateMenuItem(@"Hide Others", @selector(hideOtherApplications:), @"h");
+  item.get().keyEquivalentModifierMask =
+      NSEventModifierFlagOption | NSEventModifierFlagCommand;
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Show All", @selector(unhideAllApplications:), @"");
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Quit Content Shell", @selector(terminate:), @"q");
+  [menu addItem:item];
+
+  return menu;
+}
+
+base::scoped_nsobject<NSMenu> BuildFileMenu() {
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"File"]);
+  base::scoped_nsobject<NSMenuItem> item =
+      CreateMenuItem(@"New", @selector(newDocument:), @"n");
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Close", @selector(performClose:), @"w");
+  [menu addItem:item];
+  return menu;
+}
+
+base::scoped_nsobject<NSMenu> BuildEditMenu() {
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Edit"]);
+
+  base::scoped_nsobject<NSMenuItem> item =
+      CreateMenuItem(@"Undo", @selector(undo:), @"z");
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Redo", @selector(redo:), @"Z");
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Cut", @selector(cut:), @"x");
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Copy", @selector(copy:), @"c");
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Paste", @selector(paste:), @"v");
+  [menu addItem:item];
+
+  item = CreateMenuItem(@"Select All", @selector(selectAll:), @"a");
+  [menu addItem:item];
+  return menu;
+}
+
+base::scoped_nsobject<NSMenu> BuildViewMenu() {
+  // AppKit auto-populates this menu.
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"View"]);
+  return menu;
+}
+
+base::scoped_nsobject<NSMenu> BuildDebugMenu() {
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Debug"]);
+
+  base::scoped_nsobject<NSMenuItem> item =
+      CreateMenuItem(@"Show Developer Tools", @selector(showDevTools:), @"");
+  [menu addItem:item];
+  return menu;
+}
+
+base::scoped_nsobject<NSMenu> BuildWindowMenu() {
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Window"]);
+
+  base::scoped_nsobject<NSMenuItem> item =
+      CreateMenuItem(@"Minimize", @selector(performMiniaturize:), @"m");
+  [menu addItem:item];
+  item = CreateMenuItem(@"Zoom", @selector(performZoom:), @"");
+  [menu addItem:item];
+  item = CreateMenuItem(@"Bring All To Front", @selector(arrangeInFront:), @"");
+  [menu addItem:item];
+  return menu;
+}
+
+base::scoped_nsobject<NSMenu> BuildMainMenu() {
+  base::scoped_nsobject<NSMenu> main_menu([[NSMenu alloc] initWithTitle:@""]);
+
+  using Builder = base::scoped_nsobject<NSMenu> (*)();
+  static const Builder kBuilderFuncs[] = {&BuildAppMenu,   &BuildFileMenu,
+                                          &BuildEditMenu,  &BuildViewMenu,
+                                          &BuildDebugMenu, &BuildWindowMenu};
+  for (auto* builder : kBuilderFuncs) {
+    NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:@""
+                                                   action:NULL
+                                            keyEquivalent:@""] autorelease];
+    item.submenu = builder();
+    [main_menu addItem:item];
+  }
+  return main_menu;
+}
+
+}  // namespace
+
 namespace content {
 
 void ShellBrowserMainParts::PreMainMessageLoopStart() {
-  base::scoped_nsobject<NSNib> nib(
-      [[NSNib alloc] initWithNibNamed:@"MainMenu"
-                               bundle:base::mac::FrameworkBundle()]);
-  NSArray* top_level_objects = nil;
-  [nib instantiateWithOwner:NSApp topLevelObjects:nil];
-  for (NSObject* object : top_level_objects)
-    [object retain];
+  base::scoped_nsobject<NSMenu> main_menu = BuildMainMenu();
+  [[NSApplication sharedApplication] setMainMenu:main_menu];
 }
 
 }  // namespace content
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index 42ba9c9..ae5cd848 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -2056,8 +2056,11 @@
 
   blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
       url, blink::WebString::FromUTF8(destination_protocol),
-      blink::WebString::FromUTF8(destination_host),
-      allow_destination_subdomains,
+      blink::WebString::FromUTF8(destination_host), /*destination_port=*/0,
+      allow_destination_subdomains
+          ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
+          : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
 }
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 8544050..fa8885bb 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1035,8 +1035,8 @@
     "//services/content/public/cpp",
     "//services/content/public/mojom",
     "//services/device/public/cpp:device_features",
+    "//services/device/public/cpp:test_support",
     "//services/device/public/cpp/generic_sensor",
-    "//services/device/public/cpp/test:test_support",
     "//services/device/public/mojom",
     "//services/device/public/mojom:generic_sensor",
     "//services/image_annotation/public/cpp:cpp",
@@ -1994,8 +1994,8 @@
     "//printing",
     "//services/audio/public/cpp:test_support",
     "//services/audio/public/mojom",
+    "//services/device/public/cpp:test_support",
     "//services/device/public/cpp/generic_sensor",
-    "//services/device/public/cpp/test:test_support",
     "//services/device/public/mojom",
     "//services/file:lib",
     "//services/file/public/mojom",
diff --git a/content/test/data/accessibility/aria/aria-dropeffect-expected-android.txt b/content/test/data/accessibility/aria/aria-dropeffect-expected-android.txt
index a9c3977ce..d2a4e6385 100644
--- a/content/test/data/accessibility/aria/aria-dropeffect-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-dropeffect-expected-android.txt
@@ -1,23 +1,8 @@
 android.webkit.WebView focusable focused scrollable
-++android.widget.ListView collection item_count=7 row_count=7
-++++android.view.View collection_item
-++++++android.view.View name='• '
-++++++android.view.View name='copy'
-++++android.view.View collection_item item_index=1 row_index=1
-++++++android.view.View name='• '
-++++++android.view.View name='move'
-++++android.view.View collection_item item_index=2 row_index=2
-++++++android.view.View name='• '
-++++++android.view.View name='link'
-++++android.view.View collection_item item_index=3 row_index=3
-++++++android.view.View name='• '
-++++++android.view.View name='execute'
-++++android.view.View collection_item item_index=4 row_index=4
-++++++android.view.View name='• '
-++++++android.view.View name='popup'
-++++android.view.View collection_item item_index=5 row_index=5
-++++++android.view.View name='• '
-++++++android.view.View name='none(default)'
-++++android.view.View collection_item item_index=6 row_index=6
-++++++android.view.View name='• '
-++++++android.view.View name='link'
+++android.view.View name='copy'
+++android.view.View name='move'
+++android.view.View name='link'
+++android.view.View name='execute'
+++android.view.View name='popup'
+++android.view.View name='none(default)'
+++android.view.View name='link popup'
diff --git a/content/test/data/accessibility/aria/aria-dropeffect-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-dropeffect-expected-auralinux.txt
index 708cdd1..ff196c67 100644
--- a/content/test/data/accessibility/aria/aria-dropeffect-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-dropeffect-expected-auralinux.txt
@@ -1,23 +1,15 @@
 [document web]
-++[list]
-++++[list item] dropeffect:copy
-++++++[static] name='• '
-++++++[text] name='copy'
-++++[list item] dropeffect:move
-++++++[static] name='• '
-++++++[text] name='move'
-++++[list item] dropeffect:link
-++++++[static] name='• '
-++++++[text] name='link'
-++++[list item] dropeffect:execute
-++++++[static] name='• '
-++++++[text] name='execute'
-++++[list item] dropeffect:popup
-++++++[static] name='• '
-++++++[text] name='popup'
-++++[list item] dropeffect:none
-++++++[static] name='• '
-++++++[text] name='none(default)'
-++++[list item] dropeffect:link|popup
-++++++[static] name='• '
-++++++[text] name='link'
+++[section] dropeffect:copy
+++++[text] name='copy'
+++[section] dropeffect:move
+++++[text] name='move'
+++[section] dropeffect:link
+++++[text] name='link'
+++[section] dropeffect:execute
+++++[text] name='execute'
+++[section] dropeffect:popup
+++++[text] name='popup'
+++[section] dropeffect:none
+++++[text] name='none(default)'
+++[section] dropeffect:link popup
+++++[text] name='link popup'
diff --git a/content/test/data/accessibility/aria/aria-dropeffect-expected-blink.txt b/content/test/data/accessibility/aria/aria-dropeffect-expected-blink.txt
new file mode 100644
index 0000000..cf2fb62
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-dropeffect-expected-blink.txt
@@ -0,0 +1,22 @@
+rootWebArea
+++genericContainer dropeffect=copy
+++++staticText name='copy'
+++++++inlineTextBox name='copy'
+++genericContainer dropeffect=move
+++++staticText name='move'
+++++++inlineTextBox name='move'
+++genericContainer dropeffect=link
+++++staticText name='link'
+++++++inlineTextBox name='link'
+++genericContainer dropeffect=execute
+++++staticText name='execute'
+++++++inlineTextBox name='execute'
+++genericContainer dropeffect=popup
+++++staticText name='popup'
+++++++inlineTextBox name='popup'
+++genericContainer dropeffect=none
+++++staticText name='none(default)'
+++++++inlineTextBox name='none(default)'
+++genericContainer dropeffect=link popup
+++++staticText name='link popup'
+++++++inlineTextBox name='link popup'
diff --git a/content/test/data/accessibility/aria/aria-dropeffect-expected-mac.txt b/content/test/data/accessibility/aria/aria-dropeffect-expected-mac.txt
index 7305c2c..5acffa9 100644
--- a/content/test/data/accessibility/aria/aria-dropeffect-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-dropeffect-expected-mac.txt
@@ -1,23 +1,15 @@
 AXWebArea
-++AXList
-++++AXGroup AXDropEffects='copy'
-++++++AXListMarker AXValue='• '
-++++++AXStaticText AXValue='copy'
-++++AXGroup AXDropEffects='move'
-++++++AXListMarker AXValue='• '
-++++++AXStaticText AXValue='move'
-++++AXGroup AXDropEffects='link'
-++++++AXListMarker AXValue='• '
-++++++AXStaticText AXValue='link'
-++++AXGroup AXDropEffects='execute'
-++++++AXListMarker AXValue='• '
-++++++AXStaticText AXValue='execute'
-++++AXGroup AXDropEffects='popup'
-++++++AXListMarker AXValue='• '
-++++++AXStaticText AXValue='popup'
-++++AXGroup AXDropEffects='none'
-++++++AXListMarker AXValue='• '
-++++++AXStaticText AXValue='none(default)'
-++++AXGroup AXDropEffects='link|popup'
-++++++AXListMarker AXValue='• '
-++++++AXStaticText AXValue='link'
+++AXGroup AXDropEffects='copy'
+++++AXStaticText AXValue='copy'
+++AXGroup AXDropEffects='move'
+++++AXStaticText AXValue='move'
+++AXGroup AXDropEffects='link'
+++++AXStaticText AXValue='link'
+++AXGroup AXDropEffects='execute'
+++++AXStaticText AXValue='execute'
+++AXGroup AXDropEffects='popup'
+++++AXStaticText AXValue='popup'
+++AXGroup AXDropEffects='none'
+++++AXStaticText AXValue='none(default)'
+++AXGroup AXDropEffects='link popup'
+++++AXStaticText AXValue='link popup'
diff --git a/content/test/data/accessibility/aria/aria-dropeffect-expected-win.txt b/content/test/data/accessibility/aria/aria-dropeffect-expected-win.txt
index 5b453d7..c9020839 100644
--- a/content/test/data/accessibility/aria/aria-dropeffect-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-dropeffect-expected-win.txt
@@ -1,23 +1,15 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_LIST READONLY
-++++ROLE_SYSTEM_LISTITEM READONLY dropeffect:copy
-++++++ROLE_SYSTEM_STATICTEXT name='• '
-++++++ROLE_SYSTEM_STATICTEXT name='copy'
-++++ROLE_SYSTEM_LISTITEM READONLY dropeffect:move
-++++++ROLE_SYSTEM_STATICTEXT name='• '
-++++++ROLE_SYSTEM_STATICTEXT name='move'
-++++ROLE_SYSTEM_LISTITEM READONLY dropeffect:link
-++++++ROLE_SYSTEM_STATICTEXT name='• '
-++++++ROLE_SYSTEM_STATICTEXT name='link'
-++++ROLE_SYSTEM_LISTITEM READONLY dropeffect:execute
-++++++ROLE_SYSTEM_STATICTEXT name='• '
-++++++ROLE_SYSTEM_STATICTEXT name='execute'
-++++ROLE_SYSTEM_LISTITEM READONLY dropeffect:popup
-++++++ROLE_SYSTEM_STATICTEXT name='• '
-++++++ROLE_SYSTEM_STATICTEXT name='popup'
-++++ROLE_SYSTEM_LISTITEM READONLY dropeffect:none
-++++++ROLE_SYSTEM_STATICTEXT name='• '
-++++++ROLE_SYSTEM_STATICTEXT name='none(default)'
-++++ROLE_SYSTEM_LISTITEM READONLY dropeffect:link|popup
-++++++ROLE_SYSTEM_STATICTEXT name='• '
-++++++ROLE_SYSTEM_STATICTEXT name='link'
+++IA2_ROLE_SECTION dropeffect:copy
+++++ROLE_SYSTEM_STATICTEXT name='copy'
+++IA2_ROLE_SECTION dropeffect:move
+++++ROLE_SYSTEM_STATICTEXT name='move'
+++IA2_ROLE_SECTION dropeffect:link
+++++ROLE_SYSTEM_STATICTEXT name='link'
+++IA2_ROLE_SECTION dropeffect:execute
+++++ROLE_SYSTEM_STATICTEXT name='execute'
+++IA2_ROLE_SECTION dropeffect:popup
+++++ROLE_SYSTEM_STATICTEXT name='popup'
+++IA2_ROLE_SECTION dropeffect:none
+++++ROLE_SYSTEM_STATICTEXT name='none(default)'
+++IA2_ROLE_SECTION dropeffect:link popup
+++++ROLE_SYSTEM_STATICTEXT name='link popup'
diff --git a/content/test/data/accessibility/aria/aria-dropeffect.html b/content/test/data/accessibility/aria/aria-dropeffect.html
index d030974..881c72e 100644
--- a/content/test/data/accessibility/aria/aria-dropeffect.html
+++ b/content/test/data/accessibility/aria/aria-dropeffect.html
@@ -2,13 +2,17 @@
 @MAC-ALLOW:AXDropEffects*
 @WIN-ALLOW:dropeffect*
 @AURALINUX-ALLOW:dropeffect*
+@BLINK-ALLOW:dropeffect*
 -->
-<ul>
-  <li aria-dropeffect="copy">copy</li>
-  <li aria-dropeffect="move">move</li>
-  <li aria-dropeffect="link">link</li>
-  <li aria-dropeffect="execute">execute</li>
-  <li aria-dropeffect="popup">popup</li>
-  <li aria-dropeffect="none">none(default)</li>
-  <li aria-dropeffect="link|popup">link</li>
-</ul>
+<!DOCTYPE HTML>
+<html>
+<body>
+  <div aria-dropeffect="copy">copy</div>
+  <div aria-dropeffect="move">move</div>
+  <div aria-dropeffect="link">link</div>
+  <div aria-dropeffect="execute">execute</div>
+  <div aria-dropeffect="popup">popup</div>
+  <div aria-dropeffect="none">none(default)</div>
+  <div aria-dropeffect="link popup">link popup</div>
+</body>
+</html>
diff --git a/content/test/data/accessibility/aria/aria-empty-string-expected-blink.txt b/content/test/data/accessibility/aria/aria-empty-string-expected-blink.txt
index 43dbee9..2f1b1dc 100644
--- a/content/test/data/accessibility/aria/aria-empty-string-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-empty-string-expected-blink.txt
@@ -5,7 +5,7 @@
 ++treeItem name='Checked undefined' selected=false
 ++genericContainer name='Current undefined'
 ++genericContainer name='Disabled undefined'
-++genericContainer name='Dropeffect undefined'
+++genericContainer name='Dropeffect undefined' dropeffect=none
 ++treeItem name='Expanded undefined' selected=false
 ++genericContainer name='Grabbed undefined'
 ++button name='Haspopup undefined'
@@ -22,4 +22,4 @@
 ++tree multiselectable vertical
 ++++treeItem name='Selected undefined' selected=false
 ++columnHeader name='Sort undefined' selected=false
-++genericContainer name='Hidden undefined, display block'
\ No newline at end of file
+++genericContainer name='Hidden undefined, display block'
diff --git a/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt
index 062a09d..780db7e 100644
--- a/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-illegal-val-expected-auralinux.txt
@@ -5,9 +5,9 @@
 ++[tree item] name='Checked illegal' checked selectable checkable:true
 ++[section] name='Current illegal' current:true
 ++[section] name='Disabled illegal'
-++[section] name='Dropeffect illegal' dropeffect:X-ILLEGAL
+++[section] name='Dropeffect illegal' dropeffect:none
 ++[tree item] name='Expanded illegal' expanded selectable
-++[section] name='Grabbed illegal' grabbed:X-ILLEGAL
+++[section] name='Grabbed illegal'
 ++[push button] name='Haspopup illegal' haspopup:menu
 ++[section] name='Invalid illegal' invalid-entry
 ++[log] name='Live illegal' atomic:false live:X-ILLEGAL relevant:additions text
diff --git a/content/test/data/accessibility/aria/aria-illegal-val-expected-blink.txt b/content/test/data/accessibility/aria/aria-illegal-val-expected-blink.txt
index 3d549f3..574dab9dc 100644
--- a/content/test/data/accessibility/aria/aria-illegal-val-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-illegal-val-expected-blink.txt
@@ -5,7 +5,7 @@
 ++treeItem name='Checked illegal' checkedState=true selected=false
 ++genericContainer name='Current illegal' ariaCurrentState=true
 ++genericContainer name='Disabled illegal' restriction=disabled
-++genericContainer name='Dropeffect illegal'
+++genericContainer name='Dropeffect illegal' dropeffect=none
 ++treeItem expanded name='Expanded illegal' selected=false
 ++genericContainer name='Grabbed illegal'
 ++popUpButton name='Haspopup illegal' haspopup=menu
diff --git a/content/test/data/accessibility/aria/aria-illegal-val-expected-win.txt b/content/test/data/accessibility/aria/aria-illegal-val-expected-win.txt
index e50228b..3b4b898 100644
--- a/content/test/data/accessibility/aria/aria-illegal-val-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-illegal-val-expected-win.txt
@@ -5,9 +5,9 @@
 ++ROLE_SYSTEM_OUTLINEITEM name='Checked illegal' CHECKED IA2_STATE_CHECKABLE checkable:true
 ++IA2_ROLE_SECTION name='Current illegal' current:true
 ++IA2_ROLE_SECTION name='Disabled illegal' UNAVAILABLE
-++IA2_ROLE_SECTION name='Dropeffect illegal' dropeffect:X-ILLEGAL
+++IA2_ROLE_SECTION name='Dropeffect illegal' dropeffect:none
 ++ROLE_SYSTEM_OUTLINEITEM name='Expanded illegal' EXPANDED
-++IA2_ROLE_SECTION name='Grabbed illegal' grabbed:X-ILLEGAL
+++IA2_ROLE_SECTION name='Grabbed illegal'
 ++ROLE_SYSTEM_BUTTONMENU name='Haspopup illegal' HASPOPUP haspopup:menu
 ++IA2_ROLE_SECTION name='Invalid illegal' IA2_STATE_INVALID_ENTRY
 ++ROLE_SYSTEM_CLIENT name='Live illegal' live:X-ILLEGAL relevant:additions text atomic:false
diff --git a/content/test/data/accessibility/aria/aria-undefined-expected-blink.txt b/content/test/data/accessibility/aria/aria-undefined-expected-blink.txt
index 43dbee9..19ddc0c6 100644
--- a/content/test/data/accessibility/aria/aria-undefined-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-undefined-expected-blink.txt
@@ -22,4 +22,4 @@
 ++tree multiselectable vertical
 ++++treeItem name='Selected undefined' selected=false
 ++columnHeader name='Sort undefined' selected=false
-++genericContainer name='Hidden undefined, display block'
\ No newline at end of file
+++genericContainer name='Hidden undefined, display block'
diff --git a/content/test/data/accessibility/aria/aria-undefined-literal-expected-blink.txt b/content/test/data/accessibility/aria/aria-undefined-literal-expected-blink.txt
index 43dbee9..2f1b1dc 100644
--- a/content/test/data/accessibility/aria/aria-undefined-literal-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-undefined-literal-expected-blink.txt
@@ -5,7 +5,7 @@
 ++treeItem name='Checked undefined' selected=false
 ++genericContainer name='Current undefined'
 ++genericContainer name='Disabled undefined'
-++genericContainer name='Dropeffect undefined'
+++genericContainer name='Dropeffect undefined' dropeffect=none
 ++treeItem name='Expanded undefined' selected=false
 ++genericContainer name='Grabbed undefined'
 ++button name='Haspopup undefined'
@@ -22,4 +22,4 @@
 ++tree multiselectable vertical
 ++++treeItem name='Selected undefined' selected=false
 ++columnHeader name='Sort undefined' selected=false
-++genericContainer name='Hidden undefined, display block'
\ No newline at end of file
+++genericContainer name='Hidden undefined, display block'
diff --git a/content/test/data/accessibility/event/aria-dropeffect-changed-expected-uia-win.txt b/content/test/data/accessibility/event/aria-dropeffect-changed-expected-uia-win.txt
new file mode 100644
index 0000000..073c1f8
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-dropeffect-changed-expected-uia-win.txt
@@ -0,0 +1,3 @@
+AriaProperties changed on role=heading, name=Item2
+AriaProperties changed on role=heading, name=Item3
+AriaProperties changed on role=heading, name=Item4
diff --git a/content/test/data/accessibility/event/aria-dropeffect-changed.html b/content/test/data/accessibility/event/aria-dropeffect-changed.html
new file mode 100644
index 0000000..73d3a867
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-dropeffect-changed.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <h4 id="d1" aria-dropeffect="">Item1</h4>
+  <h4 id="d2" aria-dropeffect="move">Item2</h4>
+  <h4 id="d3" aria-dropeffect="move">Item3</h4>
+  <h4 id="d4" aria-dropeffect="copy">Item4</h4>
+  <script>
+    function go() {
+      // The dropeffect from default->none; should not fire an event.
+      document.getElementById('d1').setAttribute('aria-dropeffect', 'none');
+
+      // Set dropeffect from move->none; should fire an event.
+      document.getElementById('d2').setAttribute('aria-dropeffect', 'none');
+
+      // Set dropeffect from move->[removed]; should fire an event.
+      document.getElementById('d3').removeAttribute('aria-dropeffect');
+
+      // Set dropeffect from copy->|copy move|; should fire an event.
+      document.getElementById('d4').setAttribute('aria-dropeffect', 'copy move');
+    }
+  </script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/aria-grabbed-changed-expected-uia-win.txt b/content/test/data/accessibility/event/aria-grabbed-changed-expected-uia-win.txt
new file mode 100644
index 0000000..d5810d5
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-grabbed-changed-expected-uia-win.txt
@@ -0,0 +1,4 @@
+AriaProperties changed on role=heading, name=Item1
+AriaProperties changed on role=heading, name=Item2
+AriaProperties changed on role=heading, name=Item3
+AriaProperties changed on role=heading, name=Item4
diff --git a/content/test/data/accessibility/event/aria-grabbed-changed.html b/content/test/data/accessibility/event/aria-grabbed-changed.html
new file mode 100644
index 0000000..d7f7e0b
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-grabbed-changed.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <h4 id="d1">Item1</h4>
+  <h4 id="d2" aria-grabbed="true">Item2</h4>
+  <h4 id="d3" aria-grabbed="false">Item3</h4>
+  <h4 id="d4" aria-grabbed="true">Item4</h4>
+  <script>
+    function go() {
+      // Set aria-grabbed from undefined [removed]->false; should fire an event.
+      document.getElementById('d1').setAttribute('aria-grabbed', 'false');
+
+      // Set aria-grabbed from true->false; should fire an event.
+      document.getElementById('d2').setAttribute('aria-grabbed', 'false');
+
+      // Set aria-grabbed from false->true; should fire an event.
+      document.getElementById('d3').setAttribute('aria-grabbed', 'true');
+
+      // Set aria-grabbed from true->undefined [removed]; should fire an event.
+      document.getElementById('d4').removeAttribute('aria-grabbed');
+    }
+  </script>
+</body>
+</html>
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index a1656732..4b3227bb 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -34,7 +34,8 @@
 crbug.com/609629 [ android ] ContextLost_WebGLContextLostInHiddenTab [ Skip ]
 
 # Flaking on Nexus 5X
-crbug.com/879423 [ android qualcomm-adreno-(tm)-418 ] ContextLost_WebGLUnblockedAfterUserInitiatedReload [ RetryOnFailure ]
+crbug.com/965268 [ android qualcomm-adreno-(tm)-418 ] ContextLost_WebGLBlockedAfterJSNavigation [ Skip ]
+crbug.com/965268 [ android qualcomm-adreno-(tm)-418 ] ContextLost_WebGLUnblockedAfterUserInitiatedReload [ Skip ]
 crbug.com/880078 [ android ] ContextLost_WorkerRAFAfterGPUCrash [ Failure ]
 crbug.com/880078 [ android ] ContextLost_WorkerRAFAfterGPUCrash_OOPD [ Failure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index d9ebced..f2378955 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -33,4 +33,4 @@
 
 # Flaky for unknown reason.
 crbug.com/960007 [ win10 nvidia ] OverlayModeTraceTest_DirectComposition_Nonroot [ RetryOnFailure ]
-crbug.com/965730 [ android qualcomm-adreno-(tm)-418 ] TraceTest_Video_Context_Loss_VP9 [ RetryOnFailure ]
+crbug.com/965730 [ android qualcomm-adreno-(tm)-418 ] TraceTest_Video_Context_Loss_VP9 [ Skip ]
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 6fdf8f8..bf35694 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -492,9 +492,9 @@
     "//extensions/shell:app_shell_lib",
     "//extensions/shell:browser_tests",
     "//net:test_support",
+    "//services/device/public/cpp:test_support",
     "//services/device/public/cpp/hid",
     "//services/device/public/cpp/hid:test_support",
-    "//services/device/public/cpp/test:test_support",
     "//services/device/public/mojom",
     "//services/service_manager/public/cpp",
     "//ui/display:test_support",
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 5ebc8465..feb42c8 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -460,7 +460,7 @@
       "//extensions/browser",
       "//extensions/strings",
       "//ipc",
-      "//services/device/public/cpp/test:test_support",
+      "//services/device/public/cpp:test_support",
       "//testing/gmock",
       "//testing/gtest",
       "//ui/base",
diff --git a/extensions/common/cors_util.cc b/extensions/common/cors_util.cc
index 522af77f..bc6edc02 100644
--- a/extensions/common/cors_util.cc
+++ b/extensions/common/cors_util.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/strings/string_number_conversions.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
@@ -17,6 +18,15 @@
 
 namespace {
 
+uint16_t GetEffectivePort(const std::string& port_string) {
+  int port_int = 0;
+  bool success = base::StringToInt(port_string, &port_int);
+  // The URLPattern should verify that |port| is a number or "*", so conversion
+  // should never fail.
+  DCHECK(success) << port_string;
+  return port_int;
+}
+
 void AddURLPatternSetToList(
     const URLPatternSet& pattern_set,
     std::vector<network::mojom::CorsOriginPatternPtr>* list,
@@ -36,13 +46,22 @@
     for (const char* const scheme : kSchemes) {
       if (!pattern.MatchesScheme(scheme))
         continue;
-      // TODO(crbug.com/936900): Specify the port argument.
-      list->push_back(network::mojom::CorsOriginPattern::New(
-          scheme, pattern.host(), /*port=*/0,
+      network::mojom::CorsDomainMatchMode domain_match_mode =
           pattern.match_subdomains()
               ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
-              : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
-          network::mojom::CorsPortMatchMode::kAllowAnyPort, priority));
+              : network::mojom::CorsDomainMatchMode::kDisallowSubdomains;
+      network::mojom::CorsPortMatchMode port_match_mode =
+          (pattern.port() == "*")
+              ? network::mojom::CorsPortMatchMode::kAllowAnyPort
+              : network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort;
+      uint16_t port =
+          (port_match_mode ==
+           network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort)
+              ? GetEffectivePort(pattern.port())
+              : 0u;
+      list->push_back(network::mojom::CorsOriginPattern::New(
+          scheme, pattern.host(), port, domain_match_mode, port_match_mode,
+          priority));
     }
   }
 }
@@ -86,7 +105,6 @@
       network::mojom::CorsOriginAccessMatchPriority::kLowPriority);
 
   GURL webstore_launch_url = extension_urls::GetWebstoreLaunchURL();
-  // TODO(crbug.com/936900): Specify the port argument.
   block_list.push_back(network::mojom::CorsOriginPattern::New(
       webstore_launch_url.scheme(), webstore_launch_url.host(), /*port=*/0,
       network::mojom::CorsDomainMatchMode::kAllowSubdomains,
diff --git a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
index 361cf11..34cb6ff1 100644
--- a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
+++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
@@ -194,9 +194,11 @@
     case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
     case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
     case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED:
+    case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED:
     case ui::AXEventGenerator::Event::ENABLED_CHANGED:
     case ui::AXEventGenerator::Event::FLOW_FROM_CHANGED:
     case ui::AXEventGenerator::Event::FLOW_TO_CHANGED:
+    case ui::AXEventGenerator::Event::GRABBED_CHANGED:
     case ui::AXEventGenerator::Event::HASPOPUP_CHANGED:
     case ui::AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED:
     case ui::AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED:
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index c1b342c..c19c99b 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -1227,23 +1227,17 @@
   ExtensionsClient::Get()->AddOriginAccessPermissions(
       extension, IsExtensionActive(extension.id()), &allow_list);
   for (const auto& entry : allow_list) {
-    // TODO(crbug.com/936900): Use port information.
     WebSecurityPolicy::AddOriginAccessAllowListEntry(
         extension.url(), WebString::FromUTF8(entry->protocol),
-        WebString::FromUTF8(entry->domain),
-        entry->domain_match_mode ==
-            network::mojom::CorsDomainMatchMode::kAllowSubdomains,
-        entry->priority);
+        WebString::FromUTF8(entry->domain), entry->port,
+        entry->domain_match_mode, entry->port_match_mode, entry->priority);
   }
 
   for (const auto& entry : CreateCorsOriginAccessBlockList(extension)) {
-    // TODO(crbug.com/936900): Use port information.
     WebSecurityPolicy::AddOriginAccessBlockListEntry(
         extension.url(), WebString::FromUTF8(entry->protocol),
-        WebString::FromUTF8(entry->domain),
-        entry->domain_match_mode ==
-            network::mojom::CorsDomainMatchMode::kAllowSubdomains,
-        entry->priority);
+        WebString::FromUTF8(entry->domain), entry->port,
+        entry->domain_match_mode, entry->port_match_mode, entry->priority);
   }
 }
 
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.h b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.h
index 09b41a0..74796246 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.h
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_frame_container.h
@@ -5,6 +5,7 @@
 #ifndef EXTENSIONS_RENDERER_GUEST_VIEW_MIME_HANDLER_VIEW_MIME_HANDLER_VIEW_FRAME_CONTAINER_H_
 #define EXTENSIONS_RENDERER_GUEST_VIEW_MIME_HANDLER_VIEW_MIME_HANDLER_VIEW_FRAME_CONTAINER_H_
 
+#include "components/guest_view/common/guest_view_constants.h"
 #include "extensions/renderer/guest_view/mime_handler_view/post_message_support.h"
 #include "third_party/blink/public/web/web_element.h"
 
@@ -84,7 +85,7 @@
   const std::string mime_type_;
   // The |element_instance_id| of the MimeHandlerViewGuest associated with this
   // frame container. This is updated in DidLoad().
-  int32_t element_instance_id_;
+  int32_t element_instance_id_ = guest_view::kInstanceIDNone;
   // The routing ID of the content frame (frame or proxy) and guest frame
   // (proxy) which will be confirmed by the browser. Used to validate the
   // destination for postMessage.
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 2d93ae4..d868bed 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -6,7 +6,6 @@
 import("//build/config/ui.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
-import("//third_party/protobuf/proto_library.gni")
 import("//ui/gl/features.gni")
 
 config("gpu_implementation") {
@@ -197,68 +196,6 @@
   ]
 }
 
-if (!is_android && !is_fuchsia) {
-  proto_library("gl_lpm_fuzzer_proto") {
-    sources = [
-      "command_buffer/tests/lpm/gl_lpm_fuzzer.proto",
-    ]
-  }
-
-  static_library("gl_lpm_shader_to_string") {
-    sources = [
-      "command_buffer/tests/lpm/gl_lpm_shader_to_string.cc",
-      "command_buffer/tests/lpm/gl_lpm_shader_to_string.h",
-    ]
-
-    deps = [
-      ":gl_lpm_fuzzer_proto",
-      "//base:base",
-    ]
-  }
-
-  test("gl_lpm_shader_to_string_unittest") {
-    sources = [
-      "command_buffer/tests/lpm/gl_lpm_shader_to_string_unittest.cc",
-    ]
-
-    deps = [
-      ":gl_lpm_shader_to_string",
-      "//base/test:run_all_unittests",
-      "//testing/gtest",
-      "//third_party/protobuf:protobuf_full",
-    ]
-  }
-
-  fuzzer_test("gl_lpm_fuzzer") {
-    sources = [
-      "command_buffer/tests/gl_manager.cc",
-      "command_buffer/tests/gl_manager.h",
-      "command_buffer/tests/gl_test_utils.cc",
-      "command_buffer/tests/gl_test_utils.h",
-      "command_buffer/tests/lpm/gl_lpm_fuzzer.cc",
-    ]
-
-    defines = [ "GL_GLEXT_PROTOTYPES" ]
-
-    deps = [
-      ":gl_lpm_fuzzer_proto",
-      ":gl_lpm_shader_to_string",
-      ":gles2",
-      ":test_support",
-      "//gpu/command_buffer/client:gles2_c_lib",
-      "//gpu/command_buffer/client:gles2_implementation",
-      "//gpu/command_buffer/common:gles2_utils",
-      "//gpu/ipc:gl_in_process_context",
-      "//gpu/ipc/service:service",
-      "//testing/gtest:gtest",
-      "//third_party/libprotobuf-mutator",
-      "//ui/gfx:gfx",
-      "//ui/gl:gl",
-      "//ui/gl/init:init",
-    ]
-  }
-}
-
 test("gl_tests") {
   sources = [
     "command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc",
diff --git a/gpu/DEPS b/gpu/DEPS
index 37916e44..020fd3a 100644
--- a/gpu/DEPS
+++ b/gpu/DEPS
@@ -5,7 +5,7 @@
   "+third_party/re2",
   "+third_party/smhasher",
   "+third_party/swiftshader",
-  "+third_party/protobuf",
+  "+third_party/protbuf",
   "+third_party/zlib",
   "+crypto",
   "+ui/gfx",
diff --git a/gpu/command_buffer/tests/gl_test_utils.cc b/gpu/command_buffer/tests/gl_test_utils.cc
index e08984a..25346d7 100644
--- a/gpu/command_buffer/tests/gl_test_utils.cc
+++ b/gpu/command_buffer/tests/gl_test_utils.cc
@@ -78,14 +78,14 @@
 }
 
 bool GLTestHelper::CheckGLError(const char* msg, int line) {
-  bool success = true;
-  GLenum error = GL_NO_ERROR;
-  while ((error = glGetError()) != GL_NO_ERROR) {
-    success = false;
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), error)
-        << "GL ERROR in " << msg << " at line " << line << " : " << error;
-  }
-  return success;
+   bool success = true;
+   GLenum error = GL_NO_ERROR;
+   while ((error = glGetError()) != GL_NO_ERROR) {
+     success = false;
+     EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), error)
+         << "GL ERROR in " << msg << " at line " << line << " : " << error;
+   }
+   return success;
 }
 
 GLuint GLTestHelper::CompileShader(GLenum type, const char* shaderSrc) {
@@ -277,7 +277,7 @@
   uint8_t clr_important[4];
 };
 
-}  // namespace
+}
 
 bool GLTestHelper::SaveBackbufferAsBMP(
     const char* filename, int width, int height) {
diff --git a/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.cc b/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.cc
deleted file mode 100644
index 2b5ac2d..0000000
--- a/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef GL_GLEXT_PROTOTYPES
-#define GL_GLEXT_PROTOTYPES
-#endif
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES2/gl2extchromium.h>
-#include <GLES3/gl3.h>
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/i18n/icu_util.h"
-#include "base/strings/string_split.h"
-#include "gpu/command_buffer/client/gles2_lib.h"
-#include "gpu/command_buffer/tests/gl_manager.h"
-#include "gpu/command_buffer/tests/gl_test_utils.h"
-#include "gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.pb.h"
-#include "gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.h"
-#include "gpu/config/gpu_test_config.h"
-#include "testing/libfuzzer/proto/lpm_interface.h"
-#include "ui/gfx/extension_set.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_version_info.h"
-#include "ui/gl/init/gl_factory.h"
-
-struct Env {
-  Env() {
-    CHECK(base::i18n::InitializeICU());
-    base::CommandLine::Init(0, nullptr);
-    auto* command_line = base::CommandLine::ForCurrentProcess();
-
-    // TODO(nedwill): support switches for swiftshader, etc.
-    command_line->AppendSwitchASCII(switches::kUseGL,
-                                    gl::kGLImplementationANGLEName);
-    command_line->AppendSwitchASCII(switches::kUseANGLE,
-                                    gl::kANGLEImplementationNullName);
-    base::FeatureList::InitializeInstance(std::string(), std::string());
-    base::MessageLoopForIO message_loop;
-    gpu::GLTestHelper::InitializeGLDefault();
-    ::gles2::Initialize();
-  }
-};
-
-class ScopedGLManager {
- public:
-  ScopedGLManager() {
-    gpu::GLManager::Options options;
-    gl_.Initialize(options);
-  }
-  ~ScopedGLManager() { gl_.Destroy(); }
-
- private:
-  gpu::GLManager gl_;
-};
-
-GLuint CompileShader(GLenum type, const char* shaderSrc) {
-  GLuint shader = glCreateShader(type);
-  // Load the shader source
-  glShaderSource(shader, 1, &shaderSrc, nullptr);
-  // Compile the shader
-  glCompileShader(shader);
-
-  return shader;
-}
-
-const char* acceptable_errors[] = {
-    "void function cannot return a value",
-    "function already has a body",
-    "undeclared identifier",
-    "l-value required (can't modify a const)",
-    "cannot convert from",
-    "main function cannot return a value",
-    "illegal use of type 'void'",
-    "boolean expression expected",
-    "Missing main()",
-    "Divide by zero error during constant folding",
-    // TODO(nedwill): enable GLSL ES 3.00
-    "operator supported in GLSL ES 3.00 and above only",
-    "wrong operand types",
-    "function must have the same return type in all of its declarations",
-    "function return is not matching type",
-    "redefinition",
-    "WARNING:",
-    "can't modify void",
-};
-
-// Filter errors which we don't think interfere with fuzzing everything.
-bool ErrorOk(const base::StringPiece line) {
-  for (base::StringPiece acceptable_error : acceptable_errors) {
-    if (line.find(acceptable_error) != base::StringPiece::npos) {
-      return true;
-    }
-  }
-  LOG(WARNING) << "failed due to line: " << line;
-  return false;
-}
-
-bool ErrorsOk(const base::StringPiece log) {
-  std::vector<std::string> lines = base::SplitString(
-      log, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  for (const auto& line : lines) {
-    if (!ErrorOk(line)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-GLuint LoadShader(GLenum type, const fuzzing::Shader& shader_proto) {
-  std::string shader_s = gl_lpm_fuzzer::GetShader(shader_proto);
-  if (shader_s.empty()) {
-    return 0;
-  }
-
-  GLuint shader = CompileShader(type, shader_s.c_str());
-
-  // Check the compile status
-  GLint value = 0;
-  glGetShaderiv(shader, GL_COMPILE_STATUS, &value);
-  if (value == 0) {
-    char buffer[1024];
-    GLsizei length = 0;
-    glGetShaderInfoLog(shader, sizeof(buffer), &length, buffer);
-    base::StringPiece log(buffer, length);
-    if (value != 1 && !ErrorsOk(log)) {
-      LOG(WARNING) << "Encountered an unexpected failure when translating:\n"
-                   << log << "\nfailed to compile shader:\n"
-                   << shader_proto.DebugString() << "converted:\n"
-                   << shader_s;
-    }
-    glDeleteShader(shader);
-    shader = 0;
-  }
-  return shader;
-}
-
-DEFINE_PROTO_FUZZER(const fuzzing::Session& session) {
-  static Env* env = new Env();
-  CHECK(env);
-  // TODO(nedwill): Creating a new GLManager on each iteration
-  // is expensive. We should investigate ways to avoid expensive
-  // initialization.
-  ScopedGLManager scoped_gl_manager;
-
-  GLuint vertex_shader_id =
-      LoadShader(GL_VERTEX_SHADER, session.vertex_shader());
-  GLuint fragment_shader_id =
-      LoadShader(GL_FRAGMENT_SHADER, session.fragment_shader());
-  if (!vertex_shader_id || !fragment_shader_id) {
-    return;
-  }
-
-  GLuint program =
-      gpu::GLTestHelper::SetupProgram(vertex_shader_id, fragment_shader_id);
-  if (!program) {
-    return;
-  }
-
-  glUseProgram(program);
-  // Relink program.
-  glLinkProgram(program);
-}
diff --git a/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.proto b/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.proto
deleted file mode 100644
index 4c4860698..0000000
--- a/gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.proto
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-
-// This proto description is adapted from the one used in clang-proto-fuzzer.
-
-syntax = "proto2";
-package fuzzing;
-
-// TODO(nedwill): fuzz the following features
-// function prototypes
-// (in)variance
-// structs
-// interface blocks
-// swizzles
-// all binary operators
-// all unary operators
-// all ternary operators
-// switch/case statements
-// loops
-// branches (case, break, continue, return, kill)
-// preprocessor directives
-
-enum Var {
-  VAR_0 = 0;
-  VAR_1 = 1;
-  VAR_2 = 2;
-  VAR_3 = 3;
-}
-
-message Lvalue {
-  optional Var var = 1;
-}
-
-message Const {
-  optional int32 val = 1;
-}
-
-message BinaryOp {
-  enum Op {
-    // TODO: actually use ops from GLSL spec, not c++
-    PLUS = 0;
-    MINUS = 1;
-    MUL = 2;
-    DIV = 3;
-    MOD = 4;
-    XOR = 5;
-    AND = 6;
-    OR = 7;
-    EQ = 8;
-    NE = 9;
-    LE = 10;
-    GE = 11;
-    LT = 12;
-    GT = 13;
-  };
-  optional Op op = 1;
-  optional Rvalue left = 2;
-  optional Rvalue right = 3;
-}
-
-message Declare {
-  optional Type type = 1;
-  optional Var var = 2;
-}
-
-message Rvalue {
-  oneof rvalue {
-    Var var = 1;
-    Const cons = 2;
-    BinaryOp binary_op = 3;
-  }
-}
-
-message Assignment {
-  optional Lvalue lvalue = 1;
-  optional Rvalue rvalue = 2;
-}
-
-message IfElse {
-  optional Rvalue cond = 1;
-  optional Block if_body = 2;
-  optional Block else_body = 3;
-}
-
-message While {
-  optional Rvalue cond = 1;
-  optional Block body = 2;
-}
-
-message Statement {
-  oneof statement {
-    Assignment assignment = 1;
-    IfElse ifelse = 2;
-    While while_stmt = 3;
-    Rvalue return_stmt = 4;
-    Declare declare = 5;
-  }
-}
-
-enum FunctionName {
-  MAIN = 0;
-  NAME_1 = 1;
-  NAME_2 = 2;
-  NAME_3 = 3;
-}
-
-message Block {
-  repeated Statement statements = 1;
-}
-
-enum Type {
-  // Use suffix to avoid clashing with VOID define on Windows
-  VOID_TYPE = 0;
-  INT = 1;
-}
-
-message Function {
-  optional FunctionName function_name = 1;
-  optional Block block = 2;
-  optional Rvalue return_stmt = 3;
-  optional Type type = 4;
-}
-
-message Shader {
-  repeated Function functions = 1;
-}
-
-message Session {
-  optional Shader vertex_shader = 1;
-  optional Shader fragment_shader = 2;
-}
diff --git a/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.cc b/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.cc
deleted file mode 100644
index 122e04b..0000000
--- a/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.h"
-
-#include <ostream>
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-
-namespace gl_lpm_fuzzer {
-
-std::string GetFunctionName(const fuzzing::FunctionName& function_name) {
-  if (function_name == fuzzing::MAIN) {
-    return "main";
-  }
-  return "f" + base::NumberToString(function_name);
-}
-
-std::string GetType(const fuzzing::Type& type) {
-  switch (type) {
-    case fuzzing::VOID_TYPE: {
-      return "void";
-    }
-    case fuzzing::INT: {
-      return "int";
-    }
-  }
-  CHECK(false);
-  return "";
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Statement& statement);
-std::ostream& operator<<(std::ostream& os, const fuzzing::Rvalue& rvalue);
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Block& block) {
-  for (const fuzzing::Statement& statement : block.statements()) {
-    os << statement;
-  }
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::IfElse& ifelse) {
-  return os << "if (" << ifelse.cond() << ") {\n"
-            << ifelse.if_body() << "} else {\n"
-            << ifelse.else_body() << "}\n";
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Const& cons) {
-  return os << base::NumberToString(cons.val());
-}
-
-std::string GetOp(const fuzzing::BinaryOp::Op op) {
-  switch (op) {
-    case fuzzing::BinaryOp::PLUS:
-      return "+";
-    case fuzzing::BinaryOp::MINUS:
-      return "-";
-    case fuzzing::BinaryOp::MUL:
-      return "*";
-    case fuzzing::BinaryOp::DIV:
-      return "/";
-    case fuzzing::BinaryOp::MOD:
-      return "%";
-    case fuzzing::BinaryOp::XOR:
-      return "^";
-    case fuzzing::BinaryOp::AND:
-      return "&&";
-    case fuzzing::BinaryOp::OR:
-      return "||";
-    case fuzzing::BinaryOp::EQ:
-      return "==";
-    case fuzzing::BinaryOp::NE:
-      return "!=";
-    case fuzzing::BinaryOp::LE:
-      return "<=";
-    case fuzzing::BinaryOp::GE:
-      return ">=";
-    case fuzzing::BinaryOp::LT:
-      return "<";
-    case fuzzing::BinaryOp::GT:
-      return ">";
-    default:
-      DCHECK(false);
-  }
-  return "";
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::BinaryOp& binary_op) {
-  return os << "(" << binary_op.left() << " " << GetOp(binary_op.op()) << " "
-            << binary_op.right() << ")";
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Rvalue& rvalue) {
-  switch (rvalue.rvalue_case()) {
-    case fuzzing::Rvalue::kVar: {
-      os << rvalue.var();
-      break;
-    }
-    case fuzzing::Rvalue::kCons: {
-      os << rvalue.cons();
-      break;
-    }
-    case fuzzing::Rvalue::kBinaryOp: {
-      os << rvalue.binary_op();
-      break;
-    }
-    case fuzzing::Rvalue::RVALUE_NOT_SET: {
-      os << "1";
-      break;
-    }
-  }
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::While& while_stmt) {
-  return os << "while (" << while_stmt.cond() << ") {\n"
-            << while_stmt.body() << "}\n";
-}
-
-std::string GetVar(const fuzzing::Var& var) {
-  return "var" + base::NumberToString(var);
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Lvalue& lvalue) {
-  return os << GetVar(lvalue.var());
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const fuzzing::Assignment& assignment) {
-  return os << assignment.lvalue() << " = " << assignment.rvalue() << ";\n";
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Declare& declare) {
-  return os << GetType(declare.type()) << " " << GetVar(declare.var()) << ";\n";
-}
-
-std::ostream& operator<<(std::ostream& os,
-                         const fuzzing::Statement& statement) {
-  switch (statement.statement_case()) {
-    case fuzzing::Statement::STATEMENT_NOT_SET: {
-      break;
-    }
-    case fuzzing::Statement::kAssignment: {
-      os << statement.assignment();
-      break;
-    }
-    case fuzzing::Statement::kIfelse: {
-      os << statement.ifelse();
-      break;
-    }
-    case fuzzing::Statement::kWhileStmt: {
-      os << statement.while_stmt();
-      break;
-    }
-    case fuzzing::Statement::kReturnStmt: {
-      os << "return " << statement.return_stmt() << ";\n";
-      break;
-    }
-    case fuzzing::Statement::kDeclare: {
-      os << statement.declare();
-      break;
-    }
-  }
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Function& function) {
-  os << GetType(function.type()) << " "
-     << GetFunctionName(function.function_name()) << "() {\n";
-  os << function.block();
-  os << "return " << function.return_stmt() << ";\n";
-  os << "}";
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const fuzzing::Shader& shader) {
-  int i = 0;
-  for (const fuzzing::Function& function : shader.functions()) {
-    os << function;
-    if (i < shader.functions().size() - 1) {
-      os << "\n";
-    }
-    i++;
-  }
-  return os;
-}
-
-std::string GetShader(const fuzzing::Shader& shader) {
-  std::ostringstream os;
-  os << shader;
-  return os.str();
-}
-
-}  // namespace gl_lpm_fuzzer
diff --git a/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.h b/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.h
deleted file mode 100644
index b5815fd9..0000000
--- a/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef GPU_COMMAND_BUFFER_TESTS_LPM_GL_LPM_SHADER_TO_STRING_H_
-#define GPU_COMMAND_BUFFER_TESTS_LPM_GL_LPM_SHADER_TO_STRING_H_
-
-#include <string>
-
-#include "gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.pb.h"
-
-namespace gl_lpm_fuzzer {
-
-std::string GetShader(const fuzzing::Shader& shader);
-
-}  // namespace gl_lpm_fuzzer
-
-#endif  // GPU_COMMAND_BUFFER_TESTS_LPM_GL_LPM_SHADER_TO_STRING_H_
diff --git a/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string_unittest.cc b/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string_unittest.cc
deleted file mode 100644
index f93af792..0000000
--- a/gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string_unittest.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.h"
-
-#include <string>
-#include <utility>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/protobuf/src/google/protobuf/text_format.h"
-
-class LpmShaderTest
-    : public ::testing::TestWithParam<std::pair<std::string, std::string>> {};
-
-TEST_P(LpmShaderTest, CheckTranslation) {
-  const std::pair<std::string, std::string>& param = GetParam();
-  fuzzing::Shader shader;
-  EXPECT_TRUE(
-      google::protobuf::TextFormat::ParseFromString(param.first, &shader));
-  ASSERT_EQ(gl_lpm_fuzzer::GetShader(shader), param.second);
-}
-
-INSTANTIATE_TEST_SUITE_P(LpmFuzzer,
-                         LpmShaderTest,
-                         ::testing::Values(std::make_pair(R"(functions {
-        function_name: MAIN
-        block {
-            statements {
-            assignment {
-                lvalue {
-                var: VAR_0
-                }
-                rvalue {
-                }
-            }
-            }
-        }
-        return_stmt {
-        }
-        type: VOID_TYPE
-        })",
-                                                          R"(void main() {
-var0 = 1;
-return 1;
-})"),
-                                           std::make_pair(R"(functions {
-  function_name: MAIN
-  block {
-    statements {
-      while_stmt {
-        cond {
-          binary_op {
-            op: PLUS
-            left {
-              cons {
-                val: 0
-              }
-            }
-            right {
-            }
-          }
-        }
-        body {
-        }
-      }
-    }
-  }
-  return_stmt {
-  }
-  type: VOID_TYPE
-})",
-                                                          R"(void main() {
-while ((0 + 1)) {
-}
-return 1;
-})"),
-                                           std::make_pair(R"(functions {
-  function_name: MAIN
-  block {
-    statements {
-      while_stmt {
-        cond {
-          var: VAR_0
-        }
-        body {
-        }
-      }
-    }
-  }
-  return_stmt {
-  }
-  type: VOID_TYPE
-}
-functions {
-  function_name: MAIN
-  block {
-    statements {
-    }
-  }
-  return_stmt {
-  }
-  type: VOID_TYPE
-})",
-                                                          R"(void main() {
-while (0) {
-}
-return 1;
-}
-void main() {
-return 1;
-})"),
-                                           std::make_pair(R"(functions {
-  function_name: NAME_2
-  block {
-  }
-  return_stmt {
-  }
-  type: VOID_TYPE
-}
-functions {
-  function_name: NAME_1
-  block {
-    statements {
-      declare {
-        type: VOID_TYPE
-        var: VAR_2
-      }
-    }
-  }
-  return_stmt {
-  }
-  type: VOID_TYPE
-})",
-                                                          R"(void f2() {
-return 1;
-}
-void f1() {
-void var2;
-return 1;
-})"),
-                                           std::make_pair(R"(functions {
-  function_name: NAME_2
-  block {
-  }
-  return_stmt {
-  }
-  type: VOID_TYPE
-}
-functions {
-  function_name: MAIN
-  block {
-    statements {
-      ifelse {
-        cond {
-          cons {
-            val: 0
-          }
-        }
-        if_body {
-        }
-        else_body {
-        }
-      }
-    }
-  }
-  return_stmt {
-    cons {
-      val: 0
-    }
-  }
-  type: VOID_TYPE
-})",
-                                                          R"(void f2() {
-return 1;
-}
-void main() {
-if (0) {
-} else {
-}
-return 0;
-})")));
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
index 09419a4..11ac875 100644
--- a/ipc/ipc_channel_proxy.cc
+++ b/ipc/ipc_channel_proxy.cc
@@ -32,7 +32,7 @@
     Listener* listener,
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
     const scoped_refptr<base::SingleThreadTaskRunner>& listener_task_runner)
-    : listener_task_runner_(listener_task_runner),
+    : default_listener_task_runner_(listener_task_runner),
       listener_(listener),
       ipc_task_runner_(ipc_task_runner),
       channel_connected_called_(false),
@@ -47,7 +47,8 @@
   // Note, we currently make an exception for a NULL listener. That usage
   // basically works, but is outside the intent of ChannelProxy. This support
   // will disappear, so please don't rely on it. See crbug.com/364241
-  DCHECK(!listener || (ipc_task_runner_.get() != listener_task_runner_.get()));
+  DCHECK(!listener ||
+         (ipc_task_runner_.get() != default_listener_task_runner_.get()));
 }
 
 ChannelProxy::Context::~Context() = default;
@@ -85,9 +86,9 @@
 
   if (message_filter_router_->TryFilters(message)) {
     if (message.dispatch_error()) {
-      listener_task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(&Context::OnDispatchBadMessage, this, message));
+      GetTaskRunner(message.routing_id())
+          ->PostTask(FROM_HERE, base::BindOnce(&Context::OnDispatchBadMessage,
+                                               this, message));
     }
 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
     if (logger->Enabled())
@@ -126,8 +127,9 @@
 
 // Called on the IPC::Channel thread
 bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) {
-  listener_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&Context::OnDispatchMessage, this, message));
+  GetTaskRunner(message.routing_id())
+      ->PostTask(FROM_HERE,
+                 base::BindOnce(&Context::OnDispatchMessage, this, message));
   return true;
 }
 
@@ -145,8 +147,8 @@
   // the filter is run on the IO thread.
   OnAddFilter();
 
-  // See above comment about using listener_task_runner_ here.
-  listener_task_runner_->PostTask(
+  // See above comment about using default_listener_task_runner_ here.
+  default_listener_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&Context::OnDispatchConnected, this));
 }
 
@@ -155,8 +157,8 @@
   for (size_t i = 0; i < filters_.size(); ++i)
     filters_[i]->OnChannelError();
 
-  // See above comment about using listener_task_runner_ here.
-  listener_task_runner_->PostTask(
+  // See above comment about using default_listener_task_runner_ here.
+  default_listener_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&Context::OnDispatchError, this));
 }
 
@@ -164,7 +166,7 @@
 void ChannelProxy::Context::OnAssociatedInterfaceRequest(
     const std::string& interface_name,
     mojo::ScopedInterfaceEndpointHandle handle) {
-  listener_task_runner_->PostTask(
+  default_listener_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&Context::OnDispatchAssociatedInterfaceRequest,
                                 this, interface_name, std::move(handle)));
 }
@@ -331,6 +333,40 @@
 #endif
 }
 
+// Called on the listener's thread.
+void ChannelProxy::Context::AddListenerTaskRunner(
+    int32_t routing_id,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  DCHECK(default_listener_task_runner_->BelongsToCurrentThread());
+  DCHECK(task_runner);
+  base::AutoLock lock(listener_thread_task_runners_lock_);
+  if (!base::ContainsKey(listener_thread_task_runners_, routing_id))
+    listener_thread_task_runners_.insert({routing_id, std::move(task_runner)});
+}
+
+// Called on the listener's thread.
+void ChannelProxy::Context::RemoveListenerTaskRunner(int32_t routing_id) {
+  DCHECK(default_listener_task_runner_->BelongsToCurrentThread());
+  base::AutoLock lock(listener_thread_task_runners_lock_);
+  if (base::ContainsKey(listener_thread_task_runners_, routing_id))
+    listener_thread_task_runners_.erase(routing_id);
+}
+
+// Called on the IPC::Channel thread.
+base::SingleThreadTaskRunner* ChannelProxy::Context::GetTaskRunner(
+    int32_t routing_id) {
+  DCHECK(ipc_task_runner_->BelongsToCurrentThread());
+  if (routing_id == MSG_ROUTING_NONE)
+    return default_listener_task_runner_.get();
+
+  base::AutoLock lock(listener_thread_task_runners_lock_);
+  base::SingleThreadTaskRunner* task_runner =
+      listener_thread_task_runners_[routing_id].get();
+  if (task_runner)
+    return task_runner;
+  return default_listener_task_runner_.get();
+}
+
 // Called on the listener's thread
 void ChannelProxy::Context::OnDispatchConnected() {
   if (channel_connected_called_)
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index 864e75f..55ea097 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -263,7 +263,7 @@
     }
 
     scoped_refptr<base::SingleThreadTaskRunner> listener_task_runner() {
-      return listener_task_runner_;
+      return default_listener_task_runner_;
     }
 
     // Dispatches a message on the listener thread.
@@ -272,6 +272,18 @@
     // Sends |message| from appropriate thread.
     void Send(Message* message, const char* debug_name);
 
+    // Adds |task_runner| for the task to be executed later.
+    void AddListenerTaskRunner(
+        int32_t routing_id,
+        scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+    // Removes task runner for |routing_id|.
+    void RemoveListenerTaskRunner(int32_t routing_id);
+
+    // Called on the IPC::Channel thread.
+    // Returns the task runner associated with |routing_id|.
+    base::SingleThreadTaskRunner* GetTaskRunner(int32_t routing_id);
+
    protected:
     friend class base::RefCountedThreadSafe<Context>;
     ~Context() override;
@@ -336,7 +348,13 @@
         const std::string& name,
         const GenericAssociatedInterfaceFactory& factory);
 
-    scoped_refptr<base::SingleThreadTaskRunner> listener_task_runner_;
+    base::Lock listener_thread_task_runners_lock_;
+    // Map of routing_id and listener's thread task runner.
+    std::map<int32_t, scoped_refptr<base::SingleThreadTaskRunner>>
+        listener_thread_task_runners_
+            GUARDED_BY(listener_thread_task_runners_lock_);
+
+    scoped_refptr<base::SingleThreadTaskRunner> default_listener_task_runner_;
     Listener* listener_;
 
     // List of filters.  This is only accessed on the IPC thread.
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 73c7f9a..d8004662 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -578,6 +578,16 @@
   StartWatching();
 }
 
+void SyncChannel::AddListenerTaskRunner(
+    int32_t routing_id,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  context()->AddListenerTaskRunner(routing_id, std::move(task_runner));
+}
+
+void SyncChannel::RemoveListenerTaskRunner(int32_t routing_id) {
+  context()->RemoveListenerTaskRunner(routing_id);
+}
+
 SyncChannel::~SyncChannel() = default;
 
 void SyncChannel::SetRestrictDispatchChannelGroup(int group) {
diff --git a/ipc/ipc_sync_channel.h b/ipc/ipc_sync_channel.h
index af4a037..933344ed5 100644
--- a/ipc/ipc_sync_channel.h
+++ b/ipc/ipc_sync_channel.h
@@ -96,6 +96,12 @@
       const scoped_refptr<base::SingleThreadTaskRunner>& listener_task_runner,
       base::WaitableEvent* shutdown_event);
 
+  void AddListenerTaskRunner(
+      int32_t routing_id,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  void RemoveListenerTaskRunner(int32_t routing_id);
+
   ~SyncChannel() override;
 
   bool Send(Message* message) override;
diff --git a/media/base/android/media_codec_bridge_impl_unittest.cc b/media/base/android/media_codec_bridge_impl_unittest.cc
index e95a8c5..f4097fc 100644
--- a/media/base/android/media_codec_bridge_impl_unittest.cc
+++ b/media/base/android/media_codec_bridge_impl_unittest.cc
@@ -439,8 +439,8 @@
 TEST(MediaCodecBridgeTest, H264VideoEncodeAndValidate) {
   SKIP_TEST_IF_HW_H264_IS_NOT_AVAILABLE();
 
-  const int width = 320;
-  const int height = 192;
+  const int width = 640;
+  const int height = 360;
   const int bit_rate = 300000;
   const int frame_rate = 30;
   const int i_frame_interval = 20;
@@ -464,8 +464,8 @@
           i_frame_interval, color_format));
   ASSERT_THAT(media_codec, NotNull());
 
-  const char* src_filename = "bear_320x192_40frames.yuv";
-  base::FilePath src_file = GetTestDataFilePath(src_filename);
+  const char kSrcFileName[] = "bali_640x360_P420.yuv";
+  base::FilePath src_file = GetTestDataFilePath(kSrcFileName);
   int64_t src_file_size = 0;
   ASSERT_TRUE(base::GetFileSize(src_file, &src_file_size));
 
@@ -479,15 +479,15 @@
   base::File src(src_file, base::File::FLAG_OPEN | base::File::FLAG_READ);
   std::unique_ptr<uint8_t[]> frame_data =
       std::make_unique<uint8_t[]>(frame_size);
-  off_t src_offset = 0;
+  ASSERT_THAT(
+      src.Read(0, reinterpret_cast<char*>(frame_data.get()), frame_size),
+      frame_size);
+
   // A monotonically-growing value.
   base::TimeDelta input_timestamp;
-  // Src_file should contain 40 frames. Here we only encode 3 of them.
-  for (int frame = 0; frame < num_frames && frame < 3; frame++) {
-    ASSERT_THAT(src.Read(src_offset, (char*)frame_data.get(), frame_size),
-                frame_size);
-    src_offset += static_cast<off_t>(frame_size);
 
+  // Src_file contains 1 frames. Encode it 3 times.
+  for (int frame = 0; frame < num_frames && frame < 3; frame++) {
     input_timestamp += base::TimeDelta::FromMicroseconds(
         base::Time::kMicrosecondsPerSecond / frame_rate);
     EncodeMediaFrame(media_codec.get(), frame_data.get(), frame_size, width,
@@ -498,10 +498,6 @@
   // also contain SPS/PPS NALUs.
   media_codec->RequestKeyFrameSoon();
   for (int frame = 0; frame < num_frames && frame < 3; frame++) {
-    ASSERT_THAT(src.Read(src_offset, (char*)frame_data.get(), frame_size),
-                frame_size);
-    src_offset += static_cast<off_t>(frame_size);
-
     input_timestamp += base::TimeDelta::FromMicroseconds(
         base::Time::kMicrosecondsPerSecond / frame_rate);
     EncodeMediaFrame(media_codec.get(), frame_data.get(), frame_size, width,
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 6b12ed4..d4e634c 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -328,6 +328,20 @@
   testonly = true
 }
 
+if (is_chromeos) {
+  source_set("chromeos_test_utils") {
+    sources = [
+      "video/chromeos/local_gpu_memory_buffer_manager.cc",
+    ]
+
+    deps = [
+      "//base",
+      "//build/config/linux/libdrm",
+      "//third_party/minigbm",
+    ]
+  }
+}
+
 test("capture_unittests") {
   sources = [
     "content/animated_content_sampler_unittest.cc",
@@ -403,7 +417,6 @@
       "video/chromeos/camera_device_delegate_unittest.cc",
       "video/chromeos/camera_hal_delegate_unittest.cc",
       "video/chromeos/camera_hal_dispatcher_impl_unittest.cc",
-      "video/chromeos/local_gpu_memory_buffer_manager.cc",
       "video/chromeos/mock_camera_module.cc",
       "video/chromeos/mock_camera_module.h",
       "video/chromeos/mock_vendor_tag_ops.cc",
@@ -413,6 +426,7 @@
       "video/chromeos/request_manager_unittest.cc",
     ]
     deps += [
+      ":chromeos_test_utils",
       "//build/config/linux/libdrm",
       "//chromeos/dbus/power",
       "//media/capture/video/chromeos/mojo:cros_camera",
diff --git a/media/capture/video/chromeos/camera_device_delegate.cc b/media/capture/video/chromeos/camera_device_delegate.cc
index d982804..d352f7f 100644
--- a/media/capture/video/chromeos/camera_device_delegate.cc
+++ b/media/capture/video/chromeos/camera_device_delegate.cc
@@ -429,12 +429,12 @@
         FROM_HERE, "Failed to get camera info");
     return;
   }
+
+  reprocess_manager_->UpdateCameraInfo(device_descriptor_.device_id,
+                                       camera_info);
   SortCameraMetadata(&camera_info->static_camera_characteristics);
   static_metadata_ = std::move(camera_info->static_camera_characteristics);
 
-  reprocess_manager_->UpdateStaticMetadata(device_descriptor_.device_id,
-                                           static_metadata_);
-
   const cros::mojom::CameraMetadataEntryPtr* sensor_orientation =
       GetMetadataEntry(
           static_metadata_,
diff --git a/media/capture/video/chromeos/cros_image_capture_impl.cc b/media/capture/video/chromeos/cros_image_capture_impl.cc
index 04ac8f3..8dba5ff 100644
--- a/media/capture/video/chromeos/cros_image_capture_impl.cc
+++ b/media/capture/video/chromeos/cros_image_capture_impl.cc
@@ -23,12 +23,11 @@
   bindings_.AddBinding(this, std::move(request));
 }
 
-void CrosImageCaptureImpl::GetStaticMetadata(
-    const std::string& device_id,
-    GetStaticMetadataCallback callback) {
-  reprocess_manager_->GetStaticMetadata(
+void CrosImageCaptureImpl::GetCameraInfo(const std::string& device_id,
+                                         GetCameraInfoCallback callback) {
+  reprocess_manager_->GetCameraInfo(
       device_id, media::BindToCurrentLoop(base::BindOnce(
-                     &CrosImageCaptureImpl::OnGotStaticMetadata,
+                     &CrosImageCaptureImpl::OnGotCameraInfo,
                      base::Unretained(this), std::move(callback))));
 }
 
@@ -40,10 +39,10 @@
       device_id, effect, media::BindToCurrentLoop(std::move(callback)));
 }
 
-void CrosImageCaptureImpl::OnGotStaticMetadata(
-    GetStaticMetadataCallback callback,
-    cros::mojom::CameraMetadataPtr static_metadata) {
-  std::move(callback).Run(std::move(static_metadata));
+void CrosImageCaptureImpl::OnGotCameraInfo(
+    GetCameraInfoCallback callback,
+    cros::mojom::CameraInfoPtr camera_info) {
+  std::move(callback).Run(std::move(camera_info));
 }
 
 }  // namespace media
diff --git a/media/capture/video/chromeos/cros_image_capture_impl.h b/media/capture/video/chromeos/cros_image_capture_impl.h
index 93785b5..a7a81b1 100644
--- a/media/capture/video/chromeos/cros_image_capture_impl.h
+++ b/media/capture/video/chromeos/cros_image_capture_impl.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "media/capture/video/chromeos/mojo/camera_metadata.mojom.h"
+#include "media/capture/video/chromeos/mojo/camera_common.mojom.h"
 #include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
 #include "media/capture/video/chromeos/reprocess_manager.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
@@ -23,15 +23,15 @@
 
   // cros::mojom::CrosImageCapture implementations.
 
-  void GetStaticMetadata(const std::string& device_id,
-                         GetStaticMetadataCallback callback) override;
+  void GetCameraInfo(const std::string& device_id,
+                     GetCameraInfoCallback callback) override;
   void SetReprocessOption(const std::string& device_id,
                           cros::mojom::Effect effect,
                           SetReprocessOptionCallback callback) override;
 
  private:
-  void OnGotStaticMetadata(GetStaticMetadataCallback callback,
-                           cros::mojom::CameraMetadataPtr static_metadata);
+  void OnGotCameraInfo(GetCameraInfoCallback callback,
+                       cros::mojom::CameraInfoPtr camera_info);
 
   ReprocessManager* reprocess_manager_;  // weak
 
diff --git a/media/capture/video/chromeos/mojo/cros_image_capture.mojom b/media/capture/video/chromeos/mojo/cros_image_capture.mojom
index 7106b44..e2adaf2 100644
--- a/media/capture/video/chromeos/mojo/cros_image_capture.mojom
+++ b/media/capture/video/chromeos/mojo/cros_image_capture.mojom
@@ -5,7 +5,7 @@
 module cros.mojom;
 
 import "media/capture/mojom/image_capture.mojom";
-import "media/capture/video/chromeos/mojo/camera_metadata.mojom";
+import "media/capture/video/chromeos/mojo/camera_common.mojom";
 
 // Effect that recognized by Chrome OS.
 enum Effect {
@@ -19,10 +19,10 @@
 // translated to the actual video device id to be used in CrosImageCapture
 // implementation.
 interface CrosImageCapture {
-  // Gets camera static metadata |static_metadata| which includes camera
-  // characteristics information. The |source_id| might need
-  // translation to be actual video device id.
-  GetStaticMetadata(string source_id) => (CameraMetadata static_metadata);
+  // Gets camera information |camera_info| which includes camera facing,
+  // characteristics, orientation, etc. The |source_id| might need translation
+  // to be actual video device id.
+  GetCameraInfo(string source_id) => (CameraInfo camera_info);
 
   // Sets reprocess option to bind with the coming take photo request. When this
   // method is called, the reprocess option will be queued. All reprocess
diff --git a/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc b/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc
index cda2019..6d09adf 100644
--- a/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc
+++ b/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc
@@ -23,11 +23,11 @@
 
 RendererFacingCrosImageCapture::~RendererFacingCrosImageCapture() = default;
 
-void RendererFacingCrosImageCapture::GetStaticMetadataWithRealId(
-    GetStaticMetadataCallback callback,
+void RendererFacingCrosImageCapture::GetCameraInfoWithRealId(
+    GetCameraInfoCallback callback,
     const base::Optional<std::string>& device_id) {
   DCHECK(device_id.has_value());
-  cros_image_capture_->GetStaticMetadata(*device_id, std::move(callback));
+  cros_image_capture_->GetCameraInfo(*device_id, std::move(callback));
 }
 
 void RendererFacingCrosImageCapture::SetReprocessOptionWithRealId(
@@ -39,14 +39,13 @@
                                           std::move(callback));
 }
 
-void RendererFacingCrosImageCapture::GetStaticMetadata(
+void RendererFacingCrosImageCapture::GetCameraInfo(
     const std::string& source_id,
-    GetStaticMetadataCallback callback) {
+    GetCameraInfoCallback callback) {
   mapping_callback_.Run(
-      source_id,
-      media::BindToCurrentLoop(base::BindOnce(
-          &RendererFacingCrosImageCapture::GetStaticMetadataWithRealId,
-          weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
+      source_id, media::BindToCurrentLoop(base::BindOnce(
+                     &RendererFacingCrosImageCapture::GetCameraInfoWithRealId,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
 }
 
 void RendererFacingCrosImageCapture::SetReprocessOption(
diff --git a/media/capture/video/chromeos/renderer_facing_cros_image_capture.h b/media/capture/video/chromeos/renderer_facing_cros_image_capture.h
index a9fc405..ad6fa0b 100644
--- a/media/capture/video/chromeos/renderer_facing_cros_image_capture.h
+++ b/media/capture/video/chromeos/renderer_facing_cros_image_capture.h
@@ -33,9 +33,8 @@
                                  DeviceIdMappingCallback mapping_callback);
   ~RendererFacingCrosImageCapture() override;
 
-  void GetStaticMetadataWithRealId(
-      GetStaticMetadataCallback callback,
-      const base::Optional<std::string>& device_id);
+  void GetCameraInfoWithRealId(GetCameraInfoCallback callback,
+                               const base::Optional<std::string>& device_id);
 
   void SetReprocessOptionWithRealId(
       cros::mojom::Effect effect,
@@ -43,8 +42,8 @@
       const base::Optional<std::string>& device_id);
 
   // cros::mojom::CrosImageCapture implementations.
-  void GetStaticMetadata(const std::string& source_id,
-                         GetStaticMetadataCallback callback) override;
+  void GetCameraInfo(const std::string& source_id,
+                     GetCameraInfoCallback callback) override;
   void SetReprocessOption(const std::string& source_id,
                           cros::mojom::Effect effect,
                           SetReprocessOptionCallback callback) override;
diff --git a/media/capture/video/chromeos/reprocess_manager.cc b/media/capture/video/chromeos/reprocess_manager.cc
index c246b37..ed5e466 100644
--- a/media/capture/video/chromeos/reprocess_manager.cc
+++ b/media/capture/video/chromeos/reprocess_manager.cc
@@ -44,7 +44,7 @@
   return kReprocessSuccess;
 }
 
-ReprocessManager::ReprocessManager(UpdateStaticMetadataCallback callback)
+ReprocessManager::ReprocessManager(UpdateCameraInfoCallback callback)
     : sequenced_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::TaskPriority::USER_VISIBLE})),
       impl(new ReprocessManager::ReprocessManagerImpl(std::move(callback))) {}
@@ -86,28 +86,28 @@
           base::Unretained(impl.get()), device_id));
 }
 
-void ReprocessManager::GetStaticMetadata(const std::string& device_id,
-                                         GetStaticMetadataCallback callback) {
+void ReprocessManager::GetCameraInfo(const std::string& device_id,
+                                     GetCameraInfoCallback callback) {
   sequenced_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&ReprocessManager::ReprocessManagerImpl::GetStaticMetadata,
+      base::BindOnce(&ReprocessManager::ReprocessManagerImpl::GetCameraInfo,
                      base::Unretained(impl.get()), device_id,
                      std::move(callback)));
 }
 
-void ReprocessManager::UpdateStaticMetadata(
+void ReprocessManager::UpdateCameraInfo(
     const std::string& device_id,
-    const cros::mojom::CameraMetadataPtr& metadata) {
+    const cros::mojom::CameraInfoPtr& camera_info) {
   sequenced_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(
-          &ReprocessManager::ReprocessManagerImpl::UpdateStaticMetadata,
-          base::Unretained(impl.get()), device_id, metadata.Clone()));
+      base::BindOnce(&ReprocessManager::ReprocessManagerImpl::UpdateCameraInfo,
+                     base::Unretained(impl.get()), device_id,
+                     camera_info.Clone()));
 }
 
 ReprocessManager::ReprocessManagerImpl::ReprocessManagerImpl(
-    UpdateStaticMetadataCallback callback)
-    : update_static_metadata_callback_(std::move(callback)) {}
+    UpdateCameraInfoCallback callback)
+    : update_camera_info_callback_(std::move(callback)) {}
 
 ReprocessManager::ReprocessManagerImpl::~ReprocessManagerImpl() = default;
 
@@ -162,27 +162,25 @@
   reprocess_task_queue_map_[device_id].swap(empty_queue);
 }
 
-void ReprocessManager::ReprocessManagerImpl::GetStaticMetadata(
+void ReprocessManager::ReprocessManagerImpl::GetCameraInfo(
     const std::string& device_id,
-    GetStaticMetadataCallback callback) {
-  if (static_metadata_map_[device_id]) {
-    std::move(callback).Run(static_metadata_map_[device_id].Clone());
+    GetCameraInfoCallback callback) {
+  if (camera_info_map_[device_id]) {
+    std::move(callback).Run(camera_info_map_[device_id].Clone());
   } else {
-    get_static_metadata_callback_queue_map_[device_id].push(
-        std::move(callback));
-    update_static_metadata_callback_.Run(device_id);
+    get_camera_info_callback_queue_map_[device_id].push(std::move(callback));
+    update_camera_info_callback_.Run(device_id);
   }
 }
 
-void ReprocessManager::ReprocessManagerImpl::UpdateStaticMetadata(
+void ReprocessManager::ReprocessManagerImpl::UpdateCameraInfo(
     const std::string& device_id,
-    cros::mojom::CameraMetadataPtr metadata) {
-  static_metadata_map_[device_id] = std::move(metadata);
+    cros::mojom::CameraInfoPtr camera_info) {
+  camera_info_map_[device_id] = std::move(camera_info);
 
-  auto& callback_queue = get_static_metadata_callback_queue_map_[device_id];
+  auto& callback_queue = get_camera_info_callback_queue_map_[device_id];
   while (!callback_queue.empty()) {
-    std::move(callback_queue.front())
-        .Run(static_metadata_map_[device_id].Clone());
+    std::move(callback_queue.front()).Run(camera_info_map_[device_id].Clone());
     callback_queue.pop();
   }
 }
diff --git a/media/capture/video/chromeos/reprocess_manager.h b/media/capture/video/chromeos/reprocess_manager.h
index 5dc2617..2f362471 100644
--- a/media/capture/video/chromeos/reprocess_manager.h
+++ b/media/capture/video/chromeos/reprocess_manager.h
@@ -16,6 +16,7 @@
 #include "media/capture/capture_export.h"
 #include "media/capture/mojom/image_capture.mojom.h"
 #include "media/capture/video/chromeos/mojo/camera3.mojom.h"
+#include "media/capture/video/chromeos/mojo/camera_common.mojom.h"
 #include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
@@ -46,14 +47,14 @@
 // sequentialize to a single sequence.
 class CAPTURE_EXPORT ReprocessManager {
  public:
-  using GetStaticMetadataCallback =
-      base::OnceCallback<void(cros::mojom::CameraMetadataPtr static_metadata)>;
-  using UpdateStaticMetadataCallback =
+  using GetCameraInfoCallback =
+      base::OnceCallback<void(cros::mojom::CameraInfoPtr camera_info)>;
+  using UpdateCameraInfoCallback =
       base::RepeatingCallback<void(const std::string& device_id)>;
 
   class ReprocessManagerImpl {
    public:
-    ReprocessManagerImpl(UpdateStaticMetadataCallback callback);
+    ReprocessManagerImpl(UpdateCameraInfoCallback callback);
     ~ReprocessManagerImpl();
 
     void SetReprocessOption(
@@ -69,23 +70,22 @@
 
     void FlushReprocessOptions(const std::string& device_id);
 
-    void GetStaticMetadata(const std::string& device_id,
-                           GetStaticMetadataCallback callback);
+    void GetCameraInfo(const std::string& device_id,
+                       GetCameraInfoCallback callback);
 
-    void UpdateStaticMetadata(const std::string& device_id,
-                              cros::mojom::CameraMetadataPtr metadata);
+    void UpdateCameraInfo(const std::string& device_id,
+                          cros::mojom::CameraInfoPtr camera_info);
 
    private:
     base::flat_map<std::string, base::queue<ReprocessTask>>
         reprocess_task_queue_map_;
 
-    base::flat_map<std::string, cros::mojom::CameraMetadataPtr>
-        static_metadata_map_;
+    base::flat_map<std::string, cros::mojom::CameraInfoPtr> camera_info_map_;
 
-    base::flat_map<std::string, base::queue<GetStaticMetadataCallback>>
-        get_static_metadata_callback_queue_map_;
+    base::flat_map<std::string, base::queue<GetCameraInfoCallback>>
+        get_camera_info_callback_queue_map_;
 
-    UpdateStaticMetadataCallback update_static_metadata_callback_;
+    UpdateCameraInfoCallback update_camera_info_callback_;
 
     DISALLOW_COPY_AND_ASSIGN(ReprocessManagerImpl);
   };
@@ -93,7 +93,7 @@
   static int GetReprocessReturnCode(
       cros::mojom::Effect effect,
       const cros::mojom::CameraMetadataPtr* metadata);
-  ReprocessManager(UpdateStaticMetadataCallback callback);
+  ReprocessManager(UpdateCameraInfoCallback callback);
   ~ReprocessManager();
 
   // Sets the reprocess option for given device id and effect. Each reprocess
@@ -114,13 +114,13 @@
   // Clears all remaining ReprocessTasks in the queue for given device id.
   void FlushReprocessOptions(const std::string& device_id);
 
-  // Gets camera static metadata for current active device.
-  void GetStaticMetadata(const std::string& device_id,
-                         GetStaticMetadataCallback callback);
+  // Gets camera information for current active device.
+  void GetCameraInfo(const std::string& device_id,
+                     GetCameraInfoCallback callback);
 
-  // Updates camera static metadata for given device.
-  void UpdateStaticMetadata(const std::string& device_id,
-                            const cros::mojom::CameraMetadataPtr& metadata);
+  // Updates camera information for given device.
+  void UpdateCameraInfo(const std::string& device_id,
+                        const cros::mojom::CameraInfoPtr& camera_info);
 
  private:
   scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
index 577105ade..e3777da 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
@@ -113,8 +113,7 @@
     const std::string& device_id,
     int32_t result,
     cros::mojom::CameraInfoPtr camera_info) {
-  reprocess_manager_->UpdateStaticMetadata(
-      device_id, std::move(camera_info->static_camera_characteristics));
+  reprocess_manager_->UpdateCameraInfo(device_id, std::move(camera_info));
 }
 
 void VideoCaptureDeviceFactoryChromeOS::BindCrosImageCaptureRequest(
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 2020a4c..0ce11cf 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -482,6 +482,8 @@
       "//media/gpu",
       "//mojo/core/embedder",
       "//testing/gtest",
+      "//third_party/ffmpeg",
+      "//third_party/libyuv",
       "//ui/base",
       "//ui/gfx",
       "//ui/gfx:test_support",
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
index 523bbcb..25de2a4 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
@@ -1039,8 +1039,8 @@
     scoped_refptr<VideoFrame> input_frame,
     scoped_refptr<VideoFrame> output_frame,
     int quality,
-    int32_t buffer_id,
-    const BitstreamBuffer* exif_buffer) {
+    int32_t task_id,
+    BitstreamBuffer* exif_buffer) {
   NOTIMPLEMENTED();
 }
 
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
index cc73b5c..9ccc340 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
@@ -64,8 +64,8 @@
   void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame,
                         scoped_refptr<VideoFrame> output_frame,
                         int quality,
-                        int32_t buffer_id,
-                        const BitstreamBuffer* exif_buffer) override;
+                        int32_t task_id,
+                        BitstreamBuffer* exif_buffer) override;
 
  private:
   // Record for input buffers.
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index 3ed99a13..0786d501 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -74,12 +74,15 @@
   deps = [
     ":libva_stubs",
     "//base",
+    "//gpu/ipc/common",
     "//gpu/ipc/service",
     "//media",
     "//media/gpu:common",
     "//media/gpu:video_frame_mapper_common",
+    "//media/gpu/linux",
     "//mojo/public/cpp/bindings",
     "//third_party/libyuv",
+    "//ui/gfx",
     "//ui/gfx/geometry",
   ]
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
index fe2fb53..2c67d19 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -10,17 +10,22 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/logging.h"
+#include "base/memory/writable_shared_memory_region.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequence_checker.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/video_frame.h"
+#include "media/gpu/linux/platform_video_frame_utils.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/vaapi/vaapi_jpeg_encoder.h"
 #include "media/parsers/jpeg_parser.h"
+#include "ui/gfx/linux/native_pixmap_dmabuf.h"
 
 namespace media {
 
@@ -42,12 +47,12 @@
 }  // namespace
 
 VaapiJpegEncodeAccelerator::EncodeRequest::EncodeRequest(
-    int32_t buffer_id,
+    int32_t task_id,
     scoped_refptr<VideoFrame> video_frame,
     std::unique_ptr<UnalignedSharedMemory> exif_shm,
     std::unique_ptr<UnalignedSharedMemory> output_shm,
     int quality)
-    : buffer_id(buffer_id),
+    : task_id(task_id),
       video_frame(std::move(video_frame)),
       exif_shm(std::move(exif_shm)),
       output_shm(std::move(output_shm)),
@@ -62,6 +67,14 @@
           base::RepeatingCallback<void(int32_t, Status)> notify_error_cb);
   ~Encoder();
 
+  // Processes one encode task with DMA-buf.
+  void EncodeWithDmaBufTask(
+      scoped_refptr<VideoFrame> input_frame,
+      scoped_refptr<VideoFrame> output_frame,
+      int32_t task_id,
+      int quality,
+      std::unique_ptr<WritableUnalignedMapping> exif_mapping);
+
   // Processes one encode |request|.
   void EncodeTask(std::unique_ptr<EncodeRequest> request);
 
@@ -75,6 +88,7 @@
 
   std::unique_ptr<VaapiJpegEncoder> jpeg_encoder_;
   scoped_refptr<VaapiWrapper> vaapi_wrapper_;
+  std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support_;
 
   base::RepeatingCallback<void(int32_t, size_t)> video_frame_ready_cb_;
   base::RepeatingCallback<void(int32_t, Status)> notify_error_cb_;
@@ -96,6 +110,7 @@
     : cached_output_buffer_size_(0),
       jpeg_encoder_(new VaapiJpegEncoder(vaapi_wrapper)),
       vaapi_wrapper_(std::move(vaapi_wrapper)),
+      gpu_memory_buffer_support_(new gpu::GpuMemoryBufferSupport()),
       video_frame_ready_cb_(std::move(video_frame_ready_cb)),
       notify_error_cb_(std::move(notify_error_cb)),
       va_surface_id_(VA_INVALID_SURFACE) {
@@ -106,36 +121,52 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-void VaapiJpegEncodeAccelerator::Encoder::EncodeTask(
-    std::unique_ptr<EncodeRequest> request) {
+void VaapiJpegEncodeAccelerator::Encoder::EncodeWithDmaBufTask(
+    scoped_refptr<VideoFrame> input_frame,
+    scoped_refptr<VideoFrame> output_frame,
+    int32_t task_id,
+    int quality,
+    std::unique_ptr<WritableUnalignedMapping> exif_mapping) {
   DVLOGF(4);
-  TRACE_EVENT0("jpeg", "EncodeTask");
+  TRACE_EVENT0("jpeg", "EncodeWithDmaBufTask");
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const int buffer_id = request->buffer_id;
-  gfx::Size input_size = request->video_frame->coded_size();
+  gfx::Size input_size = input_frame->coded_size();
 
-  // Recreate VASurface if the video frame's size changed.
-  if (input_size != surface_size_ || va_surface_id_ == VA_INVALID_SURFACE) {
-    vaapi_wrapper_->DestroyContextAndSurfaces();
-    va_surface_id_ = VA_INVALID_SURFACE;
-    surface_size_ = gfx::Size();
-
-    std::vector<VASurfaceID> va_surfaces;
-    if (!vaapi_wrapper_->CreateContextAndSurfaces(
-            VA_RT_FORMAT_YUV420, input_size, 1, &va_surfaces)) {
-      VLOGF(1) << "Failed to create VA surface";
-      notify_error_cb_.Run(buffer_id, PLATFORM_FAILURE);
-      return;
-    }
-    va_surface_id_ = va_surfaces[0];
-    surface_size_ = input_size;
+  // Construct GBM Handle from VideoFrame.
+  gfx::GpuMemoryBufferHandle input_gmb_handle =
+      CreateGpuMemoryBufferHandle(input_frame.get());
+  if (input_gmb_handle.is_null()) {
+    VLOGF(1) << "Failed to create input gmb handle";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+    return;
   }
 
-  if (!vaapi_wrapper_->UploadVideoFrameToSurface(*request->video_frame,
-                                                 va_surface_id_)) {
-    VLOGF(1) << "Failed to upload video frame to VA surface";
-    notify_error_cb_.Run(buffer_id, PLATFORM_FAILURE);
+  // Create pixmap for input handle and create VA surface.
+  auto num_planes_input = VideoFrame::NumPlanes(input_frame->format());
+
+  // We only support NV12 format currently. Modify the check and |buffer_format|
+  // once we support other formats.
+  DCHECK(num_planes_input == 2);
+  gfx::BufferFormat buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR;
+
+  auto va_surface = vaapi_wrapper_->CreateVASurfaceForPixmap(
+      base::WrapRefCounted(new gfx::NativePixmapDmaBuf(
+          input_size, buffer_format,
+          std::move(input_gmb_handle.native_pixmap_handle))));
+  if (!va_surface) {
+    VLOGF(1) << "Failed to create input va surface";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+    return;
+  }
+  va_surface_id_ = va_surface->id();
+
+  vaapi_wrapper_->DestroyContextAndSurfaces();
+  const bool success = vaapi_wrapper_->CreateContext(
+      VaapiWrapper::BufferFormatToVARTFormat(buffer_format), input_size);
+  if (!success) {
+    VLOGF(1) << "Failed to create context";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
     return;
   }
 
@@ -150,7 +181,134 @@
     if (!vaapi_wrapper_->CreateVABuffer(max_coded_buffer_size,
                                         &output_buffer_id)) {
       VLOGF(1) << "Failed to create VA buffer for encoding output";
-      notify_error_cb_.Run(buffer_id, PLATFORM_FAILURE);
+      notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+      return;
+    }
+    cached_output_buffer_size_ = max_coded_buffer_size;
+    cached_output_buffer_id_ = output_buffer_id;
+  }
+
+  // Prepare exif.
+  const uint8_t* exif_buffer;
+  size_t exif_buffer_size = 0;
+  if (exif_mapping) {
+    exif_buffer = static_cast<const uint8_t*>(exif_mapping->memory());
+    exif_buffer_size = exif_mapping->size();
+  } else {
+    exif_buffer = nullptr;
+  }
+  // When the exif buffer contains a thumbnail, the VAAPI encoder would
+  // generate a corrupted JPEG. We can work around the problem by supplying an
+  // all-zero buffer with the same size and fill in the real exif buffer after
+  // encoding.
+  // TODO(shenghao): Remove this mechanism after b/79840013 is fixed.
+  std::vector<uint8_t> exif_buffer_dummy(exif_buffer_size, 0);
+  size_t exif_offset = 0;
+
+  if (!jpeg_encoder_->Encode(input_size, exif_buffer_dummy.data(),
+                             exif_buffer_size, quality, va_surface_id_,
+                             cached_output_buffer_id_, &exif_offset)) {
+    VLOGF(1) << "Encode JPEG failed";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+    return;
+  }
+
+  // Create gmb buffer from output VideoFrame.
+  auto output_gmb_handle = CreateGpuMemoryBufferHandle(output_frame.get());
+  if (output_gmb_handle.is_null()) {
+    VLOGF(1) << "Failed to create GpuMemoryBufferHandle";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+    return;
+  }
+  auto output_gmb_buffer =
+      gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
+          std::move(output_gmb_handle), output_frame->coded_size(),
+          gfx::BufferFormat::R_8, gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
+          base::DoNothing());
+  if (output_gmb_buffer == nullptr) {
+    VLOGF(1) << "Failed to create GpuMemoryBufferImpl from handle";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+    return;
+  }
+
+  bool isMapped = output_gmb_buffer->Map();
+  if (!isMapped) {
+    VLOGF(1) << "Map the output gmb buffer failed";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+    return;
+  }
+
+  // Get the encoded output. DownloadFromVABuffer() is a blocking call. It
+  // would wait until encoding is finished.
+  uint8_t* output_memory = static_cast<uint8_t*>(output_gmb_buffer->memory(0));
+  size_t encoded_size = 0;
+  // Since the format of |output_gmb_buffer| is gfx::BufferFormat::R_8, we can
+  // use its area as the maximum bytes we need to download to avoid buffer
+  // overflow.
+  if (!vaapi_wrapper_->DownloadFromVABuffer(
+          cached_output_buffer_id_, va_surface_id_,
+          static_cast<uint8_t*>(output_memory),
+          output_gmb_buffer->GetSize().GetArea(), &encoded_size)) {
+    VLOGF(1) << "Failed to retrieve output image from VA coded buffer";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+
+    output_gmb_buffer->Unmap();
+    return;
+  }
+
+  // Copy the real exif buffer into preserved space.
+  memcpy(static_cast<uint8_t*>(output_memory) + exif_offset, exif_buffer,
+         exif_buffer_size);
+
+  output_gmb_buffer->Unmap();
+  video_frame_ready_cb_.Run(task_id, encoded_size);
+}
+
+void VaapiJpegEncodeAccelerator::Encoder::EncodeTask(
+    std::unique_ptr<EncodeRequest> request) {
+  DVLOGF(4);
+  TRACE_EVENT0("jpeg", "EncodeTask");
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const int task_id = request->task_id;
+  gfx::Size input_size = request->video_frame->coded_size();
+
+  // Recreate VASurface if the video frame's size changed.
+  if (input_size != surface_size_ || va_surface_id_ == VA_INVALID_SURFACE) {
+    vaapi_wrapper_->DestroyContextAndSurfaces();
+    va_surface_id_ = VA_INVALID_SURFACE;
+    surface_size_ = gfx::Size();
+
+    std::vector<VASurfaceID> va_surfaces;
+    if (!vaapi_wrapper_->CreateContextAndSurfaces(
+            VA_RT_FORMAT_YUV420, input_size, 1, &va_surfaces)) {
+      VLOGF(1) << "Failed to create VA surface";
+      notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+      return;
+    }
+    va_surface_id_ = va_surfaces[0];
+    surface_size_ = input_size;
+  }
+
+  if (!vaapi_wrapper_->UploadVideoFrameToSurface(*request->video_frame,
+                                                 va_surface_id_)) {
+    VLOGF(1) << "Failed to upload video frame to VA surface";
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
+    return;
+  }
+
+  // Create output buffer for encoding result.
+  size_t max_coded_buffer_size =
+      VaapiJpegEncoder::GetMaxCodedBufferSize(input_size);
+  if (max_coded_buffer_size > cached_output_buffer_size_) {
+    vaapi_wrapper_->DestroyVABuffers();
+    cached_output_buffer_size_ = 0;
+
+    VABufferID output_buffer_id;
+    if (!vaapi_wrapper_->CreateVABuffer(max_coded_buffer_size,
+                                        &output_buffer_id)) {
+      VLOGF(1) << "Failed to create VA buffer for encoding output";
+      notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
       return;
     }
     cached_output_buffer_size_ = max_coded_buffer_size;
@@ -175,7 +333,7 @@
                              exif_buffer_size, request->quality, va_surface_id_,
                              cached_output_buffer_id_, &exif_offset)) {
     VLOGF(1) << "Encode JPEG failed";
-    notify_error_cb_.Run(buffer_id, PLATFORM_FAILURE);
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
     return;
   }
 
@@ -187,14 +345,14 @@
           static_cast<uint8_t*>(request->output_shm->memory()),
           request->output_shm->size(), &encoded_size)) {
     VLOGF(1) << "Failed to retrieve output image from VA coded buffer";
-    notify_error_cb_.Run(buffer_id, PLATFORM_FAILURE);
+    notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
   }
 
   // Copy the real exif buffer into preserved space.
   memcpy(static_cast<uint8_t*>(request->output_shm->memory()) + exif_offset,
          exif_buffer, exif_buffer_size);
 
-  video_frame_ready_cb_.Run(buffer_id, encoded_size);
+  video_frame_ready_cb_.Run(task_id, encoded_size);
 }
 
 VaapiJpegEncodeAccelerator::VaapiJpegEncodeAccelerator(
@@ -216,21 +374,20 @@
   }
 }
 
-void VaapiJpegEncodeAccelerator::NotifyError(int32_t buffer_id, Status status) {
+void VaapiJpegEncodeAccelerator::NotifyError(int32_t task_id, Status status) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  VLOGF(1) << "output_buffer_id=" << buffer_id << ", status=" << status;
+  VLOGF(1) << "task_id=" << task_id << ", status=" << status;
   DCHECK(client_);
-  client_->NotifyError(buffer_id, status);
+  client_->NotifyError(task_id, status);
 }
 
-void VaapiJpegEncodeAccelerator::VideoFrameReady(int32_t buffer_id,
+void VaapiJpegEncodeAccelerator::VideoFrameReady(int32_t task_id,
                                                  size_t encoded_picture_size) {
-  DVLOGF(4) << "output_buffer_id=" << buffer_id
-            << ", size=" << encoded_picture_size;
+  DVLOGF(4) << "task_id=" << task_id << ", size=" << encoded_picture_size;
   DCHECK(task_runner_->BelongsToCurrentThread());
   ReportToUMA(VAJEAEncoderResult::VAAPI_SUCCESS);
 
-  client_->VideoFrameReady(buffer_id, encoded_picture_size);
+  client_->VideoFrameReady(task_id, encoded_picture_size);
 }
 
 chromeos_camera::JpegEncodeAccelerator::Status
@@ -283,15 +440,15 @@
   DVLOGF(4);
   DCHECK(io_task_runner_->BelongsToCurrentThread());
 
-  int32_t buffer_id = output_buffer.id();
-  TRACE_EVENT1("jpeg", "Encode", "output_buffer_id", buffer_id);
+  int32_t task_id = output_buffer.id();
+  TRACE_EVENT1("jpeg", "Encode", "task_id", task_id);
 
   // TODO(shenghao): support other YUV formats.
   if (video_frame->format() != VideoPixelFormat::PIXEL_FORMAT_I420) {
     VLOGF(1) << "Unsupported input format: " << video_frame->format();
     task_runner_->PostTask(
         FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
-                                  weak_this_, buffer_id, INVALID_ARGUMENT));
+                                  weak_this_, task_id, INVALID_ARGUMENT));
     return;
   }
 
@@ -299,19 +456,19 @@
   if (exif_buffer) {
     // |exif_shm| will take ownership of the |exif_buffer->region()|.
     exif_shm = std::make_unique<UnalignedSharedMemory>(
-        exif_buffer->TakeRegion(), exif_buffer->size(), true);
+        exif_buffer->TakeRegion(), exif_buffer->size(), false);
     if (!exif_shm->MapAt(exif_buffer->offset(), exif_buffer->size())) {
       VLOGF(1) << "Failed to map exif buffer";
       task_runner_->PostTask(
           FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
-                                    weak_this_, buffer_id, PLATFORM_FAILURE));
+                                    weak_this_, task_id, PLATFORM_FAILURE));
       return;
     }
     if (exif_shm->size() > kMaxMarkerSizeAllowed) {
       VLOGF(1) << "Exif buffer too big: " << exif_shm->size();
       task_runner_->PostTask(
           FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
-                                    weak_this_, buffer_id, INVALID_ARGUMENT));
+                                    weak_this_, task_id, INVALID_ARGUMENT));
       return;
     }
   }
@@ -324,12 +481,12 @@
     task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError, weak_this_,
-                       buffer_id, INACCESSIBLE_OUTPUT_BUFFER));
+                       task_id, INACCESSIBLE_OUTPUT_BUFFER));
     return;
   }
 
   auto request = std::make_unique<EncodeRequest>(
-      buffer_id, std::move(video_frame), std::move(exif_shm),
+      task_id, std::move(video_frame), std::move(exif_shm),
       std::move(output_shm), quality);
 
   encoder_task_runner_->PostTask(
@@ -342,9 +499,55 @@
     scoped_refptr<VideoFrame> input_frame,
     scoped_refptr<VideoFrame> output_frame,
     int quality,
-    int32_t buffer_id,
-    const BitstreamBuffer* exif_buffer) {
-  NOTIMPLEMENTED();
+    int32_t task_id,
+    BitstreamBuffer* exif_buffer) {
+  DVLOGF(4);
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  TRACE_EVENT1("jpeg", "Encode", "task_id", task_id);
+
+  // TODO(wtlee): Supports other formats.
+  if (input_frame->format() != VideoPixelFormat::PIXEL_FORMAT_NV12) {
+    VLOGF(1) << "Unsupported input format: " << input_frame->format();
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
+                                  weak_this_, task_id, INVALID_ARGUMENT));
+    return;
+  }
+  if (output_frame->format() != VideoPixelFormat::PIXEL_FORMAT_MJPEG) {
+    VLOGF(1) << "Unsupported output format: " << output_frame->format();
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
+                                  weak_this_, task_id, INVALID_ARGUMENT));
+    return;
+  }
+
+  std::unique_ptr<WritableUnalignedMapping> exif_mapping;
+  if (exif_buffer) {
+    // |exif_mapping| will take ownership of the |exif_buffer->region()|.
+    exif_mapping = std::make_unique<WritableUnalignedMapping>(
+        base::UnsafeSharedMemoryRegion::Deserialize(exif_buffer->TakeRegion()),
+        exif_buffer->size(), exif_buffer->offset());
+    if (!exif_mapping->IsValid()) {
+      LOG(ERROR) << "Failed to map exif buffer";
+      task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
+                                    weak_this_, task_id, PLATFORM_FAILURE));
+      return;
+    }
+    if (exif_mapping->size() > kMaxMarkerSizeAllowed) {
+      LOG(ERROR) << "Exif buffer too big: " << exif_mapping->size();
+      task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
+                                    weak_this_, task_id, INVALID_ARGUMENT));
+      return;
+    }
+  }
+
+  encoder_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&VaapiJpegEncodeAccelerator::Encoder::EncodeWithDmaBufTask,
+                     base::Unretained(encoder_.get()), input_frame,
+                     output_frame, task_id, quality, std::move(exif_mapping)));
 }
 
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
index d31cb8e..fe857ee 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
@@ -48,21 +48,21 @@
   void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame,
                         scoped_refptr<VideoFrame> output_frame,
                         int quality,
-                        int32_t buffer_id,
-                        const BitstreamBuffer* exif_buffer) override;
+                        int32_t task_id,
+                        BitstreamBuffer* exif_buffer) override;
 
  private:
   // An input video frame and the corresponding output buffer awaiting
   // consumption, provided by the client.
   struct EncodeRequest {
-    EncodeRequest(int32_t buffer_id,
+    EncodeRequest(int32_t task_id,
                   scoped_refptr<VideoFrame> video_frame,
                   std::unique_ptr<UnalignedSharedMemory> exif_shm,
                   std::unique_ptr<UnalignedSharedMemory> output_shm,
                   int quality);
     ~EncodeRequest();
 
-    int32_t buffer_id;
+    int32_t task_id;
     scoped_refptr<VideoFrame> video_frame;
     std::unique_ptr<UnalignedSharedMemory> exif_shm;
     std::unique_ptr<UnalignedSharedMemory> output_shm;
@@ -77,9 +77,9 @@
 
   // Notifies the client that an error has occurred and encoding cannot
   // continue.
-  void NotifyError(int32_t buffer_id, Status status);
+  void NotifyError(int32_t task_id, Status status);
 
-  void VideoFrameReady(int32_t buffer_id, size_t encoded_picture_size);
+  void VideoFrameReady(int32_t task_id, size_t encoded_picture_size);
 
   // ChildThread's task runner.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e432d39e..f66ede4e 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -110,23 +110,6 @@
   }
 }
 
-uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt) {
-  switch (fmt) {
-    case gfx::BufferFormat::UYVY_422:
-      return VA_RT_FORMAT_YUV422;
-    case gfx::BufferFormat::BGRX_8888:
-    case gfx::BufferFormat::BGRA_8888:
-    case gfx::BufferFormat::RGBX_8888:
-      return VA_RT_FORMAT_RGB32;
-    case gfx::BufferFormat::YVU_420:
-    case gfx::BufferFormat::YUV_420_BIPLANAR:
-      return VA_RT_FORMAT_YUV420;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
 }  // namespace
 
 namespace media {
@@ -1207,6 +1190,24 @@
   return VASupportedImageFormats::Get().GetSupportedImageFormats();
 }
 
+// static
+uint32_t VaapiWrapper::BufferFormatToVARTFormat(gfx::BufferFormat fmt) {
+  switch (fmt) {
+    case gfx::BufferFormat::UYVY_422:
+      return VA_RT_FORMAT_YUV422;
+    case gfx::BufferFormat::BGRX_8888:
+    case gfx::BufferFormat::BGRA_8888:
+    case gfx::BufferFormat::RGBX_8888:
+      return VA_RT_FORMAT_RGB32;
+    case gfx::BufferFormat::YVU_420:
+    case gfx::BufferFormat::YUV_420_BIPLANAR:
+      return VA_RT_FORMAT_YUV420;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
 bool VaapiWrapper::CreateContextAndSurfaces(
     unsigned int va_format,
     const gfx::Size& size,
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index f57ab43..c2d0cda 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -140,6 +140,8 @@
   // Returns the list of VAImageFormats supported by the driver.
   static const std::vector<VAImageFormat>& GetSupportedImageFormatsForTesting();
 
+  static uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt);
+
   // Creates |num_surfaces| backing surfaces in driver for VASurfaces of
   // |va_format|, each of size |size| and initializes |va_context_id_| with
   // |format| and |size|. Returns true when successful, with the created IDs in
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index f920cd9..4a22f31 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -44,15 +44,19 @@
 #include "media/base/bitstream_buffer.h"
 #include "media/base/cdm_context.h"
 #include "media/base/decoder_buffer.h"
+#include "media/base/media.h"
 #include "media/base/media_switches.h"
 #include "media/base/media_util.h"
 #include "media/base/test_data_util.h"
 #include "media/base/video_decoder.h"
 #include "media/base/video_frame.h"
+#include "media/ffmpeg/ffmpeg_common.h"
 #include "media/filters/ffmpeg_video_decoder.h"
+#include "media/filters/in_memory_url_protocol.h"
 #include "media/filters/ivf_parser.h"
 #include "media/filters/vp8_parser.h"
 #include "media/filters/vp9_parser.h"
+#include "media/filters/vpx_video_decoder.h"
 #include "media/gpu/buildflags.h"
 #include "media/gpu/gpu_video_encode_accelerator_factory.h"
 #include "media/gpu/h264_decoder.h"
@@ -65,6 +69,7 @@
 #include "media/video/video_encode_accelerator.h"
 #include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libyuv/include/libyuv/planar_functions.h"
 
 #if BUILDFLAG(USE_VAAPI)
 #include "media/gpu/vaapi/vaapi_wrapper.h"
@@ -159,25 +164,25 @@
 //                     h264_parser.h. Use kDefaultH264Level if not provided.
 
 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
-const char* g_default_in_filename = "bear_320x192_40frames.yuv";
-const base::FilePath::CharType* g_default_in_parameters =
+const char kDefaultInputFileName[] = "bear_320x192_40frames.yuv.webm";
+const base::FilePath::CharType kDefaultInputParameters[] =
     FILE_PATH_LITERAL(":320:192:1:out.h264:200000");
 #elif defined(OS_MACOSX)
 // VideoToolbox falls back to SW encoder with resolutions lower than this.
-const char* g_default_in_filename = "bear_640x384_40frames.yuv";
-const base::FilePath::CharType* g_default_in_parameters =
+const char kDefaultInputFileName[] = "bear_640x384_40frames.yuv.webm";
+const base::FilePath::CharType kDefaultInputParameters[] =
     FILE_PATH_LITERAL(":640:384:1:out.h264:200000");
 #elif defined(OS_WIN)
-const char* g_default_in_filename = "bear_320x192_40frames.yuv";
-const base::FilePath::CharType* g_default_in_parameters =
+const char kDefaultInputFileName[] = "bear_320x192_40frames.yuv.webm";
+const base::FilePath::CharType kDefaultInputParameters[] =
     FILE_PATH_LITERAL(",320,192,0,out.h264,200000");
 #endif  // defined(OS_CHROMEOS) || defined(OS_LINUX)
 
 // Default params that can be overriden via command line.
 std::unique_ptr<base::FilePath::StringType> g_test_stream_data(
     new base::FilePath::StringType(
-        media::GetTestDataFilePath(media::g_default_in_filename).value() +
-        media::g_default_in_parameters));
+        media::GetTestDataFilePath(media::kDefaultInputFileName).value() +
+        media::kDefaultInputParameters));
 
 base::FilePath g_log_path;
 
@@ -341,6 +346,105 @@
 #endif  // defined(OS_WIN)
 }
 
+// Decodes webm vp9 |src_file| into |test_stream_->aligned_in_file_data|. Used
+// to save storage size in media/test/data since raw YUV files are huge.
+static bool DecodeFile(const base::FilePath& src_file,
+                       TestStream* test_stream) {
+  InitializeMediaLibrary();
+
+  const int file_size = base::checked_cast<int>([src_file]() {
+    int64_t tmp = 0;
+    CHECK(base::GetFileSize(src_file, &tmp))
+        << "Failed to get file size for '" << src_file << "'";
+    return tmp;
+  }());
+
+  // Read file data into memory.
+  auto buffer = base::MakeRefCounted<DecoderBuffer>(file_size);
+  auto* data = reinterpret_cast<char*>(buffer->writable_data());
+  CHECK_EQ(file_size, base::ReadFile(src_file, data, file_size))
+      << "Failed to read '" << src_file << "'";
+
+  // Initialize ffmpeg with the file data.
+  InMemoryUrlProtocol protocol(buffer->data(), buffer->data_size(), false);
+  FFmpegGlue glue(&protocol);
+  CHECK(glue.OpenContext());
+
+  // Find first vp9 stream in the file.
+  int stream_index = -1;
+  VideoDecoderConfig config;
+  for (size_t i = 0; i < glue.format_context()->nb_streams; ++i) {
+    AVStream* stream = glue.format_context()->streams[i];
+    const AVCodecParameters* codec_parameters = stream->codecpar;
+    const AVMediaType codec_type = codec_parameters->codec_type;
+    const AVCodecID codec_id = codec_parameters->codec_id;
+    if (codec_type == AVMEDIA_TYPE_VIDEO && codec_id == AV_CODEC_ID_VP9) {
+      CHECK(AVStreamToVideoDecoderConfig(stream, &config));
+      stream_index = i;
+      break;
+    }
+  }
+
+  CHECK(config.IsValidConfig());
+
+  test_stream->num_frames = 0;
+  test_stream->aligned_in_file_data.clear();
+
+  // Writes VideoFrames into the |test_stream_->aligned_in_file_data| structure.
+  class FrameWriter {
+   public:
+    explicit FrameWriter(TestStream* test_stream) : test_stream_(test_stream) {}
+    ~FrameWriter() = default;
+
+    void FrameReady(scoped_refptr<VideoFrame> frame) {
+      const size_t previous_end = test_stream_->aligned_in_file_data.size();
+
+      ++test_stream_->num_frames;
+      test_stream_->aligned_in_file_data.resize(
+          test_stream_->num_frames * test_stream_->aligned_buffer_size);
+      uint8_t* dest = reinterpret_cast<uint8_t*>(
+          &test_stream_->aligned_in_file_data[previous_end]);
+
+      for (size_t plane = 0;
+           plane < VideoFrame::NumPlanes(test_stream_->pixel_format); plane++) {
+        libyuv::CopyPlane(
+            frame->data(plane), frame->stride(plane), dest,
+            VideoFrame::RowBytes(plane, test_stream_->pixel_format,
+                                 test_stream_->coded_size.width()),
+            VideoFrame::RowBytes(plane, test_stream_->pixel_format,
+                                 test_stream_->visible_size.width()),
+            VideoFrame::Rows(plane, test_stream_->pixel_format,
+                             test_stream_->visible_size.height()));
+        dest += test_stream_->aligned_plane_size[plane];
+      }
+    }
+
+   private:
+    TestStream* const test_stream_;
+    DISALLOW_COPY_AND_ASSIGN(FrameWriter);
+  } frame_writer(test_stream);
+
+  // Setup decoder.
+  VpxVideoDecoder decoder;
+  decoder.Initialize(config, false, nullptr, base::DoNothing(),
+                     base::BindRepeating(&FrameWriter::FrameReady,
+                                         base::Unretained(&frame_writer)),
+                     base::NullCallback());
+
+  // Decode frames. No need to flush since VpxVideoDecoder is 1 in 1 out.
+  AVPacket packet = {};
+  while (av_read_frame(glue.format_context(), &packet) >= 0) {
+    if (packet.stream_index == stream_index) {
+      decoder.Decode(DecoderBuffer::CopyFrom(packet.data, packet.size),
+                     base::DoNothing());
+      base::RunLoop().RunUntilIdle();
+    }
+    av_packet_unref(&packet);
+  }
+
+  return true;
+}
+
 // Some platforms may have requirements on physical memory buffer alignment.
 // Since we are just mapping and passing chunks of the input file directly to
 // the VEA as input frames, to avoid copying large chunks of raw data on each
@@ -394,6 +498,13 @@
   }
 
   base::FilePath src_file(StringToFilePathStringType(test_stream->in_filename));
+
+  // File is encoded and must be decoded first.
+  if (src_file.MatchesExtension(FILE_PATH_LITERAL(".webm"))) {
+    ASSERT_TRUE(DecodeFile(src_file, test_stream));
+    return;
+  }
+
   int64_t src_file_size = 0;
   LOG_ASSERT(base::GetFileSize(src_file, &src_file_size));
 
diff --git a/media/test/data/README.md b/media/test/data/README.md
index c37d9a0..7e6305a5 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -750,9 +750,16 @@
 
 ### VEA test files:
 
-#### bear_320x192_40frames.yuv
+#### bear_320x192_40frames.yuv.webm
 First 40 raw i420 frames of bear-1280x720.mp4 scaled down to 320x192 for
-video_encode_accelerator_unittest.
+video_encode_accelerator_unittest. Encoded with vp9 lossless:
+`ffmpeg -pix_fmt yuv420p -s:v 320x192 -r 30 -i bear_320x192_40frames.yuv -lossless 1 bear_320x192_40frames.yuv.webm`
+
+#### bear_640x384_40frames.yuv.webm
+First 40 raw i420 frames of bear-1280x720.mp4 scaled down to 340x384 for
+video_encode_accelerator_unittest. Encoded with vp9 lossless:
+`ffmpeg -pix_fmt yuv420p -s:v 640x384 -r 30 -i bear_640x384_40frames.yuv -lossless 1 bear_640x384_40frames.yuv.webm`
+
 
 ### ImageProcessor Test Files
 
diff --git a/media/test/data/bear_320x192_40frames.yuv b/media/test/data/bear_320x192_40frames.yuv
deleted file mode 100644
index a2718a8..0000000
--- a/media/test/data/bear_320x192_40frames.yuv
+++ /dev/null
Binary files differ
diff --git a/media/test/data/bear_320x192_40frames.yuv.webm b/media/test/data/bear_320x192_40frames.yuv.webm
new file mode 100644
index 0000000..19dc16b
--- /dev/null
+++ b/media/test/data/bear_320x192_40frames.yuv.webm
Binary files differ
diff --git a/media/test/data/bear_640x384_40frames.yuv b/media/test/data/bear_640x384_40frames.yuv
deleted file mode 100644
index 2b7ad35..0000000
--- a/media/test/data/bear_640x384_40frames.yuv
+++ /dev/null
Binary files differ
diff --git a/media/test/data/bear_640x384_40frames.yuv.webm b/media/test/data/bear_640x384_40frames.yuv.webm
new file mode 100644
index 0000000..e0c76c9
--- /dev/null
+++ b/media/test/data/bear_640x384_40frames.yuv.webm
Binary files differ
diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h
index 3708859..f6fc439 100644
--- a/mojo/public/cpp/bindings/interface_ptr.h
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -53,7 +53,11 @@
     internal_state_.Swap(&other.internal_state_);
   }
 
-  explicit InterfacePtr(PtrInfoType&& info) noexcept { Bind(std::move(info)); }
+  explicit InterfacePtr(
+      PtrInfoType&& info,
+      scoped_refptr<base::SequencedTaskRunner> runner = nullptr) noexcept {
+    Bind(std::move(info), std::move(runner));
+  }
 
   // Takes over the binding of another InterfacePtr, and closes any message pipe
   // already bound to this pointer.
diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h
index 27433759..d9d91d47 100644
--- a/remoting/host/chromoting_messages.h
+++ b/remoting/host/chromoting_messages.h
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "base/files/file_path.h"
-#include "base/memory/shared_memory_handle.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/time/time.h"
 #include "ipc/ipc_channel_handle.h"
 #include "ipc/ipc_platform_file.h"
@@ -148,7 +148,7 @@
 // Notifies the network process that a shared buffer has been created.
 IPC_MESSAGE_CONTROL(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
                     int /* id */,
-                    base::SharedMemoryHandle /* handle */,
+                    base::ReadOnlySharedMemoryRegion /* region */,
                     uint32_t /* size */)
 
 // Request the network process to stop using a shared buffer.
diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc
index 6398a6f..1feea43 100644
--- a/remoting/host/desktop_session_agent.cc
+++ b/remoting/host/desktop_session_agent.cc
@@ -10,8 +10,9 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/platform_shared_memory_region.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/shared_memory.h"
+#include "base/memory/read_only_shared_memory_region.h"
 #include "base/process/process_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -41,8 +42,92 @@
 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
 #include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
 
+#if defined(OS_WIN)
+#include "base/memory/writable_shared_memory_region.h"
+#endif
+
 namespace remoting {
 
+// webrtc::SharedMemory implementation that creates a
+// base::ReadOnlySharedMemoryRegion along with a writable mapping.
+//
+// This is declared outside the anonymous namespace so that it can be friended
+// with base::WritableSharedMemoryRegion. It is not exported in the header.
+class SharedMemoryImpl : public webrtc::SharedMemory {
+ public:
+  static std::unique_ptr<SharedMemoryImpl>
+  Create(size_t size, int id, const base::Closure& on_deleted_callback) {
+    webrtc::SharedMemory::Handle handle = webrtc::SharedMemory::kInvalidHandle;
+#if defined(OS_WIN)
+    // webrtc::ScreenCapturer uses webrtc::SharedMemory::handle() only on
+    // windows. This handle must be writable. A WritableSharedMemoryRegion is
+    // created, and then it is converted to read-only.  On the windows platform,
+    // it happens to be the case that converting a region to read-only does not
+    // change the status of existing handles. This is not true on all other
+    // platforms, so please don't emulate this behavior!
+    base::WritableSharedMemoryRegion region =
+        base::WritableSharedMemoryRegion::Create(size);
+    base::WritableSharedMemoryMapping mapping = region.Map();
+    // Converting |region| to read-only will close its associated handle, so we
+    // must duplicate it into the handle used for |webrtc::ScreenCapturer|.
+    HANDLE process = ::GetCurrentProcess();
+    BOOL success =
+        ::DuplicateHandle(process, region.UnsafeGetPlatformHandle(), process,
+                          &handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
+    if (!success)
+      return nullptr;
+    base::ReadOnlySharedMemoryRegion read_only_region =
+        base::WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region));
+#else
+    base::MappedReadOnlyRegion region_mapping =
+        base::ReadOnlySharedMemoryRegion::Create(size);
+    base::ReadOnlySharedMemoryRegion read_only_region =
+        std::move(region_mapping.region);
+    base::WritableSharedMemoryMapping mapping =
+        std::move(region_mapping.mapping);
+#endif
+    if (!mapping.IsValid())
+      return nullptr;
+    // The SharedMemoryImpl ctor is private, so std::make_unique can't be
+    // used.
+    return base::WrapUnique(new SharedMemoryImpl(std::move(read_only_region),
+                                                 std::move(mapping), handle, id,
+                                                 on_deleted_callback));
+  }
+
+  ~SharedMemoryImpl() override { on_deleted_callback_.Run(); }
+
+  const base::ReadOnlySharedMemoryRegion& region() const { return region_; }
+
+ private:
+  SharedMemoryImpl(base::ReadOnlySharedMemoryRegion region,
+                   base::WritableSharedMemoryMapping mapping,
+                   webrtc::SharedMemory::Handle handle,
+                   int id,
+                   const base::Closure& on_deleted_callback)
+      : SharedMemory(mapping.memory(), mapping.size(), handle, id),
+        on_deleted_callback_(on_deleted_callback)
+#if defined(OS_WIN)
+        ,
+        writable_handle_(handle)
+#endif
+  {
+    region_ = std::move(region);
+    mapping_ = std::move(mapping);
+  }
+
+  base::Closure on_deleted_callback_;
+  base::ReadOnlySharedMemoryRegion region_;
+  base::WritableSharedMemoryMapping mapping_;
+#if defined(OS_WIN)
+  // Owns the handle passed to the base class which is used by
+  // webrtc::ScreenCapturer.
+  base::win::ScopedHandle writable_handle_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(SharedMemoryImpl);
+};
+
 namespace {
 
 // Routes local clipboard events though the IPC channel to the network process.
@@ -72,46 +157,6 @@
   desktop_session_agent_->InjectClipboardEvent(event);
 }
 
-// webrtc::SharedMemory implementation that creates base::SharedMemory.
-class SharedMemoryImpl : public webrtc::SharedMemory {
- public:
-  static std::unique_ptr<SharedMemoryImpl>
-  Create(size_t size, int id, const base::Closure& on_deleted_callback) {
-    std::unique_ptr<base::SharedMemory> memory(new base::SharedMemory());
-    if (!memory->CreateAndMapAnonymous(size))
-      return nullptr;
-    return base::WrapUnique(
-        new SharedMemoryImpl(std::move(memory), size, id, on_deleted_callback));
-  }
-
-  ~SharedMemoryImpl() override { on_deleted_callback_.Run(); }
-
-  base::SharedMemory* shared_memory() { return shared_memory_.get(); }
-
- private:
-  SharedMemoryImpl(std::unique_ptr<base::SharedMemory> memory,
-                   size_t size,
-                   int id,
-                   const base::Closure& on_deleted_callback)
-      : SharedMemory(memory->memory(),
-                     size,
-// webrtc::ScreenCapturer uses webrtc::SharedMemory::handle() only on Windows.
-#if defined(OS_WIN)
-                     memory->handle().GetHandle(),
-#else
-                     0,
-#endif
-                     id),
-        on_deleted_callback_(on_deleted_callback),
-        shared_memory_(std::move(memory)) {
-  }
-
-  base::Closure on_deleted_callback_;
-  std::unique_ptr<base::SharedMemory> shared_memory_;
-
-  DISALLOW_COPY_AND_ASSIGN(SharedMemoryImpl);
-};
-
 class SharedMemoryFactoryImpl : public webrtc::SharedMemoryFactory {
  public:
   typedef base::Callback<void(std::unique_ptr<IPC::Message> message)>
@@ -142,7 +187,7 @@
 
       send_message_callback_.Run(
           std::make_unique<ChromotingDesktopNetworkMsg_CreateSharedBuffer>(
-              buffer->id(), buffer->shared_memory()->handle(), buffer->size()));
+              buffer->id(), buffer->region().Duplicate(), buffer->size()));
     }
 
     return std::move(buffer);
diff --git a/remoting/host/desktop_session_proxy.cc b/remoting/host/desktop_session_proxy.cc
index d6c1683..9da7dd35 100644
--- a/remoting/host/desktop_session_proxy.cc
+++ b/remoting/host/desktop_session_proxy.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/shared_memory.h"
 #include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
 #include "build/build_config.h"
@@ -43,44 +42,44 @@
 #include "base/win/scoped_handle.h"
 #endif  // defined(OS_WIN)
 
-const bool kReadOnly = true;
-
 namespace remoting {
 
 class DesktopSessionProxy::IpcSharedBufferCore
     : public base::RefCountedThreadSafe<IpcSharedBufferCore> {
  public:
-  IpcSharedBufferCore(int id,
-                      base::SharedMemoryHandle handle,
-                      size_t size)
-      : id_(id),
-        shared_memory_(handle, kReadOnly),
-        size_(size) {
-    if (!shared_memory_.Map(size)) {
+  IpcSharedBufferCore(int id, base::ReadOnlySharedMemoryRegion region)
+      : id_(id) {
+    mapping_ = region.Map();
+    if (!mapping_.IsValid()) {
       LOG(ERROR) << "Failed to map a shared buffer: id=" << id
-                 << ", size=" << size;
+                 << ", size=" << region.GetSize();
     }
+    // After being mapped, |region| is no longer needed and can be discarded.
   }
 
-  int id() { return id_; }
-  size_t size() { return size_; }
-  void* memory() { return shared_memory_.memory(); }
+  int id() const { return id_; }
+  size_t size() const { return mapping_.size(); }
+  const void* memory() const { return mapping_.memory(); }
 
  private:
   virtual ~IpcSharedBufferCore() = default;
   friend class base::RefCountedThreadSafe<IpcSharedBufferCore>;
 
   int id_;
-  base::SharedMemory shared_memory_;
-  size_t size_;
+  base::ReadOnlySharedMemoryMapping mapping_;
 
   DISALLOW_COPY_AND_ASSIGN(IpcSharedBufferCore);
 };
 
 class DesktopSessionProxy::IpcSharedBuffer : public webrtc::SharedMemory {
  public:
+  // Note that the webrtc::SharedMemory class is used for both read-only and
+  // writable shared memory, necessitating the ugly const_cast here.
   IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)
-      : SharedMemory(core->memory(), core->size(), 0, core->id()),
+      : SharedMemory(const_cast<void*>(core->memory()),
+                     core->size(),
+                     0,
+                     core->id()),
         core_(core) {}
 
  private:
@@ -516,12 +515,12 @@
 
 void DesktopSessionProxy::OnCreateSharedBuffer(
     int id,
-    base::SharedMemoryHandle handle,
+    base::ReadOnlySharedMemoryRegion region,
     uint32_t size) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
 
   scoped_refptr<IpcSharedBufferCore> shared_buffer =
-      new IpcSharedBufferCore(id, handle, size);
+      new IpcSharedBufferCore(id, std::move(region));
 
   if (shared_buffer->memory() != nullptr &&
       !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
diff --git a/remoting/host/desktop_session_proxy.h b/remoting/host/desktop_session_proxy.h
index 0df547b..2e8e8da 100644
--- a/remoting/host/desktop_session_proxy.h
+++ b/remoting/host/desktop_session_proxy.h
@@ -10,8 +10,8 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory_handle.h"
 #include "base/memory/weak_ptr.h"
 #include "base/process/process.h"
 #include "base/sequenced_task_runner_helpers.h"
@@ -171,7 +171,7 @@
 
   // Registers a new shared buffer created by the desktop process.
   void OnCreateSharedBuffer(int id,
-                            base::SharedMemoryHandle handle,
+                            base::ReadOnlySharedMemoryRegion region,
                             uint32_t size);
 
   // Drops a cached reference to the shared buffer.
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn
index 333a8cf..173bab1d 100644
--- a/services/device/BUILD.gn
+++ b/services/device/BUILD.gn
@@ -249,7 +249,7 @@
       ":usb_test_gadget",
       "//device/base",
       "//net:test_support",
-      "//services/device/public/cpp/test:test_support",
+      "//services/device/public/cpp:test_support",
       "//services/device/public/cpp/usb",
       "//services/device/public/mojom",
       "//services/device/usb",
diff --git a/services/device/public/cpp/BUILD.gn b/services/device/public/cpp/BUILD.gn
index 55971755..7d89729 100644
--- a/services/device/public/cpp/BUILD.gn
+++ b/services/device/public/cpp/BUILD.gn
@@ -39,3 +39,44 @@
     defines = [ "USE_DBUS=1" ]
   }
 }
+
+source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "test/fake_sensor_and_provider.cc",
+    "test/fake_sensor_and_provider.h",
+    "test/fake_serial_port_manager.cc",
+    "test/fake_serial_port_manager.h",
+    "test/fake_usb_device.cc",
+    "test/fake_usb_device.h",
+    "test/fake_usb_device_info.cc",
+    "test/fake_usb_device_info.h",
+    "test/fake_usb_device_manager.cc",
+    "test/fake_usb_device_manager.h",
+    "test/mock_usb_mojo_device.cc",
+    "test/mock_usb_mojo_device.h",
+    "test/scoped_geolocation_overrider.cc",
+    "test/scoped_geolocation_overrider.h",
+    "test/test_wake_lock_provider.cc",
+    "test/test_wake_lock_provider.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//services/device/public/cpp/generic_sensor",
+    "//services/device/public/cpp/geolocation",
+    "//services/device/public/cpp/usb",
+    "//services/device/public/mojom",
+    "//services/device/public/mojom:usb",
+    "//services/service_manager/public/cpp",
+  ]
+
+  deps = [
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//url",
+  ]
+}
diff --git a/services/device/public/cpp/test/BUILD.gn b/services/device/public/cpp/test/BUILD.gn
deleted file mode 100644
index 242f83d..0000000
--- a/services/device/public/cpp/test/BUILD.gn
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("test_support") {
-  testonly = true
-
-  sources = [
-    "fake_sensor_and_provider.cc",
-    "fake_sensor_and_provider.h",
-    "fake_serial_port_manager.cc",
-    "fake_serial_port_manager.h",
-    "fake_usb_device.cc",
-    "fake_usb_device.h",
-    "fake_usb_device_info.cc",
-    "fake_usb_device_info.h",
-    "fake_usb_device_manager.cc",
-    "fake_usb_device_manager.h",
-    "mock_usb_mojo_device.cc",
-    "mock_usb_mojo_device.h",
-    "scoped_geolocation_overrider.cc",
-    "scoped_geolocation_overrider.h",
-    "test_wake_lock_provider.cc",
-    "test_wake_lock_provider.h",
-  ]
-
-  public_deps = [
-    "//base",
-    "//services/device/public/cpp/generic_sensor",
-    "//services/device/public/cpp/geolocation",
-    "//services/device/public/cpp/usb",
-    "//services/device/public/mojom",
-    "//services/device/public/mojom:usb",
-    "//services/service_manager/public/cpp",
-  ]
-
-  deps = [
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//testing/gmock",
-    "//testing/gtest",
-    "//url:url",
-  ]
-}
diff --git a/services/device/public/mojom/BUILD.gn b/services/device/public/mojom/BUILD.gn
index d535302..9f40b0f 100644
--- a/services/device/public/mojom/BUILD.gn
+++ b/services/device/public/mojom/BUILD.gn
@@ -24,6 +24,7 @@
     "public_ip_address_geolocation_provider.mojom",
     "screen_orientation.mojom",
     "screen_orientation_lock_types.mojom",
+    "serial.mojom",
     "time_zone_monitor.mojom",
     "vibration_manager.mojom",
     "wake_lock.mojom",
@@ -33,7 +34,6 @@
 
   public_deps = [
     ":constants",
-    ":serial",
     "//mojo/public/mojom/base",
     "//services/network/public/mojom",
     "//services/network/public/mojom:mutable_network_traffic_annotation_interface",
@@ -100,20 +100,6 @@
   ]
 }
 
-mojom("serial") {
-  sources = [
-    "serial.mojom",
-  ]
-
-  public_deps = [
-    "//mojo/public/mojom/base",
-  ]
-
-  export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
-  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
-  export_header_blink = "third_party/blink/public/platform/web_common.h"
-}
-
 mojom("usb") {
   sources = [
     "usb_device.mojom",
diff --git a/services/device/serial/BUILD.gn b/services/device/serial/BUILD.gn
index bf6eff78..79daf9e6 100644
--- a/services/device/serial/BUILD.gn
+++ b/services/device/serial/BUILD.gn
@@ -44,7 +44,7 @@
     public_configs = [ ":platform_support" ]
 
     public_deps = [
-      "//services/device/public/mojom:serial",
+      "//services/device/public/mojom",
     ]
 
     deps = [
diff --git a/services/network/public/cpp/cors/origin_access_list_unittest.cc b/services/network/public/cpp/cors/origin_access_list_unittest.cc
index bc0bba323..b59a5ee 100644
--- a/services/network/public/cpp/cors/origin_access_list_unittest.cc
+++ b/services/network/public/cpp/cors/origin_access_list_unittest.cc
@@ -87,7 +87,7 @@
   }
   void SetBlockListEntry(const std::string& protocol,
                          const std::string& host,
-                         const uint16_t port,
+                         const int16_t port,
                          const mojom::CorsDomainMatchMode domain_match_mode,
                          const mojom::CorsPortMatchMode port_match_mode) {
     std::vector<mojom::CorsOriginPatternPtr> patterns;
diff --git a/services/network/public/cpp/network_ipc_param_traits.h b/services/network/public/cpp/network_ipc_param_traits.h
index 32abafa9..5d5849b3 100644
--- a/services/network/public/cpp/network_ipc_param_traits.h
+++ b/services/network/public/cpp/network_ipc_param_traits.h
@@ -174,6 +174,7 @@
   IPC_STRUCT_TRAITS_MEMBER(async_revalidation_requested)
   IPC_STRUCT_TRAITS_MEMBER(did_mime_sniff)
   IPC_STRUCT_TRAITS_MEMBER(is_signed_exchange_inner_response)
+  IPC_STRUCT_TRAITS_MEMBER(was_in_prefetch_cache)
   IPC_STRUCT_TRAITS_MEMBER(is_legacy_tls_version)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/services/network/public/cpp/resource_response.cc b/services/network/public/cpp/resource_response.cc
index 3178a62..1462a40 100644
--- a/services/network/public/cpp/resource_response.cc
+++ b/services/network/public/cpp/resource_response.cc
@@ -63,6 +63,7 @@
   new_response->head.did_mime_sniff = head.did_mime_sniff;
   new_response->head.is_signed_exchange_inner_response =
       head.is_signed_exchange_inner_response;
+  new_response->head.was_in_prefetch_cache = head.was_in_prefetch_cache;
   new_response->head.intercepted_by_plugin = head.intercepted_by_plugin;
   new_response->head.is_legacy_tls_version = head.is_legacy_tls_version;
   return new_response;
diff --git a/services/network/public/cpp/resource_response_info.h b/services/network/public/cpp/resource_response_info.h
index d11e644a..cba7b7d 100644
--- a/services/network/public/cpp/resource_response_info.h
+++ b/services/network/public/cpp/resource_response_info.h
@@ -196,6 +196,9 @@
   // True if the response is an inner response of a signed exchange.
   bool is_signed_exchange_inner_response = false;
 
+  // True if this resource is served from the prefetch cache.
+  bool was_in_prefetch_cache = false;
+
   // True if the response was intercepted by a plugin.
   bool intercepted_by_plugin = false;
 
diff --git a/services/tracing/perfetto/test_utils.cc b/services/tracing/perfetto/test_utils.cc
index 19cfcb8..9ba987d9 100644
--- a/services/tracing/perfetto/test_utils.cc
+++ b/services/tracing/perfetto/test_utils.cc
@@ -204,21 +204,14 @@
     : producer_name_(producer_name),
       datasource_registered_callback_(
           std::move(datasource_registered_callback)) {
-  auto on_mojo_connected_callback =
-      [](MockProducerClient* producer_client,
-         const std::string& data_source_name, MockProducerHost* producer_host,
-         perfetto::TracingService* service,
-         mojom::ProducerClientPtr producer_client_pipe,
-         mojom::ProducerHostRequest producer_host_pipe) {
-        producer_host->OnMessagepipesReadyCallback(
-            service, std::move(producer_client_pipe),
-            std::move(producer_host_pipe));
-        producer_client->SetupDataSource(data_source_name);
-      };
-
-  producer_client->CreateMojoMessagepipes(base::BindOnce(
-      on_mojo_connected_callback, base::Unretained(producer_client),
-      data_source_name, base::Unretained(this), base::Unretained(service)));
+  mojom::ProducerClientPtr client;
+  mojom::ProducerHostPtrInfo host_info;
+  auto client_request = mojo::MakeRequest(&client);
+  Initialize(std::move(client), service, producer_name_);
+  binding_.Bind(mojo::MakeRequest(&host_info));
+  producer_client->BindClientAndHostPipesForTesting(std::move(client_request),
+                                                    std::move(host_info));
+  producer_client->SetupDataSource(data_source_name);
 }
 
 MockProducerHost::~MockProducerHost() = default;
@@ -251,15 +244,6 @@
   all_host_commit_data_requests_ += proto_string;
 }
 
-void MockProducerHost::OnMessagepipesReadyCallback(
-    perfetto::TracingService* perfetto_service,
-    mojom::ProducerClientPtr producer_client_pipe,
-    mojom::ProducerHostRequest producer_host_pipe) {
-  Initialize(std::move(producer_client_pipe), perfetto_service, producer_name_);
-  binding_ = std::make_unique<mojo::Binding<mojom::ProducerHost>>(
-      this, std::move(producer_host_pipe));
-}
-
 MockProducer::MockProducer(const std::string& producer_name,
                            const std::string& data_source_name,
                            perfetto::TracingService* service,
diff --git a/services/tracing/perfetto/test_utils.h b/services/tracing/perfetto/test_utils.h
index 5d5d215..4838507 100644
--- a/services/tracing/perfetto/test_utils.h
+++ b/services/tracing/perfetto/test_utils.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "mojo/public/cpp/bindings/binding.h"
 #include "services/tracing/perfetto/producer_host.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "services/tracing/public/cpp/perfetto/producer_client.h"
@@ -133,11 +134,6 @@
 
   void OnCommit(const perfetto::CommitDataRequest& commit_data_request);
 
-  void OnMessagepipesReadyCallback(
-      perfetto::TracingService* perfetto_service,
-      mojom::ProducerClientPtr producer_client_pipe,
-      mojom::ProducerHostRequest producer_host_pipe);
-
   const std::string& all_host_commit_data_requests() const {
     return all_host_commit_data_requests_;
   }
@@ -146,7 +142,7 @@
   const std::string producer_name_;
   base::OnceClosure datasource_registered_callback_;
   std::string all_host_commit_data_requests_;
-  std::unique_ptr<mojo::Binding<mojom::ProducerHost>> binding_;
+  mojo::Binding<mojom::ProducerHost> binding_{this};
 };
 
 class MockProducer {
diff --git a/services/tracing/public/cpp/perfetto/producer_client.cc b/services/tracing/public/cpp/perfetto/producer_client.cc
index 1fd5234..245624cf 100644
--- a/services/tracing/public/cpp/perfetto/producer_client.cc
+++ b/services/tracing/public/cpp/perfetto/producer_client.cc
@@ -30,38 +30,35 @@
 }
 
 void ProducerClient::Connect(mojom::PerfettoServicePtr perfetto_service) {
-  CreateMojoMessagepipes(base::BindOnce(
-      [](mojom::PerfettoServicePtr perfetto_service,
-         mojom::ProducerClientPtr producer_client_pipe,
-         mojom::ProducerHostRequest producer_host_pipe) {
-        perfetto_service->ConnectToProducerHost(std::move(producer_client_pipe),
-                                                std::move(producer_host_pipe));
-      },
-      std::move(perfetto_service)));
-}
-
-void ProducerClient::CreateMojoMessagepipes(
-    MessagepipesReadyCallback callback) {
-  auto origin_task_runner = base::SequencedTaskRunnerHandle::Get();
-  DCHECK(origin_task_runner);
-  mojom::ProducerClientPtr producer_client;
+  mojom::ProducerClientPtr client;
+  auto client_request = mojo::MakeRequest(&client);
+  mojom::ProducerHostPtrInfo host_info;
+  perfetto_service->ConnectToProducerHost(std::move(client),
+                                          mojo::MakeRequest(&host_info));
   task_runner()->GetOrCreateTaskRunner()->PostTask(
       FROM_HERE,
-      base::BindOnce(&ProducerClient::CreateMojoMessagepipesOnSequence,
-                     base::Unretained(this), origin_task_runner,
-                     std::move(callback), mojo::MakeRequest(&producer_client),
-                     std::move(producer_client)));
+      base::BindOnce(&ProducerClient::BindClientAndHostPipesOnSequence,
+                     base::Unretained(this), std::move(client_request),
+                     std::move(host_info)));
+}
+
+void ProducerClient::BindClientAndHostPipesForTesting(
+    mojom::ProducerClientRequest producer_client_request,
+    mojom::ProducerHostPtrInfo producer_host_info) {
+  task_runner()->GetOrCreateTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ProducerClient::BindClientAndHostPipesOnSequence,
+                     base::Unretained(this), std::move(producer_client_request),
+                     std::move(producer_host_info)));
 }
 
 // The Mojo binding should run on the same sequence as the one we get
 // callbacks from Perfetto on, to avoid additional PostTasks.
-void ProducerClient::CreateMojoMessagepipesOnSequence(
-    scoped_refptr<base::SequencedTaskRunner> origin_task_runner,
-    MessagepipesReadyCallback callback,
+void ProducerClient::BindClientAndHostPipesOnSequence(
     mojom::ProducerClientRequest producer_client_request,
-    mojom::ProducerClientPtr producer_client) {
+    mojom::ProducerHostPtrInfo producer_host_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!binding_ || !binding_->is_bound());
+  CHECK(!binding_ || !binding_->is_bound());
 
   binding_ = std::make_unique<mojo::Binding<mojom::ProducerClient>>(
       this, std::move(producer_client_request));
@@ -71,9 +68,7 @@
       },
       base::Unretained(this)));
 
-  origin_task_runner->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), std::move(producer_client),
-                                mojo::MakeRequest(&producer_host_)));
+  producer_host_.Bind(std::move(producer_host_info));
 
   // TODO(oysteine) We register the data sources in reverse as a temporary
   // workaround to make sure that the TraceEventDataSource is registered
diff --git a/services/tracing/public/cpp/perfetto/producer_client.h b/services/tracing/public/cpp/perfetto/producer_client.h
index ea209ef2..2322307 100644
--- a/services/tracing/public/cpp/perfetto/producer_client.h
+++ b/services/tracing/public/cpp/perfetto/producer_client.h
@@ -45,15 +45,6 @@
 
   void Connect(mojom::PerfettoServicePtr perfetto_service);
 
-  // Create the messagepipes that'll be used to connect
-  // to the service-side ProducerHost, on the correct
-  // sequence. The callback will be called on same sequence
-  // as CreateMojoMessagepipes() got called on.
-  using MessagepipesReadyCallback =
-      base::OnceCallback<void(mojom::ProducerClientPtr,
-                              mojom::ProducerHostRequest)>;
-  void CreateMojoMessagepipes(MessagepipesReadyCallback);
-
   void set_in_process_shmem_arbiter(perfetto::SharedMemoryArbiter* arbiter) {
     DCHECK(!in_process_arbiter_);
     in_process_arbiter_ = arbiter;
@@ -94,6 +85,9 @@
   size_t shared_buffer_page_size_kb() const override;
   perfetto::SharedMemoryArbiter* GetInProcessShmemArbiter() override;
 
+  void BindClientAndHostPipesForTesting(mojom::ProducerClientRequest,
+                                        mojom::ProducerHostPtrInfo);
+
  protected:
   perfetto::SharedMemoryArbiter* GetSharedMemoryArbiter() override;
 
@@ -101,14 +95,8 @@
   friend class base::NoDestructor<ProducerClient>;
 
   void CommitDataOnSequence(const perfetto::CommitDataRequest& request);
-
-  // The callback will be run on the |origin_task_runner|, meaning
-  // the same sequence as CreateMojoMessagePipes() got called on.
-  void CreateMojoMessagepipesOnSequence(
-      scoped_refptr<base::SequencedTaskRunner> origin_task_runner,
-      MessagepipesReadyCallback,
-      mojom::ProducerClientRequest,
-      mojom::ProducerClientPtr);
+  void BindClientAndHostPipesOnSequence(mojom::ProducerClientRequest,
+                                        mojom::ProducerHostPtrInfo);
 
   std::unique_ptr<mojo::Binding<mojom::ProducerClient>> binding_;
   mojom::ProducerHostPtr producer_host_;
diff --git a/services/tracing/public/cpp/traced_process_impl.cc b/services/tracing/public/cpp/traced_process_impl.cc
index fcd32720f..e6811d9 100644
--- a/services/tracing/public/cpp/traced_process_impl.cc
+++ b/services/tracing/public/cpp/traced_process_impl.cc
@@ -45,6 +45,7 @@
     return;
   }
 
+  DETACH_FROM_SEQUENCE(sequence_checker_);
   binding_.Bind(std::move(request));
 }
 
@@ -73,9 +74,14 @@
 }
 
 void TracedProcessImpl::ConnectToTracingService(
-    mojom::ConnectToTracingRequestPtr request) {
+    mojom::ConnectToTracingRequestPtr request,
+    ConnectToTracingServiceCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  // Acknowledge this message so the tracing service knows it was dispatched in
+  // this process.
+  std::move(callback).Run();
+
   // Tracing requires a running ThreadPool; disable tracing
   // for processes without it.
   if (!base::ThreadPool::GetInstance()) {
diff --git a/services/tracing/public/cpp/traced_process_impl.h b/services/tracing/public/cpp/traced_process_impl.h
index efd5f31..a67e2ca 100644
--- a/services/tracing/public/cpp/traced_process_impl.h
+++ b/services/tracing/public/cpp/traced_process_impl.h
@@ -46,7 +46,8 @@
 
   // tracing::mojom::TracedProcess:
   void ConnectToTracingService(
-      mojom::ConnectToTracingRequestPtr request) override;
+      mojom::ConnectToTracingRequestPtr request,
+      ConnectToTracingServiceCallback callback) override;
 
   // Lock protecting binding_.
   base::Lock lock_;
diff --git a/services/tracing/public/mojom/traced_process.mojom b/services/tracing/public/mojom/traced_process.mojom
index b1d80409..7b443ce 100644
--- a/services/tracing/public/mojom/traced_process.mojom
+++ b/services/tracing/public/mojom/traced_process.mojom
@@ -20,7 +20,7 @@
 // and pass it pointers to the interfaces within the tracing service
 // that the other services should register themselves with.
 interface TracedProcess {
-  ConnectToTracingService(ConnectToTracingRequest request);
+  ConnectToTracingService(ConnectToTracingRequest request) => ();
 };
 
 
diff --git a/services/tracing/tracing_service.cc b/services/tracing/tracing_service.cc
index 6d04bca..4ab07c9 100644
--- a/services/tracing/tracing_service.cc
+++ b/services/tracing/tracing_service.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/stl_util.h"
 #include "base/timer/timer.h"
 #include "services/service_manager/public/mojom/service_manager.mojom.h"
 #include "services/tracing/agent_registry.h"
@@ -29,8 +30,7 @@
   ServiceListener(service_manager::Connector* connector,
                   AgentRegistry* agent_registry,
                   Coordinator* coordinator)
-      : binding_(this),
-        connector_(connector),
+      : connector_(connector),
         agent_registry_(agent_registry),
         coordinator_(coordinator) {
     service_manager::mojom::ServiceManagerPtr service_manager;
@@ -45,25 +45,31 @@
 
   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;
-                         });
+                         [pid](const auto& p) { return p.second == pid; });
   }
 
   void ServiceAddedWithPID(const service_manager::Identity& identity,
                            uint32_t pid) {
     service_pid_map_[identity] = pid;
-    // Not the first service added, so we're already sent it a connection
-    // request.
-    if (CountServicesWithPID(pid) > 1) {
+
+    // Not the first service added for this PID, and the process has already
+    // accepted a connection request.
+    if (base::ContainsKey(connected_pids_, pid))
       return;
-    }
 
     // Let the Coordinator and the perfetto service know it should be expecting
     // a connection from this process.
     coordinator_->AddExpectedPID(pid);
     PerfettoService::GetInstance()->AddActiveServicePid(pid);
 
+    // NOTE: If multiple service instances are running in the same process, we
+    // may send multiple ConnectToTracingService calls to the same process in
+    // the time it takes the first call to be received and acknowledged. This is
+    // OK, because any given client process will only bind a single
+    // TracedProcess endpoint as long as this instance of the tracing service
+    // remains alive. Subsequent TracedProcess endpoints will be dropped and
+    // their calls will never be processed.
+
     mojom::TracedProcessPtr traced_process;
     connector_->BindInterface(
         service_manager::ServiceFilter::ForExactIdentity(identity),
@@ -71,14 +77,17 @@
         service_manager::mojom::BindInterfacePriority::kBestEffort);
 
     auto new_connection_request = mojom::ConnectToTracingRequest::New();
-
-    PerfettoService::GetInstance()->BindRequest(
-        mojo::MakeRequest(&new_connection_request->perfetto_service), pid);
-
-    agent_registry_->BindAgentRegistryRequest(
-        mojo::MakeRequest(&new_connection_request->agent_registry));
-
-    traced_process->ConnectToTracingService(std::move(new_connection_request));
+    auto service_request =
+        mojo::MakeRequest(&new_connection_request->perfetto_service);
+    auto registry_request =
+        mojo::MakeRequest(&new_connection_request->agent_registry);
+    mojom::TracedProcess* raw_traced_process = traced_process.get();
+    raw_traced_process->ConnectToTracingService(
+        std::move(new_connection_request),
+        base::BindOnce(&ServiceListener::OnProcessConnected,
+                       base::Unretained(this), std::move(traced_process), pid,
+                       std::move(service_request),
+                       std::move(registry_request)));
   }
 
   void ServiceRemoved(const service_manager::Identity& identity) {
@@ -91,6 +100,7 @@
       if (CountServicesWithPID(pid) == 0) {
         coordinator_->RemoveExpectedPID(pid);
         PerfettoService::GetInstance()->RemoveActiveServicePid(pid);
+        connected_pids_.erase(pid);
       }
     }
   }
@@ -130,11 +140,30 @@
       service_manager::mojom::RunningServiceInfoPtr service) override {}
 
  private:
-  mojo::Binding<service_manager::mojom::ServiceManagerListener> binding_;
-  service_manager::Connector* connector_;
-  AgentRegistry* agent_registry_;
-  Coordinator* coordinator_;
+  void OnProcessConnected(mojom::TracedProcessPtr traced_process,
+                          uint32_t pid,
+                          mojom::PerfettoServiceRequest service_request,
+                          mojom::AgentRegistryRequest registry_request) {
+    auto result = connected_pids_.insert(pid);
+    if (!result.second) {
+      // The PID was already connected. Nothing more to do.
+      return;
+    }
+
+    connected_pids_.insert(pid);
+    PerfettoService::GetInstance()->BindRequest(std::move(service_request),
+                                                pid);
+    agent_registry_->BindAgentRegistryRequest(std::move(registry_request));
+  }
+
+  mojo::Binding<service_manager::mojom::ServiceManagerListener> binding_{this};
+  service_manager::Connector* const connector_;
+  AgentRegistry* const agent_registry_;
+  Coordinator* const coordinator_;
   std::map<service_manager::Identity, uint32_t> service_pid_map_;
+  std::set<uint32_t> connected_pids_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceListener);
 };
 
 TracingService::TracingService(service_manager::mojom::ServiceRequest request)
diff --git a/services/ws/public/mojom/BUILD.gn b/services/ws/public/mojom/BUILD.gn
index 4ad5f1f4..3eb3b00 100644
--- a/services/ws/public/mojom/BUILD.gn
+++ b/services/ws/public/mojom/BUILD.gn
@@ -31,6 +31,7 @@
     "//ui/gfx/geometry/mojo",
     "//ui/gfx/image/mojo:interfaces",
     "//ui/gfx/mojo",
+    "//ui/platform_window/mojo:interfaces",
   ]
 
   if (is_chromeos) {
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index fd3e5f4..bbcda6f 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -105,7 +105,7 @@
 // Enable off-the-main-thread shared worker script fetch.
 // (https://crbug.com/924041)
 const base::Feature kOffMainThreadSharedWorkerScriptFetch{
-    "OffMainThreadSharedWorkerScriptFetch", base::FEATURE_DISABLED_BY_DEFAULT};
+    "OffMainThreadSharedWorkerScriptFetch", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Onion souping for all DOMStorage. https://crbug.com/781870
 const base::Feature kOnionSoupDOMStorage{"OnionSoupDOMStorage",
@@ -286,11 +286,6 @@
 bool IsOffMainThreadSharedWorkerScriptFetchEnabled() {
   // Off-the-main-thread shared worker script fetch depends on PlzSharedWorker
   // (NetworkService).
-  DCHECK(!base::FeatureList::IsEnabled(
-             features::kOffMainThreadSharedWorkerScriptFetch) ||
-         base::FeatureList::IsEnabled(network::features::kNetworkService))
-      << "OffMainThreadSharedWorkerScriptFetch is enabled but NetworkService "
-      << "isn't. OffMainThreadSharedWorkerScriptFetch requires NetworkService.";
   return base::FeatureList::IsEnabled(network::features::kNetworkService) &&
          base::FeatureList::IsEnabled(
              features::kOffMainThreadSharedWorkerScriptFetch);
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 5350e2ba..5abc41f 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -36,8 +36,6 @@
     kOffMainThreadDedicatedWorkerScriptFetch;
 BLINK_COMMON_EXPORT extern const base::Feature
     kOffMainThreadServiceWorkerScriptFetch;
-BLINK_COMMON_EXPORT extern const base::Feature
-    kOffMainThreadSharedWorkerScriptFetch;
 BLINK_COMMON_EXPORT extern const base::Feature kOnionSoupDOMStorage;
 BLINK_COMMON_EXPORT extern const base::Feature kPlzDedicatedWorker;
 BLINK_COMMON_EXPORT extern const base::Feature kPortals;
diff --git a/third_party/blink/public/mojom/plugins/plugin_registry.mojom b/third_party/blink/public/mojom/plugins/plugin_registry.mojom
index 0cc3d5b..ff7a8ed 100644
--- a/third_party/blink/public/mojom/plugins/plugin_registry.mojom
+++ b/third_party/blink/public/mojom/plugins/plugin_registry.mojom
@@ -20,9 +20,8 @@
   mojo_base.mojom.FilePath filename;
   uint32 background_color;
   array<PluginMimeType> mime_types;
-  // When true, the current plugin maybe implemented using a MimeHandlerView
-  // extension.
-  bool may_use_mime_handler_view;
+  // When true, the current plugin maybe implemented using an external handler.
+  bool may_use_external_handler;
 };
 
 interface PluginRegistry {
diff --git a/third_party/blink/public/platform/task_type.h b/third_party/blink/public/platform/task_type.h
index a92a6e5d..a0399ad 100644
--- a/third_party/blink/public/platform/task_type.h
+++ b/third_party/blink/public/platform/task_type.h
@@ -203,8 +203,12 @@
   // Task used for ContentCapture.
   kInternalContentCapture = 61,
 
-  // Task used for Navigations.
-  kInternalNavigation = 63,
+  // Navigation tasks and tasks which have to run in order with them, including
+  // legacy IPCs and channel associated interfaces.
+  // Note that the ordering between tasks related to different frames is not
+  // always guaranteed - tasks belonging to different frames can be reordered
+  // when one of the frames is frozen.
+  kInternalNavigationAssociated = 63,
 
   ///////////////////////////////////////
   // The following task types are only for thread-local queues.
diff --git a/third_party/blink/public/platform/web_rtc_rtp_source.h b/third_party/blink/public/platform/web_rtc_rtp_source.h
index 57eb5bb..3e052ce 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_source.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_source.h
@@ -26,6 +26,7 @@
   virtual double TimestampMs() const = 0;
   virtual uint32_t Source() const = 0;
   virtual base::Optional<double> AudioLevel() const = 0;
+  virtual uint32_t RtpTimestamp() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/platform/web_url_response.h b/third_party/blink/public/platform/web_url_response.h
index c7609e6..6216e93 100644
--- a/third_party/blink/public/platform/web_url_response.h
+++ b/third_party/blink/public/platform/web_url_response.h
@@ -287,6 +287,7 @@
   BLINK_PLATFORM_EXPORT void SetEncodedDataLength(int64_t);
 
   BLINK_PLATFORM_EXPORT void SetIsSignedExchangeInnerResponse(bool);
+  BLINK_PLATFORM_EXPORT void SetWasInPrefetchCache(bool);
 
 #if INSIDE_BLINK
  protected:
diff --git a/third_party/blink/public/web/web_ax_enums.h b/third_party/blink/public/web/web_ax_enums.h
index 0475dca0..25aab96 100644
--- a/third_party/blink/public/web/web_ax_enums.h
+++ b/third_party/blink/public/web/web_ax_enums.h
@@ -42,6 +42,14 @@
   kWebAXExpandedExpanded
 };
 
+// Grabbed State.
+// These values must match blink::AccessibilityGrabbedState values.
+enum WebAXGrabbedState {
+  kWebAXGrabbedStateUndefined = 0,
+  kWebAXGrabbedStateFalse,
+  kWebAXGrabbedStateTrue
+};
+
 // Selected State.
 // These values must match blink::AccessibilitySelectedState values.
 enum WebAXSelectedState {
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index ad4b8db..8d07410 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -131,6 +131,7 @@
   BLINK_EXPORT bool IsDefault() const;
   BLINK_EXPORT WebAXExpanded IsExpanded() const;
   BLINK_EXPORT bool IsFocused() const;
+  BLINK_EXPORT WebAXGrabbedState IsGrabbed() const;
   BLINK_EXPORT bool IsHovered() const;
   BLINK_EXPORT bool IsLinked() const;
   BLINK_EXPORT bool IsLoaded() const;
@@ -342,6 +343,10 @@
   BLINK_EXPORT WebPoint MaximumScrollOffset() const;
   BLINK_EXPORT void SetScrollOffset(const WebPoint&) const;
 
+  // aria-dropeffect is deprecated in WAI-ARIA 1.1
+  BLINK_EXPORT void Dropeffects(
+      WebVector<ax::mojom::Dropeffect>& dropeffects) const;
+
   // Every object's bounding box is returned relative to a
   // container object (which is guaranteed to be an ancestor) and
   // optionally a transformation matrix that needs to be applied too.
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 84c3ebb..838280a 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -846,11 +846,10 @@
 
   // Returns true when the contents of plugin are handled externally. This means
   // the plugin element will own a content frame but the frame is than used
-  // externally to load the required handelrs (MimeHandlerView).
-  virtual bool MaybeCreateMimeHandlerView(
-      const WebElement& plugin_element,
-      const WebURL& url,
-      const WebString& suggested_mime_type) {
+  // externally to load the required handlers.
+  virtual bool IsPluginHandledExternally(const WebElement& plugin_element,
+                                         const WebURL& url,
+                                         const WebString& suggested_mime_type) {
     return false;
   }
 
diff --git a/third_party/blink/public/web/web_security_policy.h b/third_party/blink/public/web/web_security_policy.h
index 0254232..fb3b59c 100644
--- a/third_party/blink/public/web/web_security_policy.h
+++ b/third_party/blink/public/web/web_security_policy.h
@@ -78,13 +78,17 @@
       const WebURL& source_origin,
       const WebString& destination_protocol,
       const WebString& destination_host,
-      bool allow_destination_subdomains,
+      const uint16_t destination_port,
+      network::mojom::CorsDomainMatchMode domain_match_mode,
+      network::mojom::CorsPortMatchMode port_match_mode,
       const network::mojom::CorsOriginAccessMatchPriority priority);
   BLINK_EXPORT static void AddOriginAccessBlockListEntry(
       const WebURL& source_origin,
       const WebString& destination_protocol,
       const WebString& destination_host,
-      bool disallow_destination_subdomains,
+      const uint16_t destination_port,
+      network::mojom::CorsDomainMatchMode domain_match_mode,
+      network::mojom::CorsPortMatchMode port_match_mode,
       const network::mojom::CorsOriginAccessMatchPriority priority);
   BLINK_EXPORT static void ClearOriginAccessListForOrigin(
       const WebURL& source_origin);
diff --git a/third_party/blink/renderer/core/dom/beforeunload_event_listener.h b/third_party/blink/renderer/core/dom/beforeunload_event_listener.h
index 8543dbc1..9f5f713 100644
--- a/third_party/blink/renderer/core/dom/beforeunload_event_listener.h
+++ b/third_party/blink/renderer/core/dom/beforeunload_event_listener.h
@@ -16,7 +16,8 @@
 class Visitor;
 
 // Helper class used to setup beforeunload listener for certain documents which
-// include MimeHandlerView (some PluginDocuments and HTMLDocuments).
+// include plugins that are handled externally and need user verification before
+// before closing the page.
 class BeforeUnloadEventListener : public NativeEventListener {
  public:
   explicit BeforeUnloadEventListener(Document*);
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 2d59c35..bb3aff0 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -3421,7 +3421,7 @@
     domWindow()->DocumentWasClosed();
 
   if (GetFrame()) {
-    GetFrame()->DispatchDidHandleOnloadEvents();
+    GetFrame()->Client()->DispatchDidHandleOnloadEvents();
     Loader()->GetApplicationCacheHost()->StopDeferringEvents();
   }
 
diff --git a/third_party/blink/renderer/core/dom/dom_implementation.cc b/third_party/blink/renderer/core/dom/dom_implementation.cc
index 3b4fa1e..3de30fb 100644
--- a/third_party/blink/renderer/core/dom/dom_implementation.cc
+++ b/third_party/blink/renderer/core/dom/dom_implementation.cc
@@ -256,7 +256,7 @@
   }
 
   if (RuntimeEnabledFeatures::MimeHandlerViewInCrossProcessFrameEnabled() &&
-      plugin_data && plugin_data->IsMimeHandlerViewMimeType(type)) {
+      plugin_data && plugin_data->IsExternalPluginMimeType(type)) {
     // Plugins handled by MimeHandlerView do not create a PluginDocument. They
     // are rendered inside cross-process frames and the notion of a PluginView
     // (which is associated with PluginDocument) is irrelevant here.
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 37c9f95..58227c9 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
@@ -1194,14 +1194,14 @@
   web_frame_->Client()->FrameRectsChanged(frame_rect);
 }
 
-bool LocalFrameClientImpl::MaybeCreateMimeHandlerView(
+bool LocalFrameClientImpl::IsPluginHandledExternally(
     HTMLPlugInElement& plugin_element,
     const KURL& resource_url,
     const String& suggesed_mime_type) {
   if (!RuntimeEnabledFeatures::MimeHandlerViewInCrossProcessFrameEnabled())
     return false;
 
-  return web_frame_->Client()->MaybeCreateMimeHandlerView(
+  return web_frame_->Client()->IsPluginHandledExternally(
       &plugin_element, resource_url, suggesed_mime_type);
 }
 
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 3e957d99..1befe43 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
@@ -304,9 +304,9 @@
 
   void FrameRectsChanged(const IntRect&) override;
 
-  bool MaybeCreateMimeHandlerView(HTMLPlugInElement&,
-                                  const KURL&,
-                                  const String&) override;
+  bool IsPluginHandledExternally(HTMLPlugInElement&,
+                                 const KURL&,
+                                 const String&) override;
   v8::Local<v8::Object> GetScriptableObject(HTMLPlugInElement&,
                                             v8::Isolate*) 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 8b6916d2..903c3d82 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -12729,12 +12729,12 @@
   helper.Reset();
 }
 
-class MimeHandlerViewDocumentTest
+class ExternallyHandledPluginDocumentTest
     : public WebFrameTest,
       public testing::WithParamInterface<
           bool /* mime_handler_view_in_cross_process_frame */> {};
 
-TEST_P(MimeHandlerViewDocumentTest, DocumentType) {
+TEST_P(ExternallyHandledPluginDocumentTest, DocumentType) {
   bool cross_process = GetParam();
   RuntimeEnabledFeatures::SetMimeHandlerViewInCrossProcessFrameEnabled(
       cross_process);
@@ -12750,5 +12750,7 @@
   EXPECT_NE(cross_process, document->IsPluginDocument());
 }
 
-INSTANTIATE_TEST_SUITE_P(P, MimeHandlerViewDocumentTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(P,
+                         ExternallyHandledPluginDocumentTest,
+                         testing::Bool());
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_security_policy.cc b/third_party/blink/renderer/core/exported/web_security_policy.cc
index 94f1c68..1e69f21 100644
--- a/third_party/blink/renderer/core/exported/web_security_policy.cc
+++ b/third_party/blink/renderer/core/exported/web_security_policy.cc
@@ -70,22 +70,28 @@
     const WebURL& source_origin,
     const WebString& destination_protocol,
     const WebString& destination_host,
-    bool allow_destination_subdomains,
+    const uint16_t destination_port,
+    const network::mojom::CorsDomainMatchMode domain_match_mode,
+    const network::mojom::CorsPortMatchMode port_match_mode,
     const network::mojom::CorsOriginAccessMatchPriority priority) {
   SecurityPolicy::AddOriginAccessAllowListEntry(
       *SecurityOrigin::Create(source_origin), destination_protocol,
-      destination_host, allow_destination_subdomains, priority);
+      destination_host, destination_port, domain_match_mode, port_match_mode,
+      priority);
 }
 
 void WebSecurityPolicy::AddOriginAccessBlockListEntry(
     const WebURL& source_origin,
     const WebString& destination_protocol,
     const WebString& destination_host,
-    bool allow_destination_subdomains,
+    const uint16_t destination_port,
+    const network::mojom::CorsDomainMatchMode domain_match_mode,
+    const network::mojom::CorsPortMatchMode port_match_mode,
     const network::mojom::CorsOriginAccessMatchPriority priority) {
   SecurityPolicy::AddOriginAccessBlockListEntry(
       *SecurityOrigin::Create(source_origin), destination_protocol,
-      destination_host, allow_destination_subdomains, priority);
+      destination_host, destination_port, domain_match_mode, port_match_mode,
+      priority);
 }
 
 void WebSecurityPolicy::ClearOriginAccessListForOrigin(
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 06cf42e..b5e3f64 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1702,14 +1702,6 @@
 }
 
 void LocalFrame::SetLifecycleState(mojom::FrameLifecycleState state) {
-  // If we have asked to be frozen we will only do this once the
-  // load event has fired.
-  if (GetDocument()->LoadEventStillNeeded()) {
-    pending_lifecycle_state_ = state;
-    return;
-  }
-  pending_lifecycle_state_ = base::nullopt;
-
   if (state == lifecycle_state_)
     return;
   bool is_frozen = lifecycle_state_ != mojom::FrameLifecycleState::kRunning;
@@ -1741,13 +1733,4 @@
     UseCounter::Count(GetDocument(), WebFeature::kAdClickNavigation);
 }
 
-void LocalFrame::DispatchDidHandleOnloadEvents() {
-  Client()->DispatchDidHandleOnloadEvents();
-
-  if (pending_lifecycle_state_) {
-    DCHECK(!GetDocument()->LoadEventStillNeeded());
-    SetLifecycleState(pending_lifecycle_state_.value());
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index de44ae3..25884090 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -422,8 +422,6 @@
   // navigations go through.
   void MaybeLogAdClickNavigation();
 
-  void DispatchDidHandleOnloadEvents();
-
  private:
   friend class FrameNavigationDisabler;
 
@@ -555,7 +553,6 @@
 
   mojom::FrameLifecycleState lifecycle_state_ =
       mojom::FrameLifecycleState::kRunning;
-  base::Optional<mojom::FrameLifecycleState> pending_lifecycle_state_;
 };
 
 inline FrameLoader& LocalFrame::Loader() const {
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 baf37d001..06a585ad 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -487,9 +487,9 @@
   // Returns true when the contents of plugin are handled externally. This means
   // the plugin element will own a content frame but the frame is than used
   // externally to load the required handelrs.
-  virtual bool MaybeCreateMimeHandlerView(HTMLPlugInElement&,
-                                          const KURL&,
-                                          const String&) {
+  virtual bool IsPluginHandledExternally(HTMLPlugInElement&,
+                                         const KURL&,
+                                         const String&) {
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.cc b/third_party/blink/renderer/core/html/forms/html_button_element.cc
index 88dc36fd..79460a1 100644
--- a/third_party/blink/renderer/core/html/forms/html_button_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_button_element.cc
@@ -112,7 +112,7 @@
   if (event.type() == event_type_names::kDOMActivate &&
       !IsDisabledFormControl()) {
     if (Form() && type_ == SUBMIT) {
-      Form()->PrepareForSubmission(event, this);
+      Form()->PrepareForSubmission(&event, this);
       event.SetDefaultHandled();
     }
     if (Form() && type_ == RESET) {
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 2ab7003..4c2e4611 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -212,7 +212,7 @@
     }
   }
   if (from_implicit_submission_trigger && submission_trigger_count == 1)
-    PrepareForSubmission(event, nullptr);
+    PrepareForSubmission(&event, nullptr);
 }
 
 bool HTMLFormElement::ValidateInteractively() {
@@ -258,7 +258,7 @@
 }
 
 void HTMLFormElement::PrepareForSubmission(
-    Event& event,
+    Event* event,
     HTMLFormControlElement* submit_button) {
   LocalFrame* frame = GetDocument().GetFrame();
   if (!frame || is_submitting_ || in_user_js_submit_event_)
@@ -327,7 +327,7 @@
   }
   if (should_submit) {
     planned_navigation_ = nullptr;
-    Submit(&event, submit_button);
+    Submit(event, submit_button);
   }
   if (!planned_navigation_)
     return;
@@ -340,6 +340,39 @@
   Submit(nullptr, nullptr);
 }
 
+void HTMLFormElement::requestSubmit(ExceptionState& exception_state) {
+  requestSubmit(nullptr, exception_state);
+}
+
+// https://html.spec.whatwg.org/multipage/forms.html#dom-form-requestsubmit
+void HTMLFormElement::requestSubmit(HTMLElement* submitter,
+                                    ExceptionState& exception_state) {
+  HTMLFormControlElement* control = nullptr;
+  // 1. If submitter was given, then:
+  if (submitter) {
+    // 1.1. If submitter is not a submit button, then throw a TypeError.
+    control = ToHTMLFormControlElementOrNull(submitter);
+    // button[type] is a subset of input[type]. So it's ok to compare button's
+    // type and input_type_names.
+    if (!control || (control->type() != input_type_names::kSubmit &&
+                     control->type() != input_type_names::kImage)) {
+      exception_state.ThrowTypeError(
+          "The specified element is not a submit button.");
+      return;
+    }
+    // 1.2. If submitter's form owner is not this form element, then throw a
+    // "NotFoundError" DOMException.
+    if (control->formOwner() != this) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kNotFoundError,
+          "The specified element is not owned by this form element.");
+      return;
+    }
+  }
+  // 3. Submit this form element, from submitter.
+  PrepareForSubmission(nullptr, control);
+}
+
 void HTMLFormElement::SubmitDialog(FormSubmission* form_submission) {
   for (Node* node = this; node; node = node->ParentOrShadowHostNode()) {
     if (auto* dialog = ToHTMLDialogElementOrNull(*node)) {
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.h b/third_party/blink/renderer/core/html/forms/html_form_element.h
index 8d065941..eaab0fd48 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.h
@@ -74,8 +74,10 @@
   void Disassociate(HTMLImageElement&);
   void DidAssociateByParser();
 
-  void PrepareForSubmission(Event&, HTMLFormControlElement* submit_button);
+  void PrepareForSubmission(Event*, HTMLFormControlElement* submit_button);
   void submitFromJavaScript();
+  void requestSubmit(ExceptionState& exception_state);
+  void requestSubmit(HTMLElement* submitter, ExceptionState& exception_state);
   void reset();
 
   void SubmitImplicitly(Event&, bool from_implicit_submission_trigger);
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.idl b/third_party/blink/renderer/core/html/forms/html_form_element.idl
index e91fbd5..91d58da 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.idl
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.idl
@@ -41,6 +41,7 @@
     [NotEnumerable] getter (RadioNodeList or Element) (DOMString name);
 
     [ImplementedAs=submitFromJavaScript] void submit();
+    [RaisesException] void requestSubmit(optional HTMLElement submitter);
     [CEReactions, CustomElementCallbacks] void reset();
     boolean checkValidity();
     boolean reportValidity();
diff --git a/third_party/blink/renderer/core/html/forms/image_input_type.cc b/third_party/blink/renderer/core/html/forms/image_input_type.cc
index 3db1b97..02847cc 100644
--- a/third_party/blink/renderer/core/html/forms/image_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/image_input_type.cc
@@ -101,8 +101,8 @@
   if (GetElement().IsDisabledFormControl() || !GetElement().Form())
     return;
   click_location_ = ExtractClickLocation(event);
-  GetElement().Form()->PrepareForSubmission(
-      event, &GetElement());  // Event handlers can run.
+  // Event handlers can run.
+  GetElement().Form()->PrepareForSubmission(&event, &GetElement());
   event.SetDefaultHandled();
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/submit_input_type.cc b/third_party/blink/renderer/core/html/forms/submit_input_type.cc
index 6ab4816..77ca2c9b 100644
--- a/third_party/blink/renderer/core/html/forms/submit_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/submit_input_type.cc
@@ -64,8 +64,8 @@
 void SubmitInputType::HandleDOMActivateEvent(Event& event) {
   if (GetElement().IsDisabledFormControl() || !GetElement().Form())
     return;
-  GetElement().Form()->PrepareForSubmission(
-      event, &GetElement());  // Event handlers can run.
+  // Event handlers can run.
+  GetElement().Form()->PrepareForSubmission(&event, &GetElement());
   event.SetDefaultHandled();
 }
 
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index 94060a2fbf..f48dd0f 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -179,8 +179,8 @@
 
   ObjectContentType object_type = GetObjectContentType();
   bool handled_externally =
-      object_type == ObjectContentType::kMimeHandlerViewPlugin &&
-      GetDocument().GetFrame()->Client()->MaybeCreateMimeHandlerView(
+      object_type == ObjectContentType::kExternalPlugin &&
+      GetDocument().GetFrame()->Client()->IsPluginHandledExternally(
           *this, completed_url,
           service_type_.IsEmpty() ? GetMIMETypeFromURL(completed_url)
                                   : service_type_);
@@ -425,15 +425,13 @@
     if (plugin) {
       plugin_wrapper_.Reset(isolate, plugin->ScriptableObject(isolate));
     } else {
-      // It is important to check for |handled_externally_| after calling
-      // PluginEmbeddedContentView(). Note that calling
-      // PluginEmbeddedContentView() leads to synchronously updating style and
-      // running post layout tasks, which ends up updating the plugin. It is
-      // after updating the plugin that we know whether or not the plugin is
-      // handled externally by a MimeHandlerView. To check for
-      // |handled_externally_| sooner is wrong since it is possible for JS to
-      // call int PluginWrapper() before the plugin has gone through the update
-      // phase (and wrongly assume it is not handled by MimeHandlerView). (see
+      // This step is intended for plugins with external handlers. This should
+      // checked after after calling PluginEmbeddedContentView(). Note that
+      // calling PluginEmbeddedContentView() leads to synchronously updating
+      // style and running post layout tasks, which ends up updating the plugin.
+      // It is after updating the plugin that we know whether or not the plugin
+      // is handled externally. Also note that it is possible to call
+      // PluginWrapper before the plugin has gone through the update phase(see
       // https://crbug.com/946709).
       plugin_wrapper_.Reset(
           isolate, frame->Client()->GetScriptableObject(*this, isolate));
@@ -578,9 +576,9 @@
   bool plugin_supports_mime_type =
       plugin_data && plugin_data->SupportsMimeType(mime_type);
   if (plugin_supports_mime_type &&
-      plugin_data->IsMimeHandlerViewMimeType(mime_type) &&
+      plugin_data->IsExternalPluginMimeType(mime_type) &&
       RuntimeEnabledFeatures::MimeHandlerViewInCrossProcessFrameEnabled()) {
-    return ObjectContentType::kMimeHandlerViewPlugin;
+    return ObjectContentType::kExternalPlugin;
   }
 
   if (MIMETypeRegistry::IsSupportedImageMIMEType(mime_type)) {
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.h b/third_party/blink/renderer/core/html/html_plugin_element.h
index 620b258..043e91a 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.h
+++ b/third_party/blink/renderer/core/html/html_plugin_element.h
@@ -204,7 +204,7 @@
     kImage,
     kFrame,
     kPlugin,
-    kMimeHandlerViewPlugin,
+    kExternalPlugin,
   };
   ObjectContentType GetObjectContentType() const;
 
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 77b8f805..b99d29d25 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -3936,6 +3936,8 @@
       optional boolean fromDiskCache
       # Specifies that the request was served from the ServiceWorker.
       optional boolean fromServiceWorker
+      # Specifies that the request was served from the prefetch cache.
+      optional boolean fromPrefetchCache
       # Total number of bytes received for this request so far.
       number encodedDataLength
       # Timing information for the given request.
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 c4a74f89..8d126597 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -572,6 +572,7 @@
 
   response_object->setFromDiskCache(response.WasCached());
   response_object->setFromServiceWorker(response.WasFetchedViaServiceWorker());
+  response_object->setFromPrefetchCache(response.WasInPrefetchCache());
   if (response.GetResourceLoadTiming())
     response_object->setTiming(
         BuildObjectForTiming(*response.GetResourceLoadTiming()));
diff --git a/third_party/blink/renderer/core/layout/ng/ng_link.h b/third_party/blink/renderer/core/layout/ng/ng_link.h
index b00d8aa..5d78813 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_link.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_link.h
@@ -26,6 +26,14 @@
   const NGPhysicalFragment& operator*() const { return *fragment; }
   const NGPhysicalFragment* operator->() const { return fragment; }
 
+  // Returns a |NGLink| with newer generation if exists, or |this|. See
+  // |NGPhysicalFragment::PostLayout()| for more details.
+  const NGLink PostLayout() const {
+    if (const NGPhysicalFragment* new_fragment = fragment->PostLayout())
+      return {new_fragment, offset};
+    return *this;
+  }
+
   const NGPhysicalFragment* fragment;
   PhysicalOffset offset;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
index 22bbc0a..f017736a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
@@ -67,7 +67,7 @@
     const PhysicalOffset& additional_offset,
     NGOutlineType outline_type,
     const LayoutBoxModelObject* containing_block) const {
-  for (const auto& child : Children()) {
+  for (const auto& child : PostLayoutChildren()) {
     // Outlines of out-of-flow positioned descendants are handled in
     // NGPhysicalBoxFragment::AddSelfOutlineRects().
     if (child->IsOutOfFlowPositioned())
@@ -83,16 +83,6 @@
         if (child_layout_object->IsElementContinuation() ||
             child_layout_block_flow->IsAnonymousBlockContinuation())
           continue;
-        if (child_layout_block_flow->IsRelayoutBoundary()) {
-          const NGPhysicalBoxFragment* maybe_new_child_fragment =
-              child_layout_block_flow->CurrentFragment();
-          if (maybe_new_child_fragment) {
-            AddOutlineRectsForDescendant(
-                {maybe_new_child_fragment, child.Offset()}, outline_rects,
-                additional_offset, outline_type, containing_block);
-            continue;
-          }
-        }
       }
     }
     AddOutlineRectsForDescendant(child, outline_rects, additional_offset,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
index a450ba7..d79c9f5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
@@ -20,32 +20,100 @@
 
 class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
  public:
-  class ChildLinkList {
+  class ChildLinkListBase {
    public:
-    ChildLinkList(wtf_size_t count, const NGLink* buffer)
+    ChildLinkListBase(wtf_size_t count, const NGLink* buffer)
         : count_(count), buffer_(buffer) {}
 
     wtf_size_t size() const { return count_; }
+    bool IsEmpty() const { return count_ == 0; }
+
+   protected:
+    wtf_size_t count_;
+    const NGLink* buffer_;
+  };
+
+  class ChildLinkList : public ChildLinkListBase {
+   public:
+    using ChildLinkListBase::ChildLinkListBase;
+
     const NGLink& operator[](wtf_size_t idx) const { return buffer_[idx]; }
     const NGLink& front() const { return buffer_[0]; }
     const NGLink& back() const { return buffer_[count_ - 1]; }
 
     const NGLink* begin() const { return buffer_; }
     const NGLink* end() const { return begin() + count_; }
+  };
 
-    bool IsEmpty() const { return count_ == 0; }
+  // Same as |ChildLinkList|, except that each |NGLink| has the latest
+  // generation of post-layout. See |NGPhysicalFragment::UpdatedFragment()| for
+  // more details.
+  class PostLayoutChildLinkList : public ChildLinkListBase {
+   public:
+    using ChildLinkListBase::ChildLinkListBase;
 
-   private:
-    wtf_size_t count_;
-    const NGLink* buffer_;
+    class ConstIterator {
+      STACK_ALLOCATED();
+
+     public:
+      ConstIterator(const NGLink* current) : current_(current) {}
+
+      const NGLink& operator*() const { return *PostLayoutOrCurrent(); }
+      const NGLink* operator->() const { return PostLayoutOrCurrent(); }
+
+      ConstIterator& operator++() {
+        ++current_;
+        return *this;
+      }
+      bool operator==(const ConstIterator& other) const {
+        return current_ == other.current_;
+      }
+      bool operator!=(const ConstIterator& other) const {
+        return current_ != other.current_;
+      }
+
+     private:
+      const NGLink* PostLayoutOrCurrent() const {
+        post_layout_.fragment = current_->fragment->PostLayout();
+        if (!post_layout_.fragment)
+          return current_;
+        post_layout_.offset = current_->offset;
+        return &post_layout_;
+      }
+
+      const NGLink* current_;
+      mutable NGLink post_layout_;
+    };
+    using const_iterator = ConstIterator;
+
+    const_iterator begin() const { return const_iterator(buffer_); }
+    const_iterator end() const { return const_iterator(buffer_ + count_); }
+
+    const NGLink operator[](wtf_size_t idx) const {
+      CHECK_LT(idx, count_);
+      return buffer_[idx].PostLayout();
+    }
+    const NGLink front() const { return (*this)[0]; }
+    const NGLink back() const { return (*this)[count_ - 1]; }
   };
 
   ~NGPhysicalContainerFragment();
 
+  // Returns the children of |this|.
+  //
+  // Note, children in this collection maybe old generations. Items in this
+  // collection are safe, but their children (grandchildren of |this|) maybe
+  // from deleted nodes or LayoutObjects. Also see |PostLayoutChildren()|.
   ChildLinkList Children() const {
     return ChildLinkList(num_children_, buffer_);
   }
 
+  // Similar to |Children()| but all children are the latest generation of
+  // post-layout, and therefore all descendants are safe.
+  PostLayoutChildLinkList PostLayoutChildren() const {
+    return PostLayoutChildLinkList(num_children_, buffer_);
+  }
+
   bool HasFloatingDescendants() const { return has_floating_descendants_; }
 
   bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
index 14a71dd..17a7ecf 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -347,6 +347,19 @@
   return container->IsLayoutNGMixin() || container->IsLayoutNGFlexibleBox();
 }
 
+const NGPhysicalFragment* NGPhysicalFragment::PostLayout() const {
+  if (IsBox() && !IsInlineBox()) {
+    if (const auto* block = DynamicTo<LayoutBlockFlow>(GetLayoutObject())) {
+      if (block->IsRelayoutBoundary()) {
+        const NGPhysicalFragment* new_fragment = block->CurrentFragment();
+        if (new_fragment && new_fragment != this)
+          return new_fragment;
+      }
+    }
+  }
+  return nullptr;
+}
+
 #if DCHECK_IS_ON()
 void NGPhysicalFragment::CheckCanUpdateInkOverflow() const {
   if (!GetLayoutObject())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index c740862..6bbf0cf 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -206,6 +206,14 @@
     return !IsLineBox() ? &layout_object_ : nullptr;
   }
 
+  // Returns the latest generation of the post-layout fragment. Returns
+  // |nullptr| if |this| is the one.
+  //
+  // When subtree relayout occurs at the relayout boundary, its containing block
+  // may keep the reference to old generations of this fragment. Callers can
+  // check if there were newer generations.
+  const NGPhysicalFragment* PostLayout() const;
+
   // Scrollable overflow. including contents, in the local coordinate.
   PhysicalRect ScrollableOverflow() const;
 
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
index cd76cd0..f1165de 100644
--- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
+++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
@@ -240,7 +240,7 @@
 }
 
 void OriginTrialContext::InitializePendingFeatures() {
-  if (!enabled_features_.size())
+  if (!enabled_features_.size() && !navigation_activated_features_.size())
     return;
   auto* document = DynamicTo<Document>(GetSupplementable());
   if (!document)
@@ -279,8 +279,10 @@
   if (!RuntimeEnabledFeatures::OriginTrialsEnabled())
     return false;
 
-  if (enabled_features_.Contains(feature))
+  if (enabled_features_.Contains(feature) ||
+      navigation_activated_features_.Contains(feature)) {
     return true;
+  }
 
   // HTML imports do not have a browsing context, see:
   //  - Spec: https://w3c.github.io/webcomponents/spec/imports/#terminology
diff --git a/third_party/blink/renderer/core/page/plugin_data.cc b/third_party/blink/renderer/core/page/plugin_data.cc
index 3855e47..89a12219 100644
--- a/third_party/blink/renderer/core/page/plugin_data.cc
+++ b/third_party/blink/renderer/core/page/plugin_data.cc
@@ -49,12 +49,12 @@
                        const String& filename,
                        const String& description,
                        Color background_color,
-                       bool may_use_mime_handler_view)
+                       bool may_use_external_handler)
     : name_(name),
       filename_(filename),
       description_(description),
       background_color_(background_color),
-      may_use_mime_handler_view_(may_use_mime_handler_view) {}
+      may_use_external_handler_(may_use_external_handler) {}
 
 void PluginInfo::AddMimeType(MimeClassInfo* info) {
   mimes_.push_back(info);
@@ -106,7 +106,7 @@
     auto* plugin_info = MakeGarbageCollected<PluginInfo>(
         plugin->name, FilePathToWebString(plugin->filename),
         plugin->description, plugin->background_color,
-        plugin->may_use_mime_handler_view);
+        plugin->may_use_external_handler);
     plugins_.push_back(plugin_info);
     for (const auto& mime : plugin->mime_types) {
       auto* mime_info = MakeGarbageCollected<MimeClassInfo>(
@@ -154,10 +154,10 @@
   return Color();
 }
 
-bool PluginData::IsMimeHandlerViewMimeType(const String& mime_type) const {
+bool PluginData::IsExternalPluginMimeType(const String& mime_type) const {
   for (const MimeClassInfo* info : mimes_) {
     if (info->type_ == mime_type)
-      return info->Plugin()->MayUseMimeHandlerView();
+      return info->Plugin()->MayUseExternalHandler();
   }
   return false;
 }
diff --git a/third_party/blink/renderer/core/page/plugin_data.h b/third_party/blink/renderer/core/page/plugin_data.h
index 446cf8f..7ca6da9 100644
--- a/third_party/blink/renderer/core/page/plugin_data.h
+++ b/third_party/blink/renderer/core/page/plugin_data.h
@@ -76,7 +76,7 @@
   const String& Filename() const { return filename_; }
   const String& Description() const { return description_; }
   Color BackgroundColor() const { return background_color_; }
-  bool MayUseMimeHandlerView() const { return may_use_mime_handler_view_; }
+  bool MayUseExternalHandler() const { return may_use_external_handler_; }
 
  private:
   friend class MimeClassInfo;
@@ -86,7 +86,7 @@
   String filename_;
   String description_;
   Color background_color_;
-  bool may_use_mime_handler_view_;
+  bool may_use_external_handler_;
   HeapVector<Member<MimeClassInfo>> mimes_;
 };
 
@@ -105,7 +105,7 @@
 
   bool SupportsMimeType(const String& mime_type) const;
   Color PluginBackgroundColorForMimeType(const String& mime_type) const;
-  bool IsMimeHandlerViewMimeType(const String& mime_type) const;
+  bool IsExternalPluginMimeType(const String& mime_type) const;
 
   // refreshBrowserSidePluginCache doesn't update existent instances of
   // PluginData.
diff --git a/third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.cc b/third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.cc
index f039ed5..2d3309b 100644
--- a/third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.cc
+++ b/third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.cc
@@ -38,7 +38,7 @@
     plugin->description = "pdf";
     plugin->filename = base::FilePath(FILE_PATH_LITERAL("pdf-files"));
     plugin->background_color = SkColorSetRGB(38, 38, 38);
-    plugin->may_use_mime_handler_view = true;
+    plugin->may_use_external_handler = true;
     plugin->mime_types.push_back(std::move(mime));
 
     Vector<mojom::blink::PluginInfoPtr> plugins;
diff --git a/third_party/blink/renderer/core/workers/worklet.cc b/third_party/blink/renderer/core/workers/worklet.cc
index f6c09be2..f55cf010 100644
--- a/third_party/blink/renderer/core/workers/worklet.cc
+++ b/third_party/blink/renderer/core/workers/worklet.cc
@@ -94,15 +94,6 @@
   module_responses_map_->Dispose();
   for (const auto& proxy : proxies_)
     proxy->TerminateWorkletGlobalScope();
-  for (auto iter = pending_tasks_set_.begin();
-       iter != pending_tasks_set_.end();) {
-    // Move the iterator forward before calling WorkletPendingTasks::Abort()
-    // because that modifies |pending_tasks_set_| and invalidates the iterator.
-    auto current = iter;
-    ++iter;
-    (*current)->Abort();
-  }
-  DCHECK(!HasPendingTasks());
 }
 
 bool Worklet::HasPendingTasks() const {
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js b/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
index 28971f0..642523f 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkDataGridNode.js
@@ -969,6 +969,10 @@
       cell.createTextChild(ls`(signed-exchange)`);
       cell.title = ls`Served from Signed HTTP Exchange, resource size: ${resourceSize}`;
       cell.classList.add('network-dim-cell');
+    } else if (this._request.fromPrefetchCache()) {
+      cell.createTextChild(ls`(prefetch cache)`);
+      cell.title = ls`Served from prefetch cache, resource size: ${resourceSize}`;
+      cell.classList.add('network-dim-cell');
     } else if (this._request.cached()) {
       cell.createTextChild(ls`(disk cache)`);
       cell.title = ls`Served from disk cache, resource size: ${resourceSize}`;
diff --git a/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js b/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js
index 03f76686..acc989f9 100644
--- a/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/RequestHeadersView.js
@@ -376,7 +376,8 @@
 
     if (this._request.statusCode) {
       const statusCodeFragment = createDocumentFragment();
-      statusCodeFragment.createChild('div', 'header-name').textContent = Common.UIString('Status Code') + ': ';
+      statusCodeFragment.createChild('div', 'header-name').textContent = ls`Status Code` +
+          ': ';
       statusCodeFragment.createChild('span', 'header-separator');
 
       const statusCodeImage = statusCodeFragment.createChild('span', 'resource-status-image', 'dt-icon-label');
@@ -389,23 +390,26 @@
       else
         statusCodeImage.type = 'smallicon-red-ball';
 
-      requestMethodElement.title = this._formatHeader(Common.UIString('Request Method'), this._request.requestMethod);
+      requestMethodElement.title = this._formatHeader(ls`Request Method`, this._request.requestMethod);
 
       const statusTextElement = statusCodeFragment.createChild('div', 'header-value source-code');
       let statusText = this._request.statusCode + ' ' + this._request.statusText;
       if (this._request.cachedInMemory()) {
-        statusText += ' ' + Common.UIString('(from memory cache)');
+        statusText += ' ' + ls`(from memory cache)`;
         statusTextElement.classList.add('status-from-cache');
       } else if (this._request.fetchedViaServiceWorker) {
-        statusText += ' ' + Common.UIString('(from ServiceWorker)');
+        statusText += ' ' + ls`(from ServiceWorker)`;
         statusTextElement.classList.add('status-from-cache');
       } else if (
           this._request.redirectSource() && this._request.redirectSource().signedExchangeInfo() &&
           !this._request.redirectSource().signedExchangeInfo().errors) {
-        statusText += ' ' + Common.UIString('(from signed-exchange)');
+        statusText += ' ' + ls`(from signed-exchange)`;
+        statusTextElement.classList.add('status-from-cache');
+      } else if (this._request.fromPrefetchCache()) {
+        statusText += ' ' + ls`(from prefetch cache)`;
         statusTextElement.classList.add('status-from-cache');
       } else if (this._request.cached()) {
-        statusText += ' ' + Common.UIString('(from disk cache)');
+        statusText += ' ' + ls`(from disk cache)`;
         statusTextElement.classList.add('status-from-cache');
       }
       statusTextElement.textContent = statusText;
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
index 093c5b1a..d239c895 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
@@ -339,6 +339,10 @@
 
     if (response.fromDiskCache)
       networkRequest.setFromDiskCache();
+
+    if (response.fromPrefetchCache)
+      networkRequest.setFromPrefetchCache();
+
     networkRequest.timing = response.timing;
 
     networkRequest.protocol = response.protocol;
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js b/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js
index a5f1659..74145c0d 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js
@@ -459,6 +459,13 @@
     return !!this._fromMemoryCache && !this._transferSize;
   }
 
+  /**
+   * @return {boolean}
+   */
+  fromPrefetchCache() {
+    return !!this._fromPrefetchCache;
+  }
+
   setFromMemoryCache() {
     this._fromMemoryCache = true;
     delete this._timing;
@@ -468,6 +475,10 @@
     this._fromDiskCache = true;
   }
 
+  setFromPrefetchCache() {
+    this._fromPrefetchCache = true;
+  }
+
   /**
    * Returns true if the request was intercepted by a service worker and it
    * provided its own response.
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index d5e13e5..ab9547d 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -416,7 +416,7 @@
     ":modules",
     ":modules_testing",
     "//net:quic_test_tools",
-    "//services/device/public/cpp/test:test_support",
+    "//services/device/public/cpp:test_support",
     "//services/viz/public/interfaces:interfaces_blink",
     "//skia",
     "//testing/gmock",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_enums.h b/third_party/blink/renderer/modules/accessibility/ax_enums.h
index f68c6e4..c1fea3e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_enums.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_enums.h
@@ -28,6 +28,12 @@
   kExpandedExpanded,
 };
 
+enum AccessibilityGrabbedState {
+  kGrabbedStateUndefined = 0,
+  kGrabbedStateFalse,
+  kGrabbedStateTrue,
+};
+
 enum AccessibilitySelectedState {
   kSelectedStateUndefined = 0,
   kSelectedStateFalse,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 6964040..2791541 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -476,6 +476,16 @@
   return false;
 }
 
+// aria-grabbed is deprecated in WAI-ARIA 1.1.
+AccessibilityGrabbedState AXLayoutObject::IsGrabbed() const {
+  if (!SupportsARIADragging())
+    return kGrabbedStateUndefined;
+
+  const AtomicString& grabbed = GetAttribute(kAriaGrabbedAttr);
+  return EqualIgnoringASCIICase(grabbed, "true") ? kGrabbedStateTrue
+                                                 : kGrabbedStateFalse;
+}
+
 AccessibilitySelectedState AXLayoutObject::IsSelected() const {
   if (!GetLayoutObject() || !GetNode() || !CanSetSelectedAttribute())
     return kSelectedStateUndefined;
@@ -1566,9 +1576,37 @@
          EqualIgnoringASCIICase(grabbed, "false");
 }
 
-bool AXLayoutObject::SupportsARIADropping() const {
-  const AtomicString& drop_effect = GetAttribute(kAriaDropeffectAttr);
-  return !drop_effect.IsEmpty();
+void AXLayoutObject::Dropeffects(
+    Vector<ax::mojom::Dropeffect>& dropeffects) const {
+  if (!HasAttribute(kAriaDropeffectAttr))
+    return;
+
+  Vector<String> str_dropeffects;
+  TokenVectorFromAttribute(str_dropeffects, kAriaDropeffectAttr);
+
+  if (str_dropeffects.IsEmpty()) {
+    dropeffects.push_back(ax::mojom::Dropeffect::kNone);
+    return;
+  }
+
+  for (auto&& str : str_dropeffects) {
+    dropeffects.push_back(ParseDropeffect(str));
+  }
+}
+
+ax::mojom::Dropeffect AXLayoutObject::ParseDropeffect(
+    String& dropeffect) const {
+  if (EqualIgnoringASCIICase(dropeffect, "copy"))
+    return ax::mojom::Dropeffect::kCopy;
+  if (EqualIgnoringASCIICase(dropeffect, "execute"))
+    return ax::mojom::Dropeffect::kExecute;
+  if (EqualIgnoringASCIICase(dropeffect, "link"))
+    return ax::mojom::Dropeffect::kLink;
+  if (EqualIgnoringASCIICase(dropeffect, "move"))
+    return ax::mojom::Dropeffect::kMove;
+  if (EqualIgnoringASCIICase(dropeffect, "popup"))
+    return ax::mojom::Dropeffect::kPopup;
+  return ax::mojom::Dropeffect::kNone;
 }
 
 bool AXLayoutObject::SupportsARIAFlowTo() const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
index a12f794d..d7cd685 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -88,6 +88,8 @@
 
   // Check object state.
   bool IsFocused() const override;
+  // aria-grabbed is deprecated in WAI-ARIA 1.1.
+  AccessibilityGrabbedState IsGrabbed() const override;
   AccessibilitySelectedState IsSelected() const override;
   bool IsSelectedFromFocus() const override;
 
@@ -130,7 +132,7 @@
 
   ax::mojom::HasPopup HasPopup() const override;
   bool SupportsARIADragging() const override;
-  bool SupportsARIADropping() const override;
+  void Dropeffects(Vector<ax::mojom::Dropeffect>& dropeffects) const override;
   bool SupportsARIAFlowTo() const override;
   bool SupportsARIAOwns() const override;
 
@@ -230,6 +232,7 @@
   bool CanIgnoreSpaceNextTo(LayoutObject*, bool is_after) const;
   bool HasAriaCellRole(Element*) const;
   bool IsPlaceholder() const;
+  ax::mojom::Dropeffect ParseDropeffect(String& dropeffect) const;
 
   static ax::mojom::TextDecorationStyle
   TextDecorationStyleToAXTextDecorationStyle(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index e46742f..e2b0793 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -473,6 +473,10 @@
     return kExpandedUndefined;
   }
   virtual bool IsFocused() const { return false; }
+  // aria-grabbed is deprecated in WAI-ARIA 1.1.
+  virtual AccessibilityGrabbedState IsGrabbed() const {
+    return kGrabbedStateUndefined;
+  }
   virtual bool IsHovered() const { return false; }
   virtual bool IsLinked() const { return false; }
   virtual bool IsLoaded() const { return false; }
@@ -708,7 +712,7 @@
   bool HasGlobalARIAAttribute() const;
   bool SupportsARIAExpanded() const;
   virtual bool SupportsARIADragging() const { return false; }
-  virtual bool SupportsARIADropping() const { return false; }
+  virtual void Dropeffects(Vector<ax::mojom::Dropeffect>& dropeffects) const {}
   virtual bool SupportsARIAFlowTo() const { return false; }
   virtual bool SupportsARIAOwns() const { return false; }
   bool SupportsRangeValue() const;
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index e5d46bb..a82cff31 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -317,6 +317,13 @@
   return private_->IsFocused();
 }
 
+WebAXGrabbedState WebAXObject::IsGrabbed() const {
+  if (IsDetached())
+    return kWebAXGrabbedStateUndefined;
+
+  return static_cast<WebAXGrabbedState>(private_->IsGrabbed());
+}
+
 bool WebAXObject::IsHovered() const {
   if (IsDetached())
     return false;
@@ -1487,6 +1494,21 @@
   private_->SetScrollOffset(offset);
 }
 
+void WebAXObject::Dropeffects(
+    WebVector<ax::mojom::Dropeffect>& dropeffects) const {
+  if (IsDetached())
+    return;
+  Vector<ax::mojom::Dropeffect> enum_dropeffects;
+  private_->Dropeffects(enum_dropeffects);
+  WebVector<ax::mojom::Dropeffect> web_dropeffects(enum_dropeffects.size());
+
+  for (wtf_size_t i = 0; i < enum_dropeffects.size(); ++i) {
+    web_dropeffects[i] = enum_dropeffects[i];
+  }
+
+  dropeffects.Swap(web_dropeffects);
+}
+
 void WebAXObject::GetRelativeBounds(WebAXObject& offset_container,
                                     WebFloatRect& bounds_in_container,
                                     SkMatrix44& container_transform,
diff --git a/third_party/blink/renderer/modules/locks/lock_manager.cc b/third_party/blink/renderer/modules/locks/lock_manager.cc
index 410660a..0476718 100644
--- a/third_party/blink/renderer/modules/locks/lock_manager.cc
+++ b/third_party/blink/renderer/modules/locks/lock_manager.cc
@@ -226,8 +226,10 @@
   }
 
   if (!service_.get()) {
-    if (auto* provider = context->GetInterfaceProvider())
-      provider->GetInterface(mojo::MakeRequest(&service_));
+    if (auto* provider = context->GetInterfaceProvider()) {
+      provider->GetInterface(mojo::MakeRequest(
+          &service_, context->GetTaskRunner(TaskType::kMiscPlatformAPI)));
+    }
     if (!service_.get()) {
       exception_state.ThrowTypeError("Service not available.");
       return ScriptPromise();
@@ -343,8 +345,10 @@
   }
 
   if (!service_.get()) {
-    if (auto* provider = context->GetInterfaceProvider())
-      provider->GetInterface(mojo::MakeRequest(&service_));
+    if (auto* provider = context->GetInterfaceProvider()) {
+      provider->GetInterface(mojo::MakeRequest(
+          &service_, context->GetTaskRunner(TaskType::kMiscPlatformAPI)));
+    }
     if (!service_.get()) {
       exception_state.ThrowTypeError("Service not available.");
       return ScriptPromise();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_contributing_source.idl b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_contributing_source.idl
index 0fb6e54..f682516 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_contributing_source.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_contributing_source.idl
@@ -7,4 +7,5 @@
     required DOMHighResTimeStamp timestamp;
     required unsigned long       source;
              double              audioLevel;
+    required unsigned long       rtpTimestamp;
 };
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 0169fcc..d47e9aa 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
@@ -79,6 +79,7 @@
     synchronization_source->setSource(web_source->Source());
     if (web_source->AudioLevel())
       synchronization_source->setAudioLevel(*web_source->AudioLevel());
+    synchronization_source->setRtpTimestamp(web_source->RtpTimestamp());
     synchronization_sources.push_back(synchronization_source);
   }
   return synchronization_sources;
@@ -97,6 +98,7 @@
     contributing_source->setSource(web_source->Source());
     if (web_source->AudioLevel())
       contributing_source->setAudioLevel(*web_source->AudioLevel());
+    contributing_source->setRtpTimestamp(web_source->RtpTimestamp());
     contributing_sources.push_back(contributing_source);
   }
   return contributing_sources;
diff --git a/third_party/blink/renderer/platform/exported/web_url_response.cc b/third_party/blink/renderer/platform/exported/web_url_response.cc
index 4ca94503..c3274be 100644
--- a/third_party/blink/renderer/platform/exported/web_url_response.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_response.cc
@@ -385,6 +385,10 @@
       is_signed_exchange_inner_response);
 }
 
+void WebURLResponse::SetWasInPrefetchCache(bool was_in_prefetch_cache) {
+  resource_response_->SetWasInPrefetchCache(was_in_prefetch_cache);
+}
+
 WebString WebURLResponse::AlpnNegotiatedProtocol() const {
   return resource_response_->AlpnNegotiatedProtocol();
 }
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index 8118047..5cf513a4 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -55,8 +55,8 @@
 constexpr unsigned HarfBuzzRunGlyphData::kMaxGlyphs;
 
 struct SameSizeAsHarfBuzzRunGlyphData {
-  uint16_t unsigned_int16;
-  unsigned bit_fields : 2;
+  unsigned glyph : 16;
+  unsigned char_index_and_bit_field : 16;
   float advance;
   FloatSize offset;
 };
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
index 9494c6c..1c7944e5 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
@@ -51,7 +51,7 @@
   static constexpr unsigned kMaxCharacterIndex = (1 << kCharacterIndexBits) - 1;
   static constexpr unsigned kMaxGlyphs = 1 << kCharacterIndexBits;
 
-  uint16_t glyph;
+  unsigned glyph : 16;
   unsigned character_index : kCharacterIndexBits;
   unsigned safe_to_break_before : 1;
 
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index 6dba03c..ca83492 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -99,11 +99,6 @@
   ProcessHeap::DecreaseTotalAllocatedObjectSize(bytes);
 }
 
-void ThreadHeap::IncreaseMarkedObjectSize(size_t bytes) {
-  stats_collector()->IncreaseMarkedObjectSize(bytes);
-  ProcessHeap::IncreaseTotalMarkedObjectSize(bytes);
-}
-
 void ThreadHeap::IncreaseAllocatedSpace(size_t bytes) {
   stats_collector()->IncreaseAllocatedSpace(bytes);
   ProcessHeap::IncreaseTotalAllocatedSpace(bytes);
@@ -353,6 +348,11 @@
   return object_payload_size;
 }
 
+void ThreadHeap::ResetAllocationPointForTesting() {
+  for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i)
+    arenas_[i]->ResetAllocationPointForTesting();
+}
+
 BasePage* ThreadHeap::LookupPageForAddress(Address address) {
   if (PageMemoryRegion* region = region_tree_->Lookup(address)) {
     return region->PageFromAddress(address);
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 0684133f..bf7872ba 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -274,6 +274,7 @@
   Address CheckAndMarkPointer(MarkingVisitor*, Address);
 
   size_t ObjectPayloadSizeForTesting();
+  void ResetAllocationPointForTesting();
 
   AddressCache* address_cache() const { return address_cache_.get(); }
 
@@ -373,7 +374,6 @@
 
   void IncreaseAllocatedObjectSize(size_t);
   void DecreaseAllocatedObjectSize(size_t);
-  void IncreaseMarkedObjectSize(size_t);
   void IncreaseAllocatedSpace(size_t);
   void DecreaseAllocatedSpace(size_t);
 
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.cc b/third_party/blink/renderer/platform/heap/heap_allocator.cc
index a81beba7..e55ee53 100644
--- a/third_party/blink/renderer/platform/heap/heap_allocator.cc
+++ b/third_party/blink/renderer/platform/heap/heap_allocator.cc
@@ -90,9 +90,14 @@
 
   HeapObjectHeader* header = HeapObjectHeader::FromPayload(address);
   NormalPageArena* arena = static_cast<NormalPage*>(page)->ArenaForNormalPage();
+  const size_t old_size = header->size();
   bool succeed = arena->ExpandObject(header, new_size);
-  if (succeed)
+  if (succeed) {
     state->Heap().AllocationPointAdjusted(arena->ArenaIndex());
+    if (header->IsMarked() && state->IsMarkingInProgress()) {
+      state->CurrentVisitor()->AdjustMarkedBytes(header, old_size);
+    }
+  }
   return succeed;
 }
 
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index 04cc73a2..cdebeb4 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -1245,7 +1245,6 @@
 
 bool NormalPage::Sweep() {
   object_start_bit_map()->Clear();
-  size_t marked_object_size = 0;
   Address start_of_gap = Payload();
   NormalPageArena* page_arena = ArenaForNormalPage();
   for (Address header_address = start_of_gap; header_address < PayloadEnd();) {
@@ -1296,7 +1295,6 @@
     object_start_bit_map()->SetBit(header_address);
     header->Unmark();
     header_address += size;
-    marked_object_size += size;
     start_of_gap = header_address;
   }
   // Only add the memory to the free list if the page is not completely empty
@@ -1310,11 +1308,6 @@
 #endif
   }
 
-  if (marked_object_size) {
-    page_arena->GetThreadState()->Heap().IncreaseMarkedObjectSize(
-        marked_object_size);
-  }
-
   VerifyObjectStartBitmapIsConsistentWithPayload();
   return start_of_gap == Payload();
 }
@@ -1324,7 +1317,6 @@
   NormalPage*& current_page = context.current_page_;
   size_t& allocation_point = context.allocation_point_;
 
-  size_t marked_object_size = 0;
   NormalPageArena* page_arena = ArenaForNormalPage();
 #if defined(ADDRESS_SANITIZER)
   bool is_vector_arena =
@@ -1409,14 +1401,9 @@
     }
     current_page->object_start_bit_map()->SetBit(compact_frontier);
     header_address += size;
-    marked_object_size += size;
     allocation_point += size;
     DCHECK(allocation_point <= current_page->PayloadSize());
   }
-  if (marked_object_size) {
-    page_arena->GetThreadState()->Heap().IncreaseMarkedObjectSize(
-        marked_object_size);
-  }
 
 #if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) || \
     defined(MEMORY_SANITIZER)
@@ -1432,7 +1419,6 @@
 
 void NormalPage::MakeConsistentForMutator() {
   object_start_bit_map()->Clear();
-  size_t marked_object_size = 0;
   Address start_of_gap = Payload();
   NormalPageArena* normal_arena = ArenaForNormalPage();
   for (Address header_address = Payload(); header_address < PayloadEnd();) {
@@ -1456,7 +1442,6 @@
       normal_arena->AddToFreeList(start_of_gap, header_address - start_of_gap);
     if (header->IsMarked()) {
       header->Unmark();
-      marked_object_size += size;
     }
     object_start_bit_map()->SetBit(header_address);
     header_address += size;
@@ -1466,11 +1451,6 @@
   if (start_of_gap != PayloadEnd())
     normal_arena->AddToFreeList(start_of_gap, PayloadEnd() - start_of_gap);
 
-  if (marked_object_size) {
-    ArenaForNormalPage()->GetThreadState()->Heap().IncreaseMarkedObjectSize(
-        marked_object_size);
-  }
-
   VerifyObjectStartBitmapIsConsistentWithPayload();
 }
 
@@ -1641,7 +1621,6 @@
     return true;
   }
   ObjectHeader()->Unmark();
-  Arena()->GetThreadState()->Heap().IncreaseMarkedObjectSize(size());
   return false;
 }
 
@@ -1649,7 +1628,6 @@
   HeapObjectHeader* header = ObjectHeader();
   if (header->IsMarked()) {
     header->Unmark();
-    Arena()->GetThreadState()->Heap().IncreaseMarkedObjectSize(size());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/heap/heap_page.h b/third_party/blink/renderer/platform/heap/heap_page.h
index de64181..34949d8 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.h
+++ b/third_party/blink/renderer/platform/heap/heap_page.h
@@ -218,6 +218,8 @@
   size_t size() const;
   void SetSize(size_t size);
 
+  bool IsLargeObject() const;
+
   bool IsWrapperHeaderMarked() const;
   void MarkWrapperHeader();
   void UnmarkWrapperHeader();
@@ -755,6 +757,7 @@
 
   virtual void VerifyObjectStartBitmap() {}
   virtual void VerifyMarking() {}
+  virtual void ResetAllocationPointForTesting() {}
 
  protected:
   bool SweepingCompleted() const { return !first_unswept_page_; }
@@ -819,6 +822,9 @@
 
   void VerifyObjectStartBitmap() override;
   void VerifyMarking() override;
+  void ResetAllocationPointForTesting() override {
+    SetAllocationPoint(nullptr, 0);
+  }
 
   Address CurrentAllocationPoint() const { return current_allocation_point_; }
 
@@ -869,6 +875,7 @@
 #if DCHECK_IS_ON()
   bool IsConsistentForGC() override { return true; }
 #endif
+
  private:
   Address DoAllocateLargeObjectPage(size_t, size_t gc_info_index);
   Address LazySweepPages(size_t, size_t gc_info_index) override;
@@ -929,6 +936,10 @@
   encoded_ = static_cast<uint32_t>(size) | (encoded_ & ~kHeaderSizeMask);
 }
 
+NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsLargeObject() const {
+  return (encoded_ & kHeaderSizeMask) == kLargeObjectSizeInHeader;
+}
+
 NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsInConstruction() const {
   return (encoded_ & kHeaderIsInConstructionMask) == 0;
 }
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.cc b/third_party/blink/renderer/platform/heap/heap_stats_collector.cc
index 58c37a2..1c13965 100644
--- a/third_party/blink/renderer/platform/heap/heap_stats_collector.cc
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.cc
@@ -9,11 +9,6 @@
 
 namespace blink {
 
-void ThreadHeapStatsCollector::IncreaseMarkedObjectSize(size_t bytes) {
-  DCHECK(is_started_);
-  current_.marked_bytes += bytes;
-}
-
 void ThreadHeapStatsCollector::IncreaseCompactionFreedSize(size_t bytes) {
   DCHECK(is_started_);
   current_.compaction_freed_bytes += bytes;
@@ -62,7 +57,8 @@
   current_.reason = reason;
 }
 
-void ThreadHeapStatsCollector::NotifyMarkingCompleted() {
+void ThreadHeapStatsCollector::NotifyMarkingCompleted(size_t marked_bytes) {
+  current_.marked_bytes = marked_bytes;
   current_.object_size_in_bytes_before_sweeping = object_size_in_bytes();
   current_.allocated_space_in_bytes_before_sweeping = allocated_space_bytes();
   current_.partition_alloc_bytes_before_sweeping =
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.h b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
index 494615fd..bbdf65d 100644
--- a/third_party/blink/renderer/platform/heap/heap_stats_collector.h
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
@@ -217,7 +217,7 @@
 
   // Indicates that marking of the current garbage collection cycle is
   // completed.
-  void NotifyMarkingCompleted();
+  void NotifyMarkingCompleted(size_t marked_bytes);
 
   // Indicates the end of a garbage collection cycle. This means that sweeping
   // is finished at this point.
@@ -229,7 +229,6 @@
   }
 
   void UpdateReason(BlinkGC::GCReason);
-  void IncreaseMarkedObjectSize(size_t);
   void IncreaseCompactionFreedSize(size_t);
   void IncreaseCompactionFreedPages(size_t);
   void IncreaseAllocatedObjectSize(size_t);
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
index f92147b..746a1c9 100644
--- a/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
@@ -8,6 +8,12 @@
 
 namespace blink {
 
+namespace {
+
+constexpr size_t kNoMarkedBytes = 0;
+
+}  // namespace
+
 // =============================================================================
 // ThreadHeapStatsCollector. ===================================================
 // =============================================================================
@@ -18,7 +24,7 @@
   for (int i = 0; i < ThreadHeapStatsCollector::kNumScopeIds; i++) {
     EXPECT_EQ(TimeDelta(), stats_collector.current().scope_data[i]);
   }
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
 }
 
@@ -31,7 +37,7 @@
   EXPECT_EQ(TimeDelta::FromMilliseconds(1),
             stats_collector.current()
                 .scope_data[ThreadHeapStatsCollector::kIncrementalMarkingStep]);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
 }
 
@@ -41,7 +47,7 @@
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kIncrementalMarkingStep,
       TimeDelta::FromMilliseconds(1));
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(TimeDelta::FromMilliseconds(1),
             stats_collector.previous()
@@ -54,7 +60,7 @@
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kIncrementalMarkingStep,
       TimeDelta::FromMilliseconds(1));
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(TimeDelta(),
             stats_collector.current()
@@ -66,7 +72,7 @@
   EXPECT_FALSE(stats_collector.is_started());
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_TRUE(stats_collector.is_started());
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_FALSE(stats_collector.is_started());
 }
@@ -80,7 +86,7 @@
 TEST(ThreadHeapStatsCollectorTest, UpdateReason) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.UpdateReason(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(BlinkGC::GCReason::kForcedGCForTesting,
@@ -91,7 +97,7 @@
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(0u, stats_collector.object_size_in_bytes());
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
 }
 
@@ -100,18 +106,17 @@
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedObjectSize(512);
   EXPECT_EQ(512u, stats_collector.object_size_in_bytes());
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
 }
 
 TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeInBytesWithMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.IncreaseAllocatedObjectSize(512);
   EXPECT_EQ(640u, stats_collector.object_size_in_bytes());
   stats_collector.NotifySweepingCompleted();
@@ -121,12 +126,10 @@
      EstimatedObjectSizeInBytesDoNotCountCurrentlyMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   // Currently marked bytes should not account to the estimated object size.
   stats_collector.IncreaseAllocatedObjectSize(512);
   EXPECT_EQ(640u, stats_collector.object_size_in_bytes());
@@ -139,7 +142,7 @@
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_LT(0u, stats_collector.estimated_marking_time_in_seconds());
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
 }
 
@@ -148,12 +151,11 @@
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking, TimeDelta::FromSeconds(1));
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(1024);
+  stats_collector.NotifyMarkingCompleted(1024);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_DOUBLE_EQ(1.0, stats_collector.estimated_marking_time_in_seconds());
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
 }
 
@@ -162,13 +164,12 @@
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking, TimeDelta::FromSeconds(1));
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(1024);
+  stats_collector.NotifyMarkingCompleted(1024);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedObjectSize(512);
   EXPECT_DOUBLE_EQ(1.5, stats_collector.estimated_marking_time_in_seconds());
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
 }
 
@@ -177,7 +178,7 @@
   EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
@@ -203,8 +204,7 @@
 TEST(ThreadHeapStatsCollectorTest, EventPrevGCMarkedObjectSize) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(1024);
+  stats_collector.NotifyMarkingCompleted(1024);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(1024u, stats_collector.previous().marked_bytes);
 }
@@ -225,7 +225,7 @@
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kIncrementalMarkingFinalize,
       TimeDelta::FromMilliseconds(3));
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(10.0, stats_collector.previous().marking_time_in_ms());
 }
@@ -236,7 +236,7 @@
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking,
       TimeDelta::FromMilliseconds(11));
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(11.0, stats_collector.previous().marking_time_in_ms());
 }
@@ -244,10 +244,9 @@
 TEST(ThreadHeapStatsCollectorTest, EventMarkingTimePerByteInS) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.IncreaseMarkedObjectSize(1000);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking, TimeDelta::FromSeconds(1));
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(1000);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(
       .001, stats_collector.previous().marking_time_in_bytes_per_second());
@@ -267,7 +266,7 @@
       TimeDelta::FromMilliseconds(4));
   stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kCompleteSweep,
                                     TimeDelta::FromMilliseconds(5));
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(TimeDelta::FromMilliseconds(15),
             stats_collector.previous().sweeping_time());
@@ -276,7 +275,7 @@
 TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedBytes) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.IncreaseCompactionFreedSize(512);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(512u, stats_collector.previous().compaction_freed_bytes);
@@ -285,7 +284,7 @@
 TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedPages) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.IncreaseCompactionFreedPages(3);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(3u, stats_collector.previous().compaction_freed_pages);
@@ -294,7 +293,7 @@
 TEST(ThreadHeapStatsCollectorTest, EventInitialEstimatedLiveObjectRate) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0.0, stats_collector.previous().live_object_rate);
 }
@@ -303,12 +302,10 @@
      EventEstimatedLiveObjectRateSameMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(1.0, stats_collector.previous().live_object_rate);
 }
@@ -317,12 +314,10 @@
      EventEstimatedLiveObjectRateHalfMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(256);
+  stats_collector.NotifyMarkingCompleted(256);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0.5, stats_collector.previous().live_object_rate);
 }
@@ -330,8 +325,7 @@
 TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateNoMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(256);
+  stats_collector.NotifyMarkingCompleted(256);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifySweepingCompleted();
@@ -342,13 +336,11 @@
      EventEstimatedLiveObjectRateWithAllocatedBytes1) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   stats_collector.IncreaseAllocatedObjectSize(128);
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(.5, stats_collector.previous().live_object_rate);
 }
@@ -357,12 +349,11 @@
      EventEstimatedLiveObjectRateWithAllocatedBytes2) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   stats_collector.IncreaseAllocatedObjectSize(128);
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(1.0, stats_collector.previous().live_object_rate);
 }
@@ -371,7 +362,7 @@
      EventEstimatedLiveObjectRateWithAllocatedBytes3) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0, stats_collector.previous().live_object_rate);
 }
@@ -380,11 +371,10 @@
      EventEstimatedLiveObjectRateWithAllocatedBytes4) {
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
-  stats_collector.IncreaseMarkedObjectSize(128);
+  stats_collector.NotifyMarkingCompleted(128);
   stats_collector.NotifySweepingCompleted();
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0, stats_collector.previous().live_object_rate);
 }
@@ -393,7 +383,7 @@
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedSpace(1024);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.IncreaseAllocatedSpace(2048);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(
@@ -405,7 +395,7 @@
   ThreadHeapStatsCollector stats_collector;
   stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedSpace(1024);
-  stats_collector.NotifyMarkingCompleted();
+  stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
   stats_collector.DecreaseAllocatedSpace(1024);
   stats_collector.NotifySweepingCompleted();
   EXPECT_EQ(
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index 8149cfab..090b990 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -1566,14 +1566,15 @@
     }
   }
 
-  void FinishGC() {
+  void FinishGC(bool complete_sweep = true) {
     CHECK(thread_state_->IsIncrementalMarking());
     FinishSteps(BlinkGC::StackState::kNoHeapPointersOnStack);
     CHECK_EQ(ThreadState::kIncrementalMarkingFinalizeScheduled,
              thread_state_->GetGCState());
     thread_state_->RunScheduledGC(BlinkGC::StackState::kNoHeapPointersOnStack);
     CHECK(!thread_state_->IsIncrementalMarking());
-    thread_state_->CompleteSweep();
+    if (complete_sweep)
+      thread_state_->CompleteSweep();
   }
 
   size_t GetHeapCompactLastFixupCount() {
@@ -1929,6 +1930,8 @@
   HeapVector<HeapVector<Member<Object>>, 2> nested_;
 };
 
+}  // namespace
+
 TEST(IncrementalMarkingTest,
      InPayloadWriteBarrierInEagerlyFinalizedRegistersInvalidSlotForCompaction) {
   // Regression test: https://crbug.com/918064
@@ -1950,7 +1953,35 @@
   driver.FinishGC();
 }
 
-}  // namespace
+TEST(IncrementalMarkingTest, AdjustMarkedBytesOnMarkedBackingStore) {
+  // Regression test: https://crbug.com/966456
+  //
+  // Test ensures that backing expansion does not crash in trying to adjust
+  // marked bytes when the page is actually about to be swept and marking is not
+  // in progress.
+
+  using Container = HeapVector<Member<Object>>;
+  Persistent<Container> holder(MakeGarbageCollected<Container>());
+  holder->push_back(MakeGarbageCollected<Object>());
+  holder->Grow(16);
+  ThreadState::Current()->Heap().ResetAllocationPointForTesting();
+  // Slowly shrink down the backing, only adjusting capacity without performing
+  // free as the resulting memory block is too small for a free list entry.
+  for (int i = 15; i > 0; i--) {
+    holder->Shrink(i);
+    holder->ShrinkToFit();
+  }
+  IncrementalMarkingTestDriver driver(ThreadState::Current());
+  driver.Start();
+  driver.FinishSteps();
+  // The object is marked at this point.
+  CHECK(HeapObjectHeader::FromPayload(holder.Get())->IsMarked());
+  driver.FinishGC(false);
+  // The object is still marked as sweeping did not make any progress.
+  CHECK(HeapObjectHeader::FromPayload(holder.Get())->IsMarked());
+  // Re-grow to some size within the initial payload size (capacity=16).
+  holder->Grow(8);
+}
 
 }  // namespace incremental_marking_test
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 4b25125..1c9bfab 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -92,7 +92,9 @@
 
   // Mark and push trace callback.
   header->Mark();
-  thread_state->CurrentVisitor()->marking_worklist_.Push(
+  MarkingVisitor* visitor = thread_state->CurrentVisitor();
+  visitor->AccountMarkedBytes(header);
+  visitor->marking_worklist_.Push(
       {header->Payload(),
        GCInfoTable::Get().GCInfoFromIndex(header->GcInfoIndex())->trace});
 }
@@ -182,4 +184,12 @@
   }
 }
 
+void MarkingVisitor::AdjustMarkedBytes(HeapObjectHeader* header,
+                                       size_t old_size) {
+  DCHECK(header->IsMarked());
+  // Currently, only expansion of an object is supported during marking.
+  DCHECK_GE(header->size(), old_size);
+  marked_bytes_ += header->size() - old_size;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index 4f67622..3c4a767 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -103,6 +103,8 @@
   // Unused cross-component visit methods.
   void Visit(const TraceWrapperV8Reference<v8::Value>&) override {}
 
+  size_t marked_bytes() const { return marked_bytes_; }
+
  protected:
   MarkingVisitorBase(ThreadState*, MarkingMode);
   ~MarkingVisitorBase() override = default;
@@ -114,14 +116,27 @@
   // marked upon calling.
   inline bool MarkHeaderNoTracing(HeapObjectHeader*);
 
+  // Account for an object's live bytes. Should only be adjusted when
+  // transitioning an object from unmarked to marked state.
+  ALWAYS_INLINE void AccountMarkedBytes(HeapObjectHeader*);
+
   void RegisterBackingStoreReference(void** slot);
 
   MarkingWorklist::View marking_worklist_;
   NotFullyConstructedWorklist::View not_fully_constructed_worklist_;
   WeakCallbackWorklist::View weak_callback_worklist_;
+  size_t marked_bytes_ = 0;
   const MarkingMode marking_mode_;
 };
 
+ALWAYS_INLINE void MarkingVisitorBase::AccountMarkedBytes(
+    HeapObjectHeader* header) {
+  marked_bytes_ +=
+      header->IsLargeObject()
+          ? reinterpret_cast<LargeObjectPage*>(PageFromObject(header))->size()
+          : header->size();
+}
+
 inline bool MarkingVisitorBase::MarkHeaderNoTracing(HeapObjectHeader* header) {
   DCHECK(header);
   DCHECK(State()->InAtomicMarkingPause() || State()->IsIncrementalMarking());
@@ -132,7 +147,11 @@
   // freed backing store.
   DCHECK(!header->IsFree());
 
-  return header->TryMark();
+  if (header->TryMark()) {
+    AccountMarkedBytes(header);
+    return true;
+  }
+  return false;
 }
 
 inline void MarkingVisitorBase::MarkHeader(HeapObjectHeader* header,
@@ -177,6 +196,8 @@
   // to be in construction.
   void DynamicallyMarkAddress(Address);
 
+  void AdjustMarkedBytes(HeapObjectHeader*, size_t);
+
  private:
   // Exact version of the marking write barriers.
   static void WriteBarrierSlow(void*);
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 81a1def..5c0f400 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -1617,6 +1617,7 @@
   }
   Heap().DecommitCallbackStacks();
 
+  const size_t marked_bytes = current_gc_data_.visitor->marked_bytes();
   current_gc_data_.visitor.reset();
 
   if (ShouldVerifyMarking())
@@ -1626,7 +1627,9 @@
       Heap().stats_collector()->allocated_bytes_since_prev_gc());
   ProcessHeap::DecreaseTotalMarkedObjectSize(
       Heap().stats_collector()->previous().marked_bytes);
-  Heap().stats_collector()->NotifyMarkingCompleted();
+  ProcessHeap::IncreaseTotalMarkedObjectSize(marked_bytes);
+  Heap().stats_collector()->NotifyMarkingCompleted(marked_bytes);
+
   WTF::Partitions::ReportMemoryUsageHistogram();
 
   DEFINE_THREAD_SAFE_STATIC_LOCAL(
diff --git a/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h b/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h
index c7a79e6..f2abbf3 100644
--- a/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h
+++ b/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h
@@ -32,7 +32,6 @@
 
   void SetExpectedTaskQueueingDuration(base::TimeDelta);
   void SetMainThreadTaskLoadIsLow(bool);
-  void OnRendererIsBloated();
 
  protected:
   RendererResourceCoordinator();
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
index 7059a04..e38bb9b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -418,6 +418,12 @@
     is_signed_exchange_inner_response_ = is_signed_exchange_inner_response;
   }
 
+  bool WasInPrefetchCache() const { return was_in_prefetch_cache_; }
+
+  void SetWasInPrefetchCache(bool was_in_prefetch_cache) {
+    was_in_prefetch_cache_ = was_in_prefetch_cache;
+  }
+
  private:
   void UpdateHeaderParsedState(const AtomicString& name);
 
@@ -482,6 +488,11 @@
   // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html
   bool is_signed_exchange_inner_response_ = false;
 
+  // True if this resource is served from the prefetch cache. Currently this
+  // flag is used only for prefetched signed exchanges.
+  // TODO(horo): Mark this also for general prefetch cases.
+  bool was_in_prefetch_cache_ = false;
+
   // True if this resource was loaded from the network.
   bool network_accessed_ = false;
 
diff --git a/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc b/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc
index 3fee359c..63464cb0 100644
--- a/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc
@@ -68,7 +68,7 @@
 void CooperativeSchedulingManager::RunNestedLoop() {
   TRACE_EVENT0("renderer.scheduler",
                "CooperativeSchedulingManager::RunNestedLoop");
-  base::AutoReset<bool>(&running_nested_loop_, true);
+  base::AutoReset<bool> nested_loop_scope(&running_nested_loop_, true);
   wait_until_ = WTF::CurrentTimeTicks() + kNestedLoopMinimumInterval;
 
   // TODO(keishi): Ask scheduler to run high priority tasks from different
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 97e8198..f3ea7346 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -470,7 +470,7 @@
     case TaskType::kInternalTranslation:
       return ForegroundOnlyTaskQueueTraits();
     // Navigation IPCs do not run using virtual time to avoid hanging.
-    case TaskType::kInternalNavigation:
+    case TaskType::kInternalNavigationAssociated:
       return DoesNotUseVirtualTimeTaskQueueTraits();
     case TaskType::kDeprecatedNone:
     case TaskType::kMainThreadTaskQueueV8:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
index 5fd8d5d..8ffb04a 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
@@ -129,8 +129,8 @@
       return "InternalTranslation";
     case TaskType::kInternalContentCapture:
       return "InternalContentCapture";
-    case TaskType::kInternalNavigation:
-      return "InternalNavigation";
+    case TaskType::kInternalNavigationAssociated:
+      return "InternalNavigationAssociated";
     case TaskType::kCount:
       return "Count";
   }
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
index 1db50cc..9d49d139 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
@@ -165,7 +165,7 @@
     case TaskType::kInternalIPC:
     case TaskType::kInternalInspector:
     case TaskType::kInternalTest:
-    case TaskType::kInternalNavigation:
+    case TaskType::kInternalNavigationAssociated:
       // UnthrottledTaskRunner is generally discouraged in future.
       // TODO(nhiroki): Identify which tasks can be throttled / suspendable and
       // move them into other task runners. See also comments in
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index 2308c10..215c7b71a 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -362,7 +362,10 @@
   EXPECT_FALSE(origin->CanRequest(url));
   // Adding the url to the access allowlist should allow the request.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *origin, "https", "example.com", false,
+      *origin, "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
   EXPECT_TRUE(origin->CanRequest(url));
 }
@@ -375,10 +378,16 @@
 
   // BlockList that is more or same specificity wins.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *origin, "https", "example.com", true,
+      *origin, "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   SecurityPolicy::AddOriginAccessBlockListEntry(
-      *origin, "https", "example.com", false,
+      *origin, "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kLowPriority);
   // Block since example.com is on the allowlist & blocklist.
   EXPECT_FALSE(origin->CanRequest(blocked_url));
@@ -393,10 +402,16 @@
   const blink::KURL blocked_url("https://example.com");
 
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *origin, "https", "test.example.com", true,
+      *origin, "https", "test.example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
   SecurityPolicy::AddOriginAccessBlockListEntry(
-      *origin, "https", "example.com", true,
+      *origin, "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kLowPriority);
   // Allow since test.example.com (allowlist) has a higher priority than
   // *.example.com (blocklist).
@@ -405,6 +420,29 @@
   EXPECT_FALSE(origin->CanRequest(blocked_url));
 }
 
+TEST_F(SecurityOriginTest, CanRequestWithPortSpecificAllowList) {
+  scoped_refptr<const SecurityOrigin> origin =
+      SecurityOrigin::CreateFromString("https://chromium.org");
+  SecurityPolicy::AddOriginAccessAllowListEntry(
+      *origin, "https", "test1.example.com", 443,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort,
+      network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
+  SecurityPolicy::AddOriginAccessAllowListEntry(
+      *origin, "https", "test2.example.com", 444,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort,
+      network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
+
+  EXPECT_TRUE(origin->CanRequest(blink::KURL("https://test1.example.com")));
+  EXPECT_TRUE(origin->CanRequest(blink::KURL("https://test1.example.com:443")));
+  EXPECT_FALSE(origin->CanRequest(blink::KURL("https://test1.example.com:43")));
+
+  EXPECT_FALSE(origin->CanRequest(blink::KURL("https://test2.example.com")));
+  EXPECT_FALSE(origin->CanRequest(blink::KURL("https://test2.example.com:44")));
+  EXPECT_TRUE(origin->CanRequest(blink::KURL("https://test2.example.com:444")));
+}
+
 TEST_F(SecurityOriginTest, PunycodeNotUnicode) {
   scoped_refptr<const SecurityOrigin> origin =
       SecurityOrigin::CreateFromString("https://chromium.org");
@@ -417,14 +455,20 @@
 
   // Verify unicode origin can not be allowlisted.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *origin, "https", "☃.net", true,
+      *origin, "https", "☃.net",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
   EXPECT_FALSE(origin->CanRequest(punycode_url));
   EXPECT_FALSE(origin->CanRequest(unicode_url));
 
   // Verify punycode allowlist only affects punycode URLs.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *origin, "https", "xn--n3h.net", true,
+      *origin, "https", "xn--n3h.net",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
   EXPECT_TRUE(origin->CanRequest(punycode_url));
   EXPECT_FALSE(origin->CanRequest(unicode_url));
@@ -437,7 +481,10 @@
 
   // Simulate <all_urls> being in the extension permissions.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *origin, "https", "", true,
+      *origin, "https", "",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
 
   EXPECT_TRUE(origin->CanRequest(punycode_url));
@@ -445,14 +492,20 @@
 
   // Verify unicode origin can not be blocklisted.
   SecurityPolicy::AddOriginAccessBlockListEntry(
-      *origin, "https", "☃.net", true,
+      *origin, "https", "☃.net",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kLowPriority);
   EXPECT_TRUE(origin->CanRequest(punycode_url));
   EXPECT_FALSE(origin->CanRequest(unicode_url));
 
   // Verify punycode blocklist only affects punycode URLs.
   SecurityPolicy::AddOriginAccessBlockListEntry(
-      *origin, "https", "xn--n3h.net", true,
+      *origin, "https", "xn--n3h.net",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kLowPriority);
   EXPECT_FALSE(origin->CanRequest(punycode_url));
   EXPECT_FALSE(origin->CanRequest(unicode_url));
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.cc b/third_party/blink/renderer/platform/weborigin/security_policy.cc
index 4b869b2..86b6128 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -253,32 +253,30 @@
     const SecurityOrigin& source_origin,
     const String& destination_protocol,
     const String& destination_domain,
-    bool allow_destination_subdomains,
+    const uint16_t port,
+    const network::mojom::CorsDomainMatchMode domain_match_mode,
+    const network::mojom::CorsPortMatchMode port_match_mode,
     const network::mojom::CorsOriginAccessMatchPriority priority) {
   MutexLocker lock(GetMutex());
   GetOriginAccessList().AddAllowListEntryForOrigin(
       source_origin.ToUrlOrigin(), WebString(destination_protocol).Utf8(),
-      WebString(destination_domain).Utf8(), /*port=*/0,
-      allow_destination_subdomains
-          ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
-          : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
-      network::mojom::CorsPortMatchMode::kAllowAnyPort, priority);
+      WebString(destination_domain).Utf8(), port, domain_match_mode,
+      port_match_mode, priority);
 }
 
 void SecurityPolicy::AddOriginAccessBlockListEntry(
     const SecurityOrigin& source_origin,
     const String& destination_protocol,
     const String& destination_domain,
-    bool allow_destination_subdomains,
+    const uint16_t port,
+    const network::mojom::CorsDomainMatchMode domain_match_mode,
+    const network::mojom::CorsPortMatchMode port_match_mode,
     const network::mojom::CorsOriginAccessMatchPriority priority) {
   MutexLocker lock(GetMutex());
   GetOriginAccessList().AddBlockListEntryForOrigin(
       source_origin.ToUrlOrigin(), WebString(destination_protocol).Utf8(),
-      WebString(destination_domain).Utf8(), /*port=*/0,
-      allow_destination_subdomains
-          ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
-          : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
-      network::mojom::CorsPortMatchMode::kAllowAnyPort, priority);
+      WebString(destination_domain).Utf8(), port, domain_match_mode,
+      port_match_mode, priority);
 }
 
 void SecurityPolicy::ClearOriginAccessListForOrigin(
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.h b/third_party/blink/renderer/platform/weborigin/security_policy.h
index 80f2c2c..32e9d45 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.h
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.h
@@ -74,18 +74,21 @@
                                    const KURL&,
                                    const String& referrer);
 
-  // TODO(crbug.com/936900): Update to take the port.
   static void AddOriginAccessAllowListEntry(
       const SecurityOrigin& source_origin,
       const String& destination_protocol,
       const String& destination_domain,
-      bool allow_destination_subdomains,
+      const uint16_t port,
+      const network::mojom::CorsDomainMatchMode domain_match_mode,
+      const network::mojom::CorsPortMatchMode port_match_mode,
       const network::mojom::CorsOriginAccessMatchPriority priority);
   static void AddOriginAccessBlockListEntry(
       const SecurityOrigin& source_origin,
       const String& destination_protocol,
       const String& destination_domain,
-      bool allow_destination_subdomains,
+      const uint16_t port,
+      const network::mojom::CorsDomainMatchMode domain_match_mode,
+      const network::mojom::CorsPortMatchMode port_match_mode,
       const network::mojom::CorsOriginAccessMatchPriority priority);
   static void ClearOriginAccessListForOrigin(
       const SecurityOrigin& source_origin);
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
index 931db62..f01956c64 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
@@ -395,7 +395,10 @@
   // Adding access for https://example.com should work, but should not grant
   // access to subdomains or other schemes.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "example.com", false,
+      *https_chromium_origin(), "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   EXPECT_TRUE(SecurityPolicy::IsOriginAccessAllowed(https_chromium_origin(),
                                                     https_example_origin()));
@@ -416,7 +419,10 @@
   // Adding an entry that matches subdomains should grant access to any
   // subdomains.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "example.com", true,
+      *https_chromium_origin(), "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   EXPECT_TRUE(SecurityPolicy::IsOriginAccessAllowed(https_chromium_origin(),
                                                     https_example_origin()));
@@ -429,7 +435,10 @@
 TEST_F(SecurityPolicyAccessTest, IsOriginAccessAllowedWildCard) {
   // An empty domain that matches subdomains results in matching every domain.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "", true,
+      *https_chromium_origin(), "https", "",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   EXPECT_TRUE(SecurityPolicy::IsOriginAccessAllowed(https_chromium_origin(),
                                                     https_example_origin()));
@@ -442,10 +451,16 @@
 TEST_F(SecurityPolicyAccessTest, IsOriginAccessAllowedWithBlockListEntry) {
   // The block list takes priority over the allow list.
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "example.com", true,
+      *https_chromium_origin(), "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   SecurityPolicy::AddOriginAccessBlockListEntry(
-      *https_chromium_origin(), "https", "example.com", false,
+      *https_chromium_origin(), "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
 
   EXPECT_FALSE(SecurityPolicy::IsOriginAccessAllowed(https_chromium_origin(),
@@ -457,10 +472,16 @@
 TEST_F(SecurityPolicyAccessTest,
        IsOriginAccessAllowedWildcardWithBlockListEntry) {
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "", true,
+      *https_chromium_origin(), "https", "",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   SecurityPolicy::AddOriginAccessBlockListEntry(
-      *https_chromium_origin(), "https", "google.com", false,
+      *https_chromium_origin(), "https", "google.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
 
   EXPECT_TRUE(SecurityPolicy::IsOriginAccessAllowed(https_chromium_origin(),
@@ -471,13 +492,22 @@
 
 TEST_F(SecurityPolicyAccessTest, ClearOriginAccessListForOrigin) {
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "example.com", true,
+      *https_chromium_origin(), "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "google.com", true,
+      *https_chromium_origin(), "https", "google.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_example_origin(), "https", "google.com", true,
+      *https_example_origin(), "https", "google.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
 
   SecurityPolicy::ClearOriginAccessListForOrigin(*https_chromium_origin());
@@ -494,19 +524,27 @@
   EXPECT_FALSE(SecurityPolicy::IsOriginAccessAllowed(
       https_chromium_origin(), https_sub_example_origin()));
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "sub.example.com", false,
+      *https_chromium_origin(), "https", "sub.example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kLowPriority);
   EXPECT_TRUE(SecurityPolicy::IsOriginAccessAllowed(
       https_chromium_origin(), https_sub_example_origin()));
   SecurityPolicy::AddOriginAccessBlockListEntry(
-      *https_chromium_origin(), "https", "example.com", true,
+      *https_chromium_origin(), "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
   EXPECT_FALSE(SecurityPolicy::IsOriginAccessAllowed(
       https_chromium_origin(), https_sub_example_origin()));
   SecurityPolicy::AddOriginAccessAllowListEntry(
-      *https_chromium_origin(), "https", "example.com", true,
+      *https_chromium_origin(), "https", "example.com",
+      /*destination_port=*/0,
+      network::mojom::CorsDomainMatchMode::kAllowSubdomains,
+      network::mojom::CorsPortMatchMode::kAllowAnyPort,
       network::mojom::CorsOriginAccessMatchPriority::kHighPriority);
-
   EXPECT_TRUE(SecurityPolicy::IsOriginAccessAllowed(
       https_chromium_origin(), https_sub_example_origin()));
 }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 33d1b3a..4969f2c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5715,7 +5715,3 @@
 
 # Sheriff 2019-05-21
 crbug.com/965134 [ Linux ] virtual/mouseevent_fractional/fast/events/pointerevents/multi-touch-events.html [ Failure ]
-
-# Temporarily allow failing to unblock third_party/webrtc from rolling.
-crbug.com/webrtc/10453 external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Pass Failure ]
-crbug.com/webrtc/10453 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-form-element/form-requestsubmit-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-form-element/form-requestsubmit-expected.txt
deleted file mode 100644
index aeca0632..0000000
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-form-element/form-requestsubmit-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-PASS Passing an element which is not a submit button should throw
-FAIL Passing a submit button not owned by the context object should throw assert_throws: function "() => {
-    form.requestSubmit(submitButton);
-  }" threw object "TypeError: form.requestSubmit is not a function" that is not a DOMException NotFoundError: property "code" is equal to undefined, expected 8
-FAIL requestSubmit() should accept button[type=submit], input[type=submit], and input[type=image] form.requestSubmit is not a function
-FAIL requestSubmit() should trigger interactive form validation form.requestSubmit is not a function
-FAIL requestSubmit() doesn't run form submission reentrantly form.requestSubmit is not a function
-FAIL requestSubmit() doesn't run interactive validation reentrantly form.requestSubmit is not a function
-FAIL requestSubmit() for a disconnected form should not submit the form form.requestSubmit is not a function
-FAIL The value of the submitter should be appended, and form* attributes of the submitter should be handled. form.requestSubmit is not a function
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/lifecycle/resources/subframe.html b/third_party/blink/web_tests/external/wpt/lifecycle/resources/subframe.html
index b80ba954..2f1d70a 100644
--- a/third_party/blink/web_tests/external/wpt/lifecycle/resources/subframe.html
+++ b/third_party/blink/web_tests/external/wpt/lifecycle/resources/subframe.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<img src="/common/slow.py">
 <script>
 window.addEventListener('load', () => {
   window.parent.postMessage('load');
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
index 64c28325..a777611 100755
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
@@ -28,6 +28,10 @@
 echo -n OCSP >$tmpdir/ocsp
 gen-certurl -pem $certfile -ocsp $tmpdir/ocsp > $certfile.cbor
 
+cert_base64=$(base64 -w 0 $certfile.cbor)
+data_cert_url="data:application/cert-chain+cbor;base64,$cert_base64"
+
+
 # A valid Signed Exchange.
 gen-signedexchange \
   -version $sxg_version \
@@ -516,4 +520,19 @@
   -o sxg/register-sw-after-fallback.sxg \
   -miRecordSize 100
 
+# A valid Signed Exchange using data URL for cert-url.
+gen-signedexchange \
+  -version $sxg_version \
+  -uri $inner_url_origin/signed-exchange/resources/inner-url.html \
+  -status 200 \
+  -content sxg-location.html \
+  -certificate $certfile \
+  -certUrl $data_cert_url \
+  -validityUrl $inner_url_origin/signed-exchange/resources/resource.validity.msg \
+  -privateKey $keyfile \
+  -date 2018-04-01T00:00:00Z \
+  -expire 168h \
+  -o sxg/sxg-data-cert-url.sxg \
+  -miRecordSize 100
+
 rm -fr $tmpdir
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-data-cert-url.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-data-cert-url.sxg
new file mode 100644
index 0000000..b5972d5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-data-cert-url.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-data-cert-url.tentative.html b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-data-cert-url.tentative.html
new file mode 100644
index 0000000..0d6bd56
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-data-cert-url.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>SignedHTTPExchange using data URL for cert-url</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+  const sxgUrl = get_host_info().HTTPS_ORIGIN + '/signed-exchange/resources/sxg/sxg-data-cert-url.sxg';
+  const message = await openSXGInIframeAndWaitForMessage(t, sxgUrl);
+  assert_equals(message.location, innerURLOrigin() + '/signed-exchange/resources/inner-url.html');
+  assert_false(message.is_fallback);
+}, 'SignedHTTPExchange using data URL for cert-url');
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt
index 99e0668..1a941ec8 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt
@@ -1,11 +1,13 @@
 This is a testharness.js-based test.
 PASS [audio] getSynchronizationSources() eventually returns a non-empty list
 PASS [audio] RTCRtpSynchronizationSource.timestamp is a number
+PASS [audio] RTCRtpSynchronizationSource.rtpTimestamp is a number [0, 2^32-1]
 PASS [audio] getSynchronizationSources() does not contain SSRCs older than 10 seconds
 FAIL [audio] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now() assert_true: expected true got false
 PASS [audio] RTCRtpSynchronizationSource.source is a number
 PASS [video] getSynchronizationSources() eventually returns a non-empty list
 PASS [video] RTCRtpSynchronizationSource.timestamp is a number
+PASS [video] RTCRtpSynchronizationSource.rtpTimestamp is a number [0, 2^32-1]
 PASS [video] getSynchronizationSources() does not contain SSRCs older than 10 seconds
 FAIL [video] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now() assert_true: expected true got false
 PASS [video] RTCRtpSynchronizationSource.source is a number
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html
index 82ce3bd..a78ede0 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html
@@ -43,6 +43,15 @@
 
   promise_test(async t => {
     const receiver = await initiateSingleTrackCallAndReturnReceiver(t, kind);
+    const [ssrc] = await listenForSSRCs(t, receiver);
+    assert_equals(typeof ssrc.rtpTimestamp, 'number');
+    assert_greater_than_equal(ssrc.rtpTimestamp, 0);
+    assert_less_than_equal(ssrc.rtpTimestamp, 0xffffffff);
+  }, '[' + kind + '] RTCRtpSynchronizationSource.rtpTimestamp is a number ' +
+     '[0, 2^32-1]');
+
+  promise_test(async t => {
+    const receiver = await initiateSingleTrackCallAndReturnReceiver(t, kind);
     // Wait for packets to start flowing.
     await listenForSSRCs(t, receiver);
     // Wait for 10 seconds.
diff --git a/third_party/blink/web_tests/http/tests/worklet/import-on-detached-iframe.html b/third_party/blink/web_tests/http/tests/worklet/import-on-detached-iframe.html
index fce59d7..ab7bf92d 100644
--- a/third_party/blink/web_tests/http/tests/worklet/import-on-detached-iframe.html
+++ b/third_party/blink/web_tests/http/tests/worklet/import-on-detached-iframe.html
@@ -17,53 +17,20 @@
     });
 }
 
-// These tests should not be upstreamed to WPT because the spec does not define
+// This test should not be upstreamed to WPT because the spec does not define
 // behavior in the case where addModule() is called from a detached frame.
+promise_test(t => {
+  const kFrameUrl = 'resources/blank.html';
+  const kScriptUrl = 'resources/empty-worklet-script.js';
 
-promise_test(async t => {
-  const frame = await with_iframe('resources/blank.html');
-  const worklet = frame.contentWindow.CSS.layoutWorklet;
-  frame.remove();
-  await promise_rejects(
-      t, 'InvalidStateError',
-      worklet.addModule('resources/empty-worklet-script.js'));
-}, '[main thread worklet] addModule() on a detached iframe should be ' +
-   'rejected.');
-
-promise_test(async t => {
-  const frame = await with_iframe('resources/blank.html');
-  const worklet = frame.contentWindow.CSS.animationWorklet;
-  frame.remove();
-  await promise_rejects(
-      t, 'InvalidStateError',
-      worklet.addModule('resources/empty-worklet-script.js'));
-}, '[off main thread worklet] addModule() on a detached iframe should be ' +
-   'rejected.');
-
-promise_test(async t => {
-  const frame = await with_iframe('resources/blank.html');
-  const worklet = frame.contentWindow.CSS.layoutWorklet;
-  const promise = worklet.addModule('resources/empty-worklet-script.js');
-  frame.remove();
-
-  // Wait a moment to confirm that asynchronous addModule() operation on the
-  // detached iframe doesn't crash. We cannot wait for the promise returned by
-  // addModule() because it's never settled after context destruction.
-  await new Promise(resolve => setTimeout(resolve, 10));
-}, '[main thread worklet] detaching an iframe after addModule() should not ' +
-   'crash.');
-
-promise_test(async t => {
-  const frame = await with_iframe('resources/blank.html');
-  const worklet = frame.contentWindow.CSS.animationWorklet;
-  const promise = worklet.addModule('resources/empty-worklet-script.js');
-  frame.remove();
-
-  // Wait a moment to confirm that asynchronous addModule() operation on the
-  // detached iframe doesn't crash. We cannot wait for the promise returned by
-  // addModule() because it's never settled after context destruction.
-  await new Promise(resolve => setTimeout(resolve, 10));
-}, '[off main thread worklet] detaching an iframe after addModule() should ' +
-   'not crash.');
+  return with_iframe(kFrameUrl)
+    .then(frame => {
+        let worklet = frame.contentWindow.CSS.paintWorklet;
+        frame.remove();
+        return worklet.addModule(kScriptUrl);
+      })
+    .then(() => assert_unreached('addModule() should fail.'))
+    .catch(e => assert_equals(e.name, 'InvalidStateError', e));
+}, 'addModule() on a detached iframe should be rejected.');
 
 </script>
diff --git a/third_party/blink/web_tests/scrollingcoordinator/non-fast-scrollable-region-nested.html b/third_party/blink/web_tests/scrollingcoordinator/non-fast-scrollable-region-nested.html
index 0fa0d9b..fe07532 100644
--- a/third_party/blink/web_tests/scrollingcoordinator/non-fast-scrollable-region-nested.html
+++ b/third_party/blink/web_tests/scrollingcoordinator/non-fast-scrollable-region-nested.html
@@ -24,7 +24,7 @@
               'iframe are correctly offset by the iframe location.');
 
   onload = function() {
-    nonFastScrollableRects = sortRects(internals.nonFastScrollableRects(document));
+    nonFastScrollableRects = sortRects([...internals.nonFastScrollableRects(document)]);
     shouldBe('nonFastScrollableRects.length', '3');
     shouldBeEqualToString('rectToString(nonFastScrollableRects[0])', '[51, 102, 200, 200]');
     shouldBeEqualToString('rectToString(nonFastScrollableRects[1])', '[51, 402, 211, 211]');
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index 6bfc1ac..5b171be3 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -504,6 +504,7 @@
     property name
     property noValidate
     property reportValidity
+    property requestSubmit
     property reset
     property submit
     property target
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 4bdfeef..44f3c933 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2427,6 +2427,7 @@
     method checkValidity
     method constructor
     method reportValidity
+    method requestSubmit
     method reset
     method submit
     setter acceptCharset
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index d199ffb5..493ccf6 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -567,6 +567,7 @@
     property name
     property noValidate
     property reportValidity
+    property requestSubmit
     property reset
     property submit
     property target
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 0c0f6536..7f5a115 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
@@ -2990,6 +2990,7 @@
     method checkValidity
     method constructor
     method reportValidity
+    method requestSubmit
     method reset
     method submit
     setter acceptCharset
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 06e3a8e..d1ef86a 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 249161107
-Date: 2019/05/21 UTC
+Version: 249743511
+Date: 2019/05/24 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/call_stack_profile.proto b/third_party/metrics_proto/call_stack_profile.proto
index e0433c4..a7051b86 100644
--- a/third_party/metrics_proto/call_stack_profile.proto
+++ b/third_party/metrics_proto/call_stack_profile.proto
@@ -113,9 +113,17 @@
     // Weight of the sample. When omitted the sample is presumed to have
     // a weight of 1.
     // Not currently used for CPU profiles.
-    // For heap profiles it represents the number of bytes attributed to the
-    // sample.
+    // For heap profiles it represents the total number of bytes associated with
+    // the StackSample record.
     optional int64 weight = 6;
+
+    // Number of events associated with the sample. When omitted the default
+    // value of 1 should be used.
+    // Not currently used for CPU profiles.
+    // For heap profiles it represents the number of allocations associated with
+    // the StackSample record. The following relation holds:
+    //   allocation_size * count == weight.
+    optional int64 count = 7 [default = 1];
   }
 
   // The previous sample encoding. Deprecated 2018/08/04 in favor of
diff --git a/third_party/metrics_proto/cast_logs.proto b/third_party/metrics_proto/cast_logs.proto
index 8dab9f5..7abe227 100644
--- a/third_party/metrics_proto/cast_logs.proto
+++ b/third_party/metrics_proto/cast_logs.proto
@@ -9,8 +9,7 @@
 option java_outer_classname = "CastLogsProtos";
 option java_package = "org.chromium.components.metrics";
 
-package metrics;  // Cast-enabled device specific log data included in
-                  // ChromeUserMetricsExtension.
+package metrics;  // Cast-enabled device specific log data included in ChromeUserMetricsExtension.
 
 // Next tag: 7
 message CastLogsProto {
diff --git a/third_party/metrics_proto/chrome_os_app_list_launch_event.proto b/third_party/metrics_proto/chrome_os_app_list_launch_event.proto
index 9c592c1..60d0bb6 100644
--- a/third_party/metrics_proto/chrome_os_app_list_launch_event.proto
+++ b/third_party/metrics_proto/chrome_os_app_list_launch_event.proto
@@ -8,8 +8,6 @@
 
 package metrics;
 
-import "system_profile.proto";
-
 // Provides information about the launch of an item (such as an app or a file)
 // from the ChromeOS launcher. One event is recorded for every launch
 // originating from any launcher UI component, and this is the only circumstance
diff --git a/third_party/protobuf/BUILD.gn b/third_party/protobuf/BUILD.gn
index c6f350b..fafc2ef 100644
--- a/third_party/protobuf/BUILD.gn
+++ b/third_party/protobuf/BUILD.gn
@@ -219,14 +219,13 @@
     "//third_party/perfetto/protos/ftrace:full",
     "//third_party/perfetto/gn:protobuf_full_deps",
 
+    # The SQLite fuzzer's corpus generator needs protobuf_full and is not
+    # included in Chrome.
+    "//third_party/sqlite:sqlite3_lpm_corpus_gen",
+
     # Some tests inside ChromeOS need reflection to parse golden files.
     # Not included in production code.
     "//chrome/test:usage_time_limit_unittests",
-
-    # The protobuf-based SQLite and GPU fuzzers need protobuf_full and are not
-    # included in Chrome.
-    "//third_party/sqlite:sqlite3_lpm_corpus_gen",
-    "//gpu:gl_lpm_shader_to_string_unittest",
   ]
 
   sources = protobuf_lite_sources + [
diff --git a/third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml b/third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml
index fca6ab5..7c70e4d 100644
--- a/third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml
+++ b/third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml
@@ -168,5 +168,28 @@
       </description>
       <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
     </event>
+
+    <event name="axis_added">
+      <description summary="an axis is added">
+        Adds an axis to the gamepad. Only called when the gamepad was created by
+        gamepad_added_with_device_info. The values are compatible with
+        input_absinfo.
+      </description>
+      <arg name="index" type="uint" summary="An index of the axis" />
+      <arg name="min_value" type="int" summary="minimum value of the axis" />
+      <arg name="max_value" type="int" summary="maximum value of the axis" />
+      <arg name="flat" type="int" summary="input within this value are ignored" />
+      <arg name="fuzz" type="int" summary="used to filter noise" />
+      <arg name="resolution" type="int" summary="resolution of input in units per millimeter, or units per radian for rotational axes." />
+    </event>
+
+    <event name="activated">
+      <description summary="Gamepad activated">
+        Activates the gamepad i.e. the gamepad will be visible to applications
+        after this event is fired. All axis_added events should be sent before
+        this event. Only called when the gamepad was created by
+        gamepad_added_with_device_info.
+      </description>
+    </event>
   </interface>
 </protocol>
diff --git a/tools/metrics/histograms/extract_histograms.py b/tools/metrics/histograms/extract_histograms.py
index 9ae2d0cd4..b6acf84 100644
--- a/tools/metrics/histograms/extract_histograms.py
+++ b/tools/metrics/histograms/extract_histograms.py
@@ -79,7 +79,8 @@
 def _JoinChildNodes(tag):
   """Join child nodes into a single text.
 
-  Applicable to leafs like 'summary' and 'detail'.
+  Applicable to leafs like 'summary' and 'detail'. Removes any comment in the
+  node.
 
   Args:
     tag: parent node
@@ -87,7 +88,9 @@
   Returns:
     a string with concatenated nodes' text representation.
   """
-  return ''.join(c.toxml() for c in tag.childNodes).strip()
+  return ''.join(c.toxml()
+                 for c in tag.childNodes
+                 if c.nodeType != xml.dom.minidom.Node.COMMENT_NODE).strip()
 
 
 def _NormalizeString(s):
diff --git a/tools/metrics/histograms/extract_histograms_test.py b/tools/metrics/histograms/extract_histograms_test.py
index 178dfa8a..34ff8589 100644
--- a/tools/metrics/histograms/extract_histograms_test.py
+++ b/tools/metrics/histograms/extract_histograms_test.py
@@ -46,7 +46,11 @@
  <histogram name="Test.Histogram" units="things">
   <owner> Please list the metric's owners. Add more owner tags as needed.
   </owner>
-  <summary> This is a summary </summary>
+  <summary>
+    <!-- Comments are fine -->
+    This is a summary
+    <!-- Comments are fine -->
+  </summary>
  </histogram>
 </histograms>
 </histogram-configuration>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2f299f5..e591672 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -89727,7 +89727,10 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordManager.BlacklistedSites">
+<histogram name="PasswordManager.BlacklistedSites" expires_after="M76">
+  <obsolete>
+    Deprecated in M76 in favor of PasswordManager.BlacklistedSitesHiRes.
+  </obsolete>
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -89787,6 +89790,54 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.BlacklistedSitesHiRes">
+  <owner>jdoerrie@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    The total number of sites that the user has blacklisted. Recorded by
+    iterating over stored passwords once per run of Chrome.
+  </summary>
+</histogram>
+
+<histogram base="true"
+    name="PasswordManager.BubbleSuppression.AccountsWithInStatisticsTable"
+    units="accounts" expires_after="M78">
+  <owner>battre@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    The number of accounts stored in password_manager::StatisticsTable. These
+    are accounts for which the user ignored the save bubble at least once. The
+    count is recorded once per browser start-up. (In case of multiple profiles,
+    the counts are for the profile that first has a WebContents created.)
+  </summary>
+</histogram>
+
+<histogram base="true"
+    name="PasswordManager.BubbleSuppression.AccountsWithSuppressedBubble"
+    units="accounts" expires_after="M86">
+  <owner>battre@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    The number of accounts that do not trigger password save prompts anymore.
+    The count is recorded once per browser start-up. (In case of multiple
+    profiles, the counts are for the profile that first has a WebContents
+    created.)
+  </summary>
+</histogram>
+
+<histogram base="true"
+    name="PasswordManager.BubbleSuppression.DomainsWithSuppressedBubble"
+    units="accounts" expires_after="M86">
+  <owner>battre@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    The number of domains for which at least one account exists that does not
+    trigger password save prompts anymore. The count is recorded once per
+    browser start-up. (In case of multiple profiles, the counts are for the
+    profile that first has a WebContents created.)
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.ButtonTitlePerformance.HasFormTag"
     expires_after="2019-07-01">
   <owner>kolos@chromium.org</owner>
@@ -91259,7 +91310,20 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="PasswordManager.TotalAccounts">
+<histogram base="true" name="PasswordManager.TotalAccounts" expires_after="M76">
+  <obsolete>
+    Deprecated in M76 in favor of PasswordManager.TotalAccountsHiRes.ByType.
+  </obsolete>
+  <owner>battre@chromium.org</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    The number of accounts stored in the password manager (across all sites),
+    split by whether created by the user or generated by Chrome, and further by
+    whether the user used sync with custom passphrase or not.
+  </summary>
+</histogram>
+
+<histogram base="true" name="PasswordManager.TotalAccountsHiRes.ByType">
   <owner>battre@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -155708,19 +155772,27 @@
   <affected-histogram name="PasswordManager.AccountsPerSite.AutoGenerated"/>
   <affected-histogram name="PasswordManager.AccountsPerSite.UserCreated"/>
   <affected-histogram name="PasswordManager.BlacklistedSites"/>
+  <affected-histogram name="PasswordManager.BlacklistedSitesHiRes"/>
   <affected-histogram name="PasswordManager.TimesGeneratedPasswordUsed"/>
   <affected-histogram name="PasswordManager.TimesPasswordUsed.AutoGenerated"/>
   <affected-histogram name="PasswordManager.TimesPasswordUsed.UserCreated"/>
   <affected-histogram name="PasswordManager.TotalAccounts.AutoGenerated"/>
   <affected-histogram name="PasswordManager.TotalAccounts.UserCreated"/>
+  <affected-histogram
+      name="PasswordManager.TotalAccountsHiRes.ByType.AutoGenerated"/>
+  <affected-histogram name="PasswordManager.TotalAccountsHiRes.ByType.Overall"/>
+  <affected-histogram
+      name="PasswordManager.TotalAccountsHiRes.ByType.UserCreated"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="PasswordGenerated" separator=".">
   <suffix base="true" name="AutoGenerated"/>
+  <suffix base="true" name="Overall"/>
   <suffix base="true" name="UserCreated"/>
   <affected-histogram name="PasswordManager.AccountsPerSite"/>
   <affected-histogram name="PasswordManager.TimesPasswordUsed"/>
   <affected-histogram name="PasswordManager.TotalAccounts"/>
+  <affected-histogram name="PasswordManager.TotalAccountsHiRes.ByType"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="PasswordManagerMonitor" separator="_">
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index ab62957..9792130 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -57,6 +57,7 @@
 crbug.com/910207 [ Nexus_5X ] blink_perf.paint/* [ Skip ]
 crbug.com/859979 [ Android_Webview ] blink_perf.paint/paint-offset-changes.html [ Skip ]
 crbug.com/901493 [ Nexus6_Webview ] blink_perf.paint/* [ Skip ]
+crbug.com/963967 [ Android ] blink_perf.paint/select-all-words.html [ Skip ]
 
 # Benchmark: blink_perf.shadow_dom
 crbug.com/702319 [ Nexus_5X ] blink_perf.shadow_dom/* [ Skip ]
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index 6f7f43e..ec0940e 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -1413,6 +1413,8 @@
       return "none";
     case ax::mojom::IntAttribute::kDefaultActionVerb:
       return "defaultActionVerb";
+    case ax::mojom::IntAttribute::kDropeffect:
+      return "dropeffect";
     case ax::mojom::IntAttribute::kScrollX:
       return "scrollX";
     case ax::mojom::IntAttribute::kScrollXMin:
@@ -1533,6 +1535,8 @@
     return ax::mojom::IntAttribute::kNone;
   if (0 == strcmp(int_attribute, "defaultActionVerb"))
     return ax::mojom::IntAttribute::kDefaultActionVerb;
+  if (0 == strcmp(int_attribute, "dropeffect"))
+    return ax::mojom::IntAttribute::kDropeffect;
   if (0 == strcmp(int_attribute, "scrollX"))
     return ax::mojom::IntAttribute::kScrollX;
   if (0 == strcmp(int_attribute, "scrollXMin"))
@@ -1697,6 +1701,8 @@
       return "containerLiveAtomic";
     case ax::mojom::BoolAttribute::kContainerLiveBusy:
       return "containerLiveBusy";
+    case ax::mojom::BoolAttribute::kGrabbed:
+      return "grabbed";
     case ax::mojom::BoolAttribute::kLiveAtomic:
       return "liveAtomic";
     case ax::mojom::BoolAttribute::kModal:
@@ -1731,6 +1737,8 @@
     return ax::mojom::BoolAttribute::kContainerLiveAtomic;
   if (0 == strcmp(bool_attribute, "containerLiveBusy"))
     return ax::mojom::BoolAttribute::kContainerLiveBusy;
+  if (0 == strcmp(bool_attribute, "grabbed"))
+    return ax::mojom::BoolAttribute::kGrabbed;
   if (0 == strcmp(bool_attribute, "liveAtomic"))
     return ax::mojom::BoolAttribute::kLiveAtomic;
   if (0 == strcmp(bool_attribute, "modal"))
@@ -2661,4 +2669,37 @@
   return ax::mojom::ImageAnnotationStatus::kNone;
 }
 
+ax::mojom::Dropeffect ParseDropeffect(const char* dropeffect) {
+  if (0 == strcmp(dropeffect, "copy"))
+    return ax::mojom::Dropeffect::kCopy;
+  if (0 == strcmp(dropeffect, "execute"))
+    return ax::mojom::Dropeffect::kExecute;
+  if (0 == strcmp(dropeffect, "link"))
+    return ax::mojom::Dropeffect::kLink;
+  if (0 == strcmp(dropeffect, "move"))
+    return ax::mojom::Dropeffect::kMove;
+  if (0 == strcmp(dropeffect, "popup"))
+    return ax::mojom::Dropeffect::kPopup;
+  return ax::mojom::Dropeffect::kNone;
+}
+
+const char* ToString(ax::mojom::Dropeffect dropeffect) {
+  switch (dropeffect) {
+    case ax::mojom::Dropeffect::kCopy:
+      return "copy";
+    case ax::mojom::Dropeffect::kExecute:
+      return "execute";
+    case ax::mojom::Dropeffect::kLink:
+      return "link";
+    case ax::mojom::Dropeffect::kMove:
+      return "move";
+    case ax::mojom::Dropeffect::kPopup:
+      return "popup";
+    case ax::mojom::Dropeffect::kNone:
+      return "none";
+  }
+
+  return "";
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_enum_util.h b/ui/accessibility/ax_enum_util.h
index badb66c2..2de28d4 100644
--- a/ui/accessibility/ax_enum_util.h
+++ b/ui/accessibility/ax_enum_util.h
@@ -153,6 +153,10 @@
 AX_EXPORT ax::mojom::ImageAnnotationStatus ParseImageAnnotationStatus(
     const char* status);
 
+// ax::mojom::Dropeffect
+AX_EXPORT const char* ToString(ax::mojom::Dropeffect dropeffect);
+AX_EXPORT ax::mojom::Dropeffect ParseDropeffect(const char* dropeffect);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_ENUM_UTIL_H_
diff --git a/ui/accessibility/ax_enum_util_unittest.cc b/ui/accessibility/ax_enum_util_unittest.cc
index 861e35e3..ed92700f 100644
--- a/ui/accessibility/ax_enum_util_unittest.cc
+++ b/ui/accessibility/ax_enum_util_unittest.cc
@@ -209,4 +209,8 @@
       ParseImageAnnotationStatus);
 }
 
+TEST(AXEnumUtilTest, Dropeffect) {
+  TestEnumStringConversion<ax::mojom::Dropeffect>(ParseDropeffect);
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index fd1e37c..1c53469 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -607,6 +607,11 @@
   // Focus traversal in views and Android.
   kPreviousFocusId,
   kNextFocusId,
+
+  // For indicating what functions can be performed when a dragged object
+  // is released on the drop target.
+  // Note: aria-dropeffect is deprecated in WAI-ARIA 1.1.
+  kDropeffect,
 };
 
 enum FloatAttribute {
@@ -673,6 +678,10 @@
 
   // Indicates whether this node supports text location.
   kSupportsTextLocation,
+
+  // Indicates whether this node can be grabbed for drag-and-drop operation.
+  // Note: aria-grabbed is deprecated in WAI-ARIA 1.1.
+  kGrabbed,
 };
 
 enum IntListAttribute {
@@ -992,3 +1001,12 @@
   // request timed out, etc.
   kAnnotationProcessFailed,
 };
+
+enum Dropeffect {
+  kNone,
+  kCopy,
+  kExecute,
+  kLink,
+  kMove,
+  kPopup,
+};
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index e2ae645..949ad99 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -273,6 +273,9 @@
     case ax::mojom::IntAttribute::kCheckedState:
       AddEvent(node, Event::CHECKED_STATE_CHANGED);
       break;
+    case ax::mojom::IntAttribute::kDropeffect:
+      AddEvent(node, Event::DROPEFFECT_CHANGED);
+      break;
     case ax::mojom::IntAttribute::kHasPopup:
       AddEvent(node, Event::HASPOPUP_CHANGED);
       break;
@@ -365,6 +368,9 @@
       if (!new_value)
         AddEvent(node, Event::LAYOUT_INVALIDATED);
       break;
+    case ax::mojom::BoolAttribute::kGrabbed:
+      AddEvent(node, Event::GRABBED_CHANGED);
+      break;
     case ax::mojom::BoolAttribute::kLiveAtomic:
       AddEvent(node, Event::ATOMIC_CHANGED);
       break;
diff --git a/ui/accessibility/ax_event_generator.h b/ui/accessibility/ax_event_generator.h
index 46bbb6c..100dbf2 100644
--- a/ui/accessibility/ax_event_generator.h
+++ b/ui/accessibility/ax_event_generator.h
@@ -37,10 +37,12 @@
     DESCRIPTION_CHANGED,
     DOCUMENT_SELECTION_CHANGED,
     DOCUMENT_TITLE_CHANGED,
+    DROPEFFECT_CHANGED,
     ENABLED_CHANGED,
     EXPANDED,
     FLOW_FROM_CHANGED,
     FLOW_TO_CHANGED,
+    GRABBED_CHANGED,
     HASPOPUP_CHANGED,
     HIERARCHICAL_LEVEL_CHANGED,
     IMAGE_ANNOTATION_CHANGED,
diff --git a/ui/accessibility/ax_event_generator_unittest.cc b/ui/accessibility/ax_event_generator_unittest.cc
index dc7e20d..d51a24e 100644
--- a/ui/accessibility/ax_event_generator_unittest.cc
+++ b/ui/accessibility/ax_event_generator_unittest.cc
@@ -62,6 +62,9 @@
       case AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
         event_name = "DOCUMENT_TITLE_CHANGED";
         break;
+      case AXEventGenerator::Event::DROPEFFECT_CHANGED:
+        event_name = "DROPEFFECT_CHANGED";
+        break;
       case AXEventGenerator::Event::ENABLED_CHANGED:
         event_name = "ENABLED_CHANGED";
         break;
@@ -74,6 +77,9 @@
       case AXEventGenerator::Event::FLOW_TO_CHANGED:
         event_name = "FLOW_TO_CHANGED";
         break;
+      case AXEventGenerator::Event::GRABBED_CHANGED:
+        event_name = "GRABBED_CHANGED";
+        break;
       case AXEventGenerator::Event::HASPOPUP_CHANGED:
         event_name = "HASPOPUP_CHANGED";
         break;
@@ -1289,6 +1295,36 @@
   EXPECT_EQ("ATOMIC_CHANGED on 1", DumpEvents(&event_generator));
 }
 
+TEST(AXEventGeneratorTest, DropeffectChanged) {
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(1);
+  initial_state.nodes[0].id = 1;
+
+  AXTree tree(initial_state);
+  AXEventGenerator event_generator(&tree);
+  AXTreeUpdate update = initial_state;
+
+  update.nodes[0].AddDropeffect(ax::mojom::Dropeffect::kCopy);
+  EXPECT_TRUE(tree.Unserialize(update));
+  EXPECT_EQ("DROPEFFECT_CHANGED on 1", DumpEvents(&event_generator));
+}
+
+TEST(AXEventGeneratorTest, GrabbedChanged) {
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(1);
+  initial_state.nodes[0].id = 1;
+
+  AXTree tree(initial_state);
+  AXEventGenerator event_generator(&tree);
+  AXTreeUpdate update = initial_state;
+
+  update.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kGrabbed, true);
+  EXPECT_TRUE(tree.Unserialize(update));
+  EXPECT_EQ("GRABBED_CHANGED on 1", DumpEvents(&event_generator));
+}
+
 TEST(AXEventGeneratorTest, HasPopupChanged) {
   AXTreeUpdate initial_state;
   initial_state.root_id = 1;
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 1f50bfe1..751c6b6a 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -157,6 +157,7 @@
     case ax::mojom::IntAttribute::kAriaRowCount:
     case ax::mojom::IntAttribute::kAriaCellRowIndex:
     case ax::mojom::IntAttribute::kImageAnnotationStatus:
+    case ax::mojom::IntAttribute::kDropeffect:
       return false;
   }
 
@@ -626,6 +627,11 @@
   return IsFlagSet(style, static_cast<uint32_t>(text_style_enum));
 }
 
+bool AXNodeData::HasDropeffect(ax::mojom::Dropeffect dropeffect_enum) const {
+  int32_t dropeffect = GetIntAttribute(ax::mojom::IntAttribute::kDropeffect);
+  return IsFlagSet(dropeffect, static_cast<uint32_t>(dropeffect_enum));
+}
+
 ax::mojom::State AXNodeData::AddState(ax::mojom::State state_enum) {
   DCHECK_GT(static_cast<int>(state_enum),
             static_cast<int>(ax::mojom::State::kNone));
@@ -708,6 +714,18 @@
   AddIntAttribute(ax::mojom::IntAttribute::kTextStyle, style);
 }
 
+void AXNodeData::AddDropeffect(ax::mojom::Dropeffect dropeffect_enum) {
+  DCHECK_GE(static_cast<int>(dropeffect_enum),
+            static_cast<int>(ax::mojom::Dropeffect::kMinValue));
+  DCHECK_LE(static_cast<int>(dropeffect_enum),
+            static_cast<int>(ax::mojom::Dropeffect::kMaxValue));
+  int32_t dropeffect = GetIntAttribute(ax::mojom::IntAttribute::kDropeffect);
+  dropeffect =
+      ModifyFlag(dropeffect, static_cast<uint32_t>(dropeffect_enum), true);
+  RemoveIntAttribute(ax::mojom::IntAttribute::kDropeffect);
+  AddIntAttribute(ax::mojom::IntAttribute::kDropeffect, dropeffect);
+}
+
 ax::mojom::CheckedState AXNodeData::GetCheckedState() const {
   return static_cast<ax::mojom::CheckedState>(
       GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
@@ -1235,6 +1253,9 @@
                   ui::ToString(static_cast<ax::mojom::ImageAnnotationStatus>(
                       int_attribute.second));
         break;
+      case ax::mojom::IntAttribute::kDropeffect:
+        result += " dropeffect=" + value;
+        break;
       case ax::mojom::IntAttribute::kNone:
         break;
     }
@@ -1396,6 +1417,9 @@
       case ax::mojom::BoolAttribute::kSupportsTextLocation:
         result += " supports_text_location=" + value;
         break;
+      case ax::mojom::BoolAttribute::kGrabbed:
+        result += " grabbed=" + value;
+        break;
       case ax::mojom::BoolAttribute::kNone:
         break;
     }
@@ -1501,4 +1525,22 @@
   return result;
 }
 
+std::string AXNodeData::DropeffectBitfieldToString() const {
+  if (!HasIntAttribute(ax::mojom::IntAttribute::kDropeffect))
+    return "";
+
+  std::string str;
+  for (int dropeffect_idx = static_cast<int>(ax::mojom::Dropeffect::kMinValue);
+       dropeffect_idx <= static_cast<int>(ax::mojom::Dropeffect::kMaxValue);
+       ++dropeffect_idx) {
+    ax::mojom::Dropeffect dropeffect_enum =
+        static_cast<ax::mojom::Dropeffect>(dropeffect_idx);
+    if (HasDropeffect(dropeffect_enum))
+      str += " " + std::string(ui::ToString(dropeffect_enum));
+  }
+
+  // Removing leading space in final string.
+  return str.substr(1);
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_node_data.h b/ui/accessibility/ax_node_data.h
index 9bd426f0..ccf7344 100644
--- a/ui/accessibility/ax_node_data.h
+++ b/ui/accessibility/ax_node_data.h
@@ -152,12 +152,16 @@
   bool HasState(ax::mojom::State state) const;
   bool HasAction(ax::mojom::Action action) const;
   bool HasTextStyle(ax::mojom::TextStyle text_style) const;
+  // aria-dropeffect is deprecated in WAI-ARIA 1.1.
+  bool HasDropeffect(ax::mojom::Dropeffect dropeffect) const;
 
   // Set or remove bits in the given enum's corresponding bitfield.
   ax::mojom::State AddState(ax::mojom::State state);
   ax::mojom::State RemoveState(ax::mojom::State state);
   ax::mojom::Action AddAction(ax::mojom::Action action);
   void AddTextStyle(ax::mojom::TextStyle text_style);
+  // aria-dropeffect is deprecated in WAI-ARIA 1.1.
+  void AddDropeffect(ax::mojom::Dropeffect dropeffect);
 
   // Helper functions to get or set some common int attributes with some
   // specific enum types. To remove an attribute, set it to None.
@@ -192,6 +196,11 @@
   // Return a string representation of this data, for debugging.
   virtual std::string ToString() const;
 
+  // Return a string representation of |aria-dropeffect| values, for testing
+  // and debugging.
+  // aria-dropeffect is deprecated in WAI-ARIA 1.1.
+  std::string DropeffectBitfieldToString() const;
+
   // As much as possible this should behave as a simple, serializable,
   // copyable struct.
   int32_t id = -1;
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 1f93f52..01275b3b 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -1019,16 +1019,15 @@
   }
 
   // Expose dropeffect attribute.
-  std::string drop_effect;
-  if (GetData().GetHtmlAttribute("aria-dropeffect", &drop_effect)) {
-    AddAttributeToList("dropeffect", drop_effect, attributes);
+  // aria-dropeffect is deprecated in WAI-ARIA 1.1.
+  if (GetData().HasIntAttribute(ax::mojom::IntAttribute::kDropeffect)) {
+    std::string dropeffect = GetData().DropeffectBitfieldToString();
+    AddAttributeToList("dropeffect", dropeffect, attributes);
   }
 
   // Expose grabbed attribute.
-  std::string grabbed;
-  if (GetData().GetHtmlAttribute("aria-grabbed", &grabbed)) {
-    AddAttributeToList("grabbed", grabbed, attributes);
-  }
+  // aria-grabbed is deprecated in WAI-ARIA 1.1.
+  AddAttributeToList(ax::mojom::BoolAttribute::kGrabbed, "grabbed", attributes);
 
   // Expose class attribute.
   std::string class_attr;
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 654de91c..b075208 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -5614,7 +5614,6 @@
       properties, ax::mojom::BoolAttribute::kLiveAtomic, "atomic");
   BoolAttributeToUIAAriaProperty(properties, ax::mojom::BoolAttribute::kBusy,
                                  "busy");
-  HtmlAttributeToUIAAriaProperty(properties, "aria-channel", "channel");
 
   switch (data.GetCheckedState()) {
     case ax::mojom::CheckedState::kNone:
@@ -5677,9 +5676,14 @@
       break;
   }
 
-  HtmlAttributeToUIAAriaProperty(properties, "aria-dropeffect", "dropeffect");
+  // aria-dropeffect is deprecated in WAI-ARIA 1.1.
+  if (data.HasIntAttribute(ax::mojom::IntAttribute::kDropeffect)) {
+    properties.push_back(L"dropeffect=" +
+                         base::UTF8ToUTF16(data.DropeffectBitfieldToString()));
+  }
   StateToUIAAriaProperty(properties, ax::mojom::State::kExpanded, "expanded");
-  HtmlAttributeToUIAAriaProperty(properties, "aria-grabbed", "grabbed");
+  BoolAttributeToUIAAriaProperty(properties, ax::mojom::BoolAttribute::kGrabbed,
+                                 "grabbed");
 
   // TODO(crbug.com/865101) Use
   // data.HasState(ax::mojom::State::kAutofillAvailable) instead of
@@ -5733,7 +5737,6 @@
       properties, ax::mojom::BoolAttribute::kSelected, "selected");
   IntAttributeToUIAAriaProperty(properties, ax::mojom::IntAttribute::kSetSize,
                                 "setsize");
-  HtmlAttributeToUIAAriaProperty(properties, "aria-secret", "secret");
 
   int32_t sort_direction;
   if (IsTableHeader(data.role) &&
@@ -5757,8 +5760,6 @@
     }
   }
 
-  HtmlAttributeToUIAAriaProperty(properties, "aria-tabindex", "tabindex");
-
   if (IsRangeValueSupported(data)) {
     FloatAttributeToUIAAriaProperty(
         properties, ax::mojom::FloatAttribute::kMaxValueForRange, "valuemax");
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index e296dc2..f201984 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -126,6 +126,7 @@
     "//ui/gfx/geometry",
     "//ui/gl",
     "//ui/platform_window",
+    "//ui/platform_window/mojo",
     "//ui/platform_window/stub",
   ]
 
diff --git a/ui/base/ime/chromeos/input_method_chromeos.cc b/ui/base/ime/chromeos/input_method_chromeos.cc
index a9db88f..7a9b966 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos.cc
@@ -19,12 +19,15 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/third_party/icu/icu_utf.h"
 #include "chromeos/system/devicemode.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/ime_engine_handler_interface.h"
 #include "ui/base/ime/input_method_delegate.h"
+#include "ui/base/ime/mojo/ime.mojom.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/rect.h"
@@ -57,6 +60,74 @@
 
 }  // namespace
 
+// The helper to make the InputMethodChromeOS as a ime::mojom::ImeEngineClient.
+// It forwards the ime::mojom::ImeEngineClient method calls toi methods of
+// ui::IMEInputContextHandlerInterface methods.
+// Due to the method naming conflict, InputMethodChromeOS cannot directly
+// inherit from ime::mojom::ImeEngineClient.
+class InputMethodChromeOS::MojoHelper : public ime::mojom::ImeEngineClient {
+ public:
+  explicit MojoHelper(InputMethodChromeOS* im) : im_(im), binding_(this) {}
+  ~MojoHelper() override = default;
+
+  ime::mojom::ImeEngineProxy* ime_engine() { return ime_engine_.get(); }
+
+  bool connected() const { return connected_; }
+
+  void Connect() {
+    ime::mojom::ImeEngineClientPtr client_ptr;
+    binding_.Bind(mojo::MakeRequest(&client_ptr));
+    connected_ = im_->delegate()->ConnectToImeEngine(
+        mojo::MakeRequest(&ime_engine_), std::move(client_ptr));
+  }
+
+  void Reset() {
+    binding_.Close();
+    ime_engine_.reset();
+    connected_ = false;
+  }
+
+ private:
+  // ime::mojom::ImeEngineClient:
+  void CommitText(const std::string& text) override { im_->CommitText(text); }
+  void UpdateCompositionText(const ui::CompositionText& composition_text,
+                             uint32_t cursor_pos,
+                             bool visible) override {
+    im_->UpdateCompositionText(composition_text, cursor_pos, visible);
+  }
+  void DeleteSurroundingText(int32_t offset, uint32_t length) override {
+    im_->DeleteSurroundingText(offset, length);
+  }
+  void SendKeyEvent(std::unique_ptr<ui::Event> key_event) override {
+    im_->SendKeyEvent(key_event->AsKeyEvent());
+  }
+  void Reconnect() override {
+    // Don't reconnect when the |ime_engine_| has been reset, which means the
+    // InputMethodChromeOS is not focused.
+    if (ime_engine_) {
+      Reset();
+      Connect();
+      if (!im_->IsTextInputTypeNone()) {
+        ime_engine_->StartInput(ime::mojom::EditorInfo::New(
+            im_->GetTextInputType(), im_->GetTextInputMode(),
+            im_->GetTextInputFlags(), im_->GetClientFocusReason(),
+            im_->GetClientShouldDoLearning()));
+      }
+    }
+  }
+
+  InputMethodChromeOS* im_;
+  // Whether the mojo connection is enabled.
+  // If true, |InputMethodChromeOS| works with ime::mojom::ImeEngine.
+  // If false, |InputMethodChromeOS| works with ui::IMEEngineHandlerInterface.
+  bool connected_ = false;
+
+  mojo::Binding<ime::mojom::ImeEngineClient> binding_;
+  ime::mojom::ImeEnginePtr ime_engine_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoHelper);
+};
+
 // InputMethodChromeOS implementation -----------------------------------------
 InputMethodChromeOS::InputMethodChromeOS(
     internal::InputMethodDelegate* delegate)
@@ -64,6 +135,7 @@
       composing_text_(false),
       composition_changed_(false),
       handling_key_event_(false),
+      mojo_helper_(std::make_unique<MojoHelper>(this)),
       weak_ptr_factory_(this) {
   ResetContext();
 }
@@ -159,7 +231,8 @@
   // normal input field (not a password field).
   // Note: We need to send the key event to ibus even if the |context_| is not
   // enabled, so that ibus can have a chance to enable the |context_|.
-  if (!IsNonPasswordInputFieldFocused() || !GetEngine()) {
+  const bool has_engine = GetEngine() || mojo_helper_->connected();
+  if (!IsNonPasswordInputFieldFocused() || !has_engine) {
     if (event->type() == ET_KEY_PRESSED) {
       if (ExecuteCharacterComposer(*event)) {
         // Treating as PostIME event if character composer handles key event and
@@ -174,12 +247,17 @@
   }
 
   handling_key_event_ = true;
-  GetEngine()->ProcessKeyEvent(
-      *event, base::BindOnce(&InputMethodChromeOS::KeyEventDoneCallback,
-                             weak_ptr_factory_.GetWeakPtr(),
-                             // Pass the ownership of the new copied event.
-                             base::Owned(new ui::KeyEvent(*event)),
-                             std::move(result_callback)));
+  auto callback = base::BindOnce(&InputMethodChromeOS::KeyEventDoneCallback,
+                                 weak_ptr_factory_.GetWeakPtr(),
+                                 // Pass the ownership of the new copied event.
+                                 base::Owned(new ui::KeyEvent(*event)),
+                                 std::move(result_callback));
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->ProcessKeyEvent(ui::Event::Clone(*event),
+                                                std::move(callback));
+  } else {
+    GetEngine()->ProcessKeyEvent(*event, std::move(callback));
+  }
   return ui::EventDispatchDetails();
 }
 
@@ -232,16 +310,23 @@
 
   UpdateContextFocusState();
 
-  ui::IMEEngineHandlerInterface* engine = GetEngine();
-  if (engine) {
-    ui::IMEEngineHandlerInterface::InputContext context(
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->FinishInput();
+    mojo_helper_->ime_engine()->StartInput(ime::mojom::EditorInfo::New(
         GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
-        GetClientFocusReason(), GetClientShouldDoLearning());
-    // When focused input client is not changed, a text input type change
-    // should cause blur/focus events to engine. The focus in to or out from
-    // password field should also notify engine.
-    engine->FocusOut();
-    engine->FocusIn(context);
+        GetClientFocusReason(), GetClientShouldDoLearning()));
+  } else {
+    ui::IMEEngineHandlerInterface* engine = GetEngine();
+    if (engine) {
+      ui::IMEEngineHandlerInterface::InputContext context(
+          GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
+          GetClientFocusReason(), GetClientShouldDoLearning());
+      // When focused input client is not changed, a text input type change
+      // should cause blur/focus events to engine. The focus in to or out from
+      // password field should also notify engine.
+      engine->FocusOut();
+      engine->FocusIn(context);
+    }
   }
 
   OnCaretBoundsChanged(client);
@@ -262,8 +347,12 @@
   DCHECK(client == GetTextInputClient());
   DCHECK(!IsTextInputTypeNone());
 
-  if (GetEngine())
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->UpdateCompositionBounds(
+        GetCompositionBounds(client));
+  } else if (GetEngine()) {
     GetEngine()->SetCompositionBounds(GetCompositionBounds(client));
+  }
 
   chromeos::IMECandidateWindowHandlerInterface* candidate_window =
       ui::IMEBridge::Get()->GetCandidateWindowHandler();
@@ -310,7 +399,12 @@
   // Here SetSurroundingText accepts relative position of |surrounding_text|, so
   // we have to convert |selection_range| from node coordinates to
   // |surrounding_text| coordinates.
-  if (GetEngine()) {
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->UpdateSurroundingInfo(
+        base::UTF16ToUTF8(surrounding_text),
+        selection_range.start() - text_range.start(),
+        selection_range.end() - text_range.start(), text_range.start());
+  } else if (GetEngine()) {
     GetEngine()->SetSurroundingText(
         base::UTF16ToUTF8(surrounding_text),
         selection_range.start() - text_range.start(),
@@ -339,13 +433,26 @@
   return InputMethodBase::GetInputMethodKeyboardController();
 }
 
+void InputMethodChromeOS::OnFocus() {
+  InputMethodBase::OnFocus();
+  mojo_helper_->Connect();
+}
+
+void InputMethodChromeOS::OnBlur() {
+  InputMethodBase::OnBlur();
+  mojo_helper_->Reset();
+}
+
 void InputMethodChromeOS::OnWillChangeFocusedClient(
     TextInputClient* focused_before,
     TextInputClient* focused) {
   ConfirmCompositionText();
 
-  if (GetEngine())
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->FinishInput();
+  } else if (GetEngine()) {
     GetEngine()->FocusOut();
+  }
 }
 
 void InputMethodChromeOS::OnDidChangeFocusedClient(
@@ -356,7 +463,11 @@
   // focus and after it acquires focus again are the same.
   UpdateContextFocusState();
 
-  if (GetEngine()) {
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->StartInput(ime::mojom::EditorInfo::New(
+        GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
+        GetClientFocusReason(), GetClientShouldDoLearning()));
+  } else if (GetEngine()) {
     ui::IMEEngineHandlerInterface::InputContext context(
         GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
         GetClientFocusReason(), GetClientShouldDoLearning());
@@ -411,7 +522,13 @@
   composing_text_ = false;
   composition_changed_ = false;
 
-  if (GetEngine())
+  // This function runs asynchronously.
+  // Note: some input method engines may not support reset method, such as
+  // ibus-anthy. But as we control all input method engines by ourselves, we can
+  // make sure that all of the engines we are using support it correctly.
+  if (mojo_helper_->connected())
+    mojo_helper_->ime_engine()->CancelInput();
+  else if (GetEngine())
     GetEngine()->Reset();
 
   character_composer_.Reset();
diff --git a/ui/base/ime/chromeos/input_method_chromeos.h b/ui/base/ime/chromeos/input_method_chromeos.h
index 4d18710b..da5714f 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.h
+++ b/ui/base/ime/chromeos/input_method_chromeos.h
@@ -50,6 +50,8 @@
   InputMethodKeyboardController* GetInputMethodKeyboardController() override;
 
   // Overridden from InputMethodBase:
+  void OnFocus() override;
+  void OnBlur() override;
   void OnWillChangeFocusedClient(TextInputClient* focused_before,
                                  TextInputClient* focused) override;
   void OnDidChangeFocusedClient(TextInputClient* focused_before,
@@ -77,6 +79,7 @@
   void ResetContext();
 
  private:
+  class MojoHelper;
   class PendingKeyEvent;
   friend TestableInputMethodChromeOS;
 
@@ -201,6 +204,8 @@
   // This is used in CommitText/UpdateCompositionText/etc.
   bool handling_key_event_;
 
+  std::unique_ptr<MojoHelper> mojo_helper_;
+
   // Used for making callbacks.
   base::WeakPtrFactory<InputMethodChromeOS> weak_ptr_factory_;
 
diff --git a/ui/base/ime/chromeos/input_method_chromeos_unittest.cc b/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
index b9f3b2f..f456a48 100644
--- a/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding.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"
@@ -26,6 +27,7 @@
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/ime_engine_handler_interface.h"
 #include "ui/base/ime/input_method_delegate.h"
+#include "ui/base/ime/mojo/ime.mojom.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
@@ -232,6 +234,53 @@
   DISALLOW_COPY_AND_ASSIGN(CachingInputMethodDelegate);
 };
 
+class MojoInputMethodDelegate : public ui::internal::InputMethodDelegate,
+                                public ime::mojom::ImeEngine {
+ public:
+  MojoInputMethodDelegate() : engine_binding_(this) {}
+  ~MojoInputMethodDelegate() override = default;
+
+  ime::mojom::ImeEngineClientProxy* engine_client() const {
+    return engine_client_.get();
+  }
+
+  void FlushForTesting() { engine_client_.FlushForTesting(); }
+
+ private:
+  // Overridden from ui::internal::InputMethodDelegate:
+  ui::EventDispatchDetails DispatchKeyEventPostIME(
+      ui::KeyEvent* event,
+      DispatchKeyEventPostIMECallback callback) override {
+    event->StopPropagation();
+    RunDispatchKeyEventPostIMECallback(event, std::move(callback));
+    return ui::EventDispatchDetails();
+  }
+  bool ConnectToImeEngine(ime::mojom::ImeEngineRequest engine_request,
+                          ime::mojom::ImeEngineClientPtr client) override {
+    engine_binding_.Bind(std::move(engine_request));
+    engine_client_ = std::move(client);
+    return true;
+  }
+
+  // ime::mojom::ImeEngine:
+  void StartInput(ime::mojom::EditorInfoPtr info) override {}
+  void FinishInput() override {}
+  void CancelInput() override {}
+  void ProcessKeyEvent(std::unique_ptr<ui::Event> key_event,
+                       ProcessKeyEventCallback callback) override {}
+  void UpdateSurroundingInfo(const std::string& text,
+                             int32_t cursor,
+                             int32_t anchor,
+                             int32_t offset) override {}
+  void UpdateCompositionBounds(const std::vector<gfx::Rect>& bounds) override {}
+
+  mojo::Binding<ime::mojom::ImeEngine> engine_binding_;
+
+  ime::mojom::ImeEngineClientPtr engine_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoInputMethodDelegate);
+};
+
 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
                                 public testing::Test,
                                 public DummyTextInputClient {
@@ -921,6 +970,15 @@
   IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
 }
 
+TEST_F(InputMethodChromeOSTest, MojoInteractions) {
+  MojoInputMethodDelegate delegate;
+  TestableInputMethodChromeOS im(&delegate);
+  im.OnFocus();
+  delegate.engine_client()->CommitText("test");
+  delegate.FlushForTesting();
+  EXPECT_EQ("test", im.text_committed());
+}
+
 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
  public:
   InputMethodChromeOSKeyEventTest() {}
diff --git a/ui/base/ime/mojo/BUILD.gn b/ui/base/ime/mojo/BUILD.gn
index bdb71a2..4f127d4a 100644
--- a/ui/base/ime/mojo/BUILD.gn
+++ b/ui/base/ime/mojo/BUILD.gn
@@ -6,6 +6,7 @@
 
 mojom("mojo") {
   sources = [
+    "ime.mojom",
     "ime_types.mojom",
   ]
 
diff --git a/ui/base/ime/mojo/ime.mojom b/ui/base/ime/mojo/ime.mojom
new file mode 100644
index 0000000..d28cbf8d
--- /dev/null
+++ b/ui/base/ime/mojo/ime.mojom
@@ -0,0 +1,90 @@
+// 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.
+
+module ime.mojom;
+
+import "ui/base/ime/mojo/ime_types.mojom";
+import "ui/events/mojo/event.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
+
+// The data of the text input field for the IME.
+// It is passed from the client to the IME through StartInput method.
+struct EditorInfo {
+  ui.mojom.TextInputType type;
+  ui.mojom.TextInputMode mode;
+  int32 flags;
+  ui.mojom.FocusReason focus_reason;
+  bool should_do_learning;
+};
+
+// Represents the IME (a.k.a. input-method engine).
+// The client uses this interface to communicate with the IME.
+interface ImeEngine {
+  // This method is called when the app starts to receive text (e.g. an input
+  // field is focused) and it is ready for this ImeEngine to process received
+  // events and send result text back to the app.
+  StartInput(EditorInfo info);
+
+  // This method is called when the app stops to receive text (e.g. the focused
+  // input field lost the focus). The ImeEngine usually handles this to reset
+  // its internal states.
+  FinishInput();
+
+  // This method is called when the app wants to cancel the ongoing composition.
+  CancelInput();
+
+  // Dispatches a key event to this ImeEngine, which will respond with a boolean
+  // value of true means the key event is handled and false unhandled.
+  // If the key event is handled, the app should NOT apply its default behaviors
+  // (e.g. shortcuts, generate text, etc.).
+  ProcessKeyEvent(ui.mojom.Event key_event) => (bool handled);
+
+  // Called when a new surrounding text is set by the app.
+  // The |text| is the surrounding text and |cursor| is 0 based index of cursor
+  // position in |text|. If there is selection range, |anchor| represents
+  // opposite index from |cursor|. Otherwise |anchor| is equal to |cursor|.
+  // If not all surrounding text is given |offset| indicates the starting
+  // offset of |text|.
+  UpdateSurroundingInfo(string text, int32 cursor, int32 anchor, int32 offset);
+
+  // Called when the composition bounds in screen changes.
+  // The composition bounds can be changed when ImeEngine changes composition or
+  // the text field's coordinates is changed by the app.
+  UpdateCompositionBounds(array<gfx.mojom.Rect> bounds);
+};
+
+// Used by |ImeEngine| to communicate state back to the client.
+// The app should generate the corresponding results to the input field:
+//  - immediately if didn't dispatch a key event to IME;
+//    (e.g. by IME's on-screen keyboard)
+//  - later after the IME responds the |ProcessKeyEvent| with the result;
+interface ImeEngineClient {
+  // Called when the IME wants to insert the |text| to the input field.
+  CommitText(string text);
+
+  // Called when the IME wants to generate/update the composition text to the
+  // input field.
+  UpdateCompositionText(ui.mojom.CompositionText composition_text,
+                        uint32 cursor_pos,
+                        bool visible);
+
+  // Called when the IME wants to remove a piece of text in the input field.
+  DeleteSurroundingText(int32 offset, uint32 length);
+
+  // Called when the IME wants to silumate a physical key event to the app.
+  // Usually this is for on-screen keyboard support (e.g. simulate Enter key).
+  SendKeyEvent(ui.mojom.Event key_event);
+
+  // Called when the ImeEngine is deactivated and this client should reconnect
+  // for the new active ImeEngine.
+  Reconnect();
+};
+
+// Implemented by the IME.
+// An IME should implement both ImeEngine and ImeEngineFactory interfaces.
+// The |ImeEngineFactoryRegistry| calls |CreateEngine| to make the ImeEngine
+// and ImeEngineClient can hold each other.
+interface ImeEngineFactory {
+  CreateEngine(ImeEngine& engine_request, ImeEngineClient client);
+};
diff --git a/ui/events/devices/BUILD.gn b/ui/events/devices/BUILD.gn
index ed92538..d9d2110 100644
--- a/ui/events/devices/BUILD.gn
+++ b/ui/events/devices/BUILD.gn
@@ -17,6 +17,8 @@
     "device_util_linux.cc",
     "device_util_linux.h",
     "events_devices_export.h",
+    "gamepad_device.cc",
+    "gamepad_device.h",
     "input_device.cc",
     "input_device.h",
     "input_device_event_observer.h",
diff --git a/ui/events/devices/gamepad_device.cc b/ui/events/devices/gamepad_device.cc
new file mode 100644
index 0000000..737649d
--- /dev/null
+++ b/ui/events/devices/gamepad_device.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 "ui/events/devices/gamepad_device.h"
+
+namespace ui {
+
+GamepadDevice::GamepadDevice(const InputDevice& input_device,
+                             std::vector<GamepadDevice::Axis>&& axes)
+    : InputDevice(input_device), axes(std::move(axes)) {}
+
+GamepadDevice::GamepadDevice(const GamepadDevice& other) = default;
+
+GamepadDevice::~GamepadDevice() = default;
+
+}  // namespace ui
diff --git a/ui/events/devices/gamepad_device.h b/ui/events/devices/gamepad_device.h
new file mode 100644
index 0000000..2566ab7
--- /dev/null
+++ b/ui/events/devices/gamepad_device.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_DEVICES_GAMEPAD_DEVICE_H_
+#define UI_EVENTS_DEVICES_GAMEPAD_DEVICE_H_
+
+#include <vector>
+
+#include "ui/events/devices/events_devices_export.h"
+#include "ui/events/devices/input_device.h"
+
+namespace ui {
+
+// Represents a gamepad device state.
+struct EVENTS_DEVICES_EXPORT GamepadDevice : public InputDevice {
+  // Represents an axis of a gamepad e.g. an analog thumb stick.
+  struct Axis {
+    // See input_absinfo for the definition of these variables.
+    int32_t min_value = 0;
+    int32_t max_value = 0;
+    int32_t flat = 0;
+    int32_t fuzz = 0;
+    int32_t resolution = 0;
+  };
+
+  GamepadDevice(const InputDevice& input_device, std::vector<Axis>&& axes);
+  GamepadDevice(const GamepadDevice& other);
+  ~GamepadDevice() override;
+
+  // Axes the gamepad has e.g. analog thumb sticks.
+  std::vector<Axis> axes;
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_DEVICES_GAMEPAD_DEVICE_H_
diff --git a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
index 197be1d3..f3b466c 100644
--- a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
+++ b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/time/time.h"
+#include "ui/events/devices/gamepad_device.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/touchscreen_device.h"
 #include "ui/events/event.h"
@@ -182,7 +183,7 @@
   virtual void DispatchDeviceListsComplete() = 0;
   virtual void DispatchStylusStateChanged(StylusState stylus_state) = 0;
   virtual void DispatchGamepadDevicesUpdated(
-      const std::vector<InputDevice>& devices) = 0;
+      const std::vector<GamepadDevice>& devices) = 0;
   virtual void DispatchUncategorizedDevicesUpdated(
       const std::vector<InputDevice>& devices) = 0;
 };
diff --git a/ui/events/ozone/evdev/event_converter_evdev.cc b/ui/events/ozone/evdev/event_converter_evdev.cc
index 46807e7..5684f41 100644
--- a/ui/events/ozone/evdev/event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/event_converter_evdev.cc
@@ -124,6 +124,12 @@
   return gfx::Size();
 }
 
+std::vector<ui::GamepadDevice::Axis> EventConverterEvdev::GetGamepadAxes()
+    const {
+  NOTREACHED();
+  return std::vector<ui::GamepadDevice::Axis>();
+}
+
 int EventConverterEvdev::GetTouchPoints() const {
   NOTREACHED();
   return 0;
diff --git a/ui/events/ozone/evdev/event_converter_evdev.h b/ui/events/ozone/evdev/event_converter_evdev.h
index 65650e9..a91807b6 100644
--- a/ui/events/ozone/evdev/event_converter_evdev.h
+++ b/ui/events/ozone/evdev/event_converter_evdev.h
@@ -8,11 +8,13 @@
 #include <stdint.h>
 
 #include <set>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/message_loop/message_pump_libevent.h"
+#include "ui/events/devices/gamepad_device.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/ozone/evdev/event_dispatch_callback.h"
 #include "ui/events/ozone/evdev/events_ozone_evdev_export.h"
@@ -98,6 +100,10 @@
   // called unless HasTouchscreen() returns true
   virtual int GetTouchPoints() const;
 
+  // Returns information for all axes if the converter is used for a gamepad
+  // device.
+  virtual std::vector<ui::GamepadDevice::Axis> GetGamepadAxes() const;
+
   // Sets which keyboard keys should be processed. If |enable_filter| is
   // false, all keys are allowed and |allowed_keys| is ignored.
   virtual void SetKeyFilter(bool enable_filter,
diff --git a/ui/events/ozone/evdev/event_converter_test_util.cc b/ui/events/ozone/evdev/event_converter_test_util.cc
index 57b14c7..a363712 100644
--- a/ui/events/ozone/evdev/event_converter_test_util.cc
+++ b/ui/events/ozone/evdev/event_converter_test_util.cc
@@ -94,7 +94,7 @@
   }
 
   void DispatchGamepadDevicesUpdated(
-      const std::vector<InputDevice>& devices) override {
+      const std::vector<GamepadDevice>& devices) override {
     event_factory_evdev_->DispatchGamepadDevicesUpdated(devices);
   }
 
diff --git a/ui/events/ozone/evdev/event_factory_evdev.cc b/ui/events/ozone/evdev/event_factory_evdev.cc
index 067f5662..62b32c3 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.cc
+++ b/ui/events/ozone/evdev/event_factory_evdev.cc
@@ -133,7 +133,7 @@
   }
 
   void DispatchGamepadDevicesUpdated(
-      const std::vector<InputDevice>& devices) override {
+      const std::vector<GamepadDevice>& devices) override {
     ui_thread_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&EventFactoryEvdev::DispatchGamepadDevicesUpdated,
@@ -426,7 +426,7 @@
 }
 
 void EventFactoryEvdev::DispatchGamepadDevicesUpdated(
-    const std::vector<InputDevice>& devices) {
+    const std::vector<GamepadDevice>& devices) {
   TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchGamepadDevicesUpdated");
   gamepad_provider_->DispatchGamepadDevicesUpdated(devices);
 }
diff --git a/ui/events/ozone/evdev/event_factory_evdev.h b/ui/events/ozone/evdev/event_factory_evdev.h
index ce77fb1..78eb09610 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.h
+++ b/ui/events/ozone/evdev/event_factory_evdev.h
@@ -89,7 +89,7 @@
   // Gamepad event and gamepad device event. These events are dispatched to
   // GamepadObserver through GamepadProviderOzone.
   void DispatchGamepadEvent(const GamepadEvent& event);
-  void DispatchGamepadDevicesUpdated(const std::vector<InputDevice>& devices);
+  void DispatchGamepadDevicesUpdated(const std::vector<GamepadDevice>& devices);
 
  protected:
   // DeviceEventObserver overrides:
diff --git a/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
index 1f089fb..5f6c5fba 100644
--- a/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
@@ -130,6 +130,19 @@
       axes_[code] = Axis(abs_info, mapped_type, mapped_code);
     }
   }
+
+  for (int code = 0; code < ABS_CNT; ++code) {
+    abs_info = devinfo.GetAbsInfoByCode(code);
+    if (devinfo.HasAbsEvent(code)) {
+      ui::GamepadDevice::Axis axis;
+      axis.min_value = abs_info.minimum;
+      axis.max_value = abs_info.maximum;
+      axis.flat = abs_info.flat;
+      axis.fuzz = abs_info.fuzz;
+      axis.resolution = abs_info.resolution;
+      raw_axes_.push_back(axis);
+    }
+  }
 }
 
 GamepadEventConverterEvdev::~GamepadEventConverterEvdev() {
@@ -166,6 +179,11 @@
   ResetGamepad();
 }
 
+std::vector<ui::GamepadDevice::Axis>
+GamepadEventConverterEvdev::GetGamepadAxes() const {
+  return raw_axes_;
+}
+
 void GamepadEventConverterEvdev::ProcessEvent(const input_event& evdev_ev) {
   base::TimeTicks timestamp = TimeTicksFromInputEvent(evdev_ev);
   // We may have missed Gamepad releases. Reset everything.
diff --git a/ui/events/ozone/evdev/gamepad_event_converter_evdev.h b/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
index 3a362a7..f3902dce 100644
--- a/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
+++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
@@ -39,6 +39,7 @@
   void OnFileCanReadWithoutBlocking(int fd) override;
   bool HasGamepad() const override;
   void OnDisabled() override;
+  std::vector<ui::GamepadDevice::Axis> GetGamepadAxes() const override;
 
   // This function processes one input_event from evdev.
   void ProcessEvent(const struct input_event& input);
@@ -103,6 +104,8 @@
 
   Axis axes_[ABS_CNT];
 
+  std::vector<ui::GamepadDevice::Axis> raw_axes_;
+
   // These values keeps the state of previous hat.
   bool last_hat_left_press_;
   bool last_hat_right_press_;
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc
index b621db9..8d9714a 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -476,10 +476,11 @@
 }
 
 void InputDeviceFactoryEvdev::NotifyGamepadDevicesUpdated() {
-  std::vector<InputDevice> gamepads;
+  std::vector<GamepadDevice> gamepads;
   for (auto it = converters_.begin(); it != converters_.end(); ++it) {
     if (it->second->HasGamepad()) {
-      gamepads.push_back(it->second->input_device());
+      gamepads.emplace_back(it->second->input_device(),
+                            it->second->GetGamepadAxes());
     }
   }
 
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
index 0104171..b9404ce 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -168,7 +168,7 @@
   void DispatchGamepadEvent(const GamepadEvent& event) override {}
 
   void DispatchGamepadDevicesUpdated(
-      const std::vector<InputDevice>& devices) override {}
+      const std::vector<GamepadDevice>& devices) override {}
 
  private:
   base::RepeatingCallback<void(const GenericEventParams& params)> callback_;
diff --git a/ui/events/ozone/gamepad/gamepad_provider_ozone.cc b/ui/events/ozone/gamepad/gamepad_provider_ozone.cc
index 4cc2db9..14980a3 100644
--- a/ui/events/ozone/gamepad/gamepad_provider_ozone.cc
+++ b/ui/events/ozone/gamepad/gamepad_provider_ozone.cc
@@ -22,7 +22,7 @@
 }
 
 void GamepadProviderOzone::DispatchGamepadDevicesUpdated(
-    std::vector<InputDevice> gamepad_devices) {
+    std::vector<GamepadDevice> gamepad_devices) {
   gamepad_devices_.swap(gamepad_devices);
   for (auto& observer : observers_) {
     observer.OnGamepadDevicesUpdated();
@@ -43,7 +43,7 @@
   observers_.RemoveObserver(observer);
 }
 
-std::vector<InputDevice> GamepadProviderOzone::GetGamepadDevices() {
+std::vector<GamepadDevice> GamepadProviderOzone::GetGamepadDevices() {
   return gamepad_devices_;
 }
 }  // namespace ui
diff --git a/ui/events/ozone/gamepad/gamepad_provider_ozone.h b/ui/events/ozone/gamepad/gamepad_provider_ozone.h
index a52a8c8d8..6e502970 100644
--- a/ui/events/ozone/gamepad/gamepad_provider_ozone.h
+++ b/ui/events/ozone/gamepad/gamepad_provider_ozone.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
-#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/gamepad_device.h"
 #include "ui/events/ozone/evdev/events_ozone_evdev_export.h"
 #include "ui/events/ozone/gamepad/gamepad_observer.h"
 
@@ -28,7 +28,8 @@
 
   // Dispatch GamepadDevicesUpdate event when gamepad device is connected or
   // disconnected. This function must be called on UI thread.
-  void DispatchGamepadDevicesUpdated(std::vector<InputDevice> gamepad_devices);
+  void DispatchGamepadDevicesUpdated(
+      std::vector<GamepadDevice> gamepad_devices);
 
   // Dispatch button event when gamepad event is seen.
   // Code is the index of gamepad button or gamepad axis defined in W3C standard
@@ -46,7 +47,7 @@
 
   // Get the list of currently connected gamepad devices. This function must be
   // called on UI thread.
-  std::vector<InputDevice> GetGamepadDevices();
+  std::vector<GamepadDevice> GetGamepadDevices();
 
  private:
   GamepadProviderOzone();
@@ -61,7 +62,7 @@
   base::ObserverList<GamepadObserver>::Unchecked observers_;
 
   // List of current connected gamepad events.
-  std::vector<InputDevice> gamepad_devices_;
+  std::vector<GamepadDevice> gamepad_devices_;
 
   DISALLOW_COPY_AND_ASSIGN(GamepadProviderOzone);
 };
diff --git a/ui/file_manager/file_manager/foreground/js/ui/commandbutton.js b/ui/file_manager/file_manager/foreground/js/ui/commandbutton.js
index 3d83b1e1..6fed6ef 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/commandbutton.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/commandbutton.js
@@ -8,143 +8,153 @@
 
 /**
  * Creates a new button element.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
  * @extends {HTMLButtonElement}
  */
-const CommandButton = cr.ui.define('button');
-
-/** @override */
-CommandButton.prototype.__proto__ = HTMLButtonElement.prototype;
-
-/**
- * Associated command.
- * @type {cr.ui.Command}
- * @private
- */
-CommandButton.prototype.command_ = null;
-
-/**
- * Initializes the menu item.
- */
-CommandButton.prototype.decorate = function() {
-  let commandId;
-  if ((commandId = this.getAttribute('command'))) {
-    this.setCommand(commandId);
+class CommandButton {
+  constructor() {
+    /**
+     * Associated command.
+     * @private {cr.ui.Command}
+     */
+    this.command_ = null;
   }
 
-  this.addEventListener('click', this.handleClick_.bind(this));
-};
-
-/**
- * Returns associated command.
- * @return {cr.ui.Command} associated command.
- */
-CommandButton.prototype.getCommand = function() {
-  return this.command_;
-};
-
-/**
- * Associates command with this button.
- * @param {string|cr.ui.Command} command Command id, or command object to
- * associate with this button.
- */
-CommandButton.prototype.setCommand = function(command) {
-  if (this.command_) {
-    this.command_.removeEventListener(
-        'labelChange',
-        /** @type {EventListener} */ (this));
-    this.command_.removeEventListener(
-        'disabledChange',
-        /** @type {EventListener} */ (this));
-    this.command_.removeEventListener(
-        'hiddenChange',
-        /** @type {EventListener} */ (this));
+  /**
+   * Decorates the given element as a progress item.
+   * @param {!Element} element Item to be decorated.
+   * @return {!CommandButton} Decorated item.
+   */
+  static decorate(element) {
+    element.__proto__ = CommandButton.prototype;
+    element = /** @type {!CommandButton} */ (element);
+    element.decorate();
+    return element;
   }
 
-  if (typeof command == 'string') {
-    assert(command[0] == '#');
-    command = /** @type {!cr.ui.Command} */
-        (this.ownerDocument.getElementById(command.slice(1)));
-    cr.ui.decorate(command, cr.ui.Command);
-  }
-
-  this.command_ = command;
-  if (command) {
-    if (command.id) {
-      this.setAttribute('command', '#' + command.id);
+  /**
+   * Initializes the menu item.
+   */
+  decorate() {
+    let commandId;
+    if ((commandId = this.getAttribute('command'))) {
+      this.setCommand(commandId);
     }
 
-    this.setLabel(command.label);
-    this.disabled = command.disabled;
-    this.hidden = command.hidden;
-
-    this.command_.addEventListener(
-        'labelChange',
-        /** @type {EventListener} */ (this));
-    this.command_.addEventListener(
-        'disabledChange',
-        /** @type {EventListener} */ (this));
-    this.command_.addEventListener(
-        'hiddenChange',
-        /** @type {EventListener} */ (this));
+    this.addEventListener('click', this.handleClick_.bind(this));
   }
-};
 
-/**
- * Returns button label
- * @return {string} Button label.
- */
-CommandButton.prototype.getLabel = function() {
-  return this.command_ ? this.command_.label : '';
-};
-
-/**
- * Sets button label.
- * @param {string} label New button label.
- */
-CommandButton.prototype.setLabel = function(label) {
-  // Swap the textContent with current label only when this button doesn't have
-  // any elements as children.
-  //
-  // TODO(fukino): If a user customize the button content, it becomes the
-  // user's responsibility to update the content on command label's change.
-  // Updating the label in customized button content should be done
-  // automatically by specifying an element which should be synced with the
-  // command label using class name or polymer's template binding.
-  if (!this.firstElementChild) {
-    this.textContent = label;
+  /**
+   * Returns associated command.
+   * @return {cr.ui.Command} associated command.
+   */
+  getCommand() {
+    return this.command_;
   }
-};
 
-/**
- * Handles click event and dispatches associated command.
- * @param {Event} e The mouseup event object.
- * @private
- */
-CommandButton.prototype.handleClick_ = function(e) {
-  if (!this.disabled && this.command_) {
-    this.command_.execute(this);
-  }
-};
+  /**
+   * Associates command with this button.
+   * @param {string|cr.ui.Command} command Command id, or command object to
+   * associate with this button.
+   */
+  setCommand(command) {
+    if (this.command_) {
+      this.command_.removeEventListener(
+          'labelChange',
+          /** @type {EventListener} */ (this));
+      this.command_.removeEventListener(
+          'disabledChange',
+          /** @type {EventListener} */ (this));
+      this.command_.removeEventListener(
+          'hiddenChange',
+          /** @type {EventListener} */ (this));
+    }
 
-/**
- * Handles changes to the associated command.
- * @param {Event} e The event object.
- */
-CommandButton.prototype.handleEvent = function(e) {
-  switch (e.type) {
-    case 'disabledChange':
-      this.disabled = this.command_.disabled;
-      break;
-    case 'hiddenChange':
-      this.hidden = this.command_.hidden;
-      break;
-    case 'labelChange':
-      this.setLabel(this.command_.label);
-      break;
+    if (typeof command == 'string') {
+      assert(command[0] == '#');
+      command = /** @type {!cr.ui.Command} */
+          (this.ownerDocument.getElementById(command.slice(1)));
+      cr.ui.decorate(command, cr.ui.Command);
+    }
+
+    this.command_ = command;
+    if (command) {
+      if (command.id) {
+        this.setAttribute('command', '#' + command.id);
+      }
+
+      this.setLabel(command.label);
+      this.disabled = command.disabled;
+      this.hidden = command.hidden;
+
+      this.command_.addEventListener(
+          'labelChange',
+          /** @type {EventListener} */ (this));
+      this.command_.addEventListener(
+          'disabledChange',
+          /** @type {EventListener} */ (this));
+      this.command_.addEventListener(
+          'hiddenChange',
+          /** @type {EventListener} */ (this));
+    }
   }
-};
+
+  /**
+   * Returns button label
+   * @return {string} Button label.
+   */
+  getLabel() {
+    return this.command_ ? this.command_.label : '';
+  }
+
+  /**
+   * Sets button label.
+   * @param {string} label New button label.
+   */
+  setLabel(label) {
+    // Swap the textContent with current label only when this button doesn't
+    // have any elements as children.
+    //
+    // TODO(fukino): If a user customize the button content, it becomes the
+    // user's responsibility to update the content on command label's change.
+    // Updating the label in customized button content should be done
+    // automatically by specifying an element which should be synced with the
+    // command label using class name or polymer's template binding.
+    if (!this.firstElementChild) {
+      this.textContent = label;
+    }
+  }
+
+  /**
+   * Handles click event and dispatches associated command.
+   * @param {Event} e The mouseup event object.
+   * @private
+   */
+  handleClick_(e) {
+    if (!this.disabled && this.command_) {
+      this.command_.execute(this);
+    }
+  }
+
+  /**
+   * Handles changes to the associated command.
+   * @param {Event} e The event object.
+   */
+  handleEvent(e) {
+    switch (e.type) {
+      case 'disabledChange':
+        this.disabled = this.command_.disabled;
+        break;
+      case 'hiddenChange':
+        this.hidden = this.command_.hidden;
+        break;
+      case 'labelChange':
+        this.setLabel(this.command_.label);
+        break;
+    }
+  }
+}
+
+CommandButton.prototype.__proto__ = HTMLButtonElement.prototype;
 
 /**
  * Whether the button is disabled or not.
diff --git a/ui/file_manager/image_loader/piex/Makefile b/ui/file_manager/image_loader/piex/Makefile
index 21062992..8219ce3 100644
--- a/ui/file_manager/image_loader/piex/Makefile
+++ b/ui/file_manager/image_loader/piex/Makefile
@@ -16,7 +16,7 @@
        $(PIEX)/src/binary_parse/range_checked_byte_ptr.cc \
        $(PIEX)/src/binary_parse/cached_paged_byte_array.cc
 
-INCS = -I $(PIEX) -I .
+INCS = -I $(PIEX)
 WASM = -s WASM=1 -fno-exceptions -Wall -fsanitize=cfi -flto -fvisibility=hidden
 WOPT = -Os --llvm-opts 3 \
   -s STRICT=1 \
diff --git a/ui/gl/vsync_thread_win.cc b/ui/gl/vsync_thread_win.cc
index cfb1d2d..64a917b 100644
--- a/ui/gl/vsync_thread_win.cc
+++ b/ui/gl/vsync_thread_win.cc
@@ -80,11 +80,12 @@
     window_output_ = DXGIOutputFromMonitor(monitor, d3d11_device_);
   }
 
-  MONITORINFOEX monitor_info;
-  monitor_info.cbSize = sizeof(MONITORINFOEX);
   base::TimeDelta interval = base::TimeDelta::FromSecondsD(1.0 / 60);
-  if (GetMonitorInfo(monitor, &monitor_info)) {
-    DEVMODE display_info;
+
+  MONITORINFOEX monitor_info = {};
+  monitor_info.cbSize = sizeof(MONITORINFOEX);
+  if (monitor && GetMonitorInfo(monitor, &monitor_info)) {
+    DEVMODE display_info = {};
     display_info.dmSize = sizeof(DEVMODE);
     display_info.dmDriverExtra = 0;
     if (EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS,
@@ -95,11 +96,23 @@
     }
   }
 
+  base::TimeTicks wait_for_vblank_start_time = base::TimeTicks::Now();
   bool wait_for_vblank_succeeded =
       window_output_ && SUCCEEDED(window_output_->WaitForVBlank());
 
-  if (!wait_for_vblank_succeeded)
+  // WaitForVBlank returns very early instead of waiting until vblank when the
+  // monitor goes to sleep.  We use 1ms as a threshold for the duration of
+  // WaitForVBlank and fallback to Sleep() if it returns before that.  This
+  // could happen during normal operation for the first call after the vsync
+  // callback is enabled, but it shouldn't happen often.
+  const auto kVBlankIntervalThreshold = base::TimeDelta::FromMilliseconds(1);
+  base::TimeDelta wait_for_vblank_elapsed_time =
+      base::TimeTicks::Now() - wait_for_vblank_start_time;
+
+  if (!wait_for_vblank_succeeded ||
+      wait_for_vblank_elapsed_time < kVBlankIntervalThreshold) {
     Sleep(static_cast<DWORD>(interval.InMillisecondsRoundedUp()));
+  }
 
   base::AutoLock auto_lock(lock_);
   DCHECK(started_);
diff --git a/ui/platform_window/mojo/BUILD.gn b/ui/platform_window/mojo/BUILD.gn
new file mode 100644
index 0000000..b42cf4f
--- /dev/null
+++ b/ui/platform_window/mojo/BUILD.gn
@@ -0,0 +1,39 @@
+# 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/jumbo.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# This target does NOT depend on skia. One can depend on this target to avoid
+# picking up a dependency on skia.
+jumbo_component("mojo") {
+  output_name = "mojo_ime_lib"
+
+  public_deps = [
+    "//ui/base/ime",
+  ]
+  deps = [
+    ":interfaces",
+    "//base",
+    "//ui/platform_window",
+  ]
+
+  defines = [ "MOJO_IME_IMPLEMENTATION" ]
+
+  sources = [
+    "ime_type_converters.cc",
+    "ime_type_converters.h",
+    "mojo_ime_export.h",
+  ]
+}
+
+mojom("interfaces") {
+  sources = [
+    "text_input_state.mojom",
+  ]
+
+  public_deps = [
+    "//ui/base/ime/mojo",
+  ]
+}
diff --git a/ui/platform_window/mojo/DEPS b/ui/platform_window/mojo/DEPS
new file mode 100644
index 0000000..1d56d19d
--- /dev/null
+++ b/ui/platform_window/mojo/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+ui/base/ime/text_input_flags.h",
+  "+ui/base/ime/text_input_type.h",
+  "+ui/platform_window/text_input_state.h",
+]
diff --git a/ui/platform_window/mojo/OWNERS b/ui/platform_window/mojo/OWNERS
new file mode 100644
index 0000000..5d54957
--- /dev/null
+++ b/ui/platform_window/mojo/OWNERS
@@ -0,0 +1,5 @@
+per-file *_type_converter*.*=set noparent
+per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ui/platform_window/mojo/ime_type_converters.cc b/ui/platform_window/mojo/ime_type_converters.cc
new file mode 100644
index 0000000..9435045
--- /dev/null
+++ b/ui/platform_window/mojo/ime_type_converters.cc
@@ -0,0 +1,63 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/platform_window/mojo/ime_type_converters.h"
+
+#include <stdint.h>
+
+#include "base/macros.h"
+
+namespace mojo {
+
+#define TEXT_INPUT_TYPE_ASSERT(NAME)                                    \
+  static_assert(static_cast<int32_t>(ui::mojom::TextInputType::NAME) == \
+                    static_cast<int32_t>(ui::TEXT_INPUT_TYPE_##NAME),   \
+                "TEXT_INPUT_TYPE must match")
+TEXT_INPUT_TYPE_ASSERT(NONE);
+TEXT_INPUT_TYPE_ASSERT(TEXT);
+TEXT_INPUT_TYPE_ASSERT(PASSWORD);
+TEXT_INPUT_TYPE_ASSERT(SEARCH);
+TEXT_INPUT_TYPE_ASSERT(EMAIL);
+TEXT_INPUT_TYPE_ASSERT(NUMBER);
+TEXT_INPUT_TYPE_ASSERT(TELEPHONE);
+TEXT_INPUT_TYPE_ASSERT(URL);
+TEXT_INPUT_TYPE_ASSERT(DATE);
+TEXT_INPUT_TYPE_ASSERT(DATE_TIME);
+TEXT_INPUT_TYPE_ASSERT(DATE_TIME_LOCAL);
+TEXT_INPUT_TYPE_ASSERT(MONTH);
+TEXT_INPUT_TYPE_ASSERT(TIME);
+TEXT_INPUT_TYPE_ASSERT(WEEK);
+TEXT_INPUT_TYPE_ASSERT(TEXT_AREA);
+TEXT_INPUT_TYPE_ASSERT(CONTENT_EDITABLE);
+TEXT_INPUT_TYPE_ASSERT(DATE_TIME_FIELD);
+TEXT_INPUT_TYPE_ASSERT(MAX);
+
+#define TEXT_INPUT_FLAG_ASSERT(NAME)                                    \
+  static_assert(static_cast<int32_t>(ui::mojom::TextInputFlag::NAME) == \
+                    static_cast<int32_t>(ui::TEXT_INPUT_FLAG_##NAME),   \
+                "TEXT_INPUT_FLAG must match")
+TEXT_INPUT_FLAG_ASSERT(NONE);
+TEXT_INPUT_FLAG_ASSERT(AUTOCOMPLETE_ON);
+TEXT_INPUT_FLAG_ASSERT(AUTOCOMPLETE_OFF);
+TEXT_INPUT_FLAG_ASSERT(AUTOCORRECT_ON);
+TEXT_INPUT_FLAG_ASSERT(AUTOCORRECT_OFF);
+TEXT_INPUT_FLAG_ASSERT(SPELLCHECK_ON);
+TEXT_INPUT_FLAG_ASSERT(SPELLCHECK_OFF);
+TEXT_INPUT_FLAG_ASSERT(AUTOCAPITALIZE_NONE);
+TEXT_INPUT_FLAG_ASSERT(AUTOCAPITALIZE_CHARACTERS);
+TEXT_INPUT_FLAG_ASSERT(AUTOCAPITALIZE_WORDS);
+TEXT_INPUT_FLAG_ASSERT(AUTOCAPITALIZE_SENTENCES);
+
+// static
+ui::TextInputState
+TypeConverter<ui::TextInputState, ui::mojom::TextInputStatePtr>::Convert(
+    const ui::mojom::TextInputStatePtr& input) {
+  return ui::TextInputState(
+      ConvertTo<ui::TextInputType>(input->type), input->flags,
+      input->text.has_value() ? input->text.value() : std::string(),
+      input->selection_start, input->selection_end, input->composition_start,
+      input->composition_end, input->can_compose_inline);
+}
+
+}  // namespace mojo
diff --git a/ui/platform_window/mojo/ime_type_converters.h b/ui/platform_window/mojo/ime_type_converters.h
new file mode 100644
index 0000000..a55125d
--- /dev/null
+++ b/ui/platform_window/mojo/ime_type_converters.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_PLATFORM_WINDOW_MOJO_IME_TYPE_CONVERTERS_H_
+#define UI_PLATFORM_WINDOW_MOJO_IME_TYPE_CONVERTERS_H_
+
+#include "ui/base/ime/ime_text_span.h"
+#include "ui/platform_window/mojo/mojo_ime_export.h"
+#include "ui/platform_window/mojo/text_input_state.mojom.h"
+#include "ui/platform_window/text_input_state.h"
+
+namespace mojo {
+
+template <>
+struct MOJO_IME_EXPORT
+    TypeConverter<ui::TextInputState, ui::mojom::TextInputStatePtr> {
+  static ui::TextInputState Convert(const ui::mojom::TextInputStatePtr& input);
+};
+
+}  // namespace mojo
+
+#endif  // UI_PLATFORM_WINDOW_MOJO_IME_TYPE_CONVERTERS_H_
diff --git a/ui/platform_window/mojo/mojo_ime_export.h b/ui/platform_window/mojo/mojo_ime_export.h
new file mode 100644
index 0000000..e616f25
--- /dev/null
+++ b/ui/platform_window/mojo/mojo_ime_export.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_PLATFORM_WINDOW_MOJO_MOJO_IME_EXPORT_H_
+#define UI_PLATFORM_WINDOW_MOJO_MOJO_IME_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_IME_IMPLEMENTATION)
+#define MOJO_IME_EXPORT __declspec(dllexport)
+#else
+#define MOJO_IME_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(MOJO_IME_IMPLEMENTATION)
+#define MOJO_IME_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_IME_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+#else  // !defined(COMPONENT_BUILD)
+#define MOJO_IME_EXPORT
+#endif
+
+#endif  // UI_PLATFORM_WINDOW_MOJO_MOJO_IME_EXPORT_H_
diff --git a/ui/platform_window/mojo/text_input_state.mojom b/ui/platform_window/mojo/text_input_state.mojom
new file mode 100644
index 0000000..21767a5
--- /dev/null
+++ b/ui/platform_window/mojo/text_input_state.mojom
@@ -0,0 +1,36 @@
+// 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.
+
+module ui.mojom;
+
+import "ui/base/ime/mojo/ime_types.mojom";
+
+// Text input info which is based on blink::WebTextInputInfo.
+struct TextInputState {
+  // The type of input field.
+  TextInputType type;
+
+  // The flags of the input field (autocorrect, autocomplete, etc.).
+  int32 flags;
+
+  // The value of the input field.
+  string? text;
+
+  // The cursor position of the current selection start, or the caret position
+  // if nothing is selected.
+  int32 selection_start;
+
+  // The cursor position of the current selection end, or the caret position
+  // if nothing is selected.
+  int32 selection_end;
+
+  // The start position of the current composition, or -1 if there is none.
+  int32 composition_start;
+
+  // The end position of the current composition, or -1 if there is none.
+  int32 composition_end;
+
+  // Whether or not inline composition can be performed for the current input.
+  bool can_compose_inline;
+};
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index 242047c..f8ec5ea 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -12,7 +12,6 @@
   <template>
     <style include="cr-icons cr-hidden-style">
       :host {
-        --cr-toolbar-height: 56px;
         align-items: center;
         background-color: var(--google-blue-700);
         color: #fff;
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index a16e5d48..e512172 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -238,6 +238,7 @@
         var(--cr-separator-color);
 
     --cr-toolbar-overlay-animation-duration: 150ms;
+    --cr-toolbar-height: 56px;
 
     --cr-container-shadow-height: 6px;
     --cr-container-shadow-margin: calc(-1 * var(--cr-container-shadow-height));