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..71e78fb 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 0c45170..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 fcd6255..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 f3d35cf..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..e6d1dc8 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..6d9e539 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..1bf6032 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..fe77984 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 30af4e5..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..1d76297 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..83974b8 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..57e3d08 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 dee6fd9..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 bcfdacb..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 9b910d7..fc0e44f 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 7afc176..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 56e73c8..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 58d76f3..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..a6bda83
--- /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 f4aad67..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 1e64899..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..29601c5 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 f4f2cc9..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 a1e2ff1..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..0d48e3c 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 1eb934c..aee02a6 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..8a8a3db 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 b965072..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..dd153f3 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 86c2f98..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..eed3a5a 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 a62b732..f272765 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..1cf0b6d 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 b24389e..c47ed74 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..c05c650 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 575b612..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 a6129de..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..5627397 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 b2c3b6b..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 5c196a3..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 afef87c..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 e56464e..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 36f40dc..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..4bbfaa1 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..4c8ff9c 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 427e1cb..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..76560ad 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 da482a1..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 9cf79b7..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 5a2dfec..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 0d8bde5..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 ee84cf4..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..d2a4e63 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..c902083 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..19ddc0c 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..f237895 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 5ebc846..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 522af77..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..34cb6ff 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 37916e4..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 f93af79..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..d800466 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..933344e 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..2f36247 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 fo